Merge pull request #8147 from JamesNK/jamesnk/writestring

Add .NET 5 target and improve WriteString performance with SIMD
diff --git a/CHANGES.txt b/CHANGES.txt
index 85e6a03..6658bcd 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,9 @@
 Unreleased Changes (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript)
 
+  Protocol Compiler
+  * Optional fields for proto3 are enabled by default, and no longer require
+    the --experimental_allow_proto3_optional flag.
+
   C++
   * MessageDifferencer: fixed bug when using custom ignore with multiple
     unknown fields
@@ -28,6 +32,9 @@
     crash.
   * Constant initialize the global message instances
   * Pretty print 'max' instead of numeric values in reserved ranges.
+  * Removed remaining instances of std::is_pod, which is deprecated in C++20.
+  * Changes to reduce code size for unknown field handling by making uncommon
+    cases out of line.
 
   Java
   * Avoid possible UnsupportedOperationException when using CodedInputSteam
@@ -40,6 +47,7 @@
   * Now Proto3 Oneof fields have "has" methods for checking their presence in
     Java.
   * Annotates Java proto generated *_FIELD_NUMBER constants.
+  * Add -assumevalues to remove JvmMemoryAccessor on Android.
 
   Python
   * Provided an override for the reverse() method that will reverse the internal
diff --git a/Makefile.am b/Makefile.am
index d8b9fda..53b2594 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1397,10 +1397,6 @@
   examples/pubspec.yaml                  \
   protobuf.bzl                           \
   protobuf_deps.bzl                      \
-  python/release/wheel/build_wheel_manylinux.sh  \
-  python/release/wheel/Dockerfile                \
-  python/release/wheel/protobuf_optimized_pip.sh \
-  python/release/wheel/README.md         \
   third_party/six.BUILD                  \
   third_party/zlib.BUILD                 \
   third_party/wyhash/LICENSE             \
diff --git a/conformance/binary_json_conformance_suite.cc b/conformance/binary_json_conformance_suite.cc
index 4a0d8e8..48bfa96 100644
--- a/conformance/binary_json_conformance_suite.cc
+++ b/conformance/binary_json_conformance_suite.cc
@@ -300,7 +300,7 @@
 }
 
 string UpperCase(string str) {
-  for (int i = 0; i < str.size(); i++) {
+  for (size_t i = 0; i < str.size(); i++) {
     str[i] = toupper(str[i]);
   }
   return str;
diff --git a/conformance/conformance_test_runner.cc b/conformance/conformance_test_runner.cc
index 9f893cb..1572ac0 100644
--- a/conformance/conformance_test_runner.cc
+++ b/conformance/conformance_test_runner.cc
@@ -297,7 +297,7 @@
 
     std::vector<const char *> argv;
     argv.push_back(executable.get());
-    for (int i = 0; i < executable_args_.size(); ++i) {
+    for (size_t i = 0; i < executable_args_.size(); ++i) {
       argv.push_back(executable_args_[i].c_str());
     }
     argv.push_back(nullptr);
@@ -307,7 +307,7 @@
 }
 
 void ForkPipeRunner::CheckedWrite(int fd, const void *buf, size_t len) {
-  if (write(fd, buf, len) != len) {
+  if (static_cast<size_t>(write(fd, buf, len)) != len) {
     GOOGLE_LOG(FATAL) << current_test_name_
                << ": error writing to test program: " << strerror(errno);
   }
diff --git a/csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs b/csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs
index aceb4a6..951e856 100644
--- a/csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs
+++ b/csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs
@@ -116,7 +116,22 @@
             var other = message.Clone();

 

             Assert.AreEqual(message, other);

-            Assert.AreEqual(message.CalculateSize(), message.CalculateSize());

+            Assert.AreEqual(message.CalculateSize(), other.CalculateSize());

+        }

+

+        [Test]

+        public void TestDefaultValueRoundTrip()

+        {

+            var message = new TestAllExtensions();

+            message.SetExtension(OptionalBoolExtension, false);

+            Assert.IsFalse(message.GetExtension(OptionalBoolExtension));

+            Assert.IsTrue(message.HasExtension(OptionalBoolExtension));

+

+            var bytes = message.ToByteArray();

+            var registry = new ExtensionRegistry { OptionalBoolExtension };

+            var parsed = TestAllExtensions.Parser.WithExtensionRegistry(registry).ParseFrom(bytes);

+            Assert.IsFalse(parsed.GetExtension(OptionalBoolExtension));

+            Assert.IsTrue(parsed.HasExtension(OptionalBoolExtension));

         }

     }

 }

diff --git a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
index ebb8394..fab983d 100644
--- a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
+++ b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
@@ -35,7 +35,9 @@
 using ProtobufUnittest;
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
+using UnitTest.Issues.TestProtos;
 
 namespace Google.Protobuf.Reflection
 {
@@ -70,6 +72,24 @@
             TestFileDescriptor(converted[2], converted[1], converted[0]);
         }
 
+        [Test]
+        public void FileDescriptor_BuildFromByteStrings_WithExtensionRegistry()
+        {
+            var extension = UnittestCustomOptionsProto3Extensions.MessageOpt1;
+
+            var byteStrings = new[]
+            {
+                DescriptorReflection.Descriptor.Proto.ToByteString(),
+                UnittestCustomOptionsProto3Reflection.Descriptor.Proto.ToByteString()
+            };
+            var registry = new ExtensionRegistry { extension };
+
+            var descriptor = FileDescriptor.BuildFromByteStrings(byteStrings, registry).Last();
+            var message = descriptor.MessageTypes.Single(t => t.Name == nameof(TestMessageWithCustomOptions));
+            var extensionValue = message.GetOptions().GetExtension(extension);
+            Assert.AreEqual(-56, extensionValue);
+        }
+
         private void TestFileDescriptor(FileDescriptor file, FileDescriptor importedFile, FileDescriptor importedPublicFile)
         {
             Assert.AreEqual("unittest_proto3.proto", file.Name);
diff --git a/csharp/src/Google.Protobuf/ExtensionValue.cs b/csharp/src/Google.Protobuf/ExtensionValue.cs
index ada5d79..5257c4c 100644
--- a/csharp/src/Google.Protobuf/ExtensionValue.cs
+++ b/csharp/src/Google.Protobuf/ExtensionValue.cs
@@ -59,7 +59,7 @@
 
         public int CalculateSize()
         {
-            return codec.CalculateSizeWithTag(field);
+            return codec.CalculateUnconditionalSizeWithTag(field);
         }
 
         public IExtensionValue Clone()
diff --git a/csharp/src/Google.Protobuf/FieldCodec.cs b/csharp/src/Google.Protobuf/FieldCodec.cs
index 158739d..ee6bd6a 100644
--- a/csharp/src/Google.Protobuf/FieldCodec.cs
+++ b/csharp/src/Google.Protobuf/FieldCodec.cs
@@ -876,6 +876,12 @@
         /// </summary>
         public int CalculateSizeWithTag(T value) => IsDefault(value) ? 0 : ValueSizeCalculator(value) + tagSize;
 
+        /// <summary>
+        /// Calculates the size required to write the given value, with a tag, even
+        /// if the value is the default.
+        /// </summary>
+        internal int CalculateUnconditionalSizeWithTag(T value) => ValueSizeCalculator(value) + tagSize;
+
         private bool IsDefault(T value) => EqualityComparer.Equals(value, DefaultValue);
     }
 }
diff --git a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
index 88e4a9d..724bb3a 100644
--- a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
@@ -481,18 +481,21 @@
         /// dependencies must come before the descriptor which depends on them. (If A depends on B, and B
         /// depends on C, then the descriptors must be presented in the order C, B, A.) This is compatible
         /// with the order in which protoc provides descriptors to plugins.</param>
+        /// <param name="registry">The extension registry to use when parsing, or null if no extensions are required.</param>
         /// <returns>The file descriptors corresponding to <paramref name="descriptorData"/>.</returns>
-        public static IReadOnlyList<FileDescriptor> BuildFromByteStrings(IEnumerable<ByteString> descriptorData)
+        public static IReadOnlyList<FileDescriptor> BuildFromByteStrings(IEnumerable<ByteString> descriptorData, ExtensionRegistry registry)
         {
             ProtoPreconditions.CheckNotNull(descriptorData, nameof(descriptorData));
 
+            var parser = FileDescriptorProto.Parser.WithExtensionRegistry(registry);
+
             // TODO: See if we can build a single DescriptorPool instead of building lots of them.
             // This will all behave correctly, but it's less efficient than we'd like.
             var descriptors = new List<FileDescriptor>();
             var descriptorsByName = new Dictionary<string, FileDescriptor>();
             foreach (var data in descriptorData)
             {
-                var proto = FileDescriptorProto.Parser.ParseFrom(data);
+                var proto = parser.ParseFrom(data);
                 var dependencies = new List<FileDescriptor>();
                 foreach (var dependencyName in proto.Dependency)
                 {
@@ -519,6 +522,18 @@
         }
 
         /// <summary>
+        /// Converts the given descriptor binary data into FileDescriptor objects.
+        /// Note: reflection using the returned FileDescriptors is not currently supported.
+        /// </summary>
+        /// <param name="descriptorData">The binary file descriptor proto data. Must not be null, and any
+        /// dependencies must come before the descriptor which depends on them. (If A depends on B, and B
+        /// depends on C, then the descriptors must be presented in the order C, B, A.) This is compatible
+        /// with the order in which protoc provides descriptors to plugins.</param>
+        /// <returns>The file descriptors corresponding to <paramref name="descriptorData"/>.</returns>
+        public static IReadOnlyList<FileDescriptor> BuildFromByteStrings(IEnumerable<ByteString> descriptorData) =>
+            BuildFromByteStrings(descriptorData, null);
+
+        /// <summary>
         /// Returns a <see cref="System.String" /> that represents this instance.
         /// </summary>
         /// <returns>
diff --git a/docs/options.md b/docs/options.md
index df0c828..907fe5a 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -252,11 +252,19 @@
 1. Confluent Schema Registry
    * Website: https://github.com/confluentinc/schema-registry
    * Extensions: 1088
-   
+
 1. ScalaPB Validate
    * Website: https://scalapb.github.io/docs/validation
    * Extension: 1089
-   
+
 1. Astounding (Currently Private)
-   * Website: https://github.com/PbPipes/Astounding 
+   * Website: https://github.com/PbPipes/Astounding
    * Extension: 1090
+
+1. Protoc-gen-psql
+   * Website: https://github.com/Intrinsec/protoc-gen-psql
+   * Extension: 1091-1101
+
+1. Protoc-gen-sanitize
+   * Website: https://github.com/Intrinsec/protoc-gen-sanitize
+   * Extension: 1102-1106
diff --git a/java/core/src/main/java/com/google/protobuf/Android.java b/java/core/src/main/java/com/google/protobuf/Android.java
index cad5478..5a7ce9e 100644
--- a/java/core/src/main/java/com/google/protobuf/Android.java
+++ b/java/core/src/main/java/com/google/protobuf/Android.java
@@ -31,14 +31,21 @@
 package com.google.protobuf;
 
 final class Android {
+  private Android() {
+  }
+
+  // Set to true in lite_proguard_android.pgcfg.
+  @SuppressWarnings("ConstantField")
+  private static boolean ASSUME_ANDROID;
 
   private static final Class<?> MEMORY_CLASS = getClassForName("libcore.io.Memory");
+
   private static final boolean IS_ROBOLECTRIC =
-      getClassForName("org.robolectric.Robolectric") != null;
+      !ASSUME_ANDROID && getClassForName("org.robolectric.Robolectric") != null;
 
   /** Returns {@code true} if running on an Android device. */
   static boolean isOnAndroidDevice() {
-    return MEMORY_CLASS != null && !IS_ROBOLECTRIC;
+    return ASSUME_ANDROID || (MEMORY_CLASS != null && !IS_ROBOLECTRIC);
   }
 
   /** Returns the memory class or {@code null} if not on Android device. */
diff --git a/java/core/src/main/java/com/google/protobuf/RawMessageInfo.java b/java/core/src/main/java/com/google/protobuf/RawMessageInfo.java
index 3fbfaa1..d66f5c4 100644
--- a/java/core/src/main/java/com/google/protobuf/RawMessageInfo.java
+++ b/java/core/src/main/java/com/google/protobuf/RawMessageInfo.java
@@ -184,20 +184,20 @@
     int value;
     try {
       value = (int) info.charAt(0);
-    } catch (ArrayIndexOutOfBoundsException e) {
+    } catch (StringIndexOutOfBoundsException e) {
       // This is a fix for issues
       // that error out on a subset of phones on charAt(0) with an index out of bounds exception.
       char[] infoChars = info.toCharArray();
       info = new String(infoChars);
       try {
         value = (int) info.charAt(0);
-      } catch (ArrayIndexOutOfBoundsException e2) {
+      } catch (StringIndexOutOfBoundsException e2) {
         try {
           char[] infoChars2 = new char[info.length()];
           info.getChars(0, info.length(), infoChars2, 0);
           info = new String(infoChars2);
           value = (int) info.charAt(0);
-        } catch (ArrayIndexOutOfBoundsException e3) {
+        } catch (StringIndexOutOfBoundsException | ArrayIndexOutOfBoundsException e3) {
           throw new IllegalStateException(
               String.format(
                   "Failed parsing '%s' with charArray.length of %d", info, infoChars.length),
diff --git a/java/lite/proguard.pgcfg b/java/lite/proguard.pgcfg
index 4bb31e5..54c798c 100644
--- a/java/lite/proguard.pgcfg
+++ b/java/lite/proguard.pgcfg
@@ -1,3 +1,8 @@
+
+# Skip runtime check for isOnAndroidDevice().
+# One line to make it easy to remove with sed.
+-assumevalues class com.google.protobuf.Android { static boolean ASSUME_ANDROID return true; }
+
 -keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite {
   <fields>;
 }
diff --git a/java/pom.xml b/java/pom.xml
index 2ce55db..024536c 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -93,12 +93,12 @@
       <dependency>
         <groupId>com.google.guava</groupId>
         <artifactId>guava</artifactId>
-        <version>30.0-android</version>
+        <version>30.1-android</version>
       </dependency>
       <dependency>
         <groupId>com.google.guava</groupId>
         <artifactId>guava-testlib</artifactId>
-        <version>30.0-android</version>
+        <version>30.1-android</version>
         <scope>test</scope>
       </dependency>
       <dependency>
diff --git a/js/binary/reader_test.js b/js/binary/reader_test.js
index daa0ab6..7e46828 100644
--- a/js/binary/reader_test.js
+++ b/js/binary/reader_test.js
@@ -46,7 +46,8 @@
 goog.require('jspb.BinaryDecoder');
 goog.require('jspb.BinaryReader');
 goog.require('jspb.BinaryWriter');
-
+goog.require('jspb.utils');
+goog.requireType('jspb.BinaryMessage');
 
 
 describe('binaryReaderTest', function() {
@@ -55,7 +56,7 @@
    */
   it('testInstanceCaches', /** @suppress {visibility} */ function() {
     var writer = new jspb.BinaryWriter();
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
     writer.writeMessage(1, dummyMessage, goog.nullFunction);
     writer.writeMessage(2, dummyMessage, goog.nullFunction);
 
@@ -135,7 +136,7 @@
     // Calling readMessage on a non-delimited field should trigger an
     // assertion.
     var reader = jspb.BinaryReader.alloc([8, 1]);
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
     reader.nextField();
     assertThrows(function() {
       reader.readMessage(dummyMessage, goog.nullFunction);
@@ -144,47 +145,91 @@
     // Reading past the end of the stream should trigger an assertion.
     reader = jspb.BinaryReader.alloc([9, 1]);
     reader.nextField();
-    assertThrows(function() {reader.readFixed64()});
+    assertThrows(function() {
+      reader.readFixed64()
+    });
 
     // Reading past the end of a submessage should trigger an assertion.
     reader = jspb.BinaryReader.alloc([10, 4, 13, 1, 1, 1]);
     reader.nextField();
     reader.readMessage(dummyMessage, function() {
       reader.nextField();
-      assertThrows(function() {reader.readFixed32()});
+      assertThrows(function() {
+        reader.readFixed32()
+      });
     });
 
     // Skipping an invalid field should trigger an assertion.
     reader = jspb.BinaryReader.alloc([12, 1]);
     reader.nextWireType_ = 1000;
-    assertThrows(function() {reader.skipField()});
+    assertThrows(function() {
+      reader.skipField()
+    });
 
     // Reading fields with the wrong wire type should assert.
     reader = jspb.BinaryReader.alloc([9, 0, 0, 0, 0, 0, 0, 0, 0]);
     reader.nextField();
-    assertThrows(function() {reader.readInt32()});
-    assertThrows(function() {reader.readInt32String()});
-    assertThrows(function() {reader.readInt64()});
-    assertThrows(function() {reader.readInt64String()});
-    assertThrows(function() {reader.readUint32()});
-    assertThrows(function() {reader.readUint32String()});
-    assertThrows(function() {reader.readUint64()});
-    assertThrows(function() {reader.readUint64String()});
-    assertThrows(function() {reader.readSint32()});
-    assertThrows(function() {reader.readBool()});
-    assertThrows(function() {reader.readEnum()});
+    assertThrows(function() {
+      reader.readInt32()
+    });
+    assertThrows(function() {
+      reader.readInt32String()
+    });
+    assertThrows(function() {
+      reader.readInt64()
+    });
+    assertThrows(function() {
+      reader.readInt64String()
+    });
+    assertThrows(function() {
+      reader.readUint32()
+    });
+    assertThrows(function() {
+      reader.readUint32String()
+    });
+    assertThrows(function() {
+      reader.readUint64()
+    });
+    assertThrows(function() {
+      reader.readUint64String()
+    });
+    assertThrows(function() {
+      reader.readSint32()
+    });
+    assertThrows(function() {
+      reader.readBool()
+    });
+    assertThrows(function() {
+      reader.readEnum()
+    });
 
     reader = jspb.BinaryReader.alloc([8, 1]);
     reader.nextField();
-    assertThrows(function() {reader.readFixed32()});
-    assertThrows(function() {reader.readFixed64()});
-    assertThrows(function() {reader.readSfixed32()});
-    assertThrows(function() {reader.readSfixed64()});
-    assertThrows(function() {reader.readFloat()});
-    assertThrows(function() {reader.readDouble()});
+    assertThrows(function() {
+      reader.readFixed32()
+    });
+    assertThrows(function() {
+      reader.readFixed64()
+    });
+    assertThrows(function() {
+      reader.readSfixed32()
+    });
+    assertThrows(function() {
+      reader.readSfixed64()
+    });
+    assertThrows(function() {
+      reader.readFloat()
+    });
+    assertThrows(function() {
+      reader.readDouble()
+    });
 
-    assertThrows(function() {reader.readString()});
-    assertThrows(function() {reader.readBytes()});
+    assertThrows(function() {
+      reader.readString()
+    });
+    assertThrows(function() {
+      reader.readBytes()
+    });
   });
 
 
@@ -198,8 +243,8 @@
    * @private
    * @suppress {missingProperties}
    */
-  var doTestUnsignedField_ = function(readField,
-      writeField, epsilon, upperLimit, filter) {
+  var doTestUnsignedField_ = function(
+      readField, writeField, epsilon, upperLimit, filter) {
     assertNotNull(readField);
     assertNotNull(writeField);
 
@@ -250,8 +295,8 @@
    * @private
    * @suppress {missingProperties}
    */
-  var doTestSignedField_ = function(readField,
-      writeField, epsilon, lowerLimit, upperLimit, filter) {
+  var doTestSignedField_ = function(
+      readField, writeField, epsilon, lowerLimit, upperLimit, filter) {
     var writer = new jspb.BinaryWriter();
 
     // Encode zero and limits.
@@ -267,20 +312,14 @@
     for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) {
       var val = filter(cursor);
       writeField.call(writer, 6, val);
-      inputValues.push({
-        fieldNumber: 6,
-        value: val
-      });
+      inputValues.push({fieldNumber: 6, value: val});
     }
 
     // Encode positive values.
     for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
       var val = filter(cursor);
       writeField.call(writer, 7, val);
-      inputValues.push({
-        fieldNumber: 7,
-        value: val
-      });
+      inputValues.push({fieldNumber: 7, value: val});
     }
 
     var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
@@ -327,33 +366,34 @@
     assertNotUndefined(jspb.BinaryWriter.prototype.writeBool);
     doTestUnsignedField_(
         jspb.BinaryReader.prototype.readUint32,
-        jspb.BinaryWriter.prototype.writeUint32,
-        1, Math.pow(2, 32) - 1, Math.round);
+        jspb.BinaryWriter.prototype.writeUint32, 1, Math.pow(2, 32) - 1,
+        Math.round);
 
     doTestUnsignedField_(
         jspb.BinaryReader.prototype.readUint64,
-        jspb.BinaryWriter.prototype.writeUint64,
-        1, Math.pow(2, 64) - 1025, Math.round);
+        jspb.BinaryWriter.prototype.writeUint64, 1, Math.pow(2, 64) - 1025,
+        Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readInt32,
-        jspb.BinaryWriter.prototype.writeInt32,
-        1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
+        jspb.BinaryWriter.prototype.writeInt32, 1, -Math.pow(2, 31),
+        Math.pow(2, 31) - 1, Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readInt64,
-        jspb.BinaryWriter.prototype.writeInt64,
-        1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
+        jspb.BinaryWriter.prototype.writeInt64, 1, -Math.pow(2, 63),
+        Math.pow(2, 63) - 513, Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readEnum,
-        jspb.BinaryWriter.prototype.writeEnum,
-        1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
+        jspb.BinaryWriter.prototype.writeEnum, 1, -Math.pow(2, 31),
+        Math.pow(2, 31) - 1, Math.round);
 
     doTestUnsignedField_(
         jspb.BinaryReader.prototype.readBool,
-        jspb.BinaryWriter.prototype.writeBool,
-        1, 1, function(x) { return !!x; });
+        jspb.BinaryWriter.prototype.writeBool, 1, 1, function(x) {
+          return !!x;
+        });
   });
 
 
@@ -387,24 +427,22 @@
     // uint32 and sint32 take no more than 5 bytes
     // 08 - field prefix (type = 0 means varint)
     doTestHexStringVarint_(
-      jspb.BinaryReader.prototype.readUint32,
-      12, '08 8C 80 80 80 00');
+        jspb.BinaryReader.prototype.readUint32, 12, '08 8C 80 80 80 00');
 
     // 11 stands for -6 in zigzag encoding
     doTestHexStringVarint_(
-      jspb.BinaryReader.prototype.readSint32,
-      -6, '08 8B 80 80 80 00');
+        jspb.BinaryReader.prototype.readSint32, -6, '08 8B 80 80 80 00');
 
     // uint64 and sint64 take no more than 10 bytes
     // 08 - field prefix (type = 0 means varint)
     doTestHexStringVarint_(
-      jspb.BinaryReader.prototype.readUint64,
-      12, '08 8C 80 80 80 80 80 80 80 80 00');
+        jspb.BinaryReader.prototype.readUint64, 12,
+        '08 8C 80 80 80 80 80 80 80 80 00');
 
     // 11 stands for -6 in zigzag encoding
     doTestHexStringVarint_(
-      jspb.BinaryReader.prototype.readSint64,
-      -6, '08 8B 80 80 80 80 80 80 80 80 00');
+        jspb.BinaryReader.prototype.readSint64, -6,
+        '08 8B 80 80 80 80 80 80 80 80 00');
   });
 
   /**
@@ -440,27 +478,15 @@
     var writer = new jspb.BinaryWriter();
 
     var testSignedData = [
-      '2730538252207801776',
-      '-2688470994844604560',
-      '3398529779486536359',
-      '3568577411627971000',
-      '272477188847484900',
-      '-6649058714086158188',
-      '-7695254765712060806',
-      '-4525541438037104029',
-      '-4993706538836508568',
+      '2730538252207801776', '-2688470994844604560', '3398529779486536359',
+      '3568577411627971000', '272477188847484900', '-6649058714086158188',
+      '-7695254765712060806', '-4525541438037104029', '-4993706538836508568',
       '4990160321893729138'
     ];
     var testUnsignedData = [
-      '7822732630241694882',
-      '6753602971916687352',
-      '2399935075244442116',
-      '8724292567325338867',
-      '16948784802625696584',
-      '4136275908516066934',
-      '3575388346793700364',
-      '5167142028379259461',
-      '1557573948689737699',
+      '7822732630241694882', '6753602971916687352', '2399935075244442116',
+      '8724292567325338867', '16948784802625696584', '4136275908516066934',
+      '3575388346793700364', '5167142028379259461', '1557573948689737699',
       '17100725280812548567'
     ];
 
@@ -488,13 +514,13 @@
   it('testZigzagFields', function() {
     doTestSignedField_(
         jspb.BinaryReader.prototype.readSint32,
-        jspb.BinaryWriter.prototype.writeSint32,
-        1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
+        jspb.BinaryWriter.prototype.writeSint32, 1, -Math.pow(2, 31),
+        Math.pow(2, 31) - 1, Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readSint64,
-        jspb.BinaryWriter.prototype.writeSint64,
-        1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
+        jspb.BinaryWriter.prototype.writeSint64, 1, -Math.pow(2, 63),
+        Math.pow(2, 63) - 513, Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readSintHash64,
@@ -509,23 +535,23 @@
   it('testFixedFields', function() {
     doTestUnsignedField_(
         jspb.BinaryReader.prototype.readFixed32,
-        jspb.BinaryWriter.prototype.writeFixed32,
-        1, Math.pow(2, 32) - 1, Math.round);
+        jspb.BinaryWriter.prototype.writeFixed32, 1, Math.pow(2, 32) - 1,
+        Math.round);
 
     doTestUnsignedField_(
         jspb.BinaryReader.prototype.readFixed64,
-        jspb.BinaryWriter.prototype.writeFixed64,
-        1, Math.pow(2, 64) - 1025, Math.round);
+        jspb.BinaryWriter.prototype.writeFixed64, 1, Math.pow(2, 64) - 1025,
+        Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readSfixed32,
-        jspb.BinaryWriter.prototype.writeSfixed32,
-        1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
+        jspb.BinaryWriter.prototype.writeSfixed32, 1, -Math.pow(2, 31),
+        Math.pow(2, 31) - 1, Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readSfixed64,
-        jspb.BinaryWriter.prototype.writeSfixed64,
-        1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
+        jspb.BinaryWriter.prototype.writeSfixed64, 1, -Math.pow(2, 63),
+        Math.pow(2, 63) - 513, Math.round);
   });
 
 
@@ -536,18 +562,17 @@
     doTestSignedField_(
         jspb.BinaryReader.prototype.readFloat,
         jspb.BinaryWriter.prototype.writeFloat,
-        jspb.BinaryConstants.FLOAT32_MIN,
-        -jspb.BinaryConstants.FLOAT32_MAX,
-        jspb.BinaryConstants.FLOAT32_MAX,
-        truncate);
+        jspb.BinaryConstants.FLOAT32_MIN, -jspb.BinaryConstants.FLOAT32_MAX,
+        jspb.BinaryConstants.FLOAT32_MAX, truncate);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readDouble,
         jspb.BinaryWriter.prototype.writeDouble,
         jspb.BinaryConstants.FLOAT64_EPS * 10,
-        -jspb.BinaryConstants.FLOAT64_MIN,
-        jspb.BinaryConstants.FLOAT64_MIN,
-        function(x) { return x; });
+        -jspb.BinaryConstants.FLOAT64_MIN, jspb.BinaryConstants.FLOAT64_MIN,
+        function(x) {
+          return x;
+        });
   });
 
 
@@ -614,7 +639,7 @@
    */
   it('testNesting', function() {
     var writer = new jspb.BinaryWriter();
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
 
     writer.writeInt32(1, 100);
 
@@ -707,7 +732,7 @@
 
     // Write a group with a nested group inside.
     writer.writeInt32(5, sentinel);
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
     writer.writeGroup(5, dummyMessage, function() {
       // Previously the skipGroup implementation was wrong, which only consume
       // the decoder by nextField. This case is for making the previous
@@ -726,7 +751,7 @@
         writer.writeInt64(84, 42);
         writer.writeInt64(84, 44);
         writer.writeBytes(
-          43, [255, 255, 255, 255, 255, 255, 255, 255, 255, 255]);
+            43, [255, 255, 255, 255, 255, 255, 255, 255, 255, 255]);
       });
     });
 
@@ -875,7 +900,7 @@
     var fieldTag = (1 << 3) | jspb.BinaryConstants.WireType.DELIMITED;
     var blob = [1, 2, 3, 4, 5];
     var writer = new jspb.BinaryWriter();
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
 
     writer.writeMessage(1, dummyMessage, function() {
       writer.writeMessage(1, dummyMessage, function() {
@@ -908,7 +933,7 @@
    */
   it('testReadCallbacks', function() {
     var writer = new jspb.BinaryWriter();
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
 
     // Add an int, a submessage, and another int.
     writer.writeInt32(1, 100);
diff --git a/js/binary/writer_test.js b/js/binary/writer_test.js
index fca1ba4..6c5f36e 100644
--- a/js/binary/writer_test.js
+++ b/js/binary/writer_test.js
@@ -44,7 +44,8 @@
 goog.require('jspb.BinaryReader');
 goog.require('jspb.BinaryWriter');
 goog.require('jspb.utils');
-
+goog.require('goog.crypt.base64');
+goog.requireType('jspb.BinaryMessage');
 
 /**
  * @param {function()} func This function should throw an error when run.
@@ -61,7 +62,7 @@
   it('testWriteErrors', function() {
     // Submessages with invalid field indices should assert.
     var writer = new jspb.BinaryWriter();
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
 
     assertFails(function() {
       writer.writeMessage(-1, dummyMessage, goog.nullFunction);
@@ -69,40 +70,82 @@
 
     // Writing invalid field indices should assert.
     writer = new jspb.BinaryWriter();
-    assertFails(function() {writer.writeUint64(-1, 1);});
+    assertFails(function() {
+      writer.writeUint64(-1, 1);
+    });
 
     // Writing out-of-range field values should assert.
     writer = new jspb.BinaryWriter();
 
-    assertFails(function() {writer.writeInt32(1, -Infinity);});
-    assertFails(function() {writer.writeInt32(1, Infinity);});
+    assertFails(function() {
+      writer.writeInt32(1, -Infinity);
+    });
+    assertFails(function() {
+      writer.writeInt32(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeInt64(1, -Infinity);});
-    assertFails(function() {writer.writeInt64(1, Infinity);});
+    assertFails(function() {
+      writer.writeInt64(1, -Infinity);
+    });
+    assertFails(function() {
+      writer.writeInt64(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeUint32(1, -1);});
-    assertFails(function() {writer.writeUint32(1, Infinity);});
+    assertFails(function() {
+      writer.writeUint32(1, -1);
+    });
+    assertFails(function() {
+      writer.writeUint32(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeUint64(1, -1);});
-    assertFails(function() {writer.writeUint64(1, Infinity);});
+    assertFails(function() {
+      writer.writeUint64(1, -1);
+    });
+    assertFails(function() {
+      writer.writeUint64(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeSint32(1, -Infinity);});
-    assertFails(function() {writer.writeSint32(1, Infinity);});
+    assertFails(function() {
+      writer.writeSint32(1, -Infinity);
+    });
+    assertFails(function() {
+      writer.writeSint32(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeSint64(1, -Infinity);});
-    assertFails(function() {writer.writeSint64(1, Infinity);});
+    assertFails(function() {
+      writer.writeSint64(1, -Infinity);
+    });
+    assertFails(function() {
+      writer.writeSint64(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeFixed32(1, -1);});
-    assertFails(function() {writer.writeFixed32(1, Infinity);});
+    assertFails(function() {
+      writer.writeFixed32(1, -1);
+    });
+    assertFails(function() {
+      writer.writeFixed32(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeFixed64(1, -1);});
-    assertFails(function() {writer.writeFixed64(1, Infinity);});
+    assertFails(function() {
+      writer.writeFixed64(1, -1);
+    });
+    assertFails(function() {
+      writer.writeFixed64(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeSfixed32(1, -Infinity);});
-    assertFails(function() {writer.writeSfixed32(1, Infinity);});
+    assertFails(function() {
+      writer.writeSfixed32(1, -Infinity);
+    });
+    assertFails(function() {
+      writer.writeSfixed32(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeSfixed64(1, -Infinity);});
-    assertFails(function() {writer.writeSfixed64(1, Infinity);});
+    assertFails(function() {
+      writer.writeSfixed64(1, -Infinity);
+    });
+    assertFails(function() {
+      writer.writeSfixed64(1, Infinity);
+    });
   });
 
 
diff --git a/js/commonjs/rewrite_tests_for_commonjs.js b/js/commonjs/rewrite_tests_for_commonjs.js
index b6d90d2..82eef18 100644
--- a/js/commonjs/rewrite_tests_for_commonjs.js
+++ b/js/commonjs/rewrite_tests_for_commonjs.js
@@ -80,6 +80,8 @@
   if (isRequire) {
     if (module) {  // Skip goog.require() lines before the first directive.
       var fullSym = isRequire[1];
+      // Skip lines importing from jspb.*, these are handled by the header above.
+      if (fullSym.match(/^jspb\./)) return;
       var sym = tryStripPrefix(fullSym, pkg);
       console.log("googleProtobuf.exportSymbol('" + fullSym + "', " + module + sym + ', global);');
     }
diff --git a/js/compatibility_tests/v3.1.0/binary/reader_test.js b/js/compatibility_tests/v3.1.0/binary/reader_test.js
index 9571138..d0062de 100644
--- a/js/compatibility_tests/v3.1.0/binary/reader_test.js
+++ b/js/compatibility_tests/v3.1.0/binary/reader_test.js
@@ -46,7 +46,7 @@
 goog.require('jspb.BinaryDecoder');
 goog.require('jspb.BinaryReader');
 goog.require('jspb.BinaryWriter');
-
+goog.requireType('jspb.BinaryMessage');
 
 
 describe('binaryReaderTest', function() {
@@ -55,7 +55,7 @@
    */
   it('testInstanceCaches', /** @suppress {visibility} */ function() {
     var writer = new jspb.BinaryWriter();
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
     writer.writeMessage(1, dummyMessage, goog.nullFunction);
     writer.writeMessage(2, dummyMessage, goog.nullFunction);
 
@@ -135,7 +135,7 @@
     // Calling readMessage on a non-delimited field should trigger an
     // assertion.
     var reader = jspb.BinaryReader.alloc([8, 1]);
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
     reader.nextField();
     assertThrows(function() {
       reader.readMessage(dummyMessage, goog.nullFunction);
@@ -144,47 +144,91 @@
     // Reading past the end of the stream should trigger an assertion.
     reader = jspb.BinaryReader.alloc([9, 1]);
     reader.nextField();
-    assertThrows(function() {reader.readFixed64()});
+    assertThrows(function() {
+      reader.readFixed64()
+    });
 
     // Reading past the end of a submessage should trigger an assertion.
     reader = jspb.BinaryReader.alloc([10, 4, 13, 1, 1, 1]);
     reader.nextField();
     reader.readMessage(dummyMessage, function() {
       reader.nextField();
-      assertThrows(function() {reader.readFixed32()});
+      assertThrows(function() {
+        reader.readFixed32()
+      });
     });
 
     // Skipping an invalid field should trigger an assertion.
     reader = jspb.BinaryReader.alloc([12, 1]);
     reader.nextWireType_ = 1000;
-    assertThrows(function() {reader.skipField()});
+    assertThrows(function() {
+      reader.skipField()
+    });
 
     // Reading fields with the wrong wire type should assert.
     reader = jspb.BinaryReader.alloc([9, 0, 0, 0, 0, 0, 0, 0, 0]);
     reader.nextField();
-    assertThrows(function() {reader.readInt32()});
-    assertThrows(function() {reader.readInt32String()});
-    assertThrows(function() {reader.readInt64()});
-    assertThrows(function() {reader.readInt64String()});
-    assertThrows(function() {reader.readUint32()});
-    assertThrows(function() {reader.readUint32String()});
-    assertThrows(function() {reader.readUint64()});
-    assertThrows(function() {reader.readUint64String()});
-    assertThrows(function() {reader.readSint32()});
-    assertThrows(function() {reader.readBool()});
-    assertThrows(function() {reader.readEnum()});
+    assertThrows(function() {
+      reader.readInt32()
+    });
+    assertThrows(function() {
+      reader.readInt32String()
+    });
+    assertThrows(function() {
+      reader.readInt64()
+    });
+    assertThrows(function() {
+      reader.readInt64String()
+    });
+    assertThrows(function() {
+      reader.readUint32()
+    });
+    assertThrows(function() {
+      reader.readUint32String()
+    });
+    assertThrows(function() {
+      reader.readUint64()
+    });
+    assertThrows(function() {
+      reader.readUint64String()
+    });
+    assertThrows(function() {
+      reader.readSint32()
+    });
+    assertThrows(function() {
+      reader.readBool()
+    });
+    assertThrows(function() {
+      reader.readEnum()
+    });
 
     reader = jspb.BinaryReader.alloc([8, 1]);
     reader.nextField();
-    assertThrows(function() {reader.readFixed32()});
-    assertThrows(function() {reader.readFixed64()});
-    assertThrows(function() {reader.readSfixed32()});
-    assertThrows(function() {reader.readSfixed64()});
-    assertThrows(function() {reader.readFloat()});
-    assertThrows(function() {reader.readDouble()});
+    assertThrows(function() {
+      reader.readFixed32()
+    });
+    assertThrows(function() {
+      reader.readFixed64()
+    });
+    assertThrows(function() {
+      reader.readSfixed32()
+    });
+    assertThrows(function() {
+      reader.readSfixed64()
+    });
+    assertThrows(function() {
+      reader.readFloat()
+    });
+    assertThrows(function() {
+      reader.readDouble()
+    });
 
-    assertThrows(function() {reader.readString()});
-    assertThrows(function() {reader.readBytes()});
+    assertThrows(function() {
+      reader.readString()
+    });
+    assertThrows(function() {
+      reader.readBytes()
+    });
   });
 
 
@@ -198,8 +242,8 @@
    * @private
    * @suppress {missingProperties}
    */
-  var doTestUnsignedField_ = function(readField,
-      writeField, epsilon, upperLimit, filter) {
+  var doTestUnsignedField_ = function(
+      readField, writeField, epsilon, upperLimit, filter) {
     assertNotNull(readField);
     assertNotNull(writeField);
 
@@ -250,8 +294,8 @@
    * @private
    * @suppress {missingProperties}
    */
-  var doTestSignedField_ = function(readField,
-      writeField, epsilon, lowerLimit, upperLimit, filter) {
+  var doTestSignedField_ = function(
+      readField, writeField, epsilon, lowerLimit, upperLimit, filter) {
     var writer = new jspb.BinaryWriter();
 
     // Encode zero and limits.
@@ -267,20 +311,14 @@
     for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) {
       var val = filter(cursor);
       writeField.call(writer, 6, val);
-      inputValues.push({
-        fieldNumber: 6,
-        value: val
-      });
+      inputValues.push({fieldNumber: 6, value: val});
     }
 
     // Encode positive values.
     for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
       var val = filter(cursor);
       writeField.call(writer, 7, val);
-      inputValues.push({
-        fieldNumber: 7,
-        value: val
-      });
+      inputValues.push({fieldNumber: 7, value: val});
     }
 
     var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
@@ -327,33 +365,34 @@
     assertNotUndefined(jspb.BinaryWriter.prototype.writeBool);
     doTestUnsignedField_(
         jspb.BinaryReader.prototype.readUint32,
-        jspb.BinaryWriter.prototype.writeUint32,
-        1, Math.pow(2, 32) - 1, Math.round);
+        jspb.BinaryWriter.prototype.writeUint32, 1, Math.pow(2, 32) - 1,
+        Math.round);
 
     doTestUnsignedField_(
         jspb.BinaryReader.prototype.readUint64,
-        jspb.BinaryWriter.prototype.writeUint64,
-        1, Math.pow(2, 64) - 1025, Math.round);
+        jspb.BinaryWriter.prototype.writeUint64, 1, Math.pow(2, 64) - 1025,
+        Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readInt32,
-        jspb.BinaryWriter.prototype.writeInt32,
-        1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
+        jspb.BinaryWriter.prototype.writeInt32, 1, -Math.pow(2, 31),
+        Math.pow(2, 31) - 1, Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readInt64,
-        jspb.BinaryWriter.prototype.writeInt64,
-        1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
+        jspb.BinaryWriter.prototype.writeInt64, 1, -Math.pow(2, 63),
+        Math.pow(2, 63) - 513, Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readEnum,
-        jspb.BinaryWriter.prototype.writeEnum,
-        1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
+        jspb.BinaryWriter.prototype.writeEnum, 1, -Math.pow(2, 31),
+        Math.pow(2, 31) - 1, Math.round);
 
     doTestUnsignedField_(
         jspb.BinaryReader.prototype.readBool,
-        jspb.BinaryWriter.prototype.writeBool,
-        1, 1, function(x) { return !!x; });
+        jspb.BinaryWriter.prototype.writeBool, 1, 1, function(x) {
+          return !!x;
+        });
   });
 
 
@@ -387,24 +426,22 @@
     // uint32 and sint32 take no more than 5 bytes
     // 08 - field prefix (type = 0 means varint)
     doTestHexStringVarint_(
-      jspb.BinaryReader.prototype.readUint32,
-      12, '08 8C 80 80 80 00');
+        jspb.BinaryReader.prototype.readUint32, 12, '08 8C 80 80 80 00');
 
     // 11 stands for -6 in zigzag encoding
     doTestHexStringVarint_(
-      jspb.BinaryReader.prototype.readSint32,
-      -6, '08 8B 80 80 80 00');
+        jspb.BinaryReader.prototype.readSint32, -6, '08 8B 80 80 80 00');
 
     // uint64 and sint64 take no more than 10 bytes
     // 08 - field prefix (type = 0 means varint)
     doTestHexStringVarint_(
-      jspb.BinaryReader.prototype.readUint64,
-      12, '08 8C 80 80 80 80 80 80 80 80 00');
+        jspb.BinaryReader.prototype.readUint64, 12,
+        '08 8C 80 80 80 80 80 80 80 80 00');
 
     // 11 stands for -6 in zigzag encoding
     doTestHexStringVarint_(
-      jspb.BinaryReader.prototype.readSint64,
-      -6, '08 8B 80 80 80 80 80 80 80 80 00');
+        jspb.BinaryReader.prototype.readSint64, -6,
+        '08 8B 80 80 80 80 80 80 80 80 00');
   });
 
 
@@ -415,27 +452,15 @@
     var writer = new jspb.BinaryWriter();
 
     var testSignedData = [
-      '2730538252207801776',
-      '-2688470994844604560',
-      '3398529779486536359',
-      '3568577411627971000',
-      '272477188847484900',
-      '-6649058714086158188',
-      '-7695254765712060806',
-      '-4525541438037104029',
-      '-4993706538836508568',
+      '2730538252207801776', '-2688470994844604560', '3398529779486536359',
+      '3568577411627971000', '272477188847484900', '-6649058714086158188',
+      '-7695254765712060806', '-4525541438037104029', '-4993706538836508568',
       '4990160321893729138'
     ];
     var testUnsignedData = [
-      '7822732630241694882',
-      '6753602971916687352',
-      '2399935075244442116',
-      '8724292567325338867',
-      '16948784802625696584',
-      '4136275908516066934',
-      '3575388346793700364',
-      '5167142028379259461',
-      '1557573948689737699',
+      '7822732630241694882', '6753602971916687352', '2399935075244442116',
+      '8724292567325338867', '16948784802625696584', '4136275908516066934',
+      '3575388346793700364', '5167142028379259461', '1557573948689737699',
       '17100725280812548567'
     ];
 
@@ -463,13 +488,13 @@
   it('testZigzagFields', function() {
     doTestSignedField_(
         jspb.BinaryReader.prototype.readSint32,
-        jspb.BinaryWriter.prototype.writeSint32,
-        1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
+        jspb.BinaryWriter.prototype.writeSint32, 1, -Math.pow(2, 31),
+        Math.pow(2, 31) - 1, Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readSint64,
-        jspb.BinaryWriter.prototype.writeSint64,
-        1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
+        jspb.BinaryWriter.prototype.writeSint64, 1, -Math.pow(2, 63),
+        Math.pow(2, 63) - 513, Math.round);
   });
 
 
@@ -479,23 +504,23 @@
   it('testFixedFields', function() {
     doTestUnsignedField_(
         jspb.BinaryReader.prototype.readFixed32,
-        jspb.BinaryWriter.prototype.writeFixed32,
-        1, Math.pow(2, 32) - 1, Math.round);
+        jspb.BinaryWriter.prototype.writeFixed32, 1, Math.pow(2, 32) - 1,
+        Math.round);
 
     doTestUnsignedField_(
         jspb.BinaryReader.prototype.readFixed64,
-        jspb.BinaryWriter.prototype.writeFixed64,
-        1, Math.pow(2, 64) - 1025, Math.round);
+        jspb.BinaryWriter.prototype.writeFixed64, 1, Math.pow(2, 64) - 1025,
+        Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readSfixed32,
-        jspb.BinaryWriter.prototype.writeSfixed32,
-        1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
+        jspb.BinaryWriter.prototype.writeSfixed32, 1, -Math.pow(2, 31),
+        Math.pow(2, 31) - 1, Math.round);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readSfixed64,
-        jspb.BinaryWriter.prototype.writeSfixed64,
-        1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
+        jspb.BinaryWriter.prototype.writeSfixed64, 1, -Math.pow(2, 63),
+        Math.pow(2, 63) - 513, Math.round);
   });
 
 
@@ -506,18 +531,17 @@
     doTestSignedField_(
         jspb.BinaryReader.prototype.readFloat,
         jspb.BinaryWriter.prototype.writeFloat,
-        jspb.BinaryConstants.FLOAT32_MIN,
-        -jspb.BinaryConstants.FLOAT32_MAX,
-        jspb.BinaryConstants.FLOAT32_MAX,
-        truncate);
+        jspb.BinaryConstants.FLOAT32_MIN, -jspb.BinaryConstants.FLOAT32_MAX,
+        jspb.BinaryConstants.FLOAT32_MAX, truncate);
 
     doTestSignedField_(
         jspb.BinaryReader.prototype.readDouble,
         jspb.BinaryWriter.prototype.writeDouble,
         jspb.BinaryConstants.FLOAT64_EPS * 10,
-        -jspb.BinaryConstants.FLOAT64_MIN,
-        jspb.BinaryConstants.FLOAT64_MIN,
-        function(x) { return x; });
+        -jspb.BinaryConstants.FLOAT64_MIN, jspb.BinaryConstants.FLOAT64_MIN,
+        function(x) {
+          return x;
+        });
   });
 
 
@@ -584,7 +608,7 @@
    */
   it('testNesting', function() {
     var writer = new jspb.BinaryWriter();
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
 
     writer.writeInt32(1, 100);
 
@@ -677,7 +701,7 @@
 
     // Write a group with a nested group inside.
     writer.writeInt32(5, sentinel);
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
     writer.writeGroup(5, dummyMessage, function() {
       writer.writeInt64(42, 42);
       writer.writeGroup(6, dummyMessage, function() {
@@ -830,7 +854,7 @@
     var fieldTag = (1 << 3) | jspb.BinaryConstants.WireType.DELIMITED;
     var blob = [1, 2, 3, 4, 5];
     var writer = new jspb.BinaryWriter();
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
 
     writer.writeMessage(1, dummyMessage, function() {
       writer.writeMessage(1, dummyMessage, function() {
@@ -863,7 +887,7 @@
    */
   it('testReadCallbacks', function() {
     var writer = new jspb.BinaryWriter();
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
 
     // Add an int, a submessage, and another int.
     writer.writeInt32(1, 100);
diff --git a/js/compatibility_tests/v3.1.0/binary/writer_test.js b/js/compatibility_tests/v3.1.0/binary/writer_test.js
index d5dadb4..55bc6be 100644
--- a/js/compatibility_tests/v3.1.0/binary/writer_test.js
+++ b/js/compatibility_tests/v3.1.0/binary/writer_test.js
@@ -41,14 +41,14 @@
 goog.require('goog.crypt');
 goog.require('goog.testing.asserts');
 goog.require('jspb.BinaryWriter');
-
+goog.requireType('jspb.BinaryMessage');
 
 /**
  * @param {function()} func This function should throw an error when run.
  */
 function assertFails(func) {
   var e = assertThrows(func);
-  //assertNotNull(e.toString().match(/Error/));
+  // assertNotNull(e.toString().match(/Error/));
 }
 
 
@@ -59,7 +59,7 @@
   it('testWriteErrors', function() {
     // Submessages with invalid field indices should assert.
     var writer = new jspb.BinaryWriter();
-    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
 
     assertFails(function() {
       writer.writeMessage(-1, dummyMessage, goog.nullFunction);
@@ -67,40 +67,82 @@
 
     // Writing invalid field indices should assert.
     writer = new jspb.BinaryWriter();
-    assertFails(function() {writer.writeUint64(-1, 1);});
+    assertFails(function() {
+      writer.writeUint64(-1, 1);
+    });
 
     // Writing out-of-range field values should assert.
     writer = new jspb.BinaryWriter();
 
-    assertFails(function() {writer.writeInt32(1, -Infinity);});
-    assertFails(function() {writer.writeInt32(1, Infinity);});
+    assertFails(function() {
+      writer.writeInt32(1, -Infinity);
+    });
+    assertFails(function() {
+      writer.writeInt32(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeInt64(1, -Infinity);});
-    assertFails(function() {writer.writeInt64(1, Infinity);});
+    assertFails(function() {
+      writer.writeInt64(1, -Infinity);
+    });
+    assertFails(function() {
+      writer.writeInt64(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeUint32(1, -1);});
-    assertFails(function() {writer.writeUint32(1, Infinity);});
+    assertFails(function() {
+      writer.writeUint32(1, -1);
+    });
+    assertFails(function() {
+      writer.writeUint32(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeUint64(1, -1);});
-    assertFails(function() {writer.writeUint64(1, Infinity);});
+    assertFails(function() {
+      writer.writeUint64(1, -1);
+    });
+    assertFails(function() {
+      writer.writeUint64(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeSint32(1, -Infinity);});
-    assertFails(function() {writer.writeSint32(1, Infinity);});
+    assertFails(function() {
+      writer.writeSint32(1, -Infinity);
+    });
+    assertFails(function() {
+      writer.writeSint32(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeSint64(1, -Infinity);});
-    assertFails(function() {writer.writeSint64(1, Infinity);});
+    assertFails(function() {
+      writer.writeSint64(1, -Infinity);
+    });
+    assertFails(function() {
+      writer.writeSint64(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeFixed32(1, -1);});
-    assertFails(function() {writer.writeFixed32(1, Infinity);});
+    assertFails(function() {
+      writer.writeFixed32(1, -1);
+    });
+    assertFails(function() {
+      writer.writeFixed32(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeFixed64(1, -1);});
-    assertFails(function() {writer.writeFixed64(1, Infinity);});
+    assertFails(function() {
+      writer.writeFixed64(1, -1);
+    });
+    assertFails(function() {
+      writer.writeFixed64(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeSfixed32(1, -Infinity);});
-    assertFails(function() {writer.writeSfixed32(1, Infinity);});
+    assertFails(function() {
+      writer.writeSfixed32(1, -Infinity);
+    });
+    assertFails(function() {
+      writer.writeSfixed32(1, Infinity);
+    });
 
-    assertFails(function() {writer.writeSfixed64(1, -Infinity);});
-    assertFails(function() {writer.writeSfixed64(1, Infinity);});
+    assertFails(function() {
+      writer.writeSfixed64(1, -Infinity);
+    });
+    assertFails(function() {
+      writer.writeSfixed64(1, Infinity);
+    });
   });
 
 
diff --git a/js/compatibility_tests/v3.1.0/maps_test.js b/js/compatibility_tests/v3.1.0/maps_test.js
index 0d442f4..452dfe5 100644
--- a/js/compatibility_tests/v3.1.0/maps_test.js
+++ b/js/compatibility_tests/v3.1.0/maps_test.js
@@ -40,6 +40,8 @@
 goog.require('proto.jspb.test.MapValueMessageNoBinary');
 goog.require('proto.jspb.test.TestMapFieldsNoBinary');
 
+goog.requireType('jspb.Map');
+
 /**
  * Helper: check that the given map has exactly this set of (sorted) entries.
  * @param {!jspb.Map} map
@@ -98,7 +100,9 @@
     msg.getMapStringMsgMap().get('k').setFoo(42);
     msg.getMapStringMsgMap().get('l').setFoo(84);
     msg.getMapInt32StringMap().set(-1, 'a').set(42, 'b');
-    msg.getMapInt64StringMap().set(0x123456789abc, 'c').set(0xcba987654321, 'd');
+    msg.getMapInt64StringMap()
+        .set(0x123456789abc, 'c')
+        .set(0xcba987654321, 'd');
     msg.getMapBoolStringMap().set(false, 'e').set(true, 'f');
   };
 
@@ -107,42 +111,24 @@
    * @param {?} msg
    */
   var checkMapFields = function(msg) {
-    checkMapEquals(msg.getMapStringStringMap(), [
-          ['asdf', 'jkl;'],
-          ['key 2', 'hello world']
-    ]);
-    checkMapEquals(msg.getMapStringInt32Map(), [
-          ['a', 1],
-          ['b', -2]
-    ]);
-    checkMapEquals(msg.getMapStringInt64Map(), [
-          ['c', 0x100000000],
-          ['d', 0x200000000]
-    ]);
-    checkMapEquals(msg.getMapStringBoolMap(), [
-          ['e', true],
-          ['f', false]
-    ]);
-    checkMapEquals(msg.getMapStringDoubleMap(), [
-          ['g', 3.14159],
-          ['h', 2.71828]
-    ]);
+    checkMapEquals(
+        msg.getMapStringStringMap(),
+        [['asdf', 'jkl;'], ['key 2', 'hello world']]);
+    checkMapEquals(msg.getMapStringInt32Map(), [['a', 1], ['b', -2]]);
+    checkMapEquals(
+        msg.getMapStringInt64Map(), [['c', 0x100000000], ['d', 0x200000000]]);
+    checkMapEquals(msg.getMapStringBoolMap(), [['e', true], ['f', false]]);
+    checkMapEquals(
+        msg.getMapStringDoubleMap(), [['g', 3.14159], ['h', 2.71828]]);
     checkMapEquals(msg.getMapStringEnumMap(), [
-          ['i', proto.jspb.test.MapValueEnum.MAP_VALUE_BAR],
-          ['j', proto.jspb.test.MapValueEnum.MAP_VALUE_BAZ]
+      ['i', proto.jspb.test.MapValueEnum.MAP_VALUE_BAR],
+      ['j', proto.jspb.test.MapValueEnum.MAP_VALUE_BAZ]
     ]);
-    checkMapEquals(msg.getMapInt32StringMap(), [
-          [-1, 'a'],
-          [42, 'b']
-    ]);
-    checkMapEquals(msg.getMapInt64StringMap(), [
-          [0x123456789abc, 'c'],
-          [0xcba987654321, 'd']
-    ]);
-    checkMapEquals(msg.getMapBoolStringMap(), [
-          [false, 'e'],
-          [true, 'f']
-    ]);
+    checkMapEquals(msg.getMapInt32StringMap(), [[-1, 'a'], [42, 'b']]);
+    checkMapEquals(
+        msg.getMapInt64StringMap(),
+        [[0x123456789abc, 'c'], [0xcba987654321, 'd']]);
+    checkMapEquals(msg.getMapBoolStringMap(), [[false, 'e'], [true, 'f']]);
 
     assertEquals(msg.getMapStringMsgMap().getLength(), 2);
     assertEquals(msg.getMapStringMsgMap().get('k').getFoo(), 42);
@@ -187,10 +173,7 @@
     assertElementsEquals(it.next().value, ['asdf', 'hello world']);
     assertElementsEquals(it.next().value, ['jkl;', 'key 2']);
     assertEquals(it.next().done, true);
-    checkMapEquals(m, [
-        ['asdf', 'hello world'],
-        ['jkl;', 'key 2']
-    ]);
+    checkMapEquals(m, [['asdf', 'hello world'], ['jkl;', 'key 2']]);
     m.del('jkl;');
     assertEquals(m.has('jkl;'), false);
     assertEquals(m.get('jkl;'), undefined);
@@ -242,11 +225,11 @@
       msg.getMapStringStringMap().set('A', 'a');
       var serialized = msg.serializeBinary();
       var expectedSerialized = [
-          0x0a, 0x6, // field 1 (map_string_string), delimited, length 6
-          0x0a, 0x1, // field 1 in submessage (key), delimited, length 1
-          0x41,      // ASCII 'A'
-          0x12, 0x1, // field 2 in submessage (value), delimited, length 1
-          0x61       // ASCII 'a'
+        0x0a, 0x6,  // field 1 (map_string_string), delimited, length 6
+        0x0a, 0x1,  // field 1 in submessage (key), delimited, length 1
+        0x41,       // ASCII 'A'
+        0x12, 0x1,  // field 2 in submessage (value), delimited, length 1
+        0x61        // ASCII 'a'
       ];
       assertEquals(serialized.length, expectedSerialized.length);
       for (var i = 0; i < serialized.length; i++) {
@@ -267,11 +250,7 @@
    */
   it('testLazyMapSync' + suffix, function() {
     // Start with a JSPB array containing a few map entries.
-    var entries = [
-        ['a', 'entry 1'],
-        ['c', 'entry 2'],
-        ['b', 'entry 3']
-    ];
+    var entries = [['a', 'entry 1'], ['c', 'entry 2'], ['b', 'entry 3']];
     var msg = new msgInfo.constructor([entries]);
     assertEquals(entries.length, 3);
     assertEquals(entries[0][0], 'a');
@@ -279,9 +258,9 @@
     assertEquals(entries[2][0], 'b');
     msg.getMapStringStringMap().del('a');
     assertEquals(entries.length, 3);  // not yet sync'd
-    msg.toArray();                // force a sync
+    msg.toArray();                    // force a sync
     assertEquals(entries.length, 2);
-    assertEquals(entries[0][0], 'b'); // now in sorted order
+    assertEquals(entries[0][0], 'b');  // now in sorted order
     assertEquals(entries[1][0], 'c');
 
     var a = msg.toArray();
@@ -290,12 +269,16 @@
 }
 
 describe('mapsTest', function() {
-  makeTests({
-    constructor: proto.jspb.test.TestMapFields,
-    deserializeBinary: proto.jspb.test.TestMapFields.deserializeBinary
-  }, proto.jspb.test.MapValueMessage, "_Binary");
-  makeTests({
-    constructor: proto.jspb.test.TestMapFieldsNoBinary,
-    deserializeBinary: null
-  }, proto.jspb.test.MapValueMessageNoBinary, "_NoBinary");
+  makeTests(
+      {
+        constructor: proto.jspb.test.TestMapFields,
+        deserializeBinary: proto.jspb.test.TestMapFields.deserializeBinary
+      },
+      proto.jspb.test.MapValueMessage, '_Binary');
+  makeTests(
+      {
+        constructor: proto.jspb.test.TestMapFieldsNoBinary,
+        deserializeBinary: null
+      },
+      proto.jspb.test.MapValueMessageNoBinary, '_NoBinary');
 });
diff --git a/js/compatibility_tests/v3.1.0/message_test.js b/js/compatibility_tests/v3.1.0/message_test.js
index 80a1c52..9830a02 100644
--- a/js/compatibility_tests/v3.1.0/message_test.js
+++ b/js/compatibility_tests/v3.1.0/message_test.js
@@ -67,7 +67,6 @@
 goog.require('proto.jspb.test.Simple2');
 goog.require('proto.jspb.test.SpecialCases');
 goog.require('proto.jspb.test.TestClone');
-goog.require('proto.jspb.test.TestEndsWithBytes');
 goog.require('proto.jspb.test.TestGroup');
 goog.require('proto.jspb.test.TestGroup1');
 goog.require('proto.jspb.test.TestMessageWithOneof');
@@ -77,7 +76,7 @@
 // CommonJS-LoadFromFile: test2_pb proto.jspb.test
 goog.require('proto.jspb.test.ExtensionMessage');
 goog.require('proto.jspb.test.TestExtensionsMessage');
-
+goog.require('proto.jspb.test.simple1');
 
 
 
@@ -102,59 +101,59 @@
   });
 
   it('testComplexConversion', function() {
-    var data1 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, true];
-    var data2 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, true];
+    var data1 = ['a', , , [, 11], [[, 22], [, 33]], , ['s1', 's2'], , true];
+    var data2 = ['a', , , [, 11], [[, 22], [, 33]], , ['s1', 's2'], , true];
     var foo = new proto.jspb.test.Complex(data1);
     var bar = new proto.jspb.test.Complex(data2);
     var result = foo.toObject();
-    assertObjectEquals({
-      aString: 'a',
-      anOutOfOrderBool: true,
-      aNestedMessage: {
-        anInt: 11
-      },
-      aRepeatedMessageList: [{anInt: 22}, {anInt: 33}],
-      aRepeatedStringList: ['s1', 's2']
-    }, result);
+    assertObjectEquals(
+        {
+          aString: 'a',
+          anOutOfOrderBool: true,
+          aNestedMessage: {anInt: 11},
+          aRepeatedMessageList: [{anInt: 22}, {anInt: 33}],
+          aRepeatedStringList: ['s1', 's2']
+        },
+        result);
 
     // Now test with the jspb instances included.
     result = foo.toObject(true /* opt_includeInstance */);
-    assertObjectEquals({
-      aString: 'a',
-      anOutOfOrderBool: true,
-      aNestedMessage: {
-        anInt: 11,
-        $jspbMessageInstance: foo.getANestedMessage()
-      },
-      aRepeatedMessageList: [
-        {anInt: 22, $jspbMessageInstance: foo.getARepeatedMessageList()[0]},
-        {anInt: 33, $jspbMessageInstance: foo.getARepeatedMessageList()[1]}
-      ],
-      aRepeatedStringList: ['s1', 's2'],
-      $jspbMessageInstance: foo
-    }, result);
-
+    assertObjectEquals(
+        {
+          aString: 'a',
+          anOutOfOrderBool: true,
+          aNestedMessage:
+              {anInt: 11, $jspbMessageInstance: foo.getANestedMessage()},
+          aRepeatedMessageList: [
+            {anInt: 22, $jspbMessageInstance: foo.getARepeatedMessageList()[0]},
+            {anInt: 33, $jspbMessageInstance: foo.getARepeatedMessageList()[1]}
+          ],
+          aRepeatedStringList: ['s1', 's2'],
+          $jspbMessageInstance: foo
+        },
+        result);
   });
 
   it('testMissingFields', function() {
     var foo = new proto.jspb.test.Complex([
-        undefined, undefined, undefined, [],
-        undefined, undefined, undefined, undefined]);
+      undefined, undefined, undefined, [], undefined, undefined, undefined,
+      undefined
+    ]);
     var bar = new proto.jspb.test.Complex([
-        undefined, undefined, undefined, [],
-        undefined, undefined, undefined, undefined]);
+      undefined, undefined, undefined, [], undefined, undefined, undefined,
+      undefined
+    ]);
     var result = foo.toObject();
-    assertObjectEquals({
-      aString: undefined,
-      anOutOfOrderBool: undefined,
-      aNestedMessage: {
-        anInt: undefined
-      },
-      // Note: JsPb converts undefined repeated fields to empty arrays.
-      aRepeatedMessageList: [],
-      aRepeatedStringList: []
-    }, result);
-
+    assertObjectEquals(
+        {
+          aString: undefined,
+          anOutOfOrderBool: undefined,
+          aNestedMessage: {anInt: undefined},
+          // Note: JsPb converts undefined repeated fields to empty arrays.
+          aRepeatedMessageList: [],
+          aRepeatedStringList: []
+        },
+        result);
   });
 
   it('testNestedComplexMessage', function() {
@@ -167,20 +166,21 @@
   it('testSpecialCases', function() {
     // Note: Some property names are reserved in JavaScript.
     // These names are converted to the Js property named pb_<reserved_name>.
-    var special =
-        new proto.jspb.test.SpecialCases(['normal', 'default', 'function',
-        'var']);
+    var special = new proto.jspb.test.SpecialCases(
+        ['normal', 'default', 'function', 'var']);
     var result = special.toObject();
-    assertObjectEquals({
-      normal: 'normal',
-      pb_default: 'default',
-      pb_function: 'function',
-      pb_var: 'var'
-    }, result);
+    assertObjectEquals(
+        {
+          normal: 'normal',
+          pb_default: 'default',
+          pb_function: 'function',
+          pb_var: 'var'
+        },
+        result);
   });
 
   it('testDefaultValues', function() {
-    var defaultString = "default<>\'\"abc";
+    var defaultString = 'default<>\'"abc';
     var response = new proto.jspb.test.DefaultValues();
 
     // Test toObject
@@ -244,8 +244,10 @@
 
     // Test that clearing the values reverts them to the default state.
     response = makeDefault(['blah', false, 111, 77]);
-    response.clearStringField(); response.clearBoolField();
-    response.clearIntField(); response.clearEnumField();
+    response.clearStringField();
+    response.clearBoolField();
+    response.clearIntField();
+    response.clearEnumField();
     assertEquals(defaultString, response.getStringField());
     assertEquals(true, response.getBoolField());
     assertEquals(11, response.getIntField());
@@ -257,8 +259,10 @@
 
     // Test that setFoo(null) clears the values.
     response = makeDefault(['blah', false, 111, 77]);
-    response.setStringField(null); response.setBoolField(null);
-    response.setIntField(undefined); response.setEnumField(undefined);
+    response.setStringField(null);
+    response.setBoolField(null);
+    response.setIntField(undefined);
+    response.setEnumField(undefined);
     assertEquals(defaultString, response.getStringField());
     assertEquals(true, response.getBoolField());
     assertEquals(11, response.getIntField());
@@ -288,15 +292,15 @@
     // but we actually get a sparse array instead. We could use something
     // like [1,undefined,2] to avoid this, except that this is still
     // sparse on IE. No comment...
-    var expected = [,,, [], []];
+    var expected = [, , , [], []];
     expected[0] = expected[1] = expected[2] = undefined;
     assertObjectEquals(expected, foo.toArray());
   });
 
   it('testDifferenceRawObject', /** @suppress {visibility} */ function() {
     var p1 = new proto.jspb.test.HasExtensions(['hi', 'diff', {}]);
-    var p2 = new proto.jspb.test.HasExtensions(['hi', 'what',
-                                               {1000: 'unique'}]);
+    var p2 =
+        new proto.jspb.test.HasExtensions(['hi', 'what', {1000: 'unique'}]);
     var diff = /** @type {proto.jspb.test.HasExtensions} */
         (jspb.Message.difference(p1, p2));
     assertEquals('', diff.getStr1());
@@ -310,13 +314,13 @@
     assertTrue(jspb.Message.equals(s1, new proto.jspb.test.Simple1(['hi'])));
     assertFalse(jspb.Message.equals(s1, new proto.jspb.test.Simple1(['bye'])));
     var s1b = new proto.jspb.test.Simple1(['hi', ['hello']]);
-    assertTrue(jspb.Message.equals(s1b,
-        new proto.jspb.test.Simple1(['hi', ['hello']])));
-    assertTrue(jspb.Message.equals(s1b,
-        new proto.jspb.test.Simple1(['hi', ['hello', undefined,
-                                            undefined, undefined]])));
-    assertFalse(jspb.Message.equals(s1b,
-        new proto.jspb.test.Simple1(['no', ['hello']])));
+    assertTrue(jspb.Message.equals(
+        s1b, new proto.jspb.test.Simple1(['hi', ['hello']])));
+    assertTrue(jspb.Message.equals(s1b, new proto.jspb.test.Simple1([
+      'hi', ['hello', undefined, undefined, undefined]
+    ])));
+    assertFalse(jspb.Message.equals(
+        s1b, new proto.jspb.test.Simple1(['no', ['hello']])));
     // Test with messages of different types
     var s2 = new proto.jspb.test.Simple2(['hi']);
     assertFalse(jspb.Message.equals(s1, s2));
@@ -324,18 +328,18 @@
 
   it('testEquals_softComparison', function() {
     var s1 = new proto.jspb.test.Simple1(['hi', [], null]);
-    assertTrue(jspb.Message.equals(s1,
-        new proto.jspb.test.Simple1(['hi', []])));
+    assertTrue(
+        jspb.Message.equals(s1, new proto.jspb.test.Simple1(['hi', []])));
 
     var s1b = new proto.jspb.test.Simple1(['hi', [], true]);
-    assertTrue(jspb.Message.equals(s1b,
-        new proto.jspb.test.Simple1(['hi', [], 1])));
+    assertTrue(
+        jspb.Message.equals(s1b, new proto.jspb.test.Simple1(['hi', [], 1])));
   });
 
   it('testEqualsComplex', function() {
-    var data1 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1];
-    var data2 = ['a',,, [, 11], [[, 22], [, 34]],, ['s1', 's2'],, 1];
-    var data3 = ['a',,, [, 11], [[, 22]],, ['s1', 's2'],, 1];
+    var data1 = ['a', , , [, 11], [[, 22], [, 33]], , ['s1', 's2'], , 1];
+    var data2 = ['a', , , [, 11], [[, 22], [, 34]], , ['s1', 's2'], , 1];
+    var data3 = ['a', , , [, 11], [[, 22]], , ['s1', 's2'], , 1];
     var data4 = ['hi'];
     var c1a = new proto.jspb.test.Complex(data1);
     var c1b = new proto.jspb.test.Complex(data1);
@@ -352,42 +356,34 @@
   it('testEqualsExtensionsConstructed', function() {
     assertTrue(jspb.Message.equals(
         new proto.jspb.test.HasExtensions([]),
-        new proto.jspb.test.HasExtensions([{}])
-    ));
+        new proto.jspb.test.HasExtensions([{}])));
     assertTrue(jspb.Message.equals(
         new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}]),
-        new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}])
-    ));
+        new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}])));
     assertFalse(jspb.Message.equals(
         new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}]),
-        new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'b'}]}])
-    ));
+        new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'b'}]}])));
     assertTrue(jspb.Message.equals(
         new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}]),
-        new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}])
-    ));
+        new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}])));
     assertTrue(jspb.Message.equals(
         new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}]),
-        new proto.jspb.test.HasExtensions([,,, {100: [{200: 'a'}]}])
-    ));
+        new proto.jspb.test.HasExtensions([, , , {100: [{200: 'a'}]}])));
     assertTrue(jspb.Message.equals(
-        new proto.jspb.test.HasExtensions([,,, {100: [{200: 'a'}]}]),
-        new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}])
-    ));
+        new proto.jspb.test.HasExtensions([, , , {100: [{200: 'a'}]}]),
+        new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}])));
     assertTrue(jspb.Message.equals(
         new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}]),
-        new proto.jspb.test.HasExtensions(['hi',,, {100: [{200: 'a'}]}])
-    ));
+        new proto.jspb.test.HasExtensions(['hi', , , {100: [{200: 'a'}]}])));
     assertTrue(jspb.Message.equals(
-        new proto.jspb.test.HasExtensions(['hi',,, {100: [{200: 'a'}]}]),
-        new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}])
-    ));
+        new proto.jspb.test.HasExtensions(['hi', , , {100: [{200: 'a'}]}]),
+        new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}])));
   });
 
   it('testEqualsExtensionsUnconstructed', function() {
     assertTrue(jspb.Message.compareFields([], [{}]));
-    assertTrue(jspb.Message.compareFields([,,, {}], []));
-    assertTrue(jspb.Message.compareFields([,,, {}], [,, {}]));
+    assertTrue(jspb.Message.compareFields([, , , {}], []));
+    assertTrue(jspb.Message.compareFields([, , , {}], [, , {}]));
     assertTrue(jspb.Message.compareFields(
         ['hi', {100: [{200: 'a'}]}], ['hi', {100: [{200: 'a'}]}]));
     assertFalse(jspb.Message.compareFields(
@@ -395,25 +391,25 @@
     assertTrue(jspb.Message.compareFields(
         [{100: [{200: 'a'}]}], [{100: [{200: 'a'}]}]));
     assertTrue(jspb.Message.compareFields(
-        [{100: [{200: 'a'}]}], [,,, {100: [{200: 'a'}]}]));
+        [{100: [{200: 'a'}]}], [, , , {100: [{200: 'a'}]}]));
     assertTrue(jspb.Message.compareFields(
-        [,,, {100: [{200: 'a'}]}], [{100: [{200: 'a'}]}]));
+        [, , , {100: [{200: 'a'}]}], [{100: [{200: 'a'}]}]));
     assertTrue(jspb.Message.compareFields(
-        ['hi', {100: [{200: 'a'}]}], ['hi',,, {100: [{200: 'a'}]}]));
+        ['hi', {100: [{200: 'a'}]}], ['hi', , , {100: [{200: 'a'}]}]));
     assertTrue(jspb.Message.compareFields(
-        ['hi',,, {100: [{200: 'a'}]}], ['hi', {100: [{200: 'a'}]}]));
+        ['hi', , , {100: [{200: 'a'}]}], ['hi', {100: [{200: 'a'}]}]));
   });
 
   it('testToMap', function() {
     var p1 = new proto.jspb.test.Simple1(['k', ['v']]);
     var p2 = new proto.jspb.test.Simple1(['k1', ['v1', 'v2']]);
-    var soymap = jspb.Message.toMap([p1, p2],
-        proto.jspb.test.Simple1.prototype.getAString,
+    var soymap = jspb.Message.toMap(
+        [p1, p2], proto.jspb.test.Simple1.prototype.getAString,
         proto.jspb.test.Simple1.prototype.toObject);
     assertEquals('k', soymap['k'].aString);
     assertArrayEquals(['v'], soymap['k'].aRepeatedStringList);
-    var protomap = jspb.Message.toMap([p1, p2],
-        proto.jspb.test.Simple1.prototype.getAString);
+    var protomap = jspb.Message.toMap(
+        [p1, p2], proto.jspb.test.Simple1.prototype.getAString);
     assertEquals('k', protomap['k'].getAString());
     assertArrayEquals(['v'], protomap['k'].getARepeatedStringList());
   });
@@ -434,8 +430,12 @@
     extension.setExt('e1');
     original.setExtension(proto.jspb.test.IsExtension.extField, extension);
     var clone = original.clone();
-    assertArrayEquals(['v1',, ['x1', ['y1', 'z1']],,
-      [['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]], bytes1,, { 100: [, 'e1'] }],
+    assertArrayEquals(
+        [
+          'v1', , ['x1', ['y1', 'z1']], ,
+          [['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]], bytes1, ,
+          {100: [, 'e1']}
+        ],
         clone.toArray());
     clone.setStr('v2');
     var simple4 = new proto.jspb.test.Simple1(['a1', ['b1', 'c1']]);
@@ -452,11 +452,19 @@
     var newExtension = new proto.jspb.test.CloneExtension();
     newExtension.setExt('e2');
     clone.setExtension(proto.jspb.test.CloneExtension.extField, newExtension);
-    assertArrayEquals(['v2',, ['a1', ['b1', 'c1']],,
-      [['a2', ['b2', 'c2']], ['a3', ['b3', 'c3']]], bytes2,, { 100: [, 'e2'] }],
+    assertArrayEquals(
+        [
+          'v2', , ['a1', ['b1', 'c1']], ,
+          [['a2', ['b2', 'c2']], ['a3', ['b3', 'c3']]], bytes2, ,
+          {100: [, 'e2']}
+        ],
         clone.toArray());
-    assertArrayEquals(['v1',, ['x1', ['y1', 'z1']],,
-      [['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]], bytes1,, { 100: [, 'e1'] }],
+    assertArrayEquals(
+        [
+          'v1', , ['x1', ['y1', 'z1']], ,
+          [['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]], bytes1, ,
+          {100: [, 'e1']}
+        ],
         original.toArray());
   });
 
@@ -488,11 +496,12 @@
     jspb.Message.copyInto(original, dest);
     assertArrayEquals(original.toArray(), dest.toArray());
     assertEquals('x1', dest.getSimple1().getAString());
-    assertEquals('e1',
+    assertEquals(
+        'e1',
         dest.getExtension(proto.jspb.test.CloneExtension.extField).getExt());
     dest.getSimple1().setAString('new value');
-    assertNotEquals(dest.getSimple1().getAString(),
-        original.getSimple1().getAString());
+    assertNotEquals(
+        dest.getSimple1().getAString(), original.getSimple1().getAString());
     if (supportsUint8Array) {
       dest.getBytesField()[0] = 7;
       assertObjectEquals(bytes1, original.getBytesField());
@@ -502,12 +511,12 @@
       assertObjectEquals(bytes1, original.getBytesField());
       assertObjectEquals('789', dest.getBytesField());
     }
-    dest.getExtension(proto.jspb.test.CloneExtension.extField).
-        setExt('new value');
+    dest.getExtension(proto.jspb.test.CloneExtension.extField)
+        .setExt('new value');
     assertNotEquals(
         dest.getExtension(proto.jspb.test.CloneExtension.extField).getExt(),
-        original.getExtension(
-            proto.jspb.test.CloneExtension.extField).getExt());
+        original.getExtension(proto.jspb.test.CloneExtension.extField)
+            .getExt());
   });
 
   it('testCopyInto_notSameType', function() {
@@ -525,26 +534,32 @@
     var extension2 = new proto.jspb.test.Simple1(['str', ['s1', 's2']]);
     var extendable = new proto.jspb.test.HasExtensions(['v1', 'v2', 'v3']);
     extendable.setExtension(proto.jspb.test.IsExtension.extField, extension1);
-    extendable.setExtension(proto.jspb.test.IndirectExtension.simple,
-                            extension2);
+    extendable.setExtension(
+        proto.jspb.test.IndirectExtension.simple, extension2);
     extendable.setExtension(proto.jspb.test.IndirectExtension.str, 'xyzzy');
-    extendable.setExtension(proto.jspb.test.IndirectExtension.repeatedStrList,
-        ['a', 'b']);
+    extendable.setExtension(
+        proto.jspb.test.IndirectExtension.repeatedStrList, ['a', 'b']);
     var s1 = new proto.jspb.test.Simple1(['foo', ['s1', 's2']]);
     var s2 = new proto.jspb.test.Simple1(['bar', ['t1', 't2']]);
     extendable.setExtension(
-        proto.jspb.test.IndirectExtension.repeatedSimpleList,
-        [s1, s2]);
-    assertObjectEquals(extension1,
+        proto.jspb.test.IndirectExtension.repeatedSimpleList, [s1, s2]);
+    assertObjectEquals(
+        extension1,
         extendable.getExtension(proto.jspb.test.IsExtension.extField));
-    assertObjectEquals(extension2,
+    assertObjectEquals(
+        extension2,
         extendable.getExtension(proto.jspb.test.IndirectExtension.simple));
-    assertObjectEquals('xyzzy',
+    assertObjectEquals(
+        'xyzzy',
         extendable.getExtension(proto.jspb.test.IndirectExtension.str));
-    assertObjectEquals(['a', 'b'], extendable.getExtension(
-        proto.jspb.test.IndirectExtension.repeatedStrList));
-    assertObjectEquals([s1, s2], extendable.getExtension(
-        proto.jspb.test.IndirectExtension.repeatedSimpleList));
+    assertObjectEquals(
+        ['a', 'b'],
+        extendable.getExtension(
+            proto.jspb.test.IndirectExtension.repeatedStrList));
+    assertObjectEquals(
+        [s1, s2],
+        extendable.getExtension(
+            proto.jspb.test.IndirectExtension.repeatedSimpleList));
     // Not supported yet, but it should work...
     extendable.setExtension(proto.jspb.test.IndirectExtension.simple, null);
     assertNull(
@@ -563,18 +578,18 @@
     var extendable = new proto.jspb.test.HasExtensions(['v1', 'v2', 'v3']);
     var extension = new proto.jspb.test.Simple1(['foo', ['s1', 's2']]);
     extendable.setExtension(proto.jspb.test.simple1, extension);
-    assertObjectEquals(extension,
-        extendable.getExtension(proto.jspb.test.simple1));
+    assertObjectEquals(
+        extension, extendable.getExtension(proto.jspb.test.simple1));
 
     // From _lib mode.
     extension = new proto.jspb.test.ExtensionMessage(['s1']);
     extendable = new proto.jspb.test.TestExtensionsMessage([16]);
     extendable.setExtension(proto.jspb.test.floatingMsgField, extension);
     extendable.setExtension(proto.jspb.test.floatingStrField, 's2');
-    assertObjectEquals(extension,
-        extendable.getExtension(proto.jspb.test.floatingMsgField));
-    assertObjectEquals('s2',
-        extendable.getExtension(proto.jspb.test.floatingStrField));
+    assertObjectEquals(
+        extension, extendable.getExtension(proto.jspb.test.floatingMsgField));
+    assertObjectEquals(
+        's2', extendable.getExtension(proto.jspb.test.floatingStrField));
     assertNotUndefined(proto.jspb.exttest.floatingMsgField);
     assertNotUndefined(proto.jspb.exttest.floatingMsgFieldTwo);
     assertNotUndefined(proto.jspb.exttest.beta.floatingStrField);
@@ -585,60 +600,72 @@
     var extension2 = new proto.jspb.test.Simple1(['str', ['s1', 's2'], true]);
     var extendable = new proto.jspb.test.HasExtensions(['v1', 'v2', 'v3']);
     extendable.setExtension(proto.jspb.test.IsExtension.extField, extension1);
-    extendable.setExtension(proto.jspb.test.IndirectExtension.simple,
-                            extension2);
+    extendable.setExtension(
+        proto.jspb.test.IndirectExtension.simple, extension2);
     extendable.setExtension(proto.jspb.test.IndirectExtension.str, 'xyzzy');
-    extendable.setExtension(proto.jspb.test.IndirectExtension.repeatedStrList,
-        ['a', 'b']);
+    extendable.setExtension(
+        proto.jspb.test.IndirectExtension.repeatedStrList, ['a', 'b']);
     var s1 = new proto.jspb.test.Simple1(['foo', ['s1', 's2'], true]);
     var s2 = new proto.jspb.test.Simple1(['bar', ['t1', 't2'], false]);
     extendable.setExtension(
-        proto.jspb.test.IndirectExtension.repeatedSimpleList,
-        [s1, s2]);
-    assertObjectEquals({
-      str1: 'v1', str2: 'v2', str3: 'v3',
-      extField: { ext1: 'ext1field' },
-      simple: {
-        aString: 'str', aRepeatedStringList: ['s1', 's2'], aBoolean: true
-      },
-      str: 'xyzzy',
-      repeatedStrList: ['a', 'b'],
-      repeatedSimpleList: [
-        { aString: 'foo', aRepeatedStringList: ['s1', 's2'], aBoolean: true},
-        { aString: 'bar', aRepeatedStringList: ['t1', 't2'], aBoolean: false}
-      ]
-    }, extendable.toObject());
+        proto.jspb.test.IndirectExtension.repeatedSimpleList, [s1, s2]);
+    assertObjectEquals(
+        {
+          str1: 'v1',
+          str2: 'v2',
+          str3: 'v3',
+          extField: {ext1: 'ext1field'},
+          simple: {
+            aString: 'str',
+            aRepeatedStringList: ['s1', 's2'],
+            aBoolean: true
+          },
+          str: 'xyzzy',
+          repeatedStrList: ['a', 'b'],
+          repeatedSimpleList: [
+            {aString: 'foo', aRepeatedStringList: ['s1', 's2'], aBoolean: true},
+            {aString: 'bar', aRepeatedStringList: ['t1', 't2'], aBoolean: false}
+          ]
+        },
+        extendable.toObject());
 
     // Now, with instances included.
-    assertObjectEquals({
-      str1: 'v1', str2: 'v2', str3: 'v3',
-      extField: {
-        ext1: 'ext1field',
-        $jspbMessageInstance:
-            extendable.getExtension(proto.jspb.test.IsExtension.extField)
-      },
-      simple: {
-        aString: 'str',
-        aRepeatedStringList: ['s1', 's2'],
-        aBoolean: true,
-        $jspbMessageInstance:
-            extendable.getExtension(proto.jspb.test.IndirectExtension.simple)
-      },
-      str: 'xyzzy',
-      repeatedStrList: ['a', 'b'],
-      repeatedSimpleList: [{
-        aString: 'foo',
-        aRepeatedStringList: ['s1', 's2'],
-        aBoolean: true,
-        $jspbMessageInstance: s1
-      }, {
-        aString: 'bar',
-        aRepeatedStringList: ['t1', 't2'],
-        aBoolean: false,
-        $jspbMessageInstance: s2
-      }],
-      $jspbMessageInstance: extendable
-    }, extendable.toObject(true /* opt_includeInstance */));
+    assertObjectEquals(
+        {
+          str1: 'v1',
+          str2: 'v2',
+          str3: 'v3',
+          extField: {
+            ext1: 'ext1field',
+            $jspbMessageInstance:
+                extendable.getExtension(proto.jspb.test.IsExtension.extField)
+          },
+          simple: {
+            aString: 'str',
+            aRepeatedStringList: ['s1', 's2'],
+            aBoolean: true,
+            $jspbMessageInstance: extendable.getExtension(
+                proto.jspb.test.IndirectExtension.simple)
+          },
+          str: 'xyzzy',
+          repeatedStrList: ['a', 'b'],
+          repeatedSimpleList: [
+            {
+              aString: 'foo',
+              aRepeatedStringList: ['s1', 's2'],
+              aBoolean: true,
+              $jspbMessageInstance: s1
+            },
+            {
+              aString: 'bar',
+              aRepeatedStringList: ['t1', 't2'],
+              aBoolean: false,
+              $jspbMessageInstance: s2
+            }
+          ],
+          $jspbMessageInstance: extendable
+        },
+        extendable.toObject(true /* opt_includeInstance */));
   });
 
   it('testInitialization_emptyArray', function() {
@@ -690,8 +717,7 @@
     var extensionMessage = new proto.jspb.test.IsExtension(['is_extension']);
     data.setExtension(proto.jspb.test.IsExtension.extField, extensionMessage);
     var obj = data.toObject();
-    assertNotNull(
-        data.getExtension(proto.jspb.test.IsExtension.extField));
+    assertNotNull(data.getExtension(proto.jspb.test.IsExtension.extField));
     assertEquals('is_extension', obj.extField.ext1);
   });
 
@@ -708,16 +734,18 @@
     var groups = group.getRepeatedGroupList();
     assertEquals('g1', groups[0].getId());
     assertObjectEquals([true, false], groups[0].getSomeBoolList());
-    assertObjectEquals({id: 'g1', someBoolList: [true, false]},
-        groups[0].toObject());
-    assertObjectEquals({
-      repeatedGroupList: [{id: 'g1', someBoolList: [true, false]}],
-      requiredGroup: {id: undefined},
-      optionalGroup: undefined,
-      requiredSimple: {aRepeatedStringList: [], aString: undefined},
-      optionalSimple: undefined,
-      id: undefined
-    }, group.toObject());
+    assertObjectEquals(
+        {id: 'g1', someBoolList: [true, false]}, groups[0].toObject());
+    assertObjectEquals(
+        {
+          repeatedGroupList: [{id: 'g1', someBoolList: [true, false]}],
+          requiredGroup: {id: undefined},
+          optionalGroup: undefined,
+          requiredSimple: {aRepeatedStringList: [], aString: undefined},
+          optionalSimple: undefined,
+          id: undefined
+        },
+        group.toObject());
     var group1 = new proto.jspb.test.TestGroup1();
     group1.setGroup(someGroup);
     assertEquals(someGroup, group1.getGroup());
@@ -734,25 +762,26 @@
     message.setExtension$(11);
     message.setExtension(proto.jspb.test.TestReservedNamesExtension.foo, 12);
     assertEquals(11, message.getExtension$());
-    assertEquals(12, message.getExtension(
-        proto.jspb.test.TestReservedNamesExtension.foo));
+    assertEquals(
+        12,
+        message.getExtension(proto.jspb.test.TestReservedNamesExtension.foo));
     assertObjectEquals({extension: 11, foo: 12}, message.toObject());
   });
 
   it('testInitializeMessageWithUnsetOneof', function() {
     var message = new proto.jspb.test.TestMessageWithOneof([]);
     assertEquals(
-        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.
-            PARTIAL_ONEOF_NOT_SET,
+        proto.jspb.test.TestMessageWithOneof.PartialOneofCase
+            .PARTIAL_ONEOF_NOT_SET,
         message.getPartialOneofCase());
     assertEquals(
-        proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.
-            RECURSIVE_ONEOF_NOT_SET,
+        proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase
+            .RECURSIVE_ONEOF_NOT_SET,
         message.getRecursiveOneofCase());
   });
 
   it('testInitializeMessageWithSingleValueSetInOneof', function() {
-    var message = new proto.jspb.test.TestMessageWithOneof([,, 'x']);
+    var message = new proto.jspb.test.TestMessageWithOneof([, , 'x']);
 
     assertEquals('x', message.getPone());
     assertEquals('', message.getPthree());
@@ -762,7 +791,7 @@
   });
 
   it('testKeepsLastWireValueSetInUnion_multipleValues', function() {
-    var message = new proto.jspb.test.TestMessageWithOneof([,, 'x',, 'y']);
+    var message = new proto.jspb.test.TestMessageWithOneof([, , 'x', , 'y']);
 
     assertEquals('', message.getPone());
     assertEquals('y', message.getPthree());
@@ -819,8 +848,8 @@
   it('testUnsetsOneofCaseWhenFieldIsCleared', function() {
     var message = new proto.jspb.test.TestMessageWithOneof;
     assertEquals(
-        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.
-            PARTIAL_ONEOF_NOT_SET,
+        proto.jspb.test.TestMessageWithOneof.PartialOneofCase
+            .PARTIAL_ONEOF_NOT_SET,
         message.getPartialOneofCase());
 
     message.setPone('hi');
@@ -830,8 +859,8 @@
 
     message.clearPone();
     assertEquals(
-        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.
-            PARTIAL_ONEOF_NOT_SET,
+        proto.jspb.test.TestMessageWithOneof.PartialOneofCase
+            .PARTIAL_ONEOF_NOT_SET,
         message.getPartialOneofCase());
   });
 
@@ -934,39 +963,39 @@
   });
 
   it('testInitializeMessageWithOneofDefaults_defaultNotSetOnFirstField',
-      function() {
-        var message;
+     function() {
+       var message;
 
-        message =
-            new proto.jspb.test.TestMessageWithOneof(new Array(11).concat(567));
-        assertEquals(567, message.getBone());
-        assertEquals(1234, message.getBtwo());
-        assertEquals(
-            proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BONE,
-            message.getDefaultOneofBCase());
+       message =
+           new proto.jspb.test.TestMessageWithOneof(new Array(11).concat(567));
+       assertEquals(567, message.getBone());
+       assertEquals(1234, message.getBtwo());
+       assertEquals(
+           proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BONE,
+           message.getDefaultOneofBCase());
 
-        message =
-            new proto.jspb.test.TestMessageWithOneof(new Array(12).concat(890));
-        assertEquals(0, message.getBone());
-        assertEquals(890, message.getBtwo());
-        assertEquals(
-            proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO,
-            message.getDefaultOneofBCase());
+       message =
+           new proto.jspb.test.TestMessageWithOneof(new Array(12).concat(890));
+       assertEquals(0, message.getBone());
+       assertEquals(890, message.getBtwo());
+       assertEquals(
+           proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO,
+           message.getDefaultOneofBCase());
 
-        message = new proto.jspb.test.TestMessageWithOneof(
-            new Array(11).concat(567, 890));
-        assertEquals(0, message.getBone());
-        assertEquals(890, message.getBtwo());
-        assertEquals(
-            proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO,
-            message.getDefaultOneofBCase());
-      });
+       message = new proto.jspb.test.TestMessageWithOneof(
+           new Array(11).concat(567, 890));
+       assertEquals(0, message.getBone());
+       assertEquals(890, message.getBtwo());
+       assertEquals(
+           proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO,
+           message.getDefaultOneofBCase());
+     });
 
   it('testOneofContainingAnotherMessage', function() {
     var message = new proto.jspb.test.TestMessageWithOneof;
     assertEquals(
-        proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.
-            RECURSIVE_ONEOF_NOT_SET,
+        proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase
+            .RECURSIVE_ONEOF_NOT_SET,
         message.getRecursiveOneofCase());
 
     var other = new proto.jspb.test.TestMessageWithOneof;
@@ -987,25 +1016,25 @@
 
   it('testQueryingOneofCaseEnsuresOnlyOneFieldIsSetInUnderlyingArray',
      function() {
-    var message = new proto.jspb.test.TestMessageWithOneof;
-    message.setPone('x');
-    assertEquals('x', message.getPone());
-    assertEquals('', message.getPthree());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PONE,
-        message.getPartialOneofCase());
+       var message = new proto.jspb.test.TestMessageWithOneof;
+       message.setPone('x');
+       assertEquals('x', message.getPone());
+       assertEquals('', message.getPthree());
+       assertEquals(
+           proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PONE,
+           message.getPartialOneofCase());
 
-    var array = message.toArray();
-    assertEquals('x', array[2]);
-    assertUndefined(array[4]);
-    array[4] = 'y';
+       var array = message.toArray();
+       assertEquals('x', array[2]);
+       assertUndefined(array[4]);
+       array[4] = 'y';
 
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PTHREE,
-        message.getPartialOneofCase());
-    assertUndefined(array[2]);
-    assertEquals('y', array[4]);
-  });
+       assertEquals(
+           proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PTHREE,
+           message.getPartialOneofCase());
+       assertUndefined(array[2]);
+       assertEquals('y', array[4]);
+     });
 
   it('testFloatingPointFieldsSupportNan', function() {
     var assertNan = function(x) {
@@ -1015,8 +1044,7 @@
     };
 
     var message = new proto.jspb.test.FloatingPointFields([
-      'NaN', 'NaN', ['NaN', 'NaN'], 'NaN',
-      'NaN', 'NaN', ['NaN', 'NaN'], 'NaN'
+      'NaN', 'NaN', ['NaN', 'NaN'], 'NaN', 'NaN', 'NaN', ['NaN', 'NaN'], 'NaN'
     ]);
     assertNan(message.getOptionalFloatField());
     assertNan(message.getRequiredFloatField());
@@ -1029,5 +1057,4 @@
     assertNan(message.getRepeatedDoubleFieldList()[1]);
     assertNan(message.getDefaultDoubleField());
   });
-
 });
diff --git a/js/maps_test.js b/js/maps_test.js
index 1cbff7b..68ed683 100644
--- a/js/maps_test.js
+++ b/js/maps_test.js
@@ -53,6 +53,8 @@
 goog.require('proto.jspb.test.MapValueMessageNoBinary');
 goog.require('proto.jspb.test.TestMapFieldsNoBinary');
 
+goog.requireType('jspb.Map');
+
 /**
  * Helper: check that the given map has exactly this set of (sorted) entries.
  * @param {!jspb.Map} map
@@ -116,7 +118,9 @@
     msg.getMapStringMsgMap().get('k').setFoo(42);
     msg.getMapStringMsgMap().get('l').setFoo(84);
     msg.getMapInt32StringMap().set(-1, 'a').set(42, 'b');
-    msg.getMapInt64StringMap().set(0x123456789abc, 'c').set(0xcba987654321, 'd');
+    msg.getMapInt64StringMap()
+        .set(0x123456789abc, 'c')
+        .set(0xcba987654321, 'd');
     msg.getMapBoolStringMap().set(false, 'e').set(true, 'f');
   };
 
@@ -125,42 +129,24 @@
    * @param {?} msg
    */
   var checkMapFields = function(msg) {
-    checkMapEquals(msg.getMapStringStringMap(), [
-          ['asdf', 'jkl;'],
-          ['key 2', 'hello world']
-    ]);
-    checkMapEquals(msg.getMapStringInt32Map(), [
-          ['a', 1],
-          ['b', -2]
-    ]);
-    checkMapEquals(msg.getMapStringInt64Map(), [
-          ['c', 0x100000000],
-          ['d', 0x200000000]
-    ]);
-    checkMapEquals(msg.getMapStringBoolMap(), [
-          ['e', true],
-          ['f', false]
-    ]);
-    checkMapEquals(msg.getMapStringDoubleMap(), [
-          ['g', 3.14159],
-          ['h', 2.71828]
-    ]);
+    checkMapEquals(
+        msg.getMapStringStringMap(),
+        [['asdf', 'jkl;'], ['key 2', 'hello world']]);
+    checkMapEquals(msg.getMapStringInt32Map(), [['a', 1], ['b', -2]]);
+    checkMapEquals(
+        msg.getMapStringInt64Map(), [['c', 0x100000000], ['d', 0x200000000]]);
+    checkMapEquals(msg.getMapStringBoolMap(), [['e', true], ['f', false]]);
+    checkMapEquals(
+        msg.getMapStringDoubleMap(), [['g', 3.14159], ['h', 2.71828]]);
     checkMapEquals(msg.getMapStringEnumMap(), [
-          ['i', proto.jspb.test.MapValueEnum.MAP_VALUE_BAR],
-          ['j', proto.jspb.test.MapValueEnum.MAP_VALUE_BAZ]
+      ['i', proto.jspb.test.MapValueEnum.MAP_VALUE_BAR],
+      ['j', proto.jspb.test.MapValueEnum.MAP_VALUE_BAZ]
     ]);
-    checkMapEquals(msg.getMapInt32StringMap(), [
-          [-1, 'a'],
-          [42, 'b']
-    ]);
-    checkMapEquals(msg.getMapInt64StringMap(), [
-          [0x123456789abc, 'c'],
-          [0xcba987654321, 'd']
-    ]);
-    checkMapEquals(msg.getMapBoolStringMap(), [
-          [false, 'e'],
-          [true, 'f']
-    ]);
+    checkMapEquals(msg.getMapInt32StringMap(), [[-1, 'a'], [42, 'b']]);
+    checkMapEquals(
+        msg.getMapInt64StringMap(),
+        [[0x123456789abc, 'c'], [0xcba987654321, 'd']]);
+    checkMapEquals(msg.getMapBoolStringMap(), [[false, 'e'], [true, 'f']]);
 
     assertEquals(msg.getMapStringMsgMap().getLength(), 2);
     assertEquals(msg.getMapStringMsgMap().get('k').getFoo(), 42);
@@ -205,10 +191,7 @@
     assertElementsEquals(it.next().value, ['asdf', 'hello world']);
     assertElementsEquals(it.next().value, ['jkl;', 'key 2']);
     assertEquals(it.next().done, true);
-    checkMapEquals(m, [
-        ['asdf', 'hello world'],
-        ['jkl;', 'key 2']
-    ]);
+    checkMapEquals(m, [['asdf', 'hello world'], ['jkl;', 'key 2']]);
     m.del('jkl;');
     assertEquals(m.has('jkl;'), false);
     assertEquals(m.get('jkl;'), undefined);
@@ -260,11 +243,11 @@
       msg.getMapStringStringMap().set('A', 'a');
       var serialized = msg.serializeBinary();
       var expectedSerialized = [
-          0x0a, 0x6, // field 1 (map_string_string), delimited, length 6
-          0x0a, 0x1, // field 1 in submessage (key), delimited, length 1
-          0x41,      // ASCII 'A'
-          0x12, 0x1, // field 2 in submessage (value), delimited, length 1
-          0x61       // ASCII 'a'
+        0x0a, 0x6,  // field 1 (map_string_string), delimited, length 6
+        0x0a, 0x1,  // field 1 in submessage (key), delimited, length 1
+        0x41,       // ASCII 'A'
+        0x12, 0x1,  // field 2 in submessage (value), delimited, length 1
+        0x61        // ASCII 'a'
       ];
       assertEquals(serialized.length, expectedSerialized.length);
       for (var i = 0; i < serialized.length; i++) {
@@ -284,34 +267,27 @@
      * binary format.
      */
     it('testMapDeserializationForUndefinedKeys', function() {
-      var testMessageOptionalKeys = new proto.jspb.test.TestMapFieldsOptionalKeys();
-      var mapEntryStringKey = new proto.jspb.test.MapEntryOptionalKeysStringKey();
-      mapEntryStringKey.setValue("a");
+      var testMessageOptionalKeys =
+          new proto.jspb.test.TestMapFieldsOptionalKeys();
+      var mapEntryStringKey =
+          new proto.jspb.test.MapEntryOptionalKeysStringKey();
+      mapEntryStringKey.setValue('a');
       testMessageOptionalKeys.setMapStringString(mapEntryStringKey);
       var mapEntryInt32Key = new proto.jspb.test.MapEntryOptionalKeysInt32Key();
-      mapEntryInt32Key.setValue("b");
+      mapEntryInt32Key.setValue('b');
       testMessageOptionalKeys.setMapInt32String(mapEntryInt32Key);
       var mapEntryInt64Key = new proto.jspb.test.MapEntryOptionalKeysInt64Key();
-      mapEntryInt64Key.setValue("c");
+      mapEntryInt64Key.setValue('c');
       testMessageOptionalKeys.setMapInt64String(mapEntryInt64Key);
       var mapEntryBoolKey = new proto.jspb.test.MapEntryOptionalKeysBoolKey();
-      mapEntryBoolKey.setValue("d");
+      mapEntryBoolKey.setValue('d');
       testMessageOptionalKeys.setMapBoolString(mapEntryBoolKey);
-      var deserializedMessage = msgInfo.deserializeBinary(
-        testMessageOptionalKeys.serializeBinary()
-       );
-      checkMapEquals(deserializedMessage.getMapStringStringMap(), [
-        ['', 'a']
-      ]);
-      checkMapEquals(deserializedMessage.getMapInt32StringMap(), [
-        [0, 'b']
-      ]);
-      checkMapEquals(deserializedMessage.getMapInt64StringMap(), [
-        [0, 'c']
-      ]);
-      checkMapEquals(deserializedMessage.getMapBoolStringMap(), [
-        [false, 'd']
-      ]);
+      var deserializedMessage =
+          msgInfo.deserializeBinary(testMessageOptionalKeys.serializeBinary());
+      checkMapEquals(deserializedMessage.getMapStringStringMap(), [['', 'a']]);
+      checkMapEquals(deserializedMessage.getMapInt32StringMap(), [[0, 'b']]);
+      checkMapEquals(deserializedMessage.getMapInt64StringMap(), [[0, 'c']]);
+      checkMapEquals(deserializedMessage.getMapBoolStringMap(), [[false, 'd']]);
     });
 
     /**
@@ -323,56 +299,41 @@
           new proto.jspb.test.TestMapFieldsOptionalValues();
       var mapEntryStringValue =
           new proto.jspb.test.MapEntryOptionalValuesStringValue();
-      mapEntryStringValue.setKey("a");
+      mapEntryStringValue.setKey('a');
       testMessageOptionalValues.setMapStringString(mapEntryStringValue);
       var mapEntryInt32Value =
           new proto.jspb.test.MapEntryOptionalValuesInt32Value();
-      mapEntryInt32Value.setKey("b");
+      mapEntryInt32Value.setKey('b');
       testMessageOptionalValues.setMapStringInt32(mapEntryInt32Value);
       var mapEntryInt64Value =
           new proto.jspb.test.MapEntryOptionalValuesInt64Value();
-      mapEntryInt64Value.setKey("c");
+      mapEntryInt64Value.setKey('c');
       testMessageOptionalValues.setMapStringInt64(mapEntryInt64Value);
       var mapEntryBoolValue =
           new proto.jspb.test.MapEntryOptionalValuesBoolValue();
-      mapEntryBoolValue.setKey("d");
+      mapEntryBoolValue.setKey('d');
       testMessageOptionalValues.setMapStringBool(mapEntryBoolValue);
       var mapEntryDoubleValue =
           new proto.jspb.test.MapEntryOptionalValuesDoubleValue();
-      mapEntryDoubleValue.setKey("e");
+      mapEntryDoubleValue.setKey('e');
       testMessageOptionalValues.setMapStringDouble(mapEntryDoubleValue);
       var mapEntryEnumValue =
           new proto.jspb.test.MapEntryOptionalValuesEnumValue();
-      mapEntryEnumValue.setKey("f");
+      mapEntryEnumValue.setKey('f');
       testMessageOptionalValues.setMapStringEnum(mapEntryEnumValue);
       var mapEntryMessageValue =
           new proto.jspb.test.MapEntryOptionalValuesMessageValue();
-      mapEntryMessageValue.setKey("g");
+      mapEntryMessageValue.setKey('g');
       testMessageOptionalValues.setMapStringMsg(mapEntryMessageValue);
       var deserializedMessage = msgInfo.deserializeBinary(
-        testMessageOptionalValues.serializeBinary()
-       );
-      checkMapEquals(deserializedMessage.getMapStringStringMap(), [
-        ['a', '']
-      ]);
-      checkMapEquals(deserializedMessage.getMapStringInt32Map(), [
-        ['b', 0]
-      ]);
-      checkMapEquals(deserializedMessage.getMapStringInt64Map(), [
-        ['c', 0]
-      ]);
-      checkMapEquals(deserializedMessage.getMapStringBoolMap(), [
-        ['d', false]
-      ]);
-      checkMapEquals(deserializedMessage.getMapStringDoubleMap(), [
-        ['e', 0.0]
-      ]);
-      checkMapEquals(deserializedMessage.getMapStringEnumMap(), [
-        ['f', 0]
-      ]);
-      checkMapEquals(deserializedMessage.getMapStringMsgMap(), [
-        ['g', []]
-      ]);
+          testMessageOptionalValues.serializeBinary());
+      checkMapEquals(deserializedMessage.getMapStringStringMap(), [['a', '']]);
+      checkMapEquals(deserializedMessage.getMapStringInt32Map(), [['b', 0]]);
+      checkMapEquals(deserializedMessage.getMapStringInt64Map(), [['c', 0]]);
+      checkMapEquals(deserializedMessage.getMapStringBoolMap(), [['d', false]]);
+      checkMapEquals(deserializedMessage.getMapStringDoubleMap(), [['e', 0.0]]);
+      checkMapEquals(deserializedMessage.getMapStringEnumMap(), [['f', 0]]);
+      checkMapEquals(deserializedMessage.getMapStringMsgMap(), [['g', []]]);
     });
   }
 
@@ -382,11 +343,7 @@
    */
   it('testLazyMapSync' + suffix, function() {
     // Start with a JSPB array containing a few map entries.
-    var entries = [
-        ['a', 'entry 1'],
-        ['c', 'entry 2'],
-        ['b', 'entry 3']
-    ];
+    var entries = [['a', 'entry 1'], ['c', 'entry 2'], ['b', 'entry 3']];
     var msg = new msgInfo.constructor([entries]);
     assertEquals(entries.length, 3);
     assertEquals(entries[0][0], 'a');
@@ -394,9 +351,9 @@
     assertEquals(entries[2][0], 'b');
     msg.getMapStringStringMap().del('a');
     assertEquals(entries.length, 3);  // not yet sync'd
-    msg.toArray();                // force a sync
+    msg.toArray();                    // force a sync
     assertEquals(entries.length, 2);
-    assertEquals(entries[0][0], 'b'); // now in sorted order
+    assertEquals(entries[0][0], 'b');  // now in sorted order
     assertEquals(entries[1][0], 'c');
 
     var a = msg.toArray();
diff --git a/js/message_test.js b/js/message_test.js
index 3db8716..2a524de 100644
--- a/js/message_test.js
+++ b/js/message_test.js
@@ -121,6 +121,8 @@
 // CommonJS-LoadFromFile: testlargenumbers_pb proto.jspb.test
 goog.require('proto.jspb.test.MessageWithLargeFieldNumbers');
 
+goog.require('proto.jspb.test.simple1');
+
 describe('Message test suite', function() {
   var stubs = new goog.testing.PropertyReplacer();
 
@@ -185,7 +187,6 @@
           $jspbMessageInstance: foo
         },
         result);
-
   });
 
   it('testMissingFields', function() {
@@ -209,7 +210,6 @@
           aFloatingPointField: undefined,
         },
         result);
-
   });
 
   it('testNestedComplexMessage', function() {
@@ -1108,5 +1108,4 @@
     message.setAInt(42);
     assertEquals(42, message.getAInt());
   });
-
 });
diff --git a/js/proto3_test.js b/js/proto3_test.js
index fd59ae5..8de1575 100644
--- a/js/proto3_test.js
+++ b/js/proto3_test.js
@@ -41,7 +41,7 @@
 goog.require('proto.google.protobuf.Timestamp');
 // CommonJS-LoadFromFile: google/protobuf/struct_pb proto.google.protobuf
 goog.require('proto.google.protobuf.Struct');
-
+goog.require('jspb.Message');
 
 var BYTES = new Uint8Array([1, 2, 8, 9]);
 var BYTES_B64 = goog.crypt.base64.encodeByteArray(BYTES);
@@ -70,7 +70,6 @@
 
 
 describe('proto3Test', function() {
-
   /**
    * Test default values don't affect equality test.
    */
@@ -182,8 +181,8 @@
     assertEquals(msg.getOptionalBytes_asU8().length, 0);
     assertEquals(msg.getOptionalBytes_asB64(), '');
 
-    assertEquals(msg.getOptionalForeignEnum(),
-                 proto.jspb.test.Proto3Enum.PROTO3_FOO);
+    assertEquals(
+        msg.getOptionalForeignEnum(), proto.jspb.test.Proto3Enum.PROTO3_FOO);
     assertEquals(msg.getOptionalForeignMessage(), undefined);
     assertEquals(msg.getOptionalForeignMessage(), undefined);
 
@@ -309,7 +308,8 @@
     assertEquals(true, bytesCompare(msg.getRepeatedBytesList()[0], BYTES));
     assertEquals(msg.getRepeatedForeignMessageList().length, 1);
     assertEquals(msg.getRepeatedForeignMessageList()[0].getC(), 1000);
-    assertElementsEquals(msg.getRepeatedForeignEnumList(),
+    assertElementsEquals(
+        msg.getRepeatedForeignEnumList(),
         [proto.jspb.test.Proto3Enum.PROTO3_BAR]);
 
     assertEquals(msg.getOneofString(), 'asdf');
@@ -374,7 +374,8 @@
     assertEquals(msg.getOneofUint32(), 0);
     assertEquals(msg.getOneofForeignMessage(), undefined);
     assertEquals(msg.getOneofString(), '');
-    assertEquals(msg.getOneofBytes_asB64(),
+    assertEquals(
+        msg.getOneofBytes_asB64(),
         goog.crypt.base64.encodeString('\u00FF\u00FF'));
 
     assertFalse(msg.hasOneofUint32());
@@ -454,24 +455,24 @@
 
   it('testStructWellKnownType', function() {
     var jsObj = {
-      abc: "def",
+      abc: 'def',
       number: 12345.678,
       nullKey: null,
       boolKey: true,
-      listKey: [1, null, true, false, "abc"],
-      structKey: {foo: "bar", somenum: 123},
-      complicatedKey: [{xyz: {abc: [3, 4, null, false]}}, "zzz"]
+      listKey: [1, null, true, false, 'abc'],
+      structKey: {foo: 'bar', somenum: 123},
+      complicatedKey: [{xyz: {abc: [3, 4, null, false]}}, 'zzz']
     };
 
     var struct = proto.google.protobuf.Struct.fromJavaScript(jsObj);
     var jsObj2 = struct.toJavaScript();
 
-    assertEquals("def", jsObj2.abc);
+    assertEquals('def', jsObj2.abc);
     assertEquals(12345.678, jsObj2.number);
     assertEquals(null, jsObj2.nullKey);
     assertEquals(true, jsObj2.boolKey);
-    assertEquals("abc", jsObj2.listKey[4]);
-    assertEquals("bar", jsObj2.structKey.foo);
+    assertEquals('abc', jsObj2.listKey[4]);
+    assertEquals('bar', jsObj2.structKey.foo);
     assertEquals(4, jsObj2.complicatedKey[0].xyz.abc[1]);
   });
 });
diff --git a/kokoro/README.md b/kokoro/README.md
index 0791c92..590d7bd 100644
--- a/kokoro/README.md
+++ b/kokoro/README.md
@@ -3,4 +3,10 @@
 ----------------------
 
 The files in this directory serve as plumbing for running Protobuf
-tests under Kokoro, our internal CI.
\ No newline at end of file
+tests under Kokoro, our internal CI.
+
+We have shared this part of our CI configuration in hopes that it is
+helpful to contributors who want to better understand the details of
+our test and release processes. If there are changes, please file an
+issue; unfortunately, we may not be able to accept PRs (but feel free
+to send one if it helps to explain the issue).
diff --git a/kokoro/linux/dockerfile/test/php/Dockerfile b/kokoro/linux/dockerfile/test/php/Dockerfile
index f2fe836..4c9d69d 100644
--- a/kokoro/linux/dockerfile/test/php/Dockerfile
+++ b/kokoro/linux/dockerfile/test/php/Dockerfile
@@ -55,6 +55,26 @@
 # Download php source code
 RUN git clone https://github.com/php/php-src
 
+# php 5.6
+RUN cd php-src \
+  && git checkout PHP-5.6.39 \
+  && ./buildconf --force
+RUN cd php-src \
+  && ./configure \
+  --enable-bcmath \
+  --enable-mbstring \
+  --with-gmp \
+  --with-openssl \
+  --with-zlib \
+  --prefix=/usr/local/php-5.6 \
+  && make \
+  && make install \
+  && make clean
+
+RUN wget -O phpunit https://phar.phpunit.de/phpunit-5.phar \
+  && chmod +x phpunit \
+  && mv phpunit /usr/local/php-5.6/bin
+
 # php 7.0
 RUN cd php-src \
   && git checkout PHP-7.0.33 \
diff --git a/kokoro/linux/dockerfile/test/php_32bit/Dockerfile b/kokoro/linux/dockerfile/test/php_32bit/Dockerfile
index ed2acbe..d657af1 100644
--- a/kokoro/linux/dockerfile/test/php_32bit/Dockerfile
+++ b/kokoro/linux/dockerfile/test/php_32bit/Dockerfile
@@ -53,6 +53,25 @@
 # Download php source code
 RUN git clone https://github.com/php/php-src
 
+# php 5.6
+RUN cd php-src \
+  && git checkout PHP-5.6.39 \
+  && ./buildconf --force
+RUN cd php-src \
+  && ./configure \
+  --enable-bcmath \
+  --enable-mbstring \
+  --with-openssl \
+  --with-zlib \
+  --prefix=/usr/local/php-5.6 \
+  && make \
+  && make install \
+  && make clean
+
+RUN wget -O phpunit https://phar.phpunit.de/phpunit-5.phar \
+  && chmod +x phpunit \
+  && mv phpunit /usr/local/php-5.6/bin
+
 # php 7.0
 RUN wget https://github.com/php/php-src/archive/php-7.0.33.tar.gz -O /var/local/php-7.0.33.tar.gz
 
diff --git a/kokoro/linux/dockerfile/test/python39/Dockerfile b/kokoro/linux/dockerfile/test/python39/Dockerfile
new file mode 100644
index 0000000..ee7554d
--- /dev/null
+++ b/kokoro/linux/dockerfile/test/python39/Dockerfile
@@ -0,0 +1,31 @@
+FROM python:3.9-buster
+
+# Install dependencies.  We start with the basic ones require to build protoc
+# and the C++ build
+RUN apt-get update && apt-get install -y \
+  autoconf \
+  autotools-dev \
+  build-essential \
+  bzip2 \
+  ccache \
+  curl \
+  gcc \
+  git \
+  libc6 \
+  libc6-dbg \
+  libc6-dev \
+  libgtest-dev \
+  libtool \
+  make \
+  parallel \
+  time \
+  wget \
+  && apt-get clean \
+  && rm -rf /var/lib/apt/lists/*
+
+# Install Python libraries.
+RUN python -m pip install --no-cache-dir --upgrade \
+  pip \
+  setuptools \
+  tox \
+  wheel
diff --git a/kokoro/linux/python/build.sh b/kokoro/linux/python39/build.sh
similarity index 78%
rename from kokoro/linux/python/build.sh
rename to kokoro/linux/python39/build.sh
index 86964f5..497dc66 100755
--- a/kokoro/linux/python/build.sh
+++ b/kokoro/linux/python39/build.sh
@@ -10,8 +10,9 @@
 # Change to repo root
 cd $(dirname $0)/../../..
 
-export DOCKERFILE_DIR=kokoro/linux/64-bit
+export DOCKERHUB_ORGANIZATION=protobuftesting
+export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python39
 export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh
 export OUTPUT_DIR=testoutput
-export TEST_SET="python"
+export TEST_SET="python39"
 ./kokoro/linux/build_and_run_docker.sh
diff --git a/kokoro/linux/python/continuous.cfg b/kokoro/linux/python39/continuous.cfg
similarity index 76%
rename from kokoro/linux/python/continuous.cfg
rename to kokoro/linux/python39/continuous.cfg
index e2fc413..b03bc90 100644
--- a/kokoro/linux/python/continuous.cfg
+++ b/kokoro/linux/python39/continuous.cfg
@@ -1,7 +1,7 @@
 # Config file for running tests in Kokoro
 
 # Location of the build script in repository
-build_file: "protobuf/kokoro/linux/python/build.sh"
+build_file: "protobuf/kokoro/linux/python39/build.sh"
 timeout_mins: 120
 
 action {
diff --git a/kokoro/linux/python/presubmit.cfg b/kokoro/linux/python39/presubmit.cfg
similarity index 76%
rename from kokoro/linux/python/presubmit.cfg
rename to kokoro/linux/python39/presubmit.cfg
index e2fc413..b03bc90 100644
--- a/kokoro/linux/python/presubmit.cfg
+++ b/kokoro/linux/python39/presubmit.cfg
@@ -1,7 +1,7 @@
 # Config file for running tests in Kokoro
 
 # Location of the build script in repository
-build_file: "protobuf/kokoro/linux/python/build.sh"
+build_file: "protobuf/kokoro/linux/python39/build.sh"
 timeout_mins: 120
 
 action {
diff --git a/kokoro/linux/python/build.sh b/kokoro/linux/python39_cpp/build.sh
similarity index 78%
copy from kokoro/linux/python/build.sh
copy to kokoro/linux/python39_cpp/build.sh
index 86964f5..f45d2ec 100755
--- a/kokoro/linux/python/build.sh
+++ b/kokoro/linux/python39_cpp/build.sh
@@ -10,8 +10,9 @@
 # Change to repo root
 cd $(dirname $0)/../../..
 
-export DOCKERFILE_DIR=kokoro/linux/64-bit
+export DOCKERHUB_ORGANIZATION=protobuftesting
+export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python39
 export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh
 export OUTPUT_DIR=testoutput
-export TEST_SET="python"
+export TEST_SET="python39_cpp"
 ./kokoro/linux/build_and_run_docker.sh
diff --git a/kokoro/linux/python/continuous.cfg b/kokoro/linux/python39_cpp/continuous.cfg
similarity index 74%
copy from kokoro/linux/python/continuous.cfg
copy to kokoro/linux/python39_cpp/continuous.cfg
index e2fc413..dd84fbe 100644
--- a/kokoro/linux/python/continuous.cfg
+++ b/kokoro/linux/python39_cpp/continuous.cfg
@@ -1,7 +1,7 @@
 # Config file for running tests in Kokoro
 
 # Location of the build script in repository
-build_file: "protobuf/kokoro/linux/python/build.sh"
+build_file: "protobuf/kokoro/linux/python39_cpp/build.sh"
 timeout_mins: 120
 
 action {
diff --git a/kokoro/linux/python/continuous.cfg b/kokoro/linux/python39_cpp/presubmit.cfg
similarity index 74%
copy from kokoro/linux/python/continuous.cfg
copy to kokoro/linux/python39_cpp/presubmit.cfg
index e2fc413..dd84fbe 100644
--- a/kokoro/linux/python/continuous.cfg
+++ b/kokoro/linux/python39_cpp/presubmit.cfg
@@ -1,7 +1,7 @@
 # Config file for running tests in Kokoro
 
 # Location of the build script in repository
-build_file: "protobuf/kokoro/linux/python/build.sh"
+build_file: "protobuf/kokoro/linux/python39_cpp/build.sh"
 timeout_mins: 120
 
 action {
diff --git a/kokoro/release/csharp/windows/build_nuget.bat b/kokoro/release/csharp/windows/build_nuget.bat
index 0ff8db0..590c391 100644
--- a/kokoro/release/csharp/windows/build_nuget.bat
+++ b/kokoro/release/csharp/windows/build_nuget.bat
@@ -11,4 +11,7 @@
 set DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true
 set DOTNET_CLI_TELEMETRY_OPTOUT=true
 
+@rem Work around https://github.com/dotnet/core/issues/5881
+dotnet nuget locals all --clear
+
 call build_packages.bat
diff --git a/kokoro/release/python/linux/build_artifacts.sh b/kokoro/release/python/linux/build_artifacts.sh
index fd9d5a9..6b1836f 100755
--- a/kokoro/release/python/linux/build_artifacts.sh
+++ b/kokoro/release/python/linux/build_artifacts.sh
@@ -54,3 +54,4 @@
 build_artifact_version 3.6
 build_artifact_version 3.7
 build_artifact_version 3.8
+build_artifact_version 3.9
diff --git a/kokoro/release/python/macos/build_artifacts.sh b/kokoro/release/python/macos/build_artifacts.sh
index 15aae39..7801b01 100755
--- a/kokoro/release/python/macos/build_artifacts.sh
+++ b/kokoro/release/python/macos/build_artifacts.sh
@@ -55,6 +55,7 @@
 build_artifact_version 3.6
 build_artifact_version 3.7
 build_artifact_version 3.8
+build_artifact_version 3.9
 
 # python OSX10.9 does not have python 3.5
 export MB_PYTHON_OSX_VER=10.6
diff --git a/kokoro/release/python/windows/build_artifacts.bat b/kokoro/release/python/windows/build_artifacts.bat
index 70a86a1..b2adbe0 100644
--- a/kokoro/release/python/windows/build_artifacts.bat
+++ b/kokoro/release/python/windows/build_artifacts.bat
@@ -72,6 +72,16 @@
 SET PYTHON_ARCH=64
 CALL build_single_artifact.bat || goto :error
 
+SET PYTHON=C:\python39_32bit
+SET PYTHON_VERSION=3.9
+SET PYTHON_ARCH=32
+CALL build_single_artifact.bat || goto :error
+
+SET PYTHON=C:\python39
+SET PYTHON_VERSION=3.9
+SET PYTHON_ARCH=64
+CALL build_single_artifact.bat || goto :error
+
 goto :EOF
 
 :error
diff --git a/kokoro/release/python/windows/build_single_artifact.bat b/kokoro/release/python/windows/build_single_artifact.bat
index 45843e1..a3cfef9 100644
--- a/kokoro/release/python/windows/build_single_artifact.bat
+++ b/kokoro/release/python/windows/build_single_artifact.bat
@@ -24,10 +24,16 @@
 if %PYTHON%==C:\python38 set generator=Visual Studio 14 Win64
 if %PYTHON%==C:\python38 set vcplatform=x64
 
+if %PYTHON%==C:\python39_32bit set generator=Visual Studio 14
+if %PYTHON%==C:\python39_32bit set vcplatform=Win32
+
+if %PYTHON%==C:\python39 set generator=Visual Studio 14 Win64
+if %PYTHON%==C:\python39 set vcplatform=x64
+
 REM Prepend newly installed Python to the PATH of this build (this cannot be
 REM done from inside the powershell script as it would require to restart
 REM the parent CMD process).
-SET PATH=%PYTHON%;%PYTHON%\Scripts;%OLD_PATH%
+SET PATH=C:\Program Files\CMake\bin;%PYTHON%;%PYTHON%\Scripts;%OLD_PATH%
 python -m pip install -U pip
 pip install wheel
 
diff --git a/kokoro/windows/csharp/build.bat b/kokoro/windows/csharp/build.bat
index 95224f2..4f1df37 100644
--- a/kokoro/windows/csharp/build.bat
+++ b/kokoro/windows/csharp/build.bat
@@ -11,4 +11,7 @@
 set DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true
 set DOTNET_CLI_TELEMETRY_OPTOUT=true
 
+@rem Work around https://github.com/dotnet/core/issues/5881
+dotnet nuget locals all --clear
+
 call buildall.bat
diff --git a/php/composer.json b/php/composer.json
index 28d379b..4c1b5ac 100644
--- a/php/composer.json
+++ b/php/composer.json
@@ -6,10 +6,10 @@
   "homepage": "https://developers.google.com/protocol-buffers/",
   "license": "BSD-3-Clause",
   "require": {
-    "php": ">=7.0.0"
+    "php": ">=5.5.0"
   },
   "require-dev": {
-    "phpunit/phpunit": ">=6.0.0"
+    "phpunit/phpunit": ">=5.0.0"
   },
   "autoload": {
     "psr-4": {
diff --git a/php/ext/google/protobuf/array.c b/php/ext/google/protobuf/array.c
index e6412c0..3a2f734 100644
--- a/php/ext/google/protobuf/array.c
+++ b/php/ext/google/protobuf/array.c
@@ -55,8 +55,7 @@
   zend_object std;
   zval arena;
   upb_array *array;
-  upb_fieldtype_t type;
-  const Descriptor* desc;  // When values are messages.
+  TypeInfo type;
 } RepeatedField;
 
 zend_class_entry *RepeatedField_class_entry;
@@ -76,7 +75,6 @@
   intern->std.handlers = &RepeatedField_object_handlers;
   Arena_Init(&intern->arena);
   intern->array = NULL;
-  intern->desc = NULL;
   // Skip object_properties_init(), we don't allow derived classes.
   return &intern->std;
 }
@@ -106,13 +104,35 @@
 static int RepeatedField_compare_objects(zval *rf1, zval *rf2) {
   RepeatedField* intern1 = (RepeatedField*)Z_OBJ_P(rf1);
   RepeatedField* intern2 = (RepeatedField*)Z_OBJ_P(rf2);
-  upb_fieldtype_t type = intern1->type;
-  const upb_msgdef *m = intern1->desc ? intern1->desc->msgdef : NULL;
 
-  if (type != intern2->type) return 1;
-  if (intern1->desc != intern2->desc) return 1;
+  return TypeInfo_Eq(intern1->type, intern2->type) &&
+                 ArrayEq(intern1->array, intern2->array, intern1->type)
+             ? 0
+             : 1;
+}
 
-  return ArrayEq(intern1->array, intern2->array, type, m) ? 0 : 1;
+/**
+ * RepeatedField_clone_obj()
+ *
+ * Object handler for cloning an object in PHP. Called when PHP code does:
+ *
+ *   $rf2 = clone $rf1;
+ */
+static zend_object *RepeatedField_clone_obj(PROTO_VAL *object) {
+  RepeatedField* intern = PROTO_VAL_P(object);
+  upb_arena *arena = Arena_Get(&intern->arena);
+  upb_array *clone = upb_array_new(arena, intern->type.type);
+  size_t n = upb_array_size(intern->array);
+  size_t i;
+
+  for (i = 0; i < n; i++) {
+    upb_msgval msgval = upb_array_get(intern->array, i);
+    upb_array_append(clone, msgval, arena);
+  }
+
+  zval ret;
+  RepeatedField_GetPhpWrapper(&ret, clone, intern->type, &intern->arena);
+  return Z_OBJ_P(&ret);
 }
 
 static HashTable *RepeatedField_GetProperties(PROTO_VAL *object) {
@@ -129,8 +149,8 @@
 
 // These are documented in the header file.
 
-void RepeatedField_GetPhpWrapper(zval *val, upb_array *arr,
-                                 const upb_fielddef *f, zval *arena) {
+void RepeatedField_GetPhpWrapper(zval *val, upb_array *arr, TypeInfo type,
+                                 zval *arena) {
   if (!arr) {
     ZVAL_NULL(val);
     return;
@@ -142,15 +162,14 @@
     intern->std.handlers = &RepeatedField_object_handlers;
     ZVAL_COPY(&intern->arena, arena);
     intern->array = arr;
-    intern->type = upb_fielddef_type(f);
-    intern->desc = Descriptor_GetFromFieldDef(f);
+    intern->type = type;
     // Skip object_properties_init(), we don't allow derived classes.
     ObjCache_Add(intern->array, &intern->std);
     ZVAL_OBJ(val, &intern->std);
   }
 }
 
-upb_array *RepeatedField_GetUpbArray(zval *val, const upb_fielddef *f,
+upb_array *RepeatedField_GetUpbArray(zval *val, TypeInfo type,
                                      upb_arena *arena) {
   if (Z_ISREF_P(val)) {
     ZVAL_DEREF(val);
@@ -158,11 +177,9 @@
 
   if (Z_TYPE_P(val) == IS_ARRAY) {
     // Auto-construct, eg. [1, 2, 3] -> upb_array([1, 2, 3]).
-    upb_array *arr = upb_array_new(arena, upb_fielddef_type(f));
+    upb_array *arr = upb_array_new(arena, type.type);
     HashTable *table = HASH_OF(val);
     HashPosition pos;
-    upb_fieldtype_t type = upb_fielddef_type(f);
-    const Descriptor *desc = Descriptor_GetFromFieldDef(f);
 
     zend_hash_internal_pointer_reset_ex(table, &pos);
 
@@ -172,7 +189,7 @@
 
       if (!zv) return arr;
 
-      if (!Convert_PhpToUpbAutoWrap(zv, &val, type, desc, arena)) {
+      if (!Convert_PhpToUpbAutoWrap(zv, &val, type, arena)) {
         return NULL;
       }
 
@@ -183,9 +200,8 @@
              Z_OBJCE_P(val) == RepeatedField_class_entry) {
     // Unwrap existing RepeatedField object to get the upb_array* inside.
     RepeatedField *intern = (RepeatedField*)Z_OBJ_P(val);
-    const Descriptor *desc = Descriptor_GetFromFieldDef(f);
 
-    if (intern->type != upb_fielddef_type(f) || intern->desc != desc) {
+    if (!TypeInfo_Eq(intern->type, type)) {
       php_error_docref(NULL, E_USER_ERROR,
                        "Wrong type for this repeated field.");
     }
@@ -198,8 +214,7 @@
   }
 }
 
-bool ArrayEq(const upb_array *a1, const upb_array *a2, upb_fieldtype_t type,
-             const upb_msgdef *m) {
+bool ArrayEq(const upb_array *a1, const upb_array *a2, TypeInfo type) {
   size_t i;
   size_t n;
 
@@ -212,7 +227,7 @@
   for (i = 0; i < n; i++) {
     upb_msgval val1 = upb_array_get(a1, i);
     upb_msgval val2 = upb_array_get(a2, i);
-    if (!ValueEq(val1, val2, type, m)) return false;
+    if (!ValueEq(val1, val2, type)) return false;
   }
 
   return true;
@@ -238,16 +253,16 @@
     return;
   }
 
-  intern->type = pbphp_dtype_to_type(type);
-  intern->desc = Descriptor_GetFromClassEntry(klass);
+  intern->type.type = pbphp_dtype_to_type(type);
+  intern->type.desc = Descriptor_GetFromClassEntry(klass);
 
-  if (intern->type == UPB_TYPE_MESSAGE && klass == NULL) {
+  if (intern->type.type == UPB_TYPE_MESSAGE && klass == NULL) {
     php_error_docref(NULL, E_USER_ERROR,
                      "Message/enum type must have concrete class.");
     return;
   }
 
-  intern->array = upb_array_new(arena, intern->type);
+  intern->array = upb_array_new(arena, intern->type.type);
   ObjCache_Add(intern->array, &intern->std);
 }
 
@@ -264,7 +279,7 @@
   upb_msgval msgval;
 
   if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &php_val) != SUCCESS ||
-      !Convert_PhpToUpb(php_val, &msgval, intern->type, intern->desc, arena)) {
+      !Convert_PhpToUpb(php_val, &msgval, intern->type, arena)) {
     return;
   }
 
@@ -321,7 +336,7 @@
   }
 
   msgval = upb_array_get(intern->array, index);
-  Convert_UpbToPhp(msgval, &ret, intern->type, intern->desc, &intern->arena);
+  Convert_UpbToPhp(msgval, &ret, intern->type, &intern->arena);
   RETURN_ZVAL(&ret, 0, 1);
 }
 
@@ -357,7 +372,7 @@
     return;
   }
 
-  if (!Convert_PhpToUpb(val, &msgval, intern->type, intern->desc, arena)) {
+  if (!Convert_PhpToUpb(val, &msgval, intern->type, arena)) {
     return;
   }
 
@@ -563,7 +578,7 @@
 
   msgval = upb_array_get(array, index);
 
-  Convert_UpbToPhp(msgval, &ret, field->type, field->desc, &field->arena);
+  Convert_UpbToPhp(msgval, &ret, field->type, &field->arena);
   RETURN_ZVAL(&ret, 0, 1);
 }
 
@@ -638,6 +653,7 @@
 #else
   h->compare = RepeatedField_compare_objects;
 #endif
+  h->clone_obj = RepeatedField_clone_obj;
   h->get_properties = RepeatedField_GetProperties;
   h->get_property_ptr_ptr = RepeatedField_GetPropertyPtrPtr;
 
diff --git a/php/ext/google/protobuf/array.h b/php/ext/google/protobuf/array.h
index 921e0bf..031effa 100644
--- a/php/ext/google/protobuf/array.h
+++ b/php/ext/google/protobuf/array.h
@@ -33,6 +33,7 @@
 
 #include <php.h>
 
+#include "def.h"
 #include "php-upb.h"
 
 // Registers PHP classes for RepeatedField.
@@ -40,27 +41,27 @@
 
 // Gets a upb_array* for the PHP object |val|:
 //  * If |val| is a RepeatedField object, we first check its type and verify
-//    that that the elements have the correct type for |f|. If so, we return the
-//    wrapped upb_array*. We also make sure that this array's arena is fused to
-//    |arena|, so the returned upb_array is guaranteed to live as long as
+//    that that the elements have the correct type for |type|. If so, we return
+//    the wrapped upb_array*. We also make sure that this array's arena is fused
+//    to |arena|, so the returned upb_array is guaranteed to live as long as
 //    |arena|.
 //  * If |val| is a PHP Array, we attempt to create a new upb_array using
 //    |arena| and add all of the PHP elements to it.
 //
 // If an error occurs, we raise a PHP error and return NULL.
-upb_array *RepeatedField_GetUpbArray(zval *val, const upb_fielddef *f, upb_arena *arena);
+upb_array *RepeatedField_GetUpbArray(zval *val, TypeInfo type,
+                                     upb_arena *arena);
 
-// Creates a PHP RepeatedField object for the given upb_array* and |f| and
+// Creates a PHP RepeatedField object for the given upb_array* and |type| and
 // returns it in |val|. The PHP object will keep a reference to this |arena| to
 // ensure the underlying array data stays alive.
 //
 // If |arr| is NULL, this will return a PHP null object.
-void RepeatedField_GetPhpWrapper(zval *val, upb_array *arr,
-                                 const upb_fielddef *f, zval *arena);
+void RepeatedField_GetPhpWrapper(zval *val, upb_array *arr, TypeInfo type,
+                                 zval *arena);
 
 // Returns true if the given arrays are equal. Both arrays must be of this
 // |type| and, if the type is |UPB_TYPE_MESSAGE|, must have the same |m|.
-bool ArrayEq(const upb_array *a1, const upb_array *a2, upb_fieldtype_t type,
-             const upb_msgdef *m);
+bool ArrayEq(const upb_array *a1, const upb_array *a2, TypeInfo type);
 
 #endif  // PHP_PROTOBUF_ARRAY_H_
diff --git a/php/ext/google/protobuf/convert.c b/php/ext/google/protobuf/convert.c
index 1c2f628..c518cca 100644
--- a/php/ext/google/protobuf/convert.c
+++ b/php/ext/google/protobuf/convert.c
@@ -348,15 +348,15 @@
   }
 }
 
-bool Convert_PhpToUpb(zval *php_val, upb_msgval *upb_val, upb_fieldtype_t type,
-                      const Descriptor *desc, upb_arena *arena) {
+bool Convert_PhpToUpb(zval *php_val, upb_msgval *upb_val, TypeInfo type,
+                      upb_arena *arena) {
   int64_t i64;
 
   if (Z_ISREF_P(php_val)) {
     ZVAL_DEREF(php_val);
   }
 
-  switch (type) {
+  switch (type.type) {
     case UPB_TYPE_INT64:
       return Convert_PhpToInt64(php_val, &upb_val->int64_val);
     case UPB_TYPE_INT32:
@@ -408,17 +408,17 @@
       return true;
     }
     case UPB_TYPE_MESSAGE:
-      PBPHP_ASSERT(desc);
-      return Message_GetUpbMessage(php_val, desc, arena,
+      PBPHP_ASSERT(type.desc);
+      return Message_GetUpbMessage(php_val, type.desc, arena,
                                    (upb_msg **)&upb_val->msg_val);
   }
 
   return false;
 }
 
-void Convert_UpbToPhp(upb_msgval upb_val, zval *php_val, upb_fieldtype_t type,
-                      const Descriptor *desc, zval *arena) {
-  switch (type) {
+void Convert_UpbToPhp(upb_msgval upb_val, zval *php_val, TypeInfo type,
+                      zval *arena) {
+  switch (type.type) {
     case UPB_TYPE_INT64:
 #if SIZEOF_ZEND_LONG == 8
       ZVAL_LONG(php_val, upb_val.int64_val);
@@ -467,25 +467,24 @@
       break;
     }
     case UPB_TYPE_MESSAGE:
-      PBPHP_ASSERT(desc);
-      Message_GetPhpWrapper(php_val, desc, (upb_msg*)upb_val.msg_val, arena);
+      PBPHP_ASSERT(type.desc);
+      Message_GetPhpWrapper(php_val, type.desc, (upb_msg *)upb_val.msg_val,
+                            arena);
       break;
   }
 }
 
-bool Convert_PhpToUpbAutoWrap(zval *val, upb_msgval *upb_val,
-                              upb_fieldtype_t type, const Descriptor *desc,
+bool Convert_PhpToUpbAutoWrap(zval *val, upb_msgval *upb_val, TypeInfo type,
                               upb_arena *arena) {
-  const upb_msgdef *subm = desc ? desc->msgdef : NULL;
+  const upb_msgdef *subm = type.desc ? type.desc->msgdef : NULL;
   if (subm && upb_msgdef_iswrapper(subm) && Z_TYPE_P(val) != IS_OBJECT) {
     // Assigning a scalar to a wrapper-typed value. We will automatically wrap
     // the value, so the user doesn't need to create a FooWrapper(['value': X])
     // message manually.
     upb_msg *wrapper = upb_msg_new(subm, arena);
     const upb_fielddef *val_f = upb_msgdef_itof(subm, 1);
-    upb_fieldtype_t type_f = upb_fielddef_type(val_f);
     upb_msgval msgval;
-    if (!Convert_PhpToUpb(val, &msgval, type_f, NULL, arena)) return false;
+    if (!Convert_PhpToUpb(val, &msgval, TypeInfo_Get(val_f), arena)) return false;
     upb_msg_set(wrapper, val_f, msgval, arena);
     upb_val->msg_val = wrapper;
     return true;
@@ -495,7 +494,7 @@
     //   ['foo_submsg': new Foo(['a' => 1])]
     // not:
     //   ['foo_submsg': ['a' => 1]]
-    return Convert_PhpToUpb(val, upb_val, type, desc, arena);
+    return Convert_PhpToUpb(val, upb_val, type, arena);
   }
 }
 
diff --git a/php/ext/google/protobuf/convert.h b/php/ext/google/protobuf/convert.h
index 06c05c7..1bae233 100644
--- a/php/ext/google/protobuf/convert.h
+++ b/php/ext/google/protobuf/convert.h
@@ -46,8 +46,8 @@
 // UPB_TYPE_MESSAGE, then |desc| must be the Descriptor for this message type.
 // If type is string, message, or bytes, then |arena| will be used to copy
 // string data or fuse this arena to the given message's arena.
-bool Convert_PhpToUpb(zval *php_val, upb_msgval *upb_val, upb_fieldtype_t type,
-                      const Descriptor *desc, upb_arena *arena);
+bool Convert_PhpToUpb(zval *php_val, upb_msgval *upb_val, TypeInfo type,
+                      upb_arena *arena);
 
 // Similar to Convert_PhpToUpb, but supports automatically wrapping the wrapper
 // types if a primitive is specified:
@@ -56,16 +56,15 @@
 //
 // We currently allow this implicit conversion in initializers, but not for
 // assignment.
-bool Convert_PhpToUpbAutoWrap(zval *val, upb_msgval *upb_val,
-                              upb_fieldtype_t type, const Descriptor *desc,
+bool Convert_PhpToUpbAutoWrap(zval *val, upb_msgval *upb_val, TypeInfo type,
                               upb_arena *arena);
 
 // Converts |upb_val| to a PHP zval according to |type|. This may involve
 // creating a PHP wrapper object. If type == UPB_TYPE_MESSAGE, then |desc| must
 // be the Descriptor for this message type. Any newly created wrapper object
 // will reference |arena|.
-void Convert_UpbToPhp(upb_msgval upb_val, zval *php_val, upb_fieldtype_t type,
-                      const Descriptor *desc, zval *arena);
+void Convert_UpbToPhp(upb_msgval upb_val, zval *php_val, TypeInfo type,
+                      zval *arena);
 
 // Registers the GPBUtil class.
 void Convert_ModuleInit(void);
diff --git a/php/ext/google/protobuf/def.h b/php/ext/google/protobuf/def.h
index 2a89cd0..372c889 100644
--- a/php/ext/google/protobuf/def.h
+++ b/php/ext/google/protobuf/def.h
@@ -72,4 +72,27 @@
 Descriptor* Descriptor_GetFromMessageDef(const upb_msgdef *m);
 Descriptor* Descriptor_GetFromFieldDef(const upb_fielddef *f);
 
+// Packages up a upb_fieldtype_t with a Descriptor, since many functions need
+// both.
+typedef struct {
+  upb_fieldtype_t type;
+  const Descriptor *desc;  // When type == UPB_TYPE_MESSAGE.
+} TypeInfo;
+
+static inline TypeInfo TypeInfo_Get(const upb_fielddef *f) {
+  TypeInfo ret = {upb_fielddef_type(f), Descriptor_GetFromFieldDef(f)};
+  return ret;
+}
+
+static inline TypeInfo TypeInfo_FromType(upb_fieldtype_t type) {
+  TypeInfo ret = {type};
+  return ret;
+}
+
+static inline bool TypeInfo_Eq(TypeInfo a, TypeInfo b) {
+  if (a.type != b.type) return false;
+  if (a.type == UPB_TYPE_MESSAGE && a.desc != b.desc) return false;
+  return true;
+}
+
 #endif  // PHP_PROTOBUF_DEF_H_
diff --git a/php/ext/google/protobuf/map.c b/php/ext/google/protobuf/map.c
index 26776e6..babd638 100644
--- a/php/ext/google/protobuf/map.c
+++ b/php/ext/google/protobuf/map.c
@@ -51,14 +51,31 @@
   zend_object std;
   zval arena;
   upb_map *map;
-  upb_fieldtype_t key_type;
-  upb_fieldtype_t val_type;
-  const Descriptor* desc;  // When values are messages.
+  MapField_Type type;
 } MapField;
 
 zend_class_entry *MapField_class_entry;
 static zend_object_handlers MapField_object_handlers;
 
+static bool MapType_Eq(MapField_Type a, MapField_Type b) {
+  return a.key_type == b.key_type && TypeInfo_Eq(a.val_type, b.val_type);
+}
+
+static TypeInfo KeyType(MapField_Type type) {
+  TypeInfo ret = {type.key_type};
+  return ret;
+}
+
+MapField_Type MapType_Get(const upb_fielddef *f) {
+  const upb_msgdef *ent = upb_fielddef_msgsubdef(f);
+  const upb_fielddef *key_f = upb_msgdef_itof(ent, 1);
+  const upb_fielddef *val_f = upb_msgdef_itof(ent, 2);
+  MapField_Type type = {
+      upb_fielddef_type(key_f),
+      {upb_fielddef_type(val_f), Descriptor_GetFromFieldDef(val_f)}};
+  return type;
+}
+
 // PHP Object Handlers /////////////////////////////////////////////////////////
 
 /**
@@ -102,15 +119,36 @@
 static int MapField_compare_objects(zval *map1, zval *map2) {
   MapField* intern1 = (MapField*)Z_OBJ_P(map1);
   MapField* intern2 = (MapField*)Z_OBJ_P(map2);
-  const upb_msgdef *m = intern1->desc ? intern1->desc->msgdef : NULL;
-  upb_fieldtype_t key_type = intern1->key_type;
-  upb_fieldtype_t val_type = intern1->val_type;
 
-  if (key_type != intern2->key_type) return 1;
-  if (val_type != intern2->val_type) return 1;
-  if (intern1->desc != intern2->desc) return 1;
+  return MapType_Eq(intern1->type, intern2->type) &&
+                 MapEq(intern1->map, intern2->map, intern1->type)
+             ? 0
+             : 1;
+}
 
-  return MapEq(intern1->map, intern2->map, key_type, val_type, m) ? 0 : 1;
+/**
+ * MapField_clone_obj()
+ *
+ * Object handler for cloning an object in PHP. Called when PHP code does:
+ *
+ *   $map2 = clone $map1;
+ */
+static zend_object *MapField_clone_obj(PROTO_VAL *object) {
+  MapField* intern = PROTO_VAL_P(object);
+  upb_arena *arena = Arena_Get(&intern->arena);
+  upb_map *clone =
+      upb_map_new(arena, intern->type.key_type, intern->type.val_type.type);
+  size_t iter = UPB_MAP_BEGIN;
+
+  while (upb_mapiter_next(intern->map, &iter)) {
+    upb_msgval key = upb_mapiter_key(intern->map, iter);
+    upb_msgval val = upb_mapiter_value(intern->map, iter);
+    upb_map_set(clone, key, val, arena);
+  }
+
+  zval ret;
+  MapField_GetPhpWrapper(&ret, clone, intern->type, &intern->arena);
+  return Z_OBJ_P(&ret);
 }
 
 static zval *Map_GetPropertyPtrPtr(PROTO_VAL *object, PROTO_STR *member,
@@ -126,7 +164,7 @@
 
 // These are documented in the header file.
 
-void MapField_GetPhpWrapper(zval *val, upb_map *map, const upb_fielddef *f,
+void MapField_GetPhpWrapper(zval *val, upb_map *map, MapField_Type type,
                             zval *arena) {
   if (!map) {
     ZVAL_NULL(val);
@@ -134,37 +172,25 @@
   }
 
   if (!ObjCache_Get(map, val)) {
-    const upb_msgdef *ent = upb_fielddef_msgsubdef(f);
-    const upb_fielddef *key_f = upb_msgdef_itof(ent, 1);
-    const upb_fielddef *val_f = upb_msgdef_itof(ent, 2);
     MapField *intern = emalloc(sizeof(MapField));
     zend_object_std_init(&intern->std, MapField_class_entry);
     intern->std.handlers = &MapField_object_handlers;
     ZVAL_COPY(&intern->arena, arena);
     intern->map = map;
-    intern->key_type = upb_fielddef_type(key_f);
-    intern->val_type = upb_fielddef_type(val_f);
-    intern->desc = Descriptor_GetFromFieldDef(val_f);
+    intern->type = type;
     // Skip object_properties_init(), we don't allow derived classes.
     ObjCache_Add(intern->map, &intern->std);
     ZVAL_OBJ(val, &intern->std);
   }
 }
 
-upb_map *MapField_GetUpbMap(zval *val, const upb_fielddef *f, upb_arena *arena) {
-  const upb_msgdef *ent = upb_fielddef_msgsubdef(f);
-  const upb_fielddef *key_f = upb_msgdef_itof(ent, 1);
-  const upb_fielddef *val_f = upb_msgdef_itof(ent, 2);
-  upb_fieldtype_t key_type = upb_fielddef_type(key_f);
-  upb_fieldtype_t val_type = upb_fielddef_type(val_f);
-  const Descriptor *desc = Descriptor_GetFromFieldDef(val_f);
-
+upb_map *MapField_GetUpbMap(zval *val, MapField_Type type, upb_arena *arena) {
   if (Z_ISREF_P(val)) {
     ZVAL_DEREF(val);
   }
 
   if (Z_TYPE_P(val) == IS_ARRAY) {
-    upb_map *map = upb_map_new(arena, key_type, val_type);
+    upb_map *map = upb_map_new(arena, type.key_type, type.val_type.type);
     HashTable *table = HASH_OF(val);
     HashPosition pos;
 
@@ -181,8 +207,8 @@
 
       if (!php_val) return map;
 
-      if (!Convert_PhpToUpb(&php_key, &upb_key, key_type, NULL, arena) ||
-          !Convert_PhpToUpbAutoWrap(php_val, &upb_val, val_type, desc, arena)) {
+      if (!Convert_PhpToUpb(&php_key, &upb_key, KeyType(type), arena) ||
+          !Convert_PhpToUpbAutoWrap(php_val, &upb_val, type.val_type, arena)) {
         return NULL;
       }
 
@@ -194,8 +220,7 @@
              Z_OBJCE_P(val) == MapField_class_entry) {
     MapField *intern = (MapField*)Z_OBJ_P(val);
 
-    if (intern->key_type != key_type || intern->val_type != val_type ||
-        intern->desc != desc) {
+    if (!MapType_Eq(intern->type, type)) {
       php_error_docref(NULL, E_USER_ERROR, "Wrong type for this map field.");
       return NULL;
     }
@@ -208,8 +233,7 @@
   }
 }
 
-bool MapEq(const upb_map *m1, const upb_map *m2, upb_fieldtype_t key_type,
-           upb_fieldtype_t val_type, const upb_msgdef *m) {
+bool MapEq(const upb_map *m1, const upb_map *m2, MapField_Type type) {
   size_t iter = UPB_MAP_BEGIN;
 
   if ((m1 == NULL) != (m2 == NULL)) return false;
@@ -222,7 +246,7 @@
     upb_msgval val2;
 
     if (!upb_map_get(m2, key, &val2)) return false;
-    if (!ValueEq(val1, val2, val_type, m)) return false;
+    if (!ValueEq(val1, val2, type.val_type)) return false;
   }
 
   return true;
@@ -250,12 +274,12 @@
     return;
   }
 
-  intern->key_type = pbphp_dtype_to_type(key_type);
-  intern->val_type = pbphp_dtype_to_type(val_type);
-  intern->desc = Descriptor_GetFromClassEntry(klass);
+  intern->type.key_type = pbphp_dtype_to_type(key_type);
+  intern->type.val_type.type = pbphp_dtype_to_type(val_type);
+  intern->type.val_type.desc = Descriptor_GetFromClassEntry(klass);
 
   // Check that the key type is an allowed type.
-  switch (intern->key_type) {
+  switch (intern->type.key_type) {
     case UPB_TYPE_INT32:
     case UPB_TYPE_INT64:
     case UPB_TYPE_UINT32:
@@ -269,13 +293,14 @@
       zend_error(E_USER_ERROR, "Invalid key type for map.");
   }
 
-  if (intern->val_type == UPB_TYPE_MESSAGE && klass == NULL) {
+  if (intern->type.val_type.type == UPB_TYPE_MESSAGE && klass == NULL) {
     php_error_docref(NULL, E_USER_ERROR,
                      "Message/enum type must have concrete class.");
     return;
   }
 
-  intern->map = upb_map_new(arena, intern->key_type, intern->val_type);
+  intern->map =
+      upb_map_new(arena, intern->type.key_type, intern->type.val_type.type);
   ObjCache_Add(intern->map, &intern->std);
 }
 
@@ -296,7 +321,7 @@
   upb_msgval upb_key;
 
   if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS ||
-      !Convert_PhpToUpb(key, &upb_key, intern->key_type, intern->desc, NULL)) {
+      !Convert_PhpToUpb(key, &upb_key, KeyType(intern->type), NULL)) {
     return;
   }
 
@@ -322,7 +347,7 @@
   upb_msgval upb_key, upb_val;
 
   if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS ||
-      !Convert_PhpToUpb(key, &upb_key, intern->key_type, intern->desc, NULL)) {
+      !Convert_PhpToUpb(key, &upb_key, KeyType(intern->type), NULL)) {
     return;
   }
 
@@ -331,7 +356,7 @@
     return;
   }
 
-  Convert_UpbToPhp(upb_val, &ret, intern->val_type, intern->desc, &intern->arena);
+  Convert_UpbToPhp(upb_val, &ret, intern->type.val_type, &intern->arena);
   RETURN_ZVAL(&ret, 0, 1);
 }
 
@@ -355,8 +380,8 @@
   upb_msgval upb_key, upb_val;
 
   if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &key, &val) != SUCCESS ||
-      !Convert_PhpToUpb(key, &upb_key, intern->key_type, NULL, NULL) ||
-      !Convert_PhpToUpb(val, &upb_val, intern->val_type, intern->desc, arena)) {
+      !Convert_PhpToUpb(key, &upb_key, KeyType(intern->type), NULL) ||
+      !Convert_PhpToUpb(val, &upb_val, intern->type.val_type, arena)) {
     return;
   }
 
@@ -380,7 +405,7 @@
   upb_msgval upb_key;
 
   if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS ||
-      !Convert_PhpToUpb(key, &upb_key, intern->key_type, NULL, NULL)) {
+      !Convert_PhpToUpb(key, &upb_key, KeyType(intern->type), NULL)) {
     return;
   }
 
@@ -543,7 +568,7 @@
   MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
   upb_msgval upb_val = upb_mapiter_value(field->map, intern->position);
   zval ret;
-  Convert_UpbToPhp(upb_val, &ret, field->val_type, field->desc, &field->arena);
+  Convert_UpbToPhp(upb_val, &ret, field->type.val_type, &field->arena);
   RETURN_ZVAL(&ret, 0, 1);
 }
 
@@ -557,7 +582,7 @@
   MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
   upb_msgval upb_key = upb_mapiter_key(field->map, intern->position);
   zval ret;
-  Convert_UpbToPhp(upb_key, &ret, field->key_type, NULL, NULL);
+  Convert_UpbToPhp(upb_key, &ret, KeyType(field->type), NULL);
   RETURN_ZVAL(&ret, 0, 1);
 }
 
@@ -624,6 +649,7 @@
 #else
   h->compare = MapField_compare_objects;
 #endif
+  h->clone_obj = MapField_clone_obj;
   h->get_properties = Map_GetProperties;
   h->get_property_ptr_ptr = Map_GetPropertyPtrPtr;
 
diff --git a/php/ext/google/protobuf/map.h b/php/ext/google/protobuf/map.h
index 6eb0620..c523cd0 100644
--- a/php/ext/google/protobuf/map.h
+++ b/php/ext/google/protobuf/map.h
@@ -33,10 +33,18 @@
 
 #include <php.h>
 
+#include "def.h"
 #include "php-upb.h"
 
 void Map_ModuleInit();
 
+typedef struct {
+  upb_fieldtype_t key_type;
+  TypeInfo val_type;
+} MapField_Type;
+
+MapField_Type MapType_Get(const upb_fielddef *f);
+
 // Gets a upb_map* for the PHP object |val|:
 //  * If |val| is a RepeatedField object, we first check its type and verify
 //    that that the elements have the correct type for |f|. If so, we return the
@@ -47,17 +55,16 @@
 //    |arena| and add all of the PHP elements to it.
 //
 // If an error occurs, we raise a PHP error and return NULL.
-upb_map *MapField_GetUpbMap(zval *val, const upb_fielddef *f, upb_arena *arena);
+upb_map *MapField_GetUpbMap(zval *val, MapField_Type type, upb_arena *arena);
 
 // Creates a PHP MapField object for the given upb_map* and |f| and returns it
 // in |val|. The PHP object will keep a reference to this |arena| to ensure the
 // underlying array data stays alive.
 //
 // If |map| is NULL, this will return a PHP null object.
-void MapField_GetPhpWrapper(zval *val, upb_map *arr, const upb_fielddef *f,
+void MapField_GetPhpWrapper(zval *val, upb_map *arr, MapField_Type type,
                             zval *arena);
 
-bool MapEq(const upb_map *m1, const upb_map *m2, upb_fieldtype_t key_type,
-           upb_fieldtype_t val_type, const upb_msgdef *m);
+bool MapEq(const upb_map *m1, const upb_map *m2, MapField_Type type);
 
 #endif  // PHP_PROTOBUF_MAP_H_
diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c
index 0e61770..7f27670 100644
--- a/php/ext/google/protobuf/message.c
+++ b/php/ext/google/protobuf/message.c
@@ -60,6 +60,21 @@
 zend_class_entry *message_ce;
 static zend_object_handlers message_object_handlers;
 
+static void Message_SuppressDefaultProperties(zend_class_entry *class_type) {
+  // We suppress all default properties, because all our properties are handled
+  // by our read_property handler.
+  //
+  // This also allows us to put our zend_object member at the beginning of our
+  // struct -- instead of putting it at the end with pointer fixups to access
+  // our own data, as recommended in the docs -- because Zend won't add any of
+  // its own storage directly after the zend_object if default_properties_count
+  // == 0.
+  //
+  // This is not officially supported, but since it simplifies the code, we'll
+  // do it for as long as it works in practice.
+  class_type->default_properties_count = 0;
+}
+
 // PHP Object Handlers /////////////////////////////////////////////////////////
 
 /**
@@ -69,8 +84,7 @@
  */
 static zend_object* Message_create(zend_class_entry *class_type) {
   Message *intern = emalloc(sizeof(Message));
-  // XXX(haberman): verify whether we actually want to take this route.
-  class_type->default_properties_count = 0;
+  Message_SuppressDefaultProperties(class_type);
   zend_object_std_init(&intern->std, class_type);
   intern->std.handlers = &message_object_handlers;
   Arena_Init(&intern->arena);
@@ -114,14 +128,14 @@
 
   if (upb_fielddef_ismap(f)) {
     upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena);
-    MapField_GetPhpWrapper(rv, msgval.map, f, &intern->arena);
+    MapField_GetPhpWrapper(rv, msgval.map, MapType_Get(f), &intern->arena);
   } else if (upb_fielddef_isseq(f)) {
     upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena);
-    RepeatedField_GetPhpWrapper(rv, msgval.array, f, &intern->arena);
+    RepeatedField_GetPhpWrapper(rv, msgval.array, TypeInfo_Get(f),
+                                &intern->arena);
   } else {
     upb_msgval msgval = upb_msg_get(intern->msg, f);
-    const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
-    Convert_UpbToPhp(msgval, rv, upb_fielddef_type(f), subdesc, &intern->arena);
+    Convert_UpbToPhp(msgval, rv, TypeInfo_Get(f), &intern->arena);
   }
 }
 
@@ -130,16 +144,13 @@
   upb_msgval msgval;
 
   if (upb_fielddef_ismap(f)) {
-    msgval.map_val = MapField_GetUpbMap(val, f, arena);
+    msgval.map_val = MapField_GetUpbMap(val, MapType_Get(f), arena);
     if (!msgval.map_val) return false;
   } else if (upb_fielddef_isseq(f)) {
-    msgval.array_val = RepeatedField_GetUpbArray(val, f, arena);
+    msgval.array_val = RepeatedField_GetUpbArray(val, TypeInfo_Get(f), arena);
     if (!msgval.array_val) return false;
   } else {
-    upb_fieldtype_t type = upb_fielddef_type(f);
-    const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
-    bool ok = Convert_PhpToUpb(val, &msgval, type, subdesc, arena);
-    if (!ok) return false;
+    if (!Convert_PhpToUpb(val, &msgval, TypeInfo_Get(f), arena)) return false;
   }
 
   upb_msg_set(intern->msg, f, msgval, arena);
@@ -149,11 +160,10 @@
 static bool MessageEq(const upb_msg *m1, const upb_msg *m2, const upb_msgdef *m);
 
 /**
- * ValueEq()()
+ * ValueEq()
  */
-bool ValueEq(upb_msgval val1, upb_msgval val2, upb_fieldtype_t type,
-             const upb_msgdef *m) {
-  switch (type) {
+bool ValueEq(upb_msgval val1, upb_msgval val2, TypeInfo type) {
+  switch (type.type) {
     case UPB_TYPE_BOOL:
       return val1.bool_val == val2.bool_val;
     case UPB_TYPE_INT32:
@@ -172,7 +182,7 @@
       return val1.str_val.size == val2.str_val.size &&
           memcmp(val1.str_val.data, val2.str_val.data, val1.str_val.size) == 0;
     case UPB_TYPE_MESSAGE:
-      return MessageEq(val1.msg_val, val2.msg_val, m);
+      return MessageEq(val1.msg_val, val2.msg_val, type.desc->msgdef);
     default:
       return false;
   }
@@ -190,8 +200,6 @@
     const upb_fielddef *f = upb_msg_iter_field(&i);
     upb_msgval val1 = upb_msg_get(m1, f);
     upb_msgval val2 = upb_msg_get(m2, f);
-    upb_fieldtype_t type = upb_fielddef_type(f);
-    const upb_msgdef *sub_m = upb_fielddef_msgsubdef(f);
 
     if (upb_fielddef_haspresence(f)) {
       if (upb_msg_has(m1, f) != upb_msg_has(m2, f)) {
@@ -201,18 +209,11 @@
     }
 
     if (upb_fielddef_ismap(f)) {
-      const upb_fielddef *key_f = upb_msgdef_itof(sub_m, 1);
-      const upb_fielddef *val_f = upb_msgdef_itof(sub_m, 2);
-      upb_fieldtype_t key_type = upb_fielddef_type(key_f);
-      upb_fieldtype_t val_type = upb_fielddef_type(val_f);
-      const upb_msgdef *val_m = upb_fielddef_msgsubdef(val_f);
-      if (!MapEq(val1.map_val, val2.map_val, key_type, val_type, val_m)) {
-        return false;
-      }
+      if (!MapEq(val1.map_val, val2.map_val, MapType_Get(f))) return false;
     } else if (upb_fielddef_isseq(f)) {
-      if (!ArrayEq(val1.array_val, val2.array_val, type, sub_m)) return false;
+      if (!ArrayEq(val1.array_val, val2.array_val, TypeInfo_Get(f))) return false;
     } else {
-      if (!ValueEq(val1, val2, type, sub_m)) return false;
+      if (!ValueEq(val1, val2, TypeInfo_Get(f))) return false;
     }
   }
 
@@ -258,7 +259,7 @@
 static int Message_has_property(PROTO_VAL *obj, PROTO_STR *member,
                                 int has_set_exists,
                                 void **cache_slot) {
-  Message* intern = PROTO_MSG_P(obj);
+  Message* intern = PROTO_VAL_P(obj);
   const upb_fielddef *f = get_field(intern, member);
 
   if (!f) return 0;
@@ -293,7 +294,7 @@
  */
 static void Message_unset_property(PROTO_VAL *obj, PROTO_STR *member,
                                    void **cache_slot) {
-  Message* intern = PROTO_MSG_P(obj);
+  Message* intern = PROTO_VAL_P(obj);
   const upb_fielddef *f = get_field(intern, member);
 
   if (!f) return;
@@ -330,7 +331,7 @@
  */
 static zval *Message_read_property(PROTO_VAL *obj, PROTO_STR *member,
                                    int type, void **cache_slot, zval *rv) {
-  Message* intern = PROTO_MSG_P(obj);
+  Message* intern = PROTO_VAL_P(obj);
   const upb_fielddef *f = get_field(intern, member);
 
   if (!f) return NULL;
@@ -361,7 +362,7 @@
  */
 static PROTO_RETURN_VAL Message_write_property(
     PROTO_VAL *obj, PROTO_STR *member, zval *val, void **cache_slot) {
-  Message* intern = PROTO_MSG_P(obj);
+  Message* intern = PROTO_VAL_P(obj);
   const upb_fielddef *f = get_field(intern, member);
 
   if (f && Message_set(intern, f, val)) {
@@ -393,6 +394,25 @@
 }
 
 /**
+ * Message_clone_obj()
+ *
+ * Object handler for cloning an object in PHP. Called when PHP code does:
+ *
+ *   $msg2 = clone $msg;
+ */
+static zend_object *Message_clone_obj(PROTO_VAL *object) {
+  Message* intern = PROTO_VAL_P(object);
+  upb_msg *clone = upb_msg_new(intern->desc->msgdef, Arena_Get(&intern->arena));
+
+  // TODO: copy unknown fields?
+  // TODO: use official upb msg copy function
+  memcpy(clone, intern->msg, upb_msgdef_layout(intern->desc->msgdef)->size);
+  zval ret;
+  Message_GetPhpWrapper(&ret, intern->desc, clone, &intern->arena);
+  return Z_OBJ_P(&ret);
+}
+
+/**
  * Message_get_properties()
  *
  * Object handler for the get_properties event in PHP. This returns a HashTable
@@ -415,8 +435,7 @@
 
   if (!ObjCache_Get(msg, val)) {
     Message *intern = emalloc(sizeof(Message));
-    // XXX(haberman): verify whether we actually want to take this route.
-    desc->class_entry->default_properties_count = 0;
+    Message_SuppressDefaultProperties(desc->class_entry);
     zend_object_std_init(&intern->std, desc->class_entry);
     intern->std.handlers = &message_object_handlers;
     ZVAL_COPY(&intern->arena, arena);
@@ -521,15 +540,13 @@
     }
 
     if (upb_fielddef_ismap(f)) {
-      msgval.map_val = MapField_GetUpbMap(val, f, arena);
+      msgval.map_val = MapField_GetUpbMap(val, MapType_Get(f), arena);
       if (!msgval.map_val) return false;
     } else if (upb_fielddef_isseq(f)) {
-      msgval.array_val = RepeatedField_GetUpbArray(val, f, arena);
+      msgval.array_val = RepeatedField_GetUpbArray(val, TypeInfo_Get(f), arena);
       if (!msgval.array_val) return false;
     } else {
-      const Descriptor *desc = Descriptor_GetFromFieldDef(f);
-      upb_fieldtype_t type = upb_fielddef_type(f);
-      if (!Convert_PhpToUpbAutoWrap(val, &msgval, type, desc, arena)) {
+      if (!Convert_PhpToUpbAutoWrap(val, &msgval, TypeInfo_Get(f), arena)) {
         return false;
       }
     }
@@ -554,10 +571,32 @@
  */
 PHP_METHOD(Message, __construct) {
   Message* intern = (Message*)Z_OBJ_P(getThis());
-  const Descriptor* desc = Descriptor_GetFromClassEntry(Z_OBJCE_P(getThis()));
+  const Descriptor* desc;
+  zend_class_entry *ce = Z_OBJCE_P(getThis());
   upb_arena *arena = Arena_Get(&intern->arena);
   zval *init_arr = NULL;
 
+  // This descriptor should always be available, as the generated __construct
+  // method calls initOnce() to load the descriptor prior to calling us.
+  //
+  // However, if the user created their own class derived from Message, this
+  // will trigger an infinite construction loop and blow the stack.  We
+  // temporarily clear create_object to break this loop (see check in
+  // NameMap_GetMessage()).
+  PBPHP_ASSERT(ce->create_object == Message_create);
+  ce->create_object = NULL;
+  desc = Descriptor_GetFromClassEntry(ce);
+  ce->create_object = Message_create;
+
+  if (!desc) {
+    zend_throw_exception_ex(
+        NULL, 0,
+        "Couldn't find descriptor. Note only generated code may derive from "
+        "\\Google\\Protobuf\\Internal\\Message");
+    return;
+  }
+
+
   Message_Initialize(intern, desc);
 
   if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &init_arr) == FAILURE) {
@@ -805,10 +844,9 @@
     const upb_msg *wrapper = upb_msg_get(intern->msg, f).msg_val;
     const upb_msgdef *m = upb_fielddef_msgsubdef(f);
     const upb_fielddef *val_f = upb_msgdef_itof(m, 1);
-    const upb_fieldtype_t val_type = upb_fielddef_type(val_f);
     upb_msgval msgval = upb_msg_get(wrapper, val_f);
     zval ret;
-    Convert_UpbToPhp(msgval, &ret, val_type, NULL, &intern->arena);
+    Convert_UpbToPhp(msgval, &ret, TypeInfo_Get(val_f), &intern->arena);
     RETURN_ZVAL(&ret, 1, 0);
   } else {
     RETURN_NULL();
@@ -861,10 +899,9 @@
   } else {
     const upb_msgdef *m = upb_fielddef_msgsubdef(f);
     const upb_fielddef *val_f = upb_msgdef_itof(m, 1);
-    upb_fieldtype_t val_type = upb_fielddef_type(val_f);
     upb_msg *wrapper;
 
-    if (!Convert_PhpToUpb(val, &msgval, val_type, NULL, arena)) {
+    if (!Convert_PhpToUpb(val, &msgval, TypeInfo_Get(val_f), arena)) {
       return;  // Error is already set.
     }
 
@@ -974,9 +1011,7 @@
 
   {
     upb_msgval msgval = upb_msg_get(intern->msg, f);
-    const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
-    Convert_UpbToPhp(msgval, &ret, upb_fielddef_type(f), subdesc,
-                     &intern->arena);
+    Convert_UpbToPhp(msgval, &ret, TypeInfo_Get(f), &intern->arena);
   }
 
   RETURN_ZVAL(&ret, 1, 0);
@@ -1017,8 +1052,7 @@
 
   f = upb_msgdef_itof(intern->desc->msgdef, field_num);
 
-  if (!Convert_PhpToUpb(val, &msgval, upb_fielddef_type(f),
-                        Descriptor_GetFromFieldDef(f), arena)) {
+  if (!Convert_PhpToUpb(val, &msgval, TypeInfo_Get(f), arena)) {
     return;
   }
 
@@ -1225,8 +1259,8 @@
 
     if (call_user_function(EG(function_table), NULL, &function_name, &retval, 1,
                            datetime) == FAILURE ||
-        !Convert_PhpToUpb(&retval, &timestamp_seconds, UPB_TYPE_INT64, NULL,
-                          NULL)) {
+        !Convert_PhpToUpb(&retval, &timestamp_seconds,
+                          TypeInfo_FromType(UPB_TYPE_INT64), NULL)) {
       zend_error(E_ERROR, "Cannot get timestamp from DateTime.");
       return;
     }
@@ -1251,8 +1285,8 @@
 
     if (call_user_function(EG(function_table), NULL, &function_name, &retval, 2,
                            params) == FAILURE ||
-        !Convert_PhpToUpb(&retval, &timestamp_nanos, UPB_TYPE_INT32, NULL,
-                          NULL)) {
+        !Convert_PhpToUpb(&retval, &timestamp_nanos,
+                          TypeInfo_FromType(UPB_TYPE_INT32), NULL)) {
       zend_error(E_ERROR, "Cannot format DateTime.");
       return;
     }
@@ -1338,6 +1372,7 @@
   h->unset_property = Message_unset_property;
   h->get_properties = Message_get_properties;
   h->get_property_ptr_ptr = Message_get_property_ptr_ptr;
+  h->clone_obj = Message_clone_obj;
 
   WellKnownTypes_ModuleInit();  /* From wkt.inc. */
 }
diff --git a/php/ext/google/protobuf/message.h b/php/ext/google/protobuf/message.h
index 0e6fb5a..5b49e0d 100644
--- a/php/ext/google/protobuf/message.h
+++ b/php/ext/google/protobuf/message.h
@@ -56,7 +56,6 @@
 void Message_GetPhpWrapper(zval *val, const Descriptor *desc, upb_msg *msg,
                            zval *arena);
 
-bool ValueEq(upb_msgval val1, upb_msgval val2, upb_fieldtype_t type,
-             const upb_msgdef *m);
+bool ValueEq(upb_msgval val1, upb_msgval val2, TypeInfo type);
 
 #endif  // PHP_PROTOBUF_MESSAGE_H_
diff --git a/php/ext/google/protobuf/php-upb.c b/php/ext/google/protobuf/php-upb.c
index 4c6a56a..c56a567 100644
--- a/php/ext/google/protobuf/php-upb.c
+++ b/php/ext/google/protobuf/php-upb.c
@@ -6711,6 +6711,10 @@
   return s->bytes_loaded;
 }
 
+upb_arena *_upb_symtab_arena(const upb_symtab *s) {
+  return s->arena;
+}
+
 #undef CHK_OOM
 
 
@@ -8020,7 +8024,7 @@
 
   if (!f) {
     if ((d->options & UPB_JSONDEC_IGNOREUNKNOWN) == 0) {
-      jsondec_errf(d, "Unknown field: '" UPB_STRVIEW_FORMAT "'",
+      jsondec_errf(d, "No such field: " UPB_STRVIEW_FORMAT,
                    UPB_STRVIEW_ARGS(name));
     }
     jsondec_skipval(d);
@@ -8173,7 +8177,8 @@
 
   {
     /* [+-]08:00 or Z */
-    int ofs = 0;
+    int ofs_hour = 0;
+    int ofs_min = 0;
     bool neg = false;
 
     if (ptr == end) goto malformed;
@@ -8184,9 +8189,10 @@
         /* fallthrough */
       case '+':
         if ((end - ptr) != 5) goto malformed;
-        ofs = jsondec_tsdigits(d, &ptr, 2, ":00");
-        ofs *= 60 * 60;
-        seconds.int64_val += (neg ? ofs : -ofs);
+        ofs_hour = jsondec_tsdigits(d, &ptr, 2, ":");
+        ofs_min = jsondec_tsdigits(d, &ptr, 2, NULL);
+        ofs_min = ((ofs_hour * 60) + ofs_min) * 60;
+        seconds.int64_val += (neg ? ofs_min : -ofs_min);
         break;
       case 'Z':
         if (ptr != end) goto malformed;
diff --git a/php/ext/google/protobuf/php-upb.h b/php/ext/google/protobuf/php-upb.h
index 996a6d8..bd72cd9 100644
--- a/php/ext/google/protobuf/php-upb.h
+++ b/php/ext/google/protobuf/php-upb.h
@@ -4178,6 +4178,7 @@
     upb_symtab *s, const google_protobuf_FileDescriptorProto *file,
     upb_status *status);
 size_t _upb_symtab_bytesloaded(const upb_symtab *s);
+upb_arena *_upb_symtab_arena(const upb_symtab *s);
 
 /* For generated code only: loads a generated descriptor. */
 typedef struct upb_def_init {
diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h
index 322567e..2325282 100644
--- a/php/ext/google/protobuf/protobuf.h
+++ b/php/ext/google/protobuf/protobuf.h
@@ -58,13 +58,13 @@
 #if PHP_VERSION_ID < 80000
 #define PROTO_VAL zval
 #define PROTO_STR zval
-#define PROTO_MSG_P(obj) (Message*)Z_OBJ_P(obj)
+#define PROTO_VAL_P(obj) Z_OBJ_P(obj)
 #define PROTO_STRVAL_P(obj) Z_STRVAL_P(obj)
 #define PROTO_STRLEN_P(obj) Z_STRLEN_P(obj)
 #else
 #define PROTO_VAL zend_object
 #define PROTO_STR zend_string
-#define PROTO_MSG_P(obj) (Message*)(obj)
+#define PROTO_VAL_P(obj) (void*)(obj)
 #define PROTO_STRVAL_P(obj) ZSTR_VAL(obj)
 #define PROTO_STRLEN_P(obj) ZSTR_LEN(obj)
 #endif
diff --git a/php/src/Google/Protobuf/Internal/FileOptions.php b/php/src/Google/Protobuf/Internal/FileOptions.php
index f415b07..6283b2a 100644
--- a/php/src/Google/Protobuf/Internal/FileOptions.php
+++ b/php/src/Google/Protobuf/Internal/FileOptions.php
@@ -49,6 +49,7 @@
      * This option does nothing.
      *
      * Generated from protobuf field <code>optional bool java_generate_equals_and_hash = 20 [deprecated = true];</code>
+     * @deprecated
      */
     protected $java_generate_equals_and_hash = null;
     /**
@@ -412,19 +413,23 @@
      *
      * Generated from protobuf field <code>optional bool java_generate_equals_and_hash = 20 [deprecated = true];</code>
      * @return bool
+     * @deprecated
      */
     public function getJavaGenerateEqualsAndHash()
     {
+        @trigger_error('java_generate_equals_and_hash is deprecated.', E_USER_DEPRECATED);
         return isset($this->java_generate_equals_and_hash) ? $this->java_generate_equals_and_hash : false;
     }
 
     public function hasJavaGenerateEqualsAndHash()
     {
+        @trigger_error('java_generate_equals_and_hash is deprecated.', E_USER_DEPRECATED);
         return isset($this->java_generate_equals_and_hash);
     }
 
     public function clearJavaGenerateEqualsAndHash()
     {
+        @trigger_error('java_generate_equals_and_hash is deprecated.', E_USER_DEPRECATED);
         unset($this->java_generate_equals_and_hash);
     }
 
@@ -434,9 +439,11 @@
      * Generated from protobuf field <code>optional bool java_generate_equals_and_hash = 20 [deprecated = true];</code>
      * @param bool $var
      * @return $this
+     * @deprecated
      */
     public function setJavaGenerateEqualsAndHash($var)
     {
+        @trigger_error('java_generate_equals_and_hash is deprecated.', E_USER_DEPRECATED);
         GPBUtil::checkBool($var);
         $this->java_generate_equals_and_hash = $var;
 
diff --git a/php/src/Google/Protobuf/Internal/Message.php b/php/src/Google/Protobuf/Internal/Message.php
index c02d2b4..64aadf9 100644
--- a/php/src/Google/Protobuf/Internal/Message.php
+++ b/php/src/Google/Protobuf/Internal/Message.php
@@ -93,8 +93,9 @@
         $pool = DescriptorPool::getGeneratedPool();
         $this->desc = $pool->getDescriptorByClassName(get_class($this));
         if (is_null($this->desc)) {
-            user_error(get_class($this) . " is not found in descriptor pool.");
-            return;
+          throw new \InvalidArgumentException(
+            get_class($this) ." is not found in descriptor pool. " .
+            'Only generated classes may derive from Message.');
         }
         foreach ($this->desc->getField() as $field) {
             $setter = $field->getSetter();
diff --git a/php/tests/ArrayTest.php b/php/tests/ArrayTest.php
index 269d11d..0585ca5 100644
--- a/php/tests/ArrayTest.php
+++ b/php/tests/ArrayTest.php
@@ -624,4 +624,16 @@
             new RepeatedField(GPBType::MESSAGE, TestMessage::class) ==
             new RepeatedField(GPBType::MESSAGE, Sub::class));
     }
+
+    #########################################################
+    # Test clone
+    #########################################################
+
+    public function testClone()
+    {
+        $arr = new RepeatedField(GPBType::MESSAGE, TestMessage::class);
+        $arr[] = new TestMessage;
+        $arr2 = clone $arr;
+        $this->assertSame($arr[0], $arr2[0]);
+    }
 }
diff --git a/php/tests/GeneratedClassTest.php b/php/tests/GeneratedClassTest.php
index f3f74d1..f2a9fb0 100644
--- a/php/tests/GeneratedClassTest.php
+++ b/php/tests/GeneratedClassTest.php
@@ -24,6 +24,13 @@
 use PBEmpty\PBEcho\TestEmptyPackage;
 use Php\Test\TestNamespace;
 
+# This is not allowed, but we at least shouldn't crash.
+class C extends \Google\Protobuf\Internal\Message {
+    public function __construct($data = null) {
+        parent::__construct($data);
+    }
+}
+
 class GeneratedClassTest extends TestBase
 {
 
@@ -72,6 +79,33 @@
     }
 
     #########################################################
+    # Test deprecated int32 field.
+    #########################################################
+
+    public function testDeprecatedInt32Field()
+    {
+        $m = new TestMessage();
+
+        // temporarily change error handler to capture the deprecated errors
+        $deprecationCount = 0;
+        set_error_handler(function ($errno, $errstr) use (&$deprecationCount) {
+            if ($errstr === 'deprecated_optional_int32 is deprecated.') {
+                $deprecationCount++;
+            }
+        }, E_USER_DEPRECATED);
+
+        // default test set
+        $m->setDeprecatedOptionalInt32(MAX_INT32);
+        $this->assertSame(MAX_INT32, $m->getDeprecatedOptionalInt32());
+        $m->setDeprecatedOptionalInt32(MIN_INT32);
+        $this->assertSame(MIN_INT32, $m->getDeprecatedOptionalInt32());
+
+        restore_error_handler();
+
+        $this->assertSame(4, $deprecationCount);
+    }
+
+    #########################################################
     # Test optional int32 field.
     #########################################################
 
@@ -1492,6 +1526,28 @@
     }
 
     #########################################################
+    # Test clone.
+    #########################################################
+
+    public function testClone()
+    {
+        $m = new TestMessage([
+            'optional_int32' => -42,
+            'optional_int64' => -43,
+            'optional_message' => new Sub([
+                'a' => 33
+            ]),
+            'map_int32_message' => [1 => new Sub(['a' => 36])],
+        ]);
+        $m2 = clone $m;
+        $this->assertEquals($m->getOptionalInt32(), $m2->getOptionalInt32());
+        $this->assertEquals($m->getOptionalInt64(), $m2->getOptionalInt64());
+        $this->assertSame($m->getOptionalMessage(), $m2->getOptionalMessage());
+        $this->assertSame($m->getMapInt32Message()[1], $m2->getMapInt32Message()[1]);
+        $this->assertEquals($m->serializeToJsonString(), $m2->serializeToJsonString());
+    }
+
+    #########################################################
     # Test message equals.
     #########################################################
 
@@ -1697,6 +1753,16 @@
     }
 
     #########################################################
+    # Test that we don't crash if users create their own messages.
+    #########################################################
+
+    public function testUserDefinedClass() {
+      # This is not allowed, but at least we shouldn't crash.
+      $this->expectException(Exception::class);
+      $p = new C();
+    }
+
+    #########################################################
     # Test no segfault when error happens
     #########################################################
 
diff --git a/php/tests/GeneratedPhpdocTest.php b/php/tests/GeneratedPhpdocTest.php
index de672ac..18963a9 100644
--- a/php/tests/GeneratedPhpdocTest.php
+++ b/php/tests/GeneratedPhpdocTest.php
@@ -340,6 +340,13 @@
                 ],
                 '@param \NoNamespaceMessage $var'
             ],
+            [
+                [
+                    'setDeprecatedOptionalInt32',
+                    'getDeprecatedOptionalInt32',
+                ],
+                '@deprecated'
+            ],
         ];
     }
 }
diff --git a/php/tests/MapFieldTest.php b/php/tests/MapFieldTest.php
index a96f24f..2d8ae61 100644
--- a/php/tests/MapFieldTest.php
+++ b/php/tests/MapFieldTest.php
@@ -512,6 +512,24 @@
     }
 
     #########################################################
+    # Test clone
+    #########################################################
+
+    public function testClone()
+    {
+        $map = new MapField(GPBType::INT32,
+            GPBType::MESSAGE, Sub::class);
+
+        // Test append.
+        $sub_m = new Sub();
+        $sub_m->setA(1);
+        $map[0] = $sub_m;
+
+        $map2 = clone $map;
+        $this->assertSame($map[0], $map2[0]);
+    }
+
+    #########################################################
     # Test memory leak
     #########################################################
 
diff --git a/php/tests/proto/test.proto b/php/tests/proto/test.proto
index 8835729..609b8cf 100644
--- a/php/tests/proto/test.proto
+++ b/php/tests/proto/test.proto
@@ -147,6 +147,9 @@
   map<string, google.protobuf.Any> map_string_any = 122;
   map<string, google.protobuf.ListValue> map_string_list = 123;
   map<string, google.protobuf.Struct> map_string_struct = 124;
+
+  // deprecated field
+  int32 deprecated_optional_int32 = 125 [deprecated=true];
 }
 
 enum TestEnum {
diff --git a/python/release/wheel/Dockerfile b/python/release/wheel/Dockerfile
deleted file mode 100644
index f38ec2f..0000000
--- a/python/release/wheel/Dockerfile
+++ /dev/null
@@ -1,6 +0,0 @@
-FROM quay.io/pypa/manylinux1_x86_64
-
-RUN yum install -y libtool
-RUN /opt/python/cp27-cp27mu/bin/pip install twine
-
-COPY protobuf_optimized_pip.sh /
diff --git a/python/release/wheel/README.md b/python/release/wheel/README.md
deleted file mode 100644
index edda2cd..0000000
--- a/python/release/wheel/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-Description
-------------------------------
-This directory is used to build released wheels according to PEP513 and upload
-them to pypi.
-
-Usage
-------------------------------
-For example, to release 3.3.0:
-    ./protobuf_optimized_pip.sh 3.3.0 PYPI_USERNAME PYPI_PASSWORD
-
-Structure
-------------------------------
-| Source                    | Source                                                       |
-|--------------------------------------|---------------------------------------------------|
-| protobuf_optimized_pip.sh | Entry point. Calling Dockerfile and build_wheel_manylinux.sh |
-| Dockerfile                | Build docker image according to PEP513.                      |
-| build_wheel_manylinux.sh  | Build wheel packages in the docker container.                |
diff --git a/python/release/wheel/build_wheel_manylinux.sh b/python/release/wheel/build_wheel_manylinux.sh
deleted file mode 100755
index 39fd8c1..0000000
--- a/python/release/wheel/build_wheel_manylinux.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-
-# Print usage and fail.
-function usage() {
-  echo "Usage: protobuf_optimized_pip.sh PROTOBUF_VERSION PYPI_USERNAME PYPI_PASSWORD" >&2
-  exit 1   # Causes caller to exit because we use -e.
-}
-
-# Validate arguments.
-if [ $0 != ./build_wheel_manylinux.sh ]; then
-  echo "Please run this script from the directory in which it is located." >&2
-  exit 1
-fi
-
-if [ $# -lt 3 ]; then
-  usage
-  exit 1
-fi
-
-PROTOBUF_VERSION=$1
-PYPI_USERNAME=$2
-PYPI_PASSWORD=$3
-
-docker rmi protobuf-python-wheel
-docker build . -t protobuf-python-wheel
-docker run --rm protobuf-python-wheel ./protobuf_optimized_pip.sh $PROTOBUF_VERSION $PYPI_USERNAME $PYPI_PASSWORD
-docker rmi protobuf-python-wheel
diff --git a/python/release/wheel/protobuf_optimized_pip.sh b/python/release/wheel/protobuf_optimized_pip.sh
deleted file mode 100755
index 07c2a09..0000000
--- a/python/release/wheel/protobuf_optimized_pip.sh
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/env bash
-
-# DO NOT use this script manually! Called by docker.
-
-set -ex
-
-# Print usage and fail.
-function usage() {
-  echo "Usage: protobuf_optimized_pip.sh PROTOBUF_VERSION" >&2
-  exit 1   # Causes caller to exit because we use -e.
-}
-
-# Build wheel
-function build_wheel() {
-  PYTHON_VERSION=$1
-  PYTHON_BIN=/opt/python/${PYTHON_VERSION}/bin/python
-
-  $PYTHON_BIN setup.py bdist_wheel --cpp_implementation --compile_static_extension
-  auditwheel repair dist/protobuf-${PROTOBUF_VERSION}-${PYTHON_VERSION}-linux_x86_64.whl
-}
-
-# Validate arguments.
-if [ $0 != ./protobuf_optimized_pip.sh ]; then
-  echo "Please run this script from the directory in which it is located." >&2
-  exit 1
-fi
-
-if [ $# -lt 1 ]; then
-  usage
-  exit 1
-fi
-
-PROTOBUF_VERSION=$1
-PYPI_USERNAME=$2
-PYPI_PASSWORD=$3
-
-DIR=${PWD}/'protobuf-python-build'
-PYTHON_VERSIONS=('cp27-cp27mu' 'cp33-cp33m' 'cp34-cp34m' 'cp35-cp35m' 'cp36-cp36m')
-
-mkdir -p ${DIR}
-cd ${DIR}
-curl -SsL -O https://github.com/protocolbuffers/protobuf/archive/v${PROTOBUF_VERSION}.tar.gz
-tar xzf v${PROTOBUF_VERSION}.tar.gz
-cd $DIR/protobuf-${PROTOBUF_VERSION}
-
-# Autoconf on centos 5.11 cannot recognize AC_PROG_OBJC.
-sed -i '/AC_PROG_OBJC/d' configure.ac
-sed -i 's/conformance\/Makefile//g' configure.ac
-
-# Use the /usr/bin/autoconf and related tools to pick the correct aclocal macros
-export PATH="/usr/bin:$PATH"
-
-# Build protoc
-./autogen.sh
-CXXFLAGS="-fPIC -g -O2" ./configure
-make -j8
-export PROTOC=$DIR/src/protoc
-
-cd python
-
-for PYTHON_VERSION in "${PYTHON_VERSIONS[@]}"
-do
-  build_wheel $PYTHON_VERSION
-done
-
-/opt/python/cp27-cp27mu/bin/twine upload wheelhouse/*
diff --git a/python/setup.py b/python/setup.py
index aab240a..f97ce48 100755
--- a/python/setup.py
+++ b/python/setup.py
@@ -279,6 +279,8 @@
         "Programming Language :: Python :: 3.5",
         "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
         ],
       namespace_packages=['google'],
       packages=find_packages(
diff --git a/python/tox.ini b/python/tox.ini
index 56be125..9fabb6d 100644
--- a/python/tox.ini
+++ b/python/tox.ini
@@ -1,6 +1,6 @@
 [tox]
 envlist =
-    py{27,33,34,35,36}-{cpp,python}
+    py{27,33,34,35,36,37,38,39}-{cpp,python}
 
 [testenv]
 usedevelop=true
diff --git a/ruby/ext/google/protobuf_c/ruby-upb.c b/ruby/ext/google/protobuf_c/ruby-upb.c
index 9c048da..a7aeda2 100755
--- a/ruby/ext/google/protobuf_c/ruby-upb.c
+++ b/ruby/ext/google/protobuf_c/ruby-upb.c
@@ -7793,7 +7793,8 @@
 
   {
     /* [+-]08:00 or Z */
-    int ofs = 0;
+    int ofs_hour = 0;
+    int ofs_min = 0;
     bool neg = false;
 
     if (ptr == end) goto malformed;
@@ -7804,9 +7805,10 @@
         /* fallthrough */
       case '+':
         if ((end - ptr) != 5) goto malformed;
-        ofs = jsondec_tsdigits(d, &ptr, 2, ":00");
-        ofs *= 60 * 60;
-        seconds.int64_val += (neg ? ofs : -ofs);
+        ofs_hour = jsondec_tsdigits(d, &ptr, 2, ":");
+        ofs_min = jsondec_tsdigits(d, &ptr, 2, NULL);
+        ofs_min = ((ofs_hour * 60) + ofs_min) * 60;
+        seconds.int64_val += (neg ? ofs_min : -ofs_min);
         break;
       case 'Z':
         if (ptr != end) goto malformed;
diff --git a/ruby/ext/google/protobuf_c/ruby-upb.h b/ruby/ext/google/protobuf_c/ruby-upb.h
index 106c73b..fa04393 100755
--- a/ruby/ext/google/protobuf_c/ruby-upb.h
+++ b/ruby/ext/google/protobuf_c/ruby-upb.h
@@ -4161,7 +4161,6 @@
 const upb_msgdef *upb_filedef_msg(const upb_filedef *f, int i);
 const upb_enumdef *upb_filedef_enum(const upb_filedef *f, int i);
 const upb_symtab *upb_filedef_symtab(const upb_filedef *f);
-upb_arena *_upb_symtab_arena(const upb_symtab *s);
 
 /* upb_symtab *****************************************************************/
 
@@ -4179,6 +4178,7 @@
     upb_symtab *s, const google_protobuf_FileDescriptorProto *file,
     upb_status *status);
 size_t _upb_symtab_bytesloaded(const upb_symtab *s);
+upb_arena *_upb_symtab_arena(const upb_symtab *s);
 
 /* For generated code only: loads a generated descriptor. */
 typedef struct upb_def_init {
diff --git a/src/google/protobuf/arena.h b/src/google/protobuf/arena.h
index 87443ad..8b5c7f1 100644
--- a/src/google/protobuf/arena.h
+++ b/src/google/protobuf/arena.h
@@ -327,7 +327,7 @@
   template <typename T>
   PROTOBUF_NDEBUG_INLINE static T* CreateArray(Arena* arena,
                                                size_t num_elements) {
-    static_assert(std::is_standard_layout<T>::value && std::is_trivial<T>::value,
+    static_assert(std::is_trivial<T>::value,
                   "CreateArray requires a trivially constructible type");
     static_assert(std::is_trivially_destructible<T>::value,
                   "CreateArray requires a trivially destructible type");
diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc
index ab49ac2..48594c4 100644
--- a/src/google/protobuf/compiler/command_line_interface.cc
+++ b/src/google/protobuf/compiler/command_line_interface.cc
@@ -1216,21 +1216,9 @@
 
 bool CommandLineInterface::AllowProto3Optional(
     const FileDescriptor& file) const {
-  // If the --experimental_allow_proto3_optional flag was set, we allow.
-  if (allow_proto3_optional_) return true;
-
-  // Whitelist all ads protos. Ads is an early adopter of this feature.
-  if (file.name().find("google/ads/googleads") != std::string::npos) {
-    return true;
-  }
-
-  // Whitelist all protos testing proto3 optional.
-  if (file.name().find("test_proto3_optional") != std::string::npos) {
-    return true;
-  }
-
-
-  return false;
+  // Proto3 optional is enabled by default now, the experimental flag is no
+  // longer required.
+  return true;
 }
 
 
diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc
index 1987e08..2bf0582 100644
--- a/src/google/protobuf/compiler/command_line_interface_unittest.cc
+++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc
@@ -2378,48 +2378,6 @@
   ExpectErrorText("Missing value for flag: --test_out\n");
 }
 
-TEST_F(CommandLineInterfaceTest, Proto3OptionalDisallowed) {
-  CreateTempFile("google/foo.proto",
-                 "syntax = \"proto3\";\n"
-                 "message Foo {\n"
-                 "  optional int32 i = 1;\n"
-                 "}\n");
-
-  Run("protocol_compiler --proto_path=$tmpdir google/foo.proto "
-      "-odescriptor.pb");
-
-  ExpectErrorSubstring("--experimental_allow_proto3_optional was not set");
-}
-
-TEST_F(CommandLineInterfaceTest, Proto3OptionalDisallowedDescriptor) {
-  CreateTempFile("google/foo.proto",
-                 "syntax = \"proto3\";\n"
-                 "message Foo {\n"
-                 "  optional int32 i = 1;\n"
-                 "}\n");
-
-  Run("protocol_compiler --experimental_allow_proto3_optional "
-      "--proto_path=$tmpdir google/foo.proto "
-      " -o$tmpdir/descriptor.pb");
-  ExpectNoErrors();
-
-  Run("protocol_compiler --descriptor_set_in=$tmpdir/descriptor.pb"
-      " google/foo.proto --test_out=$tmpdir");
-  ExpectErrorSubstring("--experimental_allow_proto3_optional was not set");
-}
-
-TEST_F(CommandLineInterfaceTest, Proto3OptionalDisallowedGenCode) {
-  CreateTempFile("google/foo.proto",
-                 "syntax = \"proto3\";\n"
-                 "message Foo {\n"
-                 "  optional int32 i = 1;\n"
-                 "}\n");
-
-  Run("protocol_compiler --proto_path=$tmpdir google/foo.proto "
-      "--test_out=$tmpdir");
-
-  ExpectErrorSubstring("--experimental_allow_proto3_optional was not set");
-}
 
 TEST_F(CommandLineInterfaceTest, Proto3OptionalDisallowedNoCodegenSupport) {
   CreateTempFile("google/foo.proto",
diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc
index d590af3..6d4412b 100644
--- a/src/google/protobuf/compiler/cpp/cpp_file.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_file.cc
@@ -75,11 +75,6 @@
   return val.name();
 }
 
-template <>
-std::string GetSortKey<SCC>(const SCC& val) {
-  return val.GetRepresentative()->full_name();
-}
-
 template <class T>
 bool CompareSortKeys(const T* a, const T* b) {
   return GetSortKey(*a) < GetSortKey(*b);
diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc
index 96c0c11..f657c17 100644
--- a/src/google/protobuf/compiler/java/java_message_field.cc
+++ b/src/google/protobuf/compiler/java/java_message_field.cc
@@ -49,6 +49,7 @@
 namespace compiler {
 namespace java {
 
+
 namespace {
 
 void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex,
diff --git a/src/google/protobuf/compiler/js/js_generator.cc b/src/google/protobuf/compiler/js/js_generator.cc
index 746ecb2..819622e 100644
--- a/src/google/protobuf/compiler/js/js_generator.cc
+++ b/src/google/protobuf/compiler/js/js_generator.cc
@@ -28,9 +28,19 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#include <google/protobuf/compiler/js/js_generator.h>
-
 #include <assert.h>
+#include <google/protobuf/compiler/js/js_generator.h>
+#include <google/protobuf/compiler/js/well_known_types_embed.h>
+#include <google/protobuf/compiler/scc.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/stubs/strutil.h>
+
 #include <algorithm>
 #include <limits>
 #include <map>
@@ -39,17 +49,6 @@
 #include <utility>
 #include <vector>
 
-#include <google/protobuf/stubs/logging.h>
-#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>
-#include <google/protobuf/io/zero_copy_stream.h>
-#include <google/protobuf/descriptor.pb.h>
-#include <google/protobuf/descriptor.h>
-
 namespace google {
 namespace protobuf {
 namespace compiler {
@@ -173,8 +172,8 @@
   if (descriptor == NULL) {
     return "";
   }
-  std::string result = StripPrefixString(
-      descriptor->full_name(), descriptor->file()->package());
+  std::string result =
+      StripPrefixString(descriptor->full_name(), descriptor->file()->package());
   // Add a leading dot if one is not already present.
   if (!result.empty() && result[0] != '.') {
     result = "." + result;
@@ -411,7 +410,7 @@
           GetSnakeFilename(scc->GetRepresentative()->file()->name()));
       (*long_name_dict)[scc->GetRepresentative()] =
           StrCat(snake_name, "_long_sccs_",
-                       static_cast<uint64>((*long_name_dict).size()));
+                 static_cast<uint64>((*long_name_dict).size()));
     }
     filename_base = (*long_name_dict)[scc->GetRepresentative()];
   }
@@ -431,9 +430,7 @@
 }
 
 // Returns the message/response ID, if set.
-std::string GetMessageId(const Descriptor* desc) {
-  return std::string();
-}
+std::string GetMessageId(const Descriptor* desc) { return std::string(); }
 
 bool IgnoreExtensionField(const FieldDescriptor* field) {
   // Exclude descriptor extensions from output "to avoid clutter" (from original
@@ -444,7 +441,6 @@
          file->name() == "google/protobuf/descriptor.proto";
 }
 
-
 // Used inside Google only -- do not remove.
 bool IsResponse(const Descriptor* desc) { return false; }
 
@@ -452,7 +448,6 @@
   return IgnoreExtensionField(field);
 }
 
-
 // Do we ignore this message type?
 bool IgnoreMessage(const Descriptor* d) { return d->options().map_entry(); }
 
@@ -537,7 +532,6 @@
   return name;
 }
 
-
 std::string JSOneofName(const OneofDescriptor* oneof) {
   return ToUpperCamel(ParseLowerUnderscore(oneof->name()));
 }
@@ -818,23 +812,19 @@
 
   switch (field->cpp_type()) {
     case FieldDescriptor::CPPTYPE_INT32:
-      return MaybeNumberString(field,
-                               StrCat(field->default_value_int32()));
+      return MaybeNumberString(field, StrCat(field->default_value_int32()));
     case FieldDescriptor::CPPTYPE_UINT32:
       // The original codegen is in Java, and Java protobufs store unsigned
       // integer values as signed integer values. In order to exactly match the
       // output, we need to reinterpret as base-2 signed. Ugh.
       return MaybeNumberString(
-          field,
-          StrCat(static_cast<int32>(field->default_value_uint32())));
+          field, StrCat(static_cast<int32>(field->default_value_uint32())));
     case FieldDescriptor::CPPTYPE_INT64:
-      return MaybeNumberString(field,
-                               StrCat(field->default_value_int64()));
+      return MaybeNumberString(field, StrCat(field->default_value_int64()));
     case FieldDescriptor::CPPTYPE_UINT64:
       // See above note for uint32 -- reinterpreting as signed.
       return MaybeNumberString(
-          field,
-          StrCat(static_cast<int64>(field->default_value_uint64())));
+          field, StrCat(static_cast<int64>(field->default_value_uint64())));
     case FieldDescriptor::CPPTYPE_ENUM:
       return StrCat(field->default_value_enum()->number());
     case FieldDescriptor::CPPTYPE_BOOL:
@@ -849,9 +839,10 @@
         bool is_valid = EscapeJSString(field->default_value_string(), &out);
         if (!is_valid) {
           // TODO(b/115551870): Decide whether this should be a hard error.
-          GOOGLE_LOG(WARNING) << "The default value for field " << field->full_name()
-                       << " was truncated since it contained invalid UTF-8 or"
-                          " codepoints outside the basic multilingual plane.";
+          GOOGLE_LOG(WARNING)
+              << "The default value for field " << field->full_name()
+              << " was truncated since it contained invalid UTF-8 or"
+                 " codepoints outside the basic multilingual plane.";
         }
         return "\"" + out + "\"";
       } else {  // Bytes
@@ -1114,7 +1105,6 @@
          JSBinaryReadWriteMethodName(field, /* is_writer = */ true);
 }
 
-
 std::string JSTypeTag(const FieldDescriptor* desc) {
   switch (desc->type()) {
     case FieldDescriptor::TYPE_DOUBLE:
@@ -1149,7 +1139,6 @@
   return "";
 }
 
-
 bool HasRepeatedFields(const GeneratorOptions& options,
                        const Descriptor* desc) {
   for (int i = 0; i < desc->field_count(); i++) {
@@ -1638,6 +1627,8 @@
       "/**\n"
       " * @fileoverview\n"
       " * @enhanceable\n"
+      // TODO(b/152440355): requireType/requires diverged from internal version.
+      " * @suppress {missingRequire} reports error on implicit type usages.\n"
       " * @suppress {messageConventions} JS Compiler reports an "
       "error if a variable or\n"
       " *     field starts with 'MSG_' and isn't a translatable "
@@ -1902,16 +1893,13 @@
   }
 }
 
-bool NamespaceOnly(const Descriptor* desc) {
-  return false;
-}
+bool NamespaceOnly(const Descriptor* desc) { return false; }
 
 void Generator::FindRequiresForMessage(const GeneratorOptions& options,
                                        const Descriptor* desc,
                                        std::set<std::string>* required,
                                        std::set<std::string>* forwards,
                                        bool* have_message) const {
-
   if (!NamespaceOnly(desc)) {
     *have_message = true;
     for (int i = 0; i < desc->field_count(); i++) {
@@ -1960,7 +1948,8 @@
 void Generator::FindRequiresForExtension(
     const GeneratorOptions& options, const FieldDescriptor* field,
     std::set<std::string>* required, std::set<std::string>* forwards) const {
-  if (field->containing_type()->full_name() != "google.protobuf.bridge.MessageSet") {
+  if (field->containing_type()->full_name() !=
+      "google.protobuf.bridge.MessageSet") {
     required->insert(GetMessagePath(options, field->containing_type()));
   }
   FindRequiresForField(options, field, required, forwards);
@@ -2000,7 +1989,6 @@
     printer->Print("\n");
     GenerateClassFieldInfo(options, printer, desc);
 
-
     GenerateClassToObject(options, printer, desc);
     // These must come *before* the extension-field info generation in
     // GenerateClassRegistration so that references to the binary
@@ -2084,7 +2072,8 @@
     const Descriptor* desc) const {
   if (!NamespaceOnly(desc)) {
     GenerateClassConstructor(options, printer, desc);
-    if (IsExtendable(desc) && desc->full_name() != "google.protobuf.bridge.MessageSet") {
+    if (IsExtendable(desc) &&
+        desc->full_name() != "google.protobuf.bridge.MessageSet") {
       GenerateClassExtensionFieldInfo(options, printer, desc);
     }
   }
@@ -2524,7 +2513,6 @@
       GenerateExtension(options, printer, extension);
     }
   }
-
 }
 
 void Generator::GenerateClassFields(const GeneratorOptions& options,
@@ -2687,8 +2675,7 @@
     }
 
   } else {
-    bool untyped =
-        false;
+    bool untyped = false;
 
     // Simple (primitive) field, either singular or repeated.
 
@@ -3037,7 +3024,6 @@
   }
 }
 
-
 void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options,
                                                io::Printer* printer,
                                                const Descriptor* desc) const {
@@ -3067,36 +3053,36 @@
       "$class$.deserializeBinaryFromReader = function(msg, reader) {\n"
       "  while (reader.nextField()) {\n",
       "class", GetMessagePath(options, desc));
+  printer->Print(
+      "    if (reader.isEndGroup()) {\n"
+      "      break;\n"
+      "    }\n"
+      "    var field = reader.getFieldNumber();\n"
+      "    switch (field) {\n");
+
+  for (int i = 0; i < desc->field_count(); i++) {
+    if (!IgnoreField(desc->field(i))) {
+      GenerateClassDeserializeBinaryField(options, printer, desc->field(i));
+    }
+  }
+
+  printer->Print("    default:\n");
+  if (IsExtendable(desc)) {
     printer->Print(
-        "    if (reader.isEndGroup()) {\n"
+        "      jspb.Message.readBinaryExtension(msg, reader,\n"
+        "        $extobj$Binary,\n"
+        "        $class$.prototype.getExtension,\n"
+        "        $class$.prototype.setExtension);\n"
         "      break;\n"
-        "    }\n"
-        "    var field = reader.getFieldNumber();\n"
-        "    switch (field) {\n");
-
-    for (int i = 0; i < desc->field_count(); i++) {
-      if (!IgnoreField(desc->field(i))) {
-        GenerateClassDeserializeBinaryField(options, printer, desc->field(i));
-      }
-    }
-
-    printer->Print("    default:\n");
-    if (IsExtendable(desc)) {
-      printer->Print(
-          "      jspb.Message.readBinaryExtension(msg, reader,\n"
-          "        $extobj$Binary,\n"
-          "        $class$.prototype.getExtension,\n"
-          "        $class$.prototype.setExtension);\n"
-          "      break;\n"
-          "    }\n",
-          "extobj", JSExtensionsObjectName(options, desc->file(), desc),
-          "class", GetMessagePath(options, desc));
-    } else {
-      printer->Print(
-          "      reader.skipField();\n"
-          "      break;\n"
-          "    }\n");
-    }
+        "    }\n",
+        "extobj", JSExtensionsObjectName(options, desc->file(), desc), "class",
+        GetMessagePath(options, desc));
+  } else {
+    printer->Print(
+        "      reader.skipField();\n"
+        "      break;\n"
+        "    }\n");
+  }
 
   printer->Print(
       "  }\n"
@@ -3381,9 +3367,8 @@
   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 == valid_index.back()) ? "" : ",");
+                   ToEnumCase(value->name()), "value", StrCat(value->number()),
+                   "comma", (i == valid_index.back()) ? "" : ",");
     printer->Annotate("name", value);
   }
 
@@ -3424,8 +3409,7 @@
       "!Object} */ (\n"
       "         $toObject$),\n"
       "    $repeated$);\n",
-      "index", StrCat(field->number()), "name", extension_object_name,
-      "ctor",
+      "index", StrCat(field->number()), "name", extension_object_name, "ctor",
       (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
            ? SubmessageTypeRef(options, field)
            : std::string("null")),
@@ -3732,7 +3716,6 @@
     return false;
   }
 
-
   if (options.output_mode() == GeneratorOptions::kEverythingInOneFile) {
     // All output should go in a single file.
     std::string filename = options.output_dir + "/" + options.library +
diff --git a/src/google/protobuf/compiler/php/php_generator.cc b/src/google/protobuf/compiler/php/php_generator.cc
index 04d8f8c..203b49f 100644
--- a/src/google/protobuf/compiler/php/php_generator.cc
+++ b/src/google/protobuf/compiler/php/php_generator.cc
@@ -644,43 +644,50 @@
   // Generate getter.
   GenerateFieldDocComment(printer, field, options, kFieldGetter);
 
+  // deprecation
+  std::string deprecation_trigger = (field->options().deprecated()) ? "@trigger_error('" +
+      field->name() + " is deprecated.', E_USER_DEPRECATED);\n        " : "";
+
   if (oneof != NULL) {
     printer->Print(
         "public function get^camel_name^()\n"
         "{\n"
-        "    return $this->readOneof(^number^);\n"
+        "    ^deprecation_trigger^return $this->readOneof(^number^);\n"
         "}\n\n"
         "public function has^camel_name^()\n"
         "{\n"
-        "    return $this->hasOneof(^number^);\n"
+        "    ^deprecation_trigger^return $this->hasOneof(^number^);\n"
         "}\n\n",
         "camel_name", UnderscoresToCamelCase(field->name(), true),
-        "number", IntToString(field->number()));
+        "number", IntToString(field->number()),
+        "deprecation_trigger", deprecation_trigger);
   } else if (field->has_presence()) {
     printer->Print(
         "public function get^camel_name^()\n"
         "{\n"
-        "    return isset($this->^name^) ? $this->^name^ : ^default_value^;\n"
+        "    ^deprecation_trigger^return isset($this->^name^) ? $this->^name^ : ^default_value^;\n"
         "}\n\n"
         "public function has^camel_name^()\n"
         "{\n"
-        "    return isset($this->^name^);\n"
+        "    ^deprecation_trigger^return isset($this->^name^);\n"
         "}\n\n"
         "public function clear^camel_name^()\n"
         "{\n"
-        "    unset($this->^name^);\n"
+        "    ^deprecation_trigger^unset($this->^name^);\n"
         "}\n\n",
         "camel_name", UnderscoresToCamelCase(field->name(), true),
         "name", field->name(),
-        "default_value", DefaultForField(field));
+        "default_value", DefaultForField(field),
+        "deprecation_trigger", deprecation_trigger);
   } else {
     printer->Print(
         "public function get^camel_name^()\n"
         "{\n"
-        "    return $this->^name^;\n"
+        "    ^deprecation_trigger^return $this->^name^;\n"
         "}\n\n",
-        "camel_name", UnderscoresToCamelCase(field->name(), true), "name",
-        field->name());
+        "camel_name", UnderscoresToCamelCase(field->name(), true),
+        "name", field->name(),
+        "deprecation_trigger", deprecation_trigger);
   }
 
   // For wrapper types, generate an additional getXXXUnwrapped getter
@@ -692,10 +699,11 @@
     printer->Print(
         "public function get^camel_name^Unwrapped()\n"
         "{\n"
-        "    return $this->readWrapperValue(\"^field_name^\");\n"
+        "    ^deprecation_trigger^return $this->readWrapperValue(\"^field_name^\");\n"
         "}\n\n",
         "camel_name", UnderscoresToCamelCase(field->name(), true),
-        "field_name", field->name());
+        "field_name", field->name(),
+        "deprecation_trigger", deprecation_trigger);
   }
 
   // Generate setter.
@@ -707,6 +715,13 @@
 
   Indent(printer);
 
+  if (field->options().deprecated()) {
+      printer->Print(
+          "^deprecation_trigger^",
+          "deprecation_trigger", deprecation_trigger
+      );
+  }
+
   // Type check.
   if (field->is_map()) {
     const Descriptor* map_entry = field->message_type();
@@ -1741,6 +1756,9 @@
       "php_type", PhpGetterTypeName(field, options),
       "maybe_null", can_return_null ? "|null" : "");
   }
+  if (field->options().deprecated()) {
+    printer->Print(" * @deprecated\n");
+  }
   printer->Print(" */\n");
 }
 
diff --git a/src/google/protobuf/dynamic_message.cc b/src/google/protobuf/dynamic_message.cc
index 0a52804..ef7e1f2 100644
--- a/src/google/protobuf/dynamic_message.cc
+++ b/src/google/protobuf/dynamic_message.cc
@@ -198,44 +198,6 @@
   return 0;
 }
 
-// Compute the byte size of in-memory representation of the oneof fields
-// in default oneof instance.
-int OneofFieldSpaceUsed(const FieldDescriptor* field) {
-  typedef FieldDescriptor FD;  // avoid line wrapping
-  switch (field->cpp_type()) {
-    case FD::CPPTYPE_INT32:
-      return sizeof(int32);
-    case FD::CPPTYPE_INT64:
-      return sizeof(int64);
-    case FD::CPPTYPE_UINT32:
-      return sizeof(uint32);
-    case FD::CPPTYPE_UINT64:
-      return sizeof(uint64);
-    case FD::CPPTYPE_DOUBLE:
-      return sizeof(double);
-    case FD::CPPTYPE_FLOAT:
-      return sizeof(float);
-    case FD::CPPTYPE_BOOL:
-      return sizeof(bool);
-    case FD::CPPTYPE_ENUM:
-      return sizeof(int);
-
-    case FD::CPPTYPE_MESSAGE:
-      return sizeof(Message*);
-
-    case FD::CPPTYPE_STRING:
-      switch (field->options().ctype()) {
-        default:
-        case FieldOptions::STRING:
-          return sizeof(ArenaStringPtr);
-      }
-      break;
-  }
-
-  GOOGLE_LOG(DFATAL) << "Can't get here.";
-  return 0;
-}
-
 inline int DivideRoundingUp(int i, int j) { return (i + (j - 1)) / j; }
 
 static const int kSafeAlignment = sizeof(uint64);
diff --git a/src/google/protobuf/generated_message_table_driven_lite.h b/src/google/protobuf/generated_message_table_driven_lite.h
index 9fc910a..32cc16e 100644
--- a/src/google/protobuf/generated_message_table_driven_lite.h
+++ b/src/google/protobuf/generated_message_table_driven_lite.h
@@ -87,8 +87,7 @@
 
 template <typename Type>
 inline Type* AddField(MessageLite* msg, int64 offset) {
-  static_assert(std::is_standard_layout<Type>::value && std::is_trivial<Type>::value,
-                "Do not assign");
+  static_assert(std::is_trivial<Type>::value, "Do not assign");
 
   RepeatedField<Type>* repeated = Raw<RepeatedField<Type>>(msg, offset);
   return repeated->Add();
@@ -104,8 +103,7 @@
 
 template <typename Type>
 inline void AddField(MessageLite* msg, int64 offset, Type value) {
-  static_assert(std::is_standard_layout<Type>::value && std::is_trivial<Type>::value,
-                "Do not assign");
+  static_assert(std::is_trivial<Type>::value, "Do not assign");
   *AddField<Type>(msg, offset) = value;
 }
 
@@ -126,8 +124,7 @@
 template <typename Type>
 inline void SetField(MessageLite* msg, uint32* has_bits, uint32 has_bit_index,
                      int64 offset, Type value) {
-  static_assert(std::is_standard_layout<Type>::value && std::is_trivial<Type>::value,
-                "Do not assign");
+  static_assert(std::is_trivial<Type>::value, "Do not assign");
   *MutableField<Type>(msg, has_bits, has_bit_index, offset) = value;
 }
 
diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h
index a4d5f96..df8c449 100644
--- a/src/google/protobuf/io/coded_stream.h
+++ b/src/google/protobuf/io/coded_stream.h
@@ -120,12 +120,12 @@
 #include <type_traits>
 #include <utility>
 
-#ifdef _MSC_VER
+#ifdef _WIN32
 // Assuming windows is always little-endian.
 #if !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
 #define PROTOBUF_LITTLE_ENDIAN 1
 #endif
-#if _MSC_VER >= 1300 && !defined(__INTEL_COMPILER)
+#if defined(_MSC_VER) && _MSC_VER >= 1300 && !defined(__INTEL_COMPILER)
 // If MSVC has "/RTCc" set, it will complain about truncating casts at
 // runtime.  This file contains some intentional truncating casts.
 #pragma runtime_checks("c", off)
diff --git a/src/google/protobuf/message_lite.cc b/src/google/protobuf/message_lite.cc
index 0cae40f..f137675 100644
--- a/src/google/protobuf/message_lite.cc
+++ b/src/google/protobuf/message_lite.cc
@@ -529,6 +529,23 @@
   *to = from;
 }
 
+// Non-inline variants of std::string specializations for
+// various InternalMetadata routines.
+template <>
+void InternalMetadata::DoClear<std::string>() {
+  mutable_unknown_fields<std::string>()->clear();
+}
+
+template <>
+void InternalMetadata::DoMergeFrom<std::string>(const std::string& other) {
+  mutable_unknown_fields<std::string>()->append(other);
+}
+
+template <>
+void InternalMetadata::DoSwap<std::string>(std::string* other) {
+  mutable_unknown_fields<std::string>()->swap(*other);
+}
+
 }  // namespace internal
 
 
diff --git a/src/google/protobuf/message_lite.h b/src/google/protobuf/message_lite.h
index dcc6e24..8a9b79d 100644
--- a/src/google/protobuf/message_lite.h
+++ b/src/google/protobuf/message_lite.h
@@ -146,7 +146,7 @@
  private:
   // Prefer c++14 aligned_storage, but for compatibility this will do.
   union AlignedUnion {
-    char space[sizeof(T)];
+    alignas(T) char space[sizeof(T)];
     int64 align_to_int64;
     void* align_to_ptr;
   } union_;
diff --git a/src/google/protobuf/message_unittest.inc b/src/google/protobuf/message_unittest.inc
index 4608714..003f1fe 100644
--- a/src/google/protobuf/message_unittest.inc
+++ b/src/google/protobuf/message_unittest.inc
@@ -552,6 +552,34 @@
   EXPECT_TRUE(msg.IsInitialized());
 }
 
+TEST(MESSAGE_TEST_NAME, IsInitializedSplitBytestream) {
+  UNITTEST::TestRequired ab, c;
+  ab.set_a(1);
+  ab.set_b(2);
+  c.set_c(3);
+
+  // The protobuf represented by the concatenated string has all required
+  // fields (a,b,c) set.
+  std::string bytes =
+      ab.SerializePartialAsString() + c.SerializePartialAsString();
+
+  UNITTEST::TestRequired concatenated;
+  EXPECT_TRUE(concatenated.ParsePartialFromString(bytes));
+  EXPECT_TRUE(concatenated.IsInitialized());
+
+  UNITTEST::TestRequiredForeign fab, fc;
+  fab.mutable_optional_message()->set_a(1);
+  fab.mutable_optional_message()->set_b(2);
+  fc.mutable_optional_message()->set_c(3);
+
+  bytes =
+      fab.SerializePartialAsString() + fc.SerializePartialAsString();
+
+  UNITTEST::TestRequiredForeign fconcatenated;
+  EXPECT_TRUE(fconcatenated.ParsePartialFromString(bytes));
+  EXPECT_TRUE(fconcatenated.IsInitialized());
+}
+
 TEST(MESSAGE_FACTORY_TEST_NAME, GeneratedFactoryLookup) {
   EXPECT_EQ(MessageFactory::generated_factory()->GetPrototype(
                 UNITTEST::TestAllTypes::descriptor()),
diff --git a/src/google/protobuf/metadata_lite.h b/src/google/protobuf/metadata_lite.h
index 9f16180..8e1d24e 100644
--- a/src/google/protobuf/metadata_lite.h
+++ b/src/google/protobuf/metadata_lite.h
@@ -68,7 +68,7 @@
   void Delete() {
     // Note that Delete<> should be called not more than once.
     if (have_unknown_fields() && arena() == NULL) {
-      delete PtrValue<Container<T>>();
+      DeleteOutOfLineHelper<T>();
     }
   }
 
@@ -203,6 +203,11 @@
   };
 
   template <typename T>
+  PROTOBUF_NOINLINE void DeleteOutOfLineHelper() {
+    delete PtrValue<Container<T>>();
+  }
+
+  template <typename T>
   PROTOBUF_NOINLINE T* mutable_unknown_fields_slow() {
     Arena* my_arena = arena();
     Arena* owning_arena = GetOwningArena();
@@ -221,17 +226,17 @@
   // Templated functions.
 
   template <typename T>
-  void DoClear() {
+  PROTOBUF_NOINLINE void DoClear() {
     mutable_unknown_fields<T>()->Clear();
   }
 
   template <typename T>
-  void DoMergeFrom(const T& other) {
+  PROTOBUF_NOINLINE void DoMergeFrom(const T& other) {
     mutable_unknown_fields<T>()->MergeFrom(other);
   }
 
   template <typename T>
-  void DoSwap(T* other) {
+  PROTOBUF_NOINLINE void DoSwap(T* other) {
     mutable_unknown_fields<T>()->Swap(other);
   }
 };
@@ -239,20 +244,12 @@
 // String Template specializations.
 
 template <>
-inline void InternalMetadata::DoClear<std::string>() {
-  mutable_unknown_fields<std::string>()->clear();
-}
-
+PROTOBUF_EXPORT void InternalMetadata::DoClear<std::string>();
 template <>
-inline void InternalMetadata::DoMergeFrom<std::string>(
-    const std::string& other) {
-  mutable_unknown_fields<std::string>()->append(other);
-}
-
+PROTOBUF_EXPORT void InternalMetadata::DoMergeFrom<std::string>(
+    const std::string& other);
 template <>
-inline void InternalMetadata::DoSwap<std::string>(std::string* other) {
-  mutable_unknown_fields<std::string>()->swap(*other);
-}
+PROTOBUF_EXPORT void InternalMetadata::DoSwap<std::string>(std::string* other);
 
 // This helper RAII class is needed to efficiently parse unknown fields. We
 // should only call mutable_unknown_fields if there are actual unknown fields.
diff --git a/src/google/protobuf/port_def.inc b/src/google/protobuf/port_def.inc
index d15073a..11e0160 100644
--- a/src/google/protobuf/port_def.inc
+++ b/src/google/protobuf/port_def.inc
@@ -615,7 +615,7 @@
 #define PROTOBUF_PRAGMA_INIT_SEG
 #endif
 
-#if defined(__has_attribute)
+#if defined(__has_attribute) && !defined(__MINGW32__)
 #if __has_attribute(weak)
 #define PROTOBUF_ATTRIBUTE_WEAK __attribute__((weak))
 #endif
diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h
index 85f6aed..9ba9e93 100644
--- a/src/google/protobuf/repeated_field.h
+++ b/src/google/protobuf/repeated_field.h
@@ -438,7 +438,7 @@
   //
   // The first version executes at 7 cycles per iteration while the second
   // version near 1 or 2 cycles.
-  template <int = 0, bool = std::is_pod<Element>::value>
+  template <int = 0, bool = std::is_trivial<Element>::value>
   class FastAdderImpl {
    public:
     explicit FastAdderImpl(RepeatedField* rf) : repeated_field_(rf) {
@@ -500,8 +500,7 @@
 // shouldn't be necessary, but our compiler doesn't optimize std::copy very
 // effectively.
 template <typename Element,
-          bool HasTrivialCopy =
-              std::is_standard_layout<Element>::value && std::is_trivial<Element>::value>
+          bool HasTrivialCopy = std::is_trivial<Element>::value>
 struct ElementCopier {
   void operator()(Element* to, const Element* from, int array_size);
 };
diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.cc b/src/google/protobuf/util/internal/default_value_objectwriter.cc
index 60a44db..c9ab446 100644
--- a/src/google/protobuf/util/internal/default_value_objectwriter.cc
+++ b/src/google/protobuf/util/internal/default_value_objectwriter.cc
@@ -39,7 +39,6 @@
 namespace google {
 namespace protobuf {
 namespace util {
-using util::Status;
 namespace converter {
 
 namespace {
diff --git a/src/google/protobuf/util/internal/json_stream_parser.cc b/src/google/protobuf/util/internal/json_stream_parser.cc
index 0a32138..db18b1a 100644
--- a/src/google/protobuf/util/internal/json_stream_parser.cc
+++ b/src/google/protobuf/util/internal/json_stream_parser.cc
@@ -52,7 +52,6 @@
 
 // Allow these symbols to be referenced as util::Status, util::error::* in
 // this file.
-using util::Status;
 namespace error {
 using util::error::CANCELLED;
 using util::error::INTERNAL;
diff --git a/src/google/protobuf/util/internal/json_stream_parser_test.cc b/src/google/protobuf/util/internal/json_stream_parser_test.cc
index aa4751d..ac6fe3d 100644
--- a/src/google/protobuf/util/internal/json_stream_parser_test.cc
+++ b/src/google/protobuf/util/internal/json_stream_parser_test.cc
@@ -42,13 +42,11 @@
 namespace google {
 namespace protobuf {
 namespace util {
-using util::Status;
 namespace error {
 using util::error::INVALID_ARGUMENT;
 }  // namespace error
 namespace converter {
 
-using util::Status;
 
 // Tests for the JSON Stream Parser. These tests are intended to be
 // comprehensive and cover the following:
diff --git a/src/google/protobuf/util/internal/proto_writer.cc b/src/google/protobuf/util/internal/proto_writer.cc
index a082964..0102259 100644
--- a/src/google/protobuf/util/internal/proto_writer.cc
+++ b/src/google/protobuf/util/internal/proto_writer.cc
@@ -54,7 +54,6 @@
 
 using io::CodedOutputStream;
 using ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite;
-using util::Status;
 using util::error::INVALID_ARGUMENT;
 
 
diff --git a/src/google/protobuf/util/internal/protostream_objectsource.cc b/src/google/protobuf/util/internal/protostream_objectsource.cc
index 37811b3..49074fb 100644
--- a/src/google/protobuf/util/internal/protostream_objectsource.cc
+++ b/src/google/protobuf/util/internal/protostream_objectsource.cc
@@ -59,7 +59,6 @@
 namespace google {
 namespace protobuf {
 namespace util {
-using util::Status;
 namespace error {
 using util::error::Code;
 using util::error::INTERNAL;
@@ -67,7 +66,6 @@
 namespace converter {
 using ::PROTOBUF_NAMESPACE_ID::internal::WireFormat;
 using ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite;
-using util::Status;
 
 namespace {
 
diff --git a/src/google/protobuf/util/internal/protostream_objectsource.h b/src/google/protobuf/util/internal/protostream_objectsource.h
index 7d98b4e..807cbf1 100644
--- a/src/google/protobuf/util/internal/protostream_objectsource.h
+++ b/src/google/protobuf/util/internal/protostream_objectsource.h
@@ -74,69 +74,58 @@
 class PROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource {
  public:
   struct RenderOptions {
-    RenderOptions() {}
+    RenderOptions() = default;
     RenderOptions(const RenderOptions&) = default;
 
-    // whether to render enums using lowercamelcase.
+    // Sets whether or not to use lowerCamelCase casing for enum values. If set
+    // to false, enum values are output without any case conversions.
+    //
+    // For example, if we have an enum:
+    // enum Type {
+    //   ACTION_AND_ADVENTURE = 1;
+    // }
+    // Type type = 20;
+    //
+    // And this option is set to true. Then the rendered "type" field will have
+    // the string "actionAndAdventure".
+    // {
+    //   ...
+    //   "type": "actionAndAdventure",
+    //   ...
+    // }
+    //
+    // If set to false, the rendered "type" field will have the string
+    // "ACTION_AND_ADVENTURE".
+    // {
+    //   ...
+    //   "type": "ACTION_AND_ADVENTURE",
+    //   ...
+    // }
     bool use_lower_camel_for_enums = false;
 
-    // whether to render enums as ints always. defaults to false.
+    // Sets whether to always output enums as ints, by default this is off, and
+    // enums are rendered as strings.
     bool use_ints_for_enums = false;
 
-    // whether to preserve proto field names
+    // Whether to preserve proto field names
     bool preserve_proto_field_names = false;
 
   };
 
   ProtoStreamObjectSource(io::CodedInputStream* stream,
                           TypeResolver* type_resolver,
+                          const google::protobuf::Type& type)
+      : ProtoStreamObjectSource(stream, type_resolver, type, RenderOptions()) {}
+  ProtoStreamObjectSource(io::CodedInputStream* stream,
+                          TypeResolver* type_resolver,
                           const google::protobuf::Type& type,
-                          const RenderOptions& render_options = {});
+                          const RenderOptions& render_options);
 
   ~ProtoStreamObjectSource() override;
 
   util::Status NamedWriteTo(StringPiece name,
                             ObjectWriter* ow) const override;
 
-  // Sets whether or not to use lowerCamelCase casing for enum values. If set to
-  // false, enum values are output without any case conversions.
-  //
-  // For example, if we have an enum:
-  // enum Type {
-  //   ACTION_AND_ADVENTURE = 1;
-  // }
-  // Type type = 20;
-  //
-  // And this option is set to true. Then the rendered "type" field will have
-  // the string "actionAndAdventure".
-  // {
-  //   ...
-  //   "type": "actionAndAdventure",
-  //   ...
-  // }
-  //
-  // If set to false, the rendered "type" field will have the string
-  // "ACTION_AND_ADVENTURE".
-  // {
-  //   ...
-  //   "type": "ACTION_AND_ADVENTURE",
-  //   ...
-  // }
-  void set_use_lower_camel_for_enums(bool value) {
-    render_options_.use_lower_camel_for_enums = value;
-  }
-
-  // Sets whether to always output enums as ints, by default this is off, and
-  // enums are rendered as strings.
-  void set_use_ints_for_enums(bool value) {
-    render_options_.use_ints_for_enums = value;
-  }
-
-  // Sets whether to use original proto field names
-  void set_preserve_proto_field_names(bool value) {
-    render_options_.preserve_proto_field_names = value;
-  }
-
   // Sets the max recursion depth of proto message to be deserialized. Proto
   // messages over this depth will fail to be deserialized.
   // Default value is 64.
@@ -144,7 +133,6 @@
     max_recursion_depth_ = max_depth;
   }
 
-
  protected:
   // Writes a proto2 Message to the ObjectWriter. When the given end_tag is
   // found this method will complete, allowing it to be used for parsing both
@@ -317,7 +305,7 @@
   const google::protobuf::Type& type_;
 
 
-  RenderOptions render_options_;
+  const RenderOptions render_options_;
 
   // Tracks current recursion depth.
   mutable int recursion_depth_;
diff --git a/src/google/protobuf/util/internal/protostream_objectsource_test.cc b/src/google/protobuf/util/internal/protostream_objectsource_test.cc
index 4c0caed..100fef9 100644
--- a/src/google/protobuf/util/internal/protostream_objectsource_test.cc
+++ b/src/google/protobuf/util/internal/protostream_objectsource_test.cc
@@ -79,7 +79,6 @@
 using proto_util_converter::testing::StructType;
 using proto_util_converter::testing::TimestampDuration;
 using ::testing::_;
-using util::Status;
 
 
 namespace {
@@ -117,12 +116,13 @@
     ArrayInputStream arr_stream(proto.data(), proto.size());
     CodedInputStream in_stream(&arr_stream);
 
+    ProtoStreamObjectSource::RenderOptions render_options;
+    render_options.use_lower_camel_for_enums = use_lower_camel_for_enums_;
+    render_options.use_ints_for_enums = use_ints_for_enums_;
+    render_options.preserve_proto_field_names = use_preserve_proto_field_names_;
     std::unique_ptr<ProtoStreamObjectSource> os(
-        helper_.NewProtoSource(&in_stream, GetTypeUrl(descriptor)));
-    if (use_lower_camel_for_enums_) os->set_use_lower_camel_for_enums(true);
-    if (use_ints_for_enums_) os->set_use_ints_for_enums(true);
-    if (use_preserve_proto_field_names_)
-      os->set_preserve_proto_field_names(true);
+        helper_.NewProtoSource(&in_stream, GetTypeUrl(descriptor),
+                               render_options));
     os->set_max_recursion_depth(64);
     return os->WriteTo(&mock_);
   }
diff --git a/src/google/protobuf/util/internal/type_info_test_helper.cc b/src/google/protobuf/util/internal/type_info_test_helper.cc
index ccf6fbd..e8c3c5a 100644
--- a/src/google/protobuf/util/internal/type_info_test_helper.cc
+++ b/src/google/protobuf/util/internal/type_info_test_helper.cc
@@ -86,12 +86,13 @@
 TypeInfo* TypeInfoTestHelper::GetTypeInfo() { return typeinfo_.get(); }
 
 ProtoStreamObjectSource* TypeInfoTestHelper::NewProtoSource(
-    io::CodedInputStream* coded_input, const std::string& type_url) {
+    io::CodedInputStream* coded_input, const std::string& type_url,
+    ProtoStreamObjectSource::RenderOptions render_options) {
   const google::protobuf::Type* type = typeinfo_->GetTypeByTypeUrl(type_url);
   switch (type_) {
     case USE_TYPE_RESOLVER: {
       return new ProtoStreamObjectSource(coded_input, type_resolver_.get(),
-                                         *type);
+                                         *type, render_options);
     }
   }
   GOOGLE_LOG(FATAL) << "Can not reach here.";
diff --git a/src/google/protobuf/util/internal/type_info_test_helper.h b/src/google/protobuf/util/internal/type_info_test_helper.h
index 923050d..c42289e 100644
--- a/src/google/protobuf/util/internal/type_info_test_helper.h
+++ b/src/google/protobuf/util/internal/type_info_test_helper.h
@@ -71,8 +71,9 @@
   // Returns the TypeInfo created after ResetTypeInfo.
   TypeInfo* GetTypeInfo();
 
-  ProtoStreamObjectSource* NewProtoSource(io::CodedInputStream* coded_input,
-                                          const std::string& type_url);
+  ProtoStreamObjectSource* NewProtoSource(
+      io::CodedInputStream* coded_input, const std::string& type_url,
+      ProtoStreamObjectSource::RenderOptions render_options = {});
 
   ProtoStreamObjectWriter* NewProtoWriter(
       const std::string& type_url, strings::ByteSink* output,
diff --git a/src/google/protobuf/util/json_util.cc b/src/google/protobuf/util/json_util.cc
index 4cf720e..eb81b65 100644
--- a/src/google/protobuf/util/json_util.cc
+++ b/src/google/protobuf/util/json_util.cc
@@ -92,10 +92,12 @@
   io::CodedInputStream in_stream(binary_input);
   google::protobuf::Type type;
   RETURN_IF_ERROR(resolver->ResolveMessageType(type_url, &type));
-  converter::ProtoStreamObjectSource proto_source(&in_stream, resolver, type);
-  proto_source.set_use_ints_for_enums(options.always_print_enums_as_ints);
-  proto_source.set_preserve_proto_field_names(
-      options.preserve_proto_field_names);
+  converter::ProtoStreamObjectSource::RenderOptions render_options;
+  render_options.use_ints_for_enums = options.always_print_enums_as_ints;
+  render_options.preserve_proto_field_names =
+      options.preserve_proto_field_names;
+  converter::ProtoStreamObjectSource proto_source(&in_stream, resolver, type,
+                                                  render_options);
   io::CodedOutputStream out_stream(json_output);
   converter::JsonObjectWriter json_writer(options.add_whitespace ? " " : "",
                                           &out_stream);
diff --git a/src/google/protobuf/util/message_differencer.cc b/src/google/protobuf/util/message_differencer.cc
index bf016e4..c3c24da 100644
--- a/src/google/protobuf/util/message_differencer.cc
+++ b/src/google/protobuf/util/message_differencer.cc
@@ -1927,11 +1927,6 @@
   }
 }
 
-void MessageDifferencer::StreamReporter::PrintPath(
-    const std::vector<SpecificField>& field_path, bool left_side,
-    const Message& message) {
-  PrintPath(field_path, left_side);
-}
 
 void MessageDifferencer::StreamReporter::PrintValue(
     const Message& message, const std::vector<SpecificField>& field_path,
@@ -2100,7 +2095,7 @@
     const Message& message1, const Message& message2,
     const std::vector<SpecificField>& field_path) {
   printer_->Print("added: ");
-  PrintPath(field_path, false, message2);
+  PrintPath(field_path, false);
   printer_->Print(": ");
   PrintValue(message2, field_path, false);
   printer_->Print("\n");  // Print for newlines.
@@ -2110,7 +2105,7 @@
     const Message& message1, const Message& message2,
     const std::vector<SpecificField>& field_path) {
   printer_->Print("deleted: ");
-  PrintPath(field_path, true, message1);
+  PrintPath(field_path, true);
   printer_->Print(": ");
   PrintValue(message1, field_path, true);
   printer_->Print("\n");  // Print for newlines
@@ -2133,10 +2128,10 @@
   }
 
   printer_->Print("modified: ");
-  PrintPath(field_path, true, message1);
+  PrintPath(field_path, true);
   if (CheckPathChanged(field_path)) {
     printer_->Print(" -> ");
-    PrintPath(field_path, false, message2);
+    PrintPath(field_path, false);
   }
   printer_->Print(": ");
   PrintValue(message1, field_path, true);
@@ -2149,9 +2144,9 @@
     const Message& message1, const Message& message2,
     const std::vector<SpecificField>& field_path) {
   printer_->Print("moved: ");
-  PrintPath(field_path, true, message1);
+  PrintPath(field_path, true);
   printer_->Print(" -> ");
-  PrintPath(field_path, false, message2);
+  PrintPath(field_path, false);
   printer_->Print(" : ");
   PrintValue(message1, field_path, true);
   printer_->Print("\n");  // Print for newlines.
@@ -2161,10 +2156,10 @@
     const Message& message1, const Message& message2,
     const std::vector<SpecificField>& field_path) {
   printer_->Print("matched: ");
-  PrintPath(field_path, true, message1);
+  PrintPath(field_path, true);
   if (CheckPathChanged(field_path)) {
     printer_->Print(" -> ");
-    PrintPath(field_path, false, message2);
+    PrintPath(field_path, false);
   }
   printer_->Print(" : ");
   PrintValue(message1, field_path, true);
@@ -2175,10 +2170,10 @@
     const Message& message1, const Message& message2,
     const std::vector<SpecificField>& field_path) {
   printer_->Print("ignored: ");
-  PrintPath(field_path, true, message1);
+  PrintPath(field_path, true);
   if (CheckPathChanged(field_path)) {
     printer_->Print(" -> ");
-    PrintPath(field_path, false, message2);
+    PrintPath(field_path, false);
   }
   printer_->Print("\n");  // Print for newlines.
 }
@@ -2193,10 +2188,10 @@
     const Message& message1, const Message& message2,
     const std::vector<SpecificField>& field_path) {
   printer_->Print("ignored: ");
-  PrintPath(field_path, true, message1);
+  PrintPath(field_path, true);
   if (CheckPathChanged(field_path)) {
     printer_->Print(" -> ");
-    PrintPath(field_path, false, message2);
+    PrintPath(field_path, false);
   }
   printer_->Print("\n");  // Print for newlines.
 }
diff --git a/src/google/protobuf/util/message_differencer.h b/src/google/protobuf/util/message_differencer.h
index 5889a9a..61d9f9b 100644
--- a/src/google/protobuf/util/message_differencer.h
+++ b/src/google/protobuf/util/message_differencer.h
@@ -673,11 +673,6 @@
     void SetMessages(const Message& message1, const Message& message2);
 
    protected:
-    // Prints the specified path of fields to the buffer.  message is used to
-    // print map keys.
-    virtual void PrintPath(const std::vector<SpecificField>& field_path,
-                           bool left_side, const Message& message);
-
     // Prints the specified path of fields to the buffer.
     virtual void PrintPath(const std::vector<SpecificField>& field_path,
                            bool left_side);
diff --git a/tests.sh b/tests.sh
index e445384..1135f95 100755
--- a/tests.sh
+++ b/tests.sh
@@ -356,6 +356,10 @@
   build_python_version py38-python
 }
 
+build_python39() {
+  build_python_version py39-python
+}
+
 build_python_cpp() {
   internal_build_cpp
   export LD_LIBRARY_PATH=../src/.libs # for Linux
@@ -408,6 +412,10 @@
   build_python_cpp_version py38-cpp
 }
 
+build_python39_cpp() {
+  build_python_cpp_version py39-cpp
+}
+
 build_python_compatibility() {
   internal_build_cpp
   # Use the unit-tests extracted from 2.5.0 to test the compatibility.
@@ -476,6 +484,16 @@
   internal_build_cpp
 }
 
+build_php5.6() {
+  use_php 5.6
+  pushd php
+  rm -rf vendor
+  composer update
+  composer test
+  popd
+  (cd conformance && make test_php)
+}
+
 build_php7.0() {
   use_php 7.0
   pushd php
@@ -675,6 +693,7 @@
 }
 
 build_php_all_32() {
+  build_php5.6
   build_php7.0
   build_php7.1
   build_php7.4