Browse Source

Merge pull request #3529 from pherl/merge3.4.x

Merge 3.4.x into master
Jisi Liu 8 years ago
parent
commit
ba4e54724d
100 changed files with 7910 additions and 889 deletions
  1. 2 4
      BUILD
  2. 132 0
      CHANGES.txt
  3. 26 1
      Makefile.am
  4. 1 1
      Protobuf.podspec
  5. 1 3
      cmake/extract_includes.bat.in
  6. 1 0
      cmake/libprotobuf-lite.cmake
  7. 0 3
      cmake/libprotoc.cmake
  8. 1 2
      cmake/tests.cmake
  9. 1 1
      configure.ac
  10. 4 3
      conformance/conformance_test.cc
  11. 2 0
      conformance/failure_list_cpp.txt
  12. 2 0
      conformance/failure_list_csharp.txt
  13. 2 0
      conformance/failure_list_php.txt
  14. 2 1
      conformance/failure_list_python.txt
  15. 2 1
      conformance/failure_list_python_cpp.txt
  16. 2 0
      conformance/failure_list_ruby.txt
  17. 1 1
      csharp/Google.Protobuf.Tools.nuspec
  18. 1 1
      csharp/src/Google.Protobuf/Google.Protobuf.csproj
  19. 279 126
      csharp/src/Google.Protobuf/Reflection/Descriptor.cs
  20. 10 0
      csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
  21. 29 21
      csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
  22. 26 9
      generate_descriptor_proto.sh
  23. 2 1
      java/core/generate-test-sources-build.xml
  24. 1 1
      java/core/pom.xml
  25. 17 10
      java/core/src/main/java/com/google/protobuf/AbstractMessage.java
  26. 60 24
      java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java
  27. 214 85
      java/core/src/main/java/com/google/protobuf/CodedInputStream.java
  28. 35 47
      java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
  29. 71 0
      java/core/src/main/java/com/google/protobuf/DiscardUnknownFieldsParser.java
  30. 4 6
      java/core/src/main/java/com/google/protobuf/DynamicMessage.java
  31. 2 5
      java/core/src/main/java/com/google/protobuf/Extension.java
  32. 3 2
      java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java
  33. 11 67
      java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
  34. 53 40
      java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
  35. 2 3
      java/core/src/main/java/com/google/protobuf/Internal.java
  36. 4 0
      java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
  37. 16 8
      java/core/src/main/java/com/google/protobuf/MessageReflection.java
  38. 106 105
      java/core/src/main/java/com/google/protobuf/TextFormat.java
  39. 1 1
      java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java
  40. 125 48
      java/core/src/main/java/com/google/protobuf/UnsafeUtil.java
  41. 9 24
      java/core/src/main/java/com/google/protobuf/Utf8.java
  42. 4 2
      java/core/src/main/java/com/google/protobuf/WireFormat.java
  43. 123 0
      java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
  44. 157 0
      java/core/src/test/java/com/google/protobuf/DiscardUnknownFieldsTest.java
  45. 3 9
      java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java
  46. 36 42
      java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
  47. 214 0
      java/core/src/test/java/com/google/protobuf/LiteTest.java
  48. 4 4
      java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
  49. 4 4
      java/core/src/test/java/com/google/protobuf/MapForProto2Test.java
  50. 4 4
      java/core/src/test/java/com/google/protobuf/MapTest.java
  51. 8 4
      java/core/src/test/java/com/google/protobuf/MessageTest.java
  52. 26 0
      java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java
  53. 83 0
      java/core/src/test/java/com/google/protobuf/TestBadIdentifiersLite.java
  54. 5 9
      java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java
  55. 17 0
      java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto
  56. 1 1
      java/pom.xml
  57. 1 1
      java/util/pom.xml
  58. 16 7
      java/util/src/main/java/com/google/protobuf/util/Durations.java
  59. 6 3
      java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java
  60. 5 1
      java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
  61. 16 7
      java/util/src/main/java/com/google/protobuf/util/Timestamps.java
  62. 2 2
      java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
  63. 5 3
      js/binary/encoder.js
  64. 6 4
      js/binary/writer.js
  65. 355 0
      js/compatibility_tests/v3.1.0/binary/arith_test.js
  66. 334 0
      js/compatibility_tests/v3.1.0/binary/decoder_test.js
  67. 628 0
      js/compatibility_tests/v3.1.0/binary/proto_test.js
  68. 922 0
      js/compatibility_tests/v3.1.0/binary/reader_test.js
  69. 668 0
      js/compatibility_tests/v3.1.0/binary/utils_test.js
  70. 122 0
      js/compatibility_tests/v3.1.0/binary/writer_test.js
  71. 40 0
      js/compatibility_tests/v3.1.0/commonjs/test6/test6.proto
  72. 42 0
      js/compatibility_tests/v3.1.0/commonjs/test7/test7.proto
  73. 51 0
      js/compatibility_tests/v3.1.0/data.proto
  74. 105 0
      js/compatibility_tests/v3.1.0/debug_test.js
  75. 301 0
      js/compatibility_tests/v3.1.0/maps_test.js
  76. 1037 0
      js/compatibility_tests/v3.1.0/message_test.js
  77. 329 0
      js/compatibility_tests/v3.1.0/proto3_test.js
  78. 89 0
      js/compatibility_tests/v3.1.0/proto3_test.proto
  79. 262 0
      js/compatibility_tests/v3.1.0/test.proto
  80. 16 30
      js/compatibility_tests/v3.1.0/test2.proto
  81. 53 0
      js/compatibility_tests/v3.1.0/test3.proto
  82. 42 0
      js/compatibility_tests/v3.1.0/test4.proto
  83. 44 0
      js/compatibility_tests/v3.1.0/test5.proto
  84. 212 0
      js/compatibility_tests/v3.1.0/testbinary.proto
  85. 34 0
      js/compatibility_tests/v3.1.0/testempty.proto
  86. 53 33
      js/message.js
  87. 2 14
      js/message_test.js
  88. 1 1
      js/package.json
  89. 1 0
      js/proto3_test.proto
  90. 8 0
      js/test.proto
  91. 19 0
      m4/ax_cxx_compile_stdcxx.m4
  92. 10 0
      objectivec/google/protobuf/Any.pbobjc.h
  93. 29 21
      objectivec/google/protobuf/Api.pbobjc.h
  94. 18 2
      php/ext/google/protobuf/package.xml
  95. 1 1
      php/ext/google/protobuf/protobuf.h
  96. 5 0
      php/src/GPBMetadata/Google/Protobuf/Internal/Descriptor.php
  97. 33 0
      php/src/Google/Protobuf/Internal/DescriptorProto_ExtensionRange.php
  98. 24 21
      php/src/Google/Protobuf/Internal/FieldOptions.php
  99. 6 0
      protoc-artifacts/README.md
  100. 5 4
      protoc-artifacts/build-protoc.sh

+ 2 - 4
BUILD

@@ -97,6 +97,7 @@ cc_library(
         "src/google/protobuf/arena.cc",
         "src/google/protobuf/arena.cc",
         "src/google/protobuf/arenastring.cc",
         "src/google/protobuf/arenastring.cc",
         "src/google/protobuf/extension_set.cc",
         "src/google/protobuf/extension_set.cc",
+        "src/google/protobuf/generated_message_table_driven_lite.cc",
         "src/google/protobuf/generated_message_util.cc",
         "src/google/protobuf/generated_message_util.cc",
         "src/google/protobuf/io/coded_stream.cc",
         "src/google/protobuf/io/coded_stream.cc",
         "src/google/protobuf/io/zero_copy_stream.cc",
         "src/google/protobuf/io/zero_copy_stream.cc",
@@ -161,7 +162,6 @@ cc_library(
         "src/google/protobuf/service.cc",
         "src/google/protobuf/service.cc",
         "src/google/protobuf/source_context.pb.cc",
         "src/google/protobuf/source_context.pb.cc",
         "src/google/protobuf/struct.pb.cc",
         "src/google/protobuf/struct.pb.cc",
-        "src/google/protobuf/stubs/io_win32.cc",
         "src/google/protobuf/stubs/mathlimits.cc",
         "src/google/protobuf/stubs/mathlimits.cc",
         "src/google/protobuf/stubs/substitute.cc",
         "src/google/protobuf/stubs/substitute.cc",
         "src/google/protobuf/text_format.cc",
         "src/google/protobuf/text_format.cc",
@@ -229,7 +229,6 @@ RELATIVE_WELL_KNOWN_PROTOS = [
     "google/protobuf/any.proto",
     "google/protobuf/any.proto",
     "google/protobuf/api.proto",
     "google/protobuf/api.proto",
     "google/protobuf/compiler/plugin.proto",
     "google/protobuf/compiler/plugin.proto",
-    "google/protobuf/compiler/profile.proto",
     "google/protobuf/descriptor.proto",
     "google/protobuf/descriptor.proto",
     "google/protobuf/duration.proto",
     "google/protobuf/duration.proto",
     "google/protobuf/empty.proto",
     "google/protobuf/empty.proto",
@@ -374,7 +373,6 @@ cc_library(
         "src/google/protobuf/compiler/php/php_generator.cc",
         "src/google/protobuf/compiler/php/php_generator.cc",
         "src/google/protobuf/compiler/plugin.cc",
         "src/google/protobuf/compiler/plugin.cc",
         "src/google/protobuf/compiler/plugin.pb.cc",
         "src/google/protobuf/compiler/plugin.pb.cc",
-        "src/google/protobuf/compiler/profile.pb.cc",
         "src/google/protobuf/compiler/python/python_generator.cc",
         "src/google/protobuf/compiler/python/python_generator.cc",
         "src/google/protobuf/compiler/ruby/ruby_generator.cc",
         "src/google/protobuf/compiler/ruby/ruby_generator.cc",
         "src/google/protobuf/compiler/subprocess.cc",
         "src/google/protobuf/compiler/subprocess.cc",
@@ -473,7 +471,6 @@ COMMON_TEST_SRCS = [
     # AUTOGEN(common_test_srcs)
     # AUTOGEN(common_test_srcs)
     "src/google/protobuf/arena_test_util.cc",
     "src/google/protobuf/arena_test_util.cc",
     "src/google/protobuf/map_test_util.cc",
     "src/google/protobuf/map_test_util.cc",
-    "src/google/protobuf/stubs/io_win32.cc",
     "src/google/protobuf/test_util.cc",
     "src/google/protobuf/test_util.cc",
     "src/google/protobuf/testing/file.cc",
     "src/google/protobuf/testing/file.cc",
     "src/google/protobuf/testing/googletest.cc",
     "src/google/protobuf/testing/googletest.cc",
@@ -513,6 +510,7 @@ cc_test(
         "src/google/protobuf/arenastring_unittest.cc",
         "src/google/protobuf/arenastring_unittest.cc",
         "src/google/protobuf/compiler/command_line_interface_unittest.cc",
         "src/google/protobuf/compiler/command_line_interface_unittest.cc",
         "src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc",
         "src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc",
+        "src/google/protobuf/compiler/cpp/cpp_move_unittest.cc",
         "src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc",
         "src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc",
         "src/google/protobuf/compiler/cpp/cpp_unittest.cc",
         "src/google/protobuf/compiler/cpp/cpp_unittest.cc",
         "src/google/protobuf/compiler/cpp/metadata_test.cc",
         "src/google/protobuf/compiler/cpp/metadata_test.cc",

+ 132 - 0
CHANGES.txt

@@ -1,3 +1,135 @@
+2017-08-14 version 3.4.0 (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript)
+  Planned Future Changes
+  * There are some changes that are not included in this release but are planned
+    for the near future
+      - Preserve unknown fields in proto3: We are going to bring unknown fields
+        back into proto3. In this release, some languages start to support
+        preserving unknown fields in proto3, controlled by flags/options. Some
+        languages also introduce explicit APIs to drop unknown fields for
+        migration. Please read the change log sections by languages for details.
+        For general timeline and plan:
+
+          https://docs.google.com/document/d/1KMRX-G91Aa-Y2FkEaHeeviLRRNblgIahbsk4wA14gRk/view
+
+        For issues and discussions:
+
+          https://github.com/google/protobuf/issues/272
+
+      - Make C++ implementation C++11 only: we plan to require C++11 to build
+        protobuf code starting from 3.5.0 or 3.6.0 release, after unknown fields
+        semantic changes are finished. Please join this
+        github issue:
+
+          https://github.com/google/protobuf/issues/2780
+
+        to provide your feedback.
+
+  General
+  * Extension ranges now accept options and are customizable.
+  * "reserve" keyword now supports “max” in field number ranges,
+    e.g.  reserve 1000 to max;
+
+  C++
+  * Proto3 messages are now able to preserve unknown fields. The default
+    behavior is still to drop unknowns, which will be flipped in a future
+    release. If you rely on unknowns fields being dropped. Please use
+    Message::DiscardUnknownFields() explicitly.
+  * Packable proto3 fields are now packed by default in serialization.
+  * Following C++11 features are introduced when C++11 is available:
+      - move-constructor and move-assignment are introduced to messages
+      - Repeated fields constructor now takes std::initializer_list
+      - rvalue setters are introduced for string fields
+  * Experimental Table-Driven parsing and serialization available to test. To
+    enable it, pass in table_driven_parsing table_driven_serialization protoc
+    generator flags for C++
+
+      $ protoc --cpp_out=table_driven_parsing,table_driven_serialization:./ \
+        test.proto
+
+  * lite generator parameter supported by the generator. Once set, all generated
+    files, use lite runtime regardless of the optimizer_for setting in the
+    .proto file.
+  * Various optimizations to make C++ code more performant on PowerPC platform
+  * Fixed maps data corruption when the maps are modified by both reflection API
+    and generated API.
+  * Deterministic serialization on maps reflection now uses stable sort.
+  * file() accessors are introduced to various *Descriptor classes to make
+    writing template function easier.
+  * ByteSize() and SpaceUsed() are deprecated.Use ByteSizeLong() and
+    SpaceUsedLong() instead
+  * Consistent hash function is used for maps in DEBUG and NDEBUG build.
+  * "using namespace std" is removed from stubs/common.h
+  * Various performance optimizations and bug fixes
+
+  Java
+  * Introduced new parser API DiscardUnknownFieldsParser in preparation of
+    proto3 unknown fields preservation change. Users who want to drop unknown
+    fields should migrate to use this new parser API. For example:
+
+      Parser<Foo> parser = DiscardUnknownFieldsParser.wrap(Foo.parser());
+      Foo foo = parser.parseFrom(input);
+
+  * Introduced new TextFormat API printUnicodeFieldValue() that prints field
+    value without escaping unicode characters.
+  * Added Durations.compare(Duration, Duration) and
+    Timestamps.compare(Timestamp, Timestamp).
+  * JsonFormat now accepts base64url encoded bytes fields.
+  * Optimized CodedInputStream to do less copies when parsing large bytes
+    fields.
+  * Optimized TextFormat to allocate less memory when printing.
+
+  Python
+  * SerializeToString API is changed to SerializeToString(self, **kwargs),
+    deterministic parameter is accepted for deterministic serialization.
+  * Added sort_keys parameter in json format to make the output deterministic.
+  * Added indent parameter in json format.
+  * Added extension support in json format.
+  * Added __repr__ support for repeated field in cpp implementation.
+  * Added file in FieldDescriptor.
+  * Added pretty-print filter to text format.
+  * Services and method descriptors are always printed even if generic_service
+    option is turned off.
+  * Note: AppEngine 2.5 is deprecated on June 2017 that AppEngine 2.5 will
+    never update protobuf runtime. Users who depend on AppEngine 2.5 should use
+    old protoc.
+  
+  PHP
+  * Support PHP generic services. Specify file option php_generic_service=true
+    to enable generating service interface.
+  * Message, repeated and map fields setters take value instead of reference.
+  * Added map iterator in c extension.
+  * Support json  encode/decode.
+  * Added more type info in getter/setter phpdoc
+  * Fixed the problem that c extension and php implementation cannot be used
+    together.
+  * Added file option php_namespace to use custom php namespace instead of
+    package.
+  * Added fluent setter.
+  * Added descriptor API in runtime for custom encode/decode.
+  * Various bug fixes.
+
+  Objective-C
+  * Fix for GPBExtensionRegistry copying and add tests.
+  * Optimize GPBDictionary.m codegen to reduce size of overall library by 46K
+    per architecture.
+  * Fix some cases of reading of 64bit map values.
+  * Properly error on a tag with field number zero.
+  * Preserve unknown fields in proto3 syntax files.
+  * Document the exceptions on some of the writing apis.
+
+  C#
+  * Implemented IReadOnlyDictionary<K,V> in MapField<K,V>
+  * Added TryUnpack method for Any message in addition to Unpack.
+  * Converted C# projects to MSBuild (csproj) format.
+
+  Ruby
+  * Several bug fixes.
+
+  Javascript
+  * Added support of field option js_type. Now one can specify the JS type of a
+    64-bit integer field to be string in the generated code by adding option
+    [jstype = JS_STRING] on the field.
+
 2017-04-05 version 3.3.0 (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript)
 2017-04-05 version 3.3.0 (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript)
   Planned Future Changes
   Planned Future Changes
   * There are some changes that are not included in this release but are
   * There are some changes that are not included in this release but are

+ 26 - 1
Makefile.am

@@ -207,6 +207,7 @@ java_EXTRA_DIST=
   java/core/src/main/java/com/google/protobuf/ByteString.java                      \
   java/core/src/main/java/com/google/protobuf/ByteString.java                      \
   java/core/src/main/java/com/google/protobuf/CodedInputStream.java                \
   java/core/src/main/java/com/google/protobuf/CodedInputStream.java                \
   java/core/src/main/java/com/google/protobuf/CodedOutputStream.java               \
   java/core/src/main/java/com/google/protobuf/CodedOutputStream.java               \
+  java/core/src/main/java/com/google/protobuf/DiscardUnknownFieldsParser.java      \
   java/core/src/main/java/com/google/protobuf/Descriptors.java                     \
   java/core/src/main/java/com/google/protobuf/Descriptors.java                     \
   java/core/src/main/java/com/google/protobuf/DoubleArrayList.java                 \
   java/core/src/main/java/com/google/protobuf/DoubleArrayList.java                 \
   java/core/src/main/java/com/google/protobuf/DynamicMessage.java                  \
   java/core/src/main/java/com/google/protobuf/DynamicMessage.java                  \
@@ -281,6 +282,7 @@ java_EXTRA_DIST=
   java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java           \
   java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java           \
   java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java             \
   java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java             \
   java/core/src/test/java/com/google/protobuf/DescriptorsTest.java                 \
   java/core/src/test/java/com/google/protobuf/DescriptorsTest.java                 \
+  java/core/src/test/java/com/google/protobuf/DiscardUnknownFieldsTest.java        \
   java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java             \
   java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java             \
   java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java              \
   java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java              \
   java/core/src/test/java/com/google/protobuf/EnumTest.java                        \
   java/core/src/test/java/com/google/protobuf/EnumTest.java                        \
@@ -317,6 +319,7 @@ java_EXTRA_DIST=
   java/core/src/test/java/com/google/protobuf/SingleFieldBuilderV3Test.java        \
   java/core/src/test/java/com/google/protobuf/SingleFieldBuilderV3Test.java        \
   java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java              \
   java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java              \
   java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java              \
   java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java              \
+  java/core/src/test/java/com/google/protobuf/TestBadIdentifiersLite.java          \
   java/core/src/test/java/com/google/protobuf/TestUtil.java                        \
   java/core/src/test/java/com/google/protobuf/TestUtil.java                        \
   java/core/src/test/java/com/google/protobuf/TestUtilLite.java                    \
   java/core/src/test/java/com/google/protobuf/TestUtilLite.java                    \
   java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java     \
   java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java     \
@@ -756,6 +759,7 @@ python_EXTRA_DIST=                                                           \
   python/google/protobuf/json_format.py                                      \
   python/google/protobuf/json_format.py                                      \
   python/google/protobuf/message.py                                          \
   python/google/protobuf/message.py                                          \
   python/google/protobuf/message_factory.py                                  \
   python/google/protobuf/message_factory.py                                  \
+  python/google/protobuf/python_protobuf.h                                   \
   python/google/protobuf/proto_builder.py                                    \
   python/google/protobuf/proto_builder.py                                    \
   python/google/protobuf/pyext/README                                        \
   python/google/protobuf/pyext/README                                        \
   python/google/protobuf/pyext/__init__.py                                   \
   python/google/protobuf/pyext/__init__.py                                   \
@@ -785,13 +789,13 @@ python_EXTRA_DIST=                                                           \
   python/google/protobuf/pyext/repeated_scalar_container.h                   \
   python/google/protobuf/pyext/repeated_scalar_container.h                   \
   python/google/protobuf/pyext/safe_numerics.h                               \
   python/google/protobuf/pyext/safe_numerics.h                               \
   python/google/protobuf/pyext/scoped_pyobject_ptr.h                         \
   python/google/protobuf/pyext/scoped_pyobject_ptr.h                         \
-  python/google/protobuf/python_protobuf.h                                   \
   python/google/protobuf/reflection.py                                       \
   python/google/protobuf/reflection.py                                       \
   python/google/protobuf/service.py                                          \
   python/google/protobuf/service.py                                          \
   python/google/protobuf/service_reflection.py                               \
   python/google/protobuf/service_reflection.py                               \
   python/google/protobuf/symbol_database.py                                  \
   python/google/protobuf/symbol_database.py                                  \
   python/google/protobuf/text_encoding.py                                    \
   python/google/protobuf/text_encoding.py                                    \
   python/google/protobuf/text_format.py                                      \
   python/google/protobuf/text_format.py                                      \
+  python/release.sh                                                          \
   python/mox.py                                                              \
   python/mox.py                                                              \
   python/setup.cfg                                                           \
   python/setup.cfg                                                           \
   python/setup.py                                                            \
   python/setup.py                                                            \
@@ -911,6 +915,27 @@ js_EXTRA_DIST=                                                         \
   js/compatibility_tests/v3.0.0/testempty.proto                        \
   js/compatibility_tests/v3.0.0/testempty.proto                        \
   js/compatibility_tests/v3.0.0/test.proto                             \
   js/compatibility_tests/v3.0.0/test.proto                             \
   js/compatibility_tests/v3.0.0/test.sh                                \
   js/compatibility_tests/v3.0.0/test.sh                                \
+  js/compatibility_tests/v3.1.0/testempty.proto                        \
+  js/compatibility_tests/v3.1.0/testbinary.proto                       \
+  js/compatibility_tests/v3.1.0/test5.proto                            \
+  js/compatibility_tests/v3.1.0/test4.proto                            \
+  js/compatibility_tests/v3.1.0/test3.proto                            \
+  js/compatibility_tests/v3.1.0/test2.proto                            \
+  js/compatibility_tests/v3.1.0/test.proto                             \
+  js/compatibility_tests/v3.1.0/proto3_test.proto                      \
+  js/compatibility_tests/v3.1.0/proto3_test.js                         \
+  js/compatibility_tests/v3.1.0/message_test.js                        \
+  js/compatibility_tests/v3.1.0/maps_test.js                           \
+  js/compatibility_tests/v3.1.0/debug_test.js                          \
+  js/compatibility_tests/v3.1.0/data.proto                             \
+  js/compatibility_tests/v3.1.0/commonjs/test7/test7.proto             \
+  js/compatibility_tests/v3.1.0/commonjs/test6/test6.proto             \
+  js/compatibility_tests/v3.1.0/binary/writer_test.js                  \
+  js/compatibility_tests/v3.1.0/binary/utils_test.js                   \
+  js/compatibility_tests/v3.1.0/binary/reader_test.js                  \
+  js/compatibility_tests/v3.1.0/binary/proto_test.js                   \
+  js/compatibility_tests/v3.1.0/binary/decoder_test.js                 \
+  js/compatibility_tests/v3.1.0/binary/arith_test.js                   \
   js/data.proto                                                        \
   js/data.proto                                                        \
   js/debug.js                                                          \
   js/debug.js                                                          \
   js/debug_test.js                                                     \
   js/debug_test.js                                                     \

+ 1 - 1
Protobuf.podspec

@@ -5,7 +5,7 @@
 # dependent projects use the :git notation to refer to the library.
 # dependent projects use the :git notation to refer to the library.
 Pod::Spec.new do |s|
 Pod::Spec.new do |s|
   s.name     = 'Protobuf'
   s.name     = 'Protobuf'
-  s.version  = '3.3.2'
+  s.version  = '3.4.0'
   s.summary  = 'Protocol Buffers v.3 runtime library for Objective-C.'
   s.summary  = 'Protocol Buffers v.3 runtime library for Objective-C.'
   s.homepage = 'https://github.com/google/protobuf'
   s.homepage = 'https://github.com/google/protobuf'
   s.license  = '3-Clause BSD License'
   s.license  = '3-Clause BSD License'

+ 1 - 3
cmake/extract_includes.bat.in

@@ -18,6 +18,7 @@ copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\any.h" include\google
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\any.pb.h" include\google\protobuf\any.pb.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\any.pb.h" include\google\protobuf\any.pb.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\api.pb.h" include\google\protobuf\api.pb.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\api.pb.h" include\google\protobuf\api.pb.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\arena.h" include\google\protobuf\arena.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\arena.h" include\google\protobuf\arena.h
+copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\arena_impl.h" include\google\protobuf\arena_impl.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\arenastring.h" include\google\protobuf\arenastring.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\arenastring.h" include\google\protobuf\arenastring.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\code_generator.h" include\google\protobuf\compiler\code_generator.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\code_generator.h" include\google\protobuf\compiler\code_generator.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\command_line_interface.h" include\google\protobuf\compiler\command_line_interface.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\command_line_interface.h" include\google\protobuf\compiler\command_line_interface.h
@@ -36,7 +37,6 @@ copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\parser.h" in
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\php\php_generator.h" include\google\protobuf\compiler\php\php_generator.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\php\php_generator.h" include\google\protobuf\compiler\php\php_generator.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\plugin.h" include\google\protobuf\compiler\plugin.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\plugin.h" include\google\protobuf\compiler\plugin.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\plugin.pb.h" include\google\protobuf\compiler\plugin.pb.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\plugin.pb.h" include\google\protobuf\compiler\plugin.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\profile.pb.h" include\google\protobuf\compiler\profile.pb.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\python\python_generator.h" include\google\protobuf\compiler\python\python_generator.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\python\python_generator.h" include\google\protobuf\compiler\python\python_generator.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\ruby\ruby_generator.h" include\google\protobuf\compiler\ruby\ruby_generator.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\ruby\ruby_generator.h" include\google\protobuf\compiler\ruby\ruby_generator.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\descriptor.h" include\google\protobuf\descriptor.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\descriptor.h" include\google\protobuf\descriptor.h
@@ -52,7 +52,6 @@ copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_enum_util.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_message_reflection.h" include\google\protobuf\generated_message_reflection.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_message_reflection.h" include\google\protobuf\generated_message_reflection.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_message_table_driven.h" include\google\protobuf\generated_message_table_driven.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_message_table_driven.h" include\google\protobuf\generated_message_table_driven.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_message_util.h" include\google\protobuf\generated_message_util.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_message_util.h" include\google\protobuf\generated_message_util.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_message_table_driven.h" include\google\protobuf\generated_message_table_driven.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\has_bits.h" include\google\protobuf\has_bits.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\has_bits.h" include\google\protobuf\has_bits.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\coded_stream.h" include\google\protobuf\io\coded_stream.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\coded_stream.h" include\google\protobuf\io\coded_stream.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\gzip_stream.h" include\google\protobuf\io\gzip_stream.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\gzip_stream.h" include\google\protobuf\io\gzip_stream.h
@@ -100,7 +99,6 @@ copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\casts.h" includ
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\common.h" include\google\protobuf\stubs\common.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\common.h" include\google\protobuf\stubs\common.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\fastmem.h" include\google\protobuf\stubs\fastmem.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\fastmem.h" include\google\protobuf\stubs\fastmem.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\hash.h" include\google\protobuf\stubs\hash.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\hash.h" include\google\protobuf\stubs\hash.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\io_win32.h" include\google\protobuf\stubs\io_win32.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\logging.h" include\google\protobuf\stubs\logging.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\logging.h" include\google\protobuf\stubs\logging.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\macros.h" include\google\protobuf\stubs\macros.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\macros.h" include\google\protobuf\stubs\macros.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\mutex.h" include\google\protobuf\stubs\mutex.h
 copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\mutex.h" include\google\protobuf\stubs\mutex.h

+ 1 - 0
cmake/libprotobuf-lite.cmake

@@ -2,6 +2,7 @@ set(libprotobuf_lite_files
   ${protobuf_source_dir}/src/google/protobuf/arena.cc
   ${protobuf_source_dir}/src/google/protobuf/arena.cc
   ${protobuf_source_dir}/src/google/protobuf/arenastring.cc
   ${protobuf_source_dir}/src/google/protobuf/arenastring.cc
   ${protobuf_source_dir}/src/google/protobuf/extension_set.cc
   ${protobuf_source_dir}/src/google/protobuf/extension_set.cc
+  ${protobuf_source_dir}/src/google/protobuf/generated_message_table_driven_lite.cc
   ${protobuf_source_dir}/src/google/protobuf/generated_message_util.cc
   ${protobuf_source_dir}/src/google/protobuf/generated_message_util.cc
   ${protobuf_source_dir}/src/google/protobuf/io/coded_stream.cc
   ${protobuf_source_dir}/src/google/protobuf/io/coded_stream.cc
   ${protobuf_source_dir}/src/google/protobuf/io/zero_copy_stream.cc
   ${protobuf_source_dir}/src/google/protobuf/io/zero_copy_stream.cc

+ 0 - 3
cmake/libprotoc.cmake

@@ -88,12 +88,10 @@ set(libprotoc_files
   ${protobuf_source_dir}/src/google/protobuf/compiler/php/php_generator.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/php/php_generator.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/plugin.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/plugin.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/plugin.pb.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/plugin.pb.cc
-  ${protobuf_source_dir}/src/google/protobuf/compiler/profile.pb.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/python/python_generator.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/python/python_generator.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/ruby/ruby_generator.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/ruby/ruby_generator.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/subprocess.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/subprocess.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/zip_writer.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/zip_writer.cc
-  ${protobuf_source_dir}/src/google/protobuf/stubs/io_win32.cc
 )
 )
 
 
 set(libprotoc_headers
 set(libprotoc_headers
@@ -105,7 +103,6 @@ set(libprotoc_headers
   ${protobuf_source_dir}/src/google/protobuf/compiler/parser.h
   ${protobuf_source_dir}/src/google/protobuf/compiler/parser.h
   ${protobuf_source_dir}/src/google/protobuf/compiler/plugin.h
   ${protobuf_source_dir}/src/google/protobuf/compiler/plugin.h
   ${protobuf_source_dir}/src/google/protobuf/compiler/plugin.pb.h
   ${protobuf_source_dir}/src/google/protobuf/compiler/plugin.pb.h
-  ${protobuf_source_dir}/src/google/protobuf/compiler/profile.pb.h
   ${protobuf_source_dir}/src/google/protobuf/compiler/subprocess.h
   ${protobuf_source_dir}/src/google/protobuf/compiler/subprocess.h
   ${protobuf_source_dir}/src/google/protobuf/compiler/zip_writer.h
   ${protobuf_source_dir}/src/google/protobuf/compiler/zip_writer.h
   ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/cpp_enum.h
   ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/cpp_enum.h

+ 1 - 2
cmake/tests.cmake

@@ -123,6 +123,7 @@ set(tests_files
   ${protobuf_source_dir}/src/google/protobuf/arenastring_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/arenastring_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/command_line_interface_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/command_line_interface_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc
+  ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/cpp_move_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/cpp_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/cpp_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/metadata_test.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/metadata_test.cc
@@ -160,7 +161,6 @@ set(tests_files
   ${protobuf_source_dir}/src/google/protobuf/stubs/bytestream_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/stubs/bytestream_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/stubs/common_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/stubs/common_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/stubs/int128_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/stubs/int128_unittest.cc
-  ${protobuf_source_dir}/src/google/protobuf/stubs/io_win32.cc
   ${protobuf_source_dir}/src/google/protobuf/stubs/io_win32_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/stubs/io_win32_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/stubs/once_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/stubs/once_unittest.cc
   ${protobuf_source_dir}/src/google/protobuf/stubs/status_test.cc
   ${protobuf_source_dir}/src/google/protobuf/stubs/status_test.cc
@@ -200,7 +200,6 @@ target_link_libraries(tests libprotoc libprotobuf gmock_main)
 
 
 set(test_plugin_files
 set(test_plugin_files
   ${protobuf_source_dir}/src/google/protobuf/compiler/mock_code_generator.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/mock_code_generator.cc
-  ${protobuf_source_dir}/src/google/protobuf/stubs/io_win32.cc
   ${protobuf_source_dir}/src/google/protobuf/testing/file.cc
   ${protobuf_source_dir}/src/google/protobuf/testing/file.cc
   ${protobuf_source_dir}/src/google/protobuf/testing/file.h
   ${protobuf_source_dir}/src/google/protobuf/testing/file.h
   ${protobuf_source_dir}/src/google/protobuf/compiler/test_plugin.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/test_plugin.cc

+ 1 - 1
configure.ac

@@ -17,7 +17,7 @@ AC_PREREQ(2.59)
 # In the SVN trunk, the version should always be the next anticipated release
 # In the SVN trunk, the version should always be the next anticipated release
 # version with the "-pre" suffix.  (We used to use "-SNAPSHOT" but this pushed
 # version with the "-pre" suffix.  (We used to use "-SNAPSHOT" but this pushed
 # the size of one file name in the dist tarfile over the 99-char limit.)
 # the size of one file name in the dist tarfile over the 99-char limit.)
-AC_INIT([Protocol Buffers],[3.3.2],[protobuf@googlegroups.com],[protobuf])
+AC_INIT([Protocol Buffers],[3.4.0],[protobuf@googlegroups.com],[protobuf])
 
 
 AM_MAINTAINER_MODE([enable])
 AM_MAINTAINER_MODE([enable])
 
 

+ 4 - 3
conformance/conformance_test.cc

@@ -1605,9 +1605,10 @@ bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner,
       "BytesField", REQUIRED,
       "BytesField", REQUIRED,
       R"({"optionalBytes": "AQI="})",
       R"({"optionalBytes": "AQI="})",
       R"(optional_bytes: "\x01\x02")");
       R"(optional_bytes: "\x01\x02")");
-  ExpectParseFailureForJson(
-      "BytesFieldInvalidBase64Characters", REQUIRED,
-      R"({"optionalBytes": "-_=="})");
+  RunValidJsonTest(
+      "BytesFieldBase64Url", RECOMMENDED,
+      R"({"optionalBytes": "-_"})",
+      R"(optional_bytes: "\xfb")");
 
 
   // Message fields.
   // Message fields.
   RunValidJsonTest(
   RunValidJsonTest(

+ 2 - 0
conformance/failure_list_cpp.txt

@@ -12,6 +12,8 @@ Recommended.FieldMaskPathsDontRoundTrip.JsonOutput
 Recommended.FieldMaskTooManyUnderscore.JsonOutput
 Recommended.FieldMaskTooManyUnderscore.JsonOutput
 Recommended.Proto3.JsonInput.BoolFieldDoubleQuotedFalse
 Recommended.Proto3.JsonInput.BoolFieldDoubleQuotedFalse
 Recommended.Proto3.JsonInput.BoolFieldDoubleQuotedTrue
 Recommended.Proto3.JsonInput.BoolFieldDoubleQuotedTrue
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.JsonOutput
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.ProtobufOutput
 Recommended.Proto3.JsonInput.FieldMaskInvalidCharacter
 Recommended.Proto3.JsonInput.FieldMaskInvalidCharacter
 Recommended.Proto3.JsonInput.FieldNameDuplicate
 Recommended.Proto3.JsonInput.FieldNameDuplicate
 Recommended.Proto3.JsonInput.FieldNameDuplicateDifferentCasing1
 Recommended.Proto3.JsonInput.FieldNameDuplicateDifferentCasing1

+ 2 - 0
conformance/failure_list_csharp.txt

@@ -0,0 +1,2 @@
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.JsonOutput
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.ProtobufOutput

+ 2 - 0
conformance/failure_list_php.txt

@@ -1,6 +1,8 @@
 Recommended.FieldMaskNumbersDontRoundTrip.JsonOutput
 Recommended.FieldMaskNumbersDontRoundTrip.JsonOutput
 Recommended.FieldMaskPathsDontRoundTrip.JsonOutput
 Recommended.FieldMaskPathsDontRoundTrip.JsonOutput
 Recommended.FieldMaskTooManyUnderscore.JsonOutput
 Recommended.FieldMaskTooManyUnderscore.JsonOutput
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.JsonOutput
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.ProtobufOutput
 Recommended.Proto3.JsonInput.DurationHas3FractionalDigits.Validator
 Recommended.Proto3.JsonInput.DurationHas3FractionalDigits.Validator
 Recommended.Proto3.JsonInput.DurationHas6FractionalDigits.Validator
 Recommended.Proto3.JsonInput.DurationHas6FractionalDigits.Validator
 Recommended.Proto3.JsonInput.DurationHas9FractionalDigits.Validator
 Recommended.Proto3.JsonInput.DurationHas9FractionalDigits.Validator

+ 2 - 1
conformance/failure_list_python.txt

@@ -1,10 +1,11 @@
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.JsonOutput
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.ProtobufOutput
 Recommended.Proto3.JsonInput.DoubleFieldInfinityNotQuoted
 Recommended.Proto3.JsonInput.DoubleFieldInfinityNotQuoted
 Recommended.Proto3.JsonInput.DoubleFieldNanNotQuoted
 Recommended.Proto3.JsonInput.DoubleFieldNanNotQuoted
 Recommended.Proto3.JsonInput.DoubleFieldNegativeInfinityNotQuoted
 Recommended.Proto3.JsonInput.DoubleFieldNegativeInfinityNotQuoted
 Recommended.Proto3.JsonInput.FloatFieldInfinityNotQuoted
 Recommended.Proto3.JsonInput.FloatFieldInfinityNotQuoted
 Recommended.Proto3.JsonInput.FloatFieldNanNotQuoted
 Recommended.Proto3.JsonInput.FloatFieldNanNotQuoted
 Recommended.Proto3.JsonInput.FloatFieldNegativeInfinityNotQuoted
 Recommended.Proto3.JsonInput.FloatFieldNegativeInfinityNotQuoted
-Required.Proto3.JsonInput.BytesFieldInvalidBase64Characters
 Required.Proto3.JsonInput.DoubleFieldTooSmall
 Required.Proto3.JsonInput.DoubleFieldTooSmall
 Required.Proto3.JsonInput.EnumFieldUnknownValue.Validator
 Required.Proto3.JsonInput.EnumFieldUnknownValue.Validator
 Required.Proto3.JsonInput.FloatFieldTooLarge
 Required.Proto3.JsonInput.FloatFieldTooLarge

+ 2 - 1
conformance/failure_list_python_cpp.txt

@@ -7,13 +7,14 @@
 # TODO(haberman): insert links to corresponding bugs tracking the issue.
 # TODO(haberman): insert links to corresponding bugs tracking the issue.
 # Should we use GitHub issues or the Google-internal bug tracker?
 # Should we use GitHub issues or the Google-internal bug tracker?
 
 
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.JsonOutput
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.ProtobufOutput
 Recommended.Proto3.JsonInput.DoubleFieldInfinityNotQuoted
 Recommended.Proto3.JsonInput.DoubleFieldInfinityNotQuoted
 Recommended.Proto3.JsonInput.DoubleFieldNanNotQuoted
 Recommended.Proto3.JsonInput.DoubleFieldNanNotQuoted
 Recommended.Proto3.JsonInput.DoubleFieldNegativeInfinityNotQuoted
 Recommended.Proto3.JsonInput.DoubleFieldNegativeInfinityNotQuoted
 Recommended.Proto3.JsonInput.FloatFieldInfinityNotQuoted
 Recommended.Proto3.JsonInput.FloatFieldInfinityNotQuoted
 Recommended.Proto3.JsonInput.FloatFieldNanNotQuoted
 Recommended.Proto3.JsonInput.FloatFieldNanNotQuoted
 Recommended.Proto3.JsonInput.FloatFieldNegativeInfinityNotQuoted
 Recommended.Proto3.JsonInput.FloatFieldNegativeInfinityNotQuoted
-Required.Proto3.JsonInput.BytesFieldInvalidBase64Characters
 Required.Proto3.JsonInput.DoubleFieldTooSmall
 Required.Proto3.JsonInput.DoubleFieldTooSmall
 Required.Proto3.JsonInput.EnumFieldUnknownValue.Validator
 Required.Proto3.JsonInput.EnumFieldUnknownValue.Validator
 Required.Proto3.JsonInput.FloatFieldTooLarge
 Required.Proto3.JsonInput.FloatFieldTooLarge

+ 2 - 0
conformance/failure_list_ruby.txt

@@ -1,6 +1,8 @@
 Recommended.FieldMaskNumbersDontRoundTrip.JsonOutput
 Recommended.FieldMaskNumbersDontRoundTrip.JsonOutput
 Recommended.FieldMaskPathsDontRoundTrip.JsonOutput
 Recommended.FieldMaskPathsDontRoundTrip.JsonOutput
 Recommended.FieldMaskTooManyUnderscore.JsonOutput
 Recommended.FieldMaskTooManyUnderscore.JsonOutput
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.JsonOutput
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.ProtobufOutput
 Recommended.Proto3.JsonInput.DurationHas3FractionalDigits.Validator
 Recommended.Proto3.JsonInput.DurationHas3FractionalDigits.Validator
 Recommended.Proto3.JsonInput.DurationHas6FractionalDigits.Validator
 Recommended.Proto3.JsonInput.DurationHas6FractionalDigits.Validator
 Recommended.Proto3.JsonInput.DurationHas9FractionalDigits.Validator
 Recommended.Proto3.JsonInput.DurationHas9FractionalDigits.Validator

+ 1 - 1
csharp/Google.Protobuf.Tools.nuspec

@@ -5,7 +5,7 @@
     <title>Google Protocol Buffers tools</title>
     <title>Google Protocol Buffers tools</title>
     <summary>Tools for Protocol Buffers - Google's data interchange format.</summary>
     <summary>Tools for Protocol Buffers - Google's data interchange format.</summary>
     <description>See project site for more info.</description>
     <description>See project site for more info.</description>
-    <version>3.3.2</version>
+    <version>3.4.0</version>
     <authors>Google Inc.</authors>
     <authors>Google Inc.</authors>
     <owners>protobuf-packages</owners>
     <owners>protobuf-packages</owners>
     <licenseUrl>https://github.com/google/protobuf/blob/master/LICENSE</licenseUrl>
     <licenseUrl>https://github.com/google/protobuf/blob/master/LICENSE</licenseUrl>

+ 1 - 1
csharp/src/Google.Protobuf/Google.Protobuf.csproj

@@ -4,7 +4,7 @@
     <Description>C# runtime library for Protocol Buffers - Google's data interchange format.</Description>
     <Description>C# runtime library for Protocol Buffers - Google's data interchange format.</Description>
     <Copyright>Copyright 2015, Google Inc.</Copyright>
     <Copyright>Copyright 2015, Google Inc.</Copyright>
     <AssemblyTitle>Google Protocol Buffers</AssemblyTitle>
     <AssemblyTitle>Google Protocol Buffers</AssemblyTitle>
-    <VersionPrefix>3.3.0</VersionPrefix>
+    <VersionPrefix>3.4.0</VersionPrefix>
     <Authors>Google Inc.</Authors>
     <Authors>Google Inc.</Authors>
     <TargetFrameworks>netstandard1.0;net451</TargetFrameworks>
     <TargetFrameworks>netstandard1.0;net451</TargetFrameworks>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>

+ 279 - 126
csharp/src/Google.Protobuf/Reflection/Descriptor.cs

@@ -35,7 +35,7 @@ namespace Google.Protobuf.Reflection {
             "LnByb3RvYnVmLkZpZWxkRGVzY3JpcHRvclByb3RvEi0KB29wdGlvbnMYCCAB",
             "LnByb3RvYnVmLkZpZWxkRGVzY3JpcHRvclByb3RvEi0KB29wdGlvbnMYCCAB",
             "KAsyHC5nb29nbGUucHJvdG9idWYuRmlsZU9wdGlvbnMSOQoQc291cmNlX2Nv",
             "KAsyHC5nb29nbGUucHJvdG9idWYuRmlsZU9wdGlvbnMSOQoQc291cmNlX2Nv",
             "ZGVfaW5mbxgJIAEoCzIfLmdvb2dsZS5wcm90b2J1Zi5Tb3VyY2VDb2RlSW5m",
             "ZGVfaW5mbxgJIAEoCzIfLmdvb2dsZS5wcm90b2J1Zi5Tb3VyY2VDb2RlSW5m",
-            "bxIOCgZzeW50YXgYDCABKAki8AQKD0Rlc2NyaXB0b3JQcm90bxIMCgRuYW1l",
+            "bxIOCgZzeW50YXgYDCABKAkiqQUKD0Rlc2NyaXB0b3JQcm90bxIMCgRuYW1l",
             "GAEgASgJEjQKBWZpZWxkGAIgAygLMiUuZ29vZ2xlLnByb3RvYnVmLkZpZWxk",
             "GAEgASgJEjQKBWZpZWxkGAIgAygLMiUuZ29vZ2xlLnByb3RvYnVmLkZpZWxk",
             "RGVzY3JpcHRvclByb3RvEjgKCWV4dGVuc2lvbhgGIAMoCzIlLmdvb2dsZS5w",
             "RGVzY3JpcHRvclByb3RvEjgKCWV4dGVuc2lvbhgGIAMoCzIlLmdvb2dsZS5w",
             "cm90b2J1Zi5GaWVsZERlc2NyaXB0b3JQcm90bxI1CgtuZXN0ZWRfdHlwZRgD",
             "cm90b2J1Zi5GaWVsZERlc2NyaXB0b3JQcm90bxI1CgtuZXN0ZWRfdHlwZRgD",
@@ -47,114 +47,119 @@ namespace Google.Protobuf.Reflection {
             "b3RvEjAKB29wdGlvbnMYByABKAsyHy5nb29nbGUucHJvdG9idWYuTWVzc2Fn",
             "b3RvEjAKB29wdGlvbnMYByABKAsyHy5nb29nbGUucHJvdG9idWYuTWVzc2Fn",
             "ZU9wdGlvbnMSRgoOcmVzZXJ2ZWRfcmFuZ2UYCSADKAsyLi5nb29nbGUucHJv",
             "ZU9wdGlvbnMSRgoOcmVzZXJ2ZWRfcmFuZ2UYCSADKAsyLi5nb29nbGUucHJv",
             "dG9idWYuRGVzY3JpcHRvclByb3RvLlJlc2VydmVkUmFuZ2USFQoNcmVzZXJ2",
             "dG9idWYuRGVzY3JpcHRvclByb3RvLlJlc2VydmVkUmFuZ2USFQoNcmVzZXJ2",
-            "ZWRfbmFtZRgKIAMoCRosCg5FeHRlbnNpb25SYW5nZRINCgVzdGFydBgBIAEo",
-            "BRILCgNlbmQYAiABKAUaKwoNUmVzZXJ2ZWRSYW5nZRINCgVzdGFydBgBIAEo",
-            "BRILCgNlbmQYAiABKAUivAUKFEZpZWxkRGVzY3JpcHRvclByb3RvEgwKBG5h",
-            "bWUYASABKAkSDgoGbnVtYmVyGAMgASgFEjoKBWxhYmVsGAQgASgOMisuZ29v",
-            "Z2xlLnByb3RvYnVmLkZpZWxkRGVzY3JpcHRvclByb3RvLkxhYmVsEjgKBHR5",
-            "cGUYBSABKA4yKi5nb29nbGUucHJvdG9idWYuRmllbGREZXNjcmlwdG9yUHJv",
-            "dG8uVHlwZRIRCgl0eXBlX25hbWUYBiABKAkSEAoIZXh0ZW5kZWUYAiABKAkS",
-            "FQoNZGVmYXVsdF92YWx1ZRgHIAEoCRITCgtvbmVvZl9pbmRleBgJIAEoBRIR",
-            "Cglqc29uX25hbWUYCiABKAkSLgoHb3B0aW9ucxgIIAEoCzIdLmdvb2dsZS5w",
-            "cm90b2J1Zi5GaWVsZE9wdGlvbnMitgIKBFR5cGUSDwoLVFlQRV9ET1VCTEUQ",
-            "ARIOCgpUWVBFX0ZMT0FUEAISDgoKVFlQRV9JTlQ2NBADEg8KC1RZUEVfVUlO",
-            "VDY0EAQSDgoKVFlQRV9JTlQzMhAFEhAKDFRZUEVfRklYRUQ2NBAGEhAKDFRZ",
-            "UEVfRklYRUQzMhAHEg0KCVRZUEVfQk9PTBAIEg8KC1RZUEVfU1RSSU5HEAkS",
-            "DgoKVFlQRV9HUk9VUBAKEhAKDFRZUEVfTUVTU0FHRRALEg4KClRZUEVfQllU",
-            "RVMQDBIPCgtUWVBFX1VJTlQzMhANEg0KCVRZUEVfRU5VTRAOEhEKDVRZUEVf",
-            "U0ZJWEVEMzIQDxIRCg1UWVBFX1NGSVhFRDY0EBASDwoLVFlQRV9TSU5UMzIQ",
-            "ERIPCgtUWVBFX1NJTlQ2NBASIkMKBUxhYmVsEhIKDkxBQkVMX09QVElPTkFM",
-            "EAESEgoOTEFCRUxfUkVRVUlSRUQQAhISCg5MQUJFTF9SRVBFQVRFRBADIlQK",
-            "FE9uZW9mRGVzY3JpcHRvclByb3RvEgwKBG5hbWUYASABKAkSLgoHb3B0aW9u",
-            "cxgCIAEoCzIdLmdvb2dsZS5wcm90b2J1Zi5PbmVvZk9wdGlvbnMijAEKE0Vu",
-            "dW1EZXNjcmlwdG9yUHJvdG8SDAoEbmFtZRgBIAEoCRI4CgV2YWx1ZRgCIAMo",
-            "CzIpLmdvb2dsZS5wcm90b2J1Zi5FbnVtVmFsdWVEZXNjcmlwdG9yUHJvdG8S",
-            "LQoHb3B0aW9ucxgDIAEoCzIcLmdvb2dsZS5wcm90b2J1Zi5FbnVtT3B0aW9u",
-            "cyJsChhFbnVtVmFsdWVEZXNjcmlwdG9yUHJvdG8SDAoEbmFtZRgBIAEoCRIO",
-            "CgZudW1iZXIYAiABKAUSMgoHb3B0aW9ucxgDIAEoCzIhLmdvb2dsZS5wcm90",
-            "b2J1Zi5FbnVtVmFsdWVPcHRpb25zIpABChZTZXJ2aWNlRGVzY3JpcHRvclBy",
-            "b3RvEgwKBG5hbWUYASABKAkSNgoGbWV0aG9kGAIgAygLMiYuZ29vZ2xlLnBy",
-            "b3RvYnVmLk1ldGhvZERlc2NyaXB0b3JQcm90bxIwCgdvcHRpb25zGAMgASgL",
-            "Mh8uZ29vZ2xlLnByb3RvYnVmLlNlcnZpY2VPcHRpb25zIsEBChVNZXRob2RE",
-            "ZXNjcmlwdG9yUHJvdG8SDAoEbmFtZRgBIAEoCRISCgppbnB1dF90eXBlGAIg",
-            "ASgJEhMKC291dHB1dF90eXBlGAMgASgJEi8KB29wdGlvbnMYBCABKAsyHi5n",
-            "b29nbGUucHJvdG9idWYuTWV0aG9kT3B0aW9ucxIfChBjbGllbnRfc3RyZWFt",
-            "aW5nGAUgASgIOgVmYWxzZRIfChBzZXJ2ZXJfc3RyZWFtaW5nGAYgASgIOgVm",
-            "YWxzZSLwBQoLRmlsZU9wdGlvbnMSFAoMamF2YV9wYWNrYWdlGAEgASgJEhwK",
-            "FGphdmFfb3V0ZXJfY2xhc3NuYW1lGAggASgJEiIKE2phdmFfbXVsdGlwbGVf",
-            "ZmlsZXMYCiABKAg6BWZhbHNlEikKHWphdmFfZ2VuZXJhdGVfZXF1YWxzX2Fu",
-            "ZF9oYXNoGBQgASgIQgIYARIlChZqYXZhX3N0cmluZ19jaGVja191dGY4GBsg",
-            "ASgIOgVmYWxzZRJGCgxvcHRpbWl6ZV9mb3IYCSABKA4yKS5nb29nbGUucHJv",
-            "dG9idWYuRmlsZU9wdGlvbnMuT3B0aW1pemVNb2RlOgVTUEVFRBISCgpnb19w",
-            "YWNrYWdlGAsgASgJEiIKE2NjX2dlbmVyaWNfc2VydmljZXMYECABKAg6BWZh",
-            "bHNlEiQKFWphdmFfZ2VuZXJpY19zZXJ2aWNlcxgRIAEoCDoFZmFsc2USIgoT",
-            "cHlfZ2VuZXJpY19zZXJ2aWNlcxgSIAEoCDoFZmFsc2USIwoUcGhwX2dlbmVy",
-            "aWNfc2VydmljZXMYEyABKAg6BWZhbHNlEhkKCmRlcHJlY2F0ZWQYFyABKAg6",
-            "BWZhbHNlEh8KEGNjX2VuYWJsZV9hcmVuYXMYHyABKAg6BWZhbHNlEhkKEW9i",
-            "amNfY2xhc3NfcHJlZml4GCQgASgJEhgKEGNzaGFycF9uYW1lc3BhY2UYJSAB",
-            "KAkSFAoMc3dpZnRfcHJlZml4GCcgASgJEhgKEHBocF9jbGFzc19wcmVmaXgY",
-            "KCABKAkSFQoNcGhwX25hbWVzcGFjZRgpIAEoCRJDChR1bmludGVycHJldGVk",
-            "X29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRl",
-            "ZE9wdGlvbiI6CgxPcHRpbWl6ZU1vZGUSCQoFU1BFRUQQARINCglDT0RFX1NJ",
-            "WkUQAhIQCgxMSVRFX1JVTlRJTUUQAyoJCOgHEICAgIACSgQIJhAnIvIBCg5N",
-            "ZXNzYWdlT3B0aW9ucxImChdtZXNzYWdlX3NldF93aXJlX2Zvcm1hdBgBIAEo",
-            "CDoFZmFsc2USLgofbm9fc3RhbmRhcmRfZGVzY3JpcHRvcl9hY2Nlc3NvchgC",
-            "IAEoCDoFZmFsc2USGQoKZGVwcmVjYXRlZBgDIAEoCDoFZmFsc2USEQoJbWFw",
-            "X2VudHJ5GAcgASgIEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIk",
-            "Lmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQgICA",
-            "gAJKBAgIEAlKBAgJEAoingMKDEZpZWxkT3B0aW9ucxI6CgVjdHlwZRgBIAEo",
-            "DjIjLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnMuQ1R5cGU6BlNUUklO",
-            "RxIOCgZwYWNrZWQYAiABKAgSPwoGanN0eXBlGAYgASgOMiQuZ29vZ2xlLnBy",
-            "b3RvYnVmLkZpZWxkT3B0aW9ucy5KU1R5cGU6CUpTX05PUk1BTBITCgRsYXp5",
-            "GAUgASgIOgVmYWxzZRIZCgpkZXByZWNhdGVkGAMgASgIOgVmYWxzZRITCgR3",
-            "ZWFrGAogASgIOgVmYWxzZRJDChR1bmludGVycHJldGVkX29wdGlvbhjnByAD",
-            "KAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbiIvCgVD",
-            "VHlwZRIKCgZTVFJJTkcQABIICgRDT1JEEAESEAoMU1RSSU5HX1BJRUNFEAIi",
-            "NQoGSlNUeXBlEg0KCUpTX05PUk1BTBAAEg0KCUpTX1NUUklORxABEg0KCUpT",
-            "X05VTUJFUhACKgkI6AcQgICAgAJKBAgEEAUiXgoMT25lb2ZPcHRpb25zEkMK",
-            "FHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1",
-            "Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIikwEKC0VudW1PcHRp",
-            "b25zEhMKC2FsbG93X2FsaWFzGAIgASgIEhkKCmRlcHJlY2F0ZWQYAyABKAg6",
-            "BWZhbHNlEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2ds",
-            "ZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAJKBAgF",
-            "EAYifQoQRW51bVZhbHVlT3B0aW9ucxIZCgpkZXByZWNhdGVkGAEgASgIOgVm",
-            "YWxzZRJDChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUu",
-            "cHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbioJCOgHEICAgIACInsKDlNl",
-            "cnZpY2VPcHRpb25zEhkKCmRlcHJlY2F0ZWQYISABKAg6BWZhbHNlEkMKFHVu",
+            "ZWRfbmFtZRgKIAMoCRplCg5FeHRlbnNpb25SYW5nZRINCgVzdGFydBgBIAEo",
+            "BRILCgNlbmQYAiABKAUSNwoHb3B0aW9ucxgDIAEoCzImLmdvb2dsZS5wcm90",
+            "b2J1Zi5FeHRlbnNpb25SYW5nZU9wdGlvbnMaKwoNUmVzZXJ2ZWRSYW5nZRIN",
+            "CgVzdGFydBgBIAEoBRILCgNlbmQYAiABKAUiZwoVRXh0ZW5zaW9uUmFuZ2VP",
+            "cHRpb25zEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2ds",
+            "ZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIivAUK",
+            "FEZpZWxkRGVzY3JpcHRvclByb3RvEgwKBG5hbWUYASABKAkSDgoGbnVtYmVy",
+            "GAMgASgFEjoKBWxhYmVsGAQgASgOMisuZ29vZ2xlLnByb3RvYnVmLkZpZWxk",
+            "RGVzY3JpcHRvclByb3RvLkxhYmVsEjgKBHR5cGUYBSABKA4yKi5nb29nbGUu",
+            "cHJvdG9idWYuRmllbGREZXNjcmlwdG9yUHJvdG8uVHlwZRIRCgl0eXBlX25h",
+            "bWUYBiABKAkSEAoIZXh0ZW5kZWUYAiABKAkSFQoNZGVmYXVsdF92YWx1ZRgH",
+            "IAEoCRITCgtvbmVvZl9pbmRleBgJIAEoBRIRCglqc29uX25hbWUYCiABKAkS",
+            "LgoHb3B0aW9ucxgIIAEoCzIdLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlv",
+            "bnMitgIKBFR5cGUSDwoLVFlQRV9ET1VCTEUQARIOCgpUWVBFX0ZMT0FUEAIS",
+            "DgoKVFlQRV9JTlQ2NBADEg8KC1RZUEVfVUlOVDY0EAQSDgoKVFlQRV9JTlQz",
+            "MhAFEhAKDFRZUEVfRklYRUQ2NBAGEhAKDFRZUEVfRklYRUQzMhAHEg0KCVRZ",
+            "UEVfQk9PTBAIEg8KC1RZUEVfU1RSSU5HEAkSDgoKVFlQRV9HUk9VUBAKEhAK",
+            "DFRZUEVfTUVTU0FHRRALEg4KClRZUEVfQllURVMQDBIPCgtUWVBFX1VJTlQz",
+            "MhANEg0KCVRZUEVfRU5VTRAOEhEKDVRZUEVfU0ZJWEVEMzIQDxIRCg1UWVBF",
+            "X1NGSVhFRDY0EBASDwoLVFlQRV9TSU5UMzIQERIPCgtUWVBFX1NJTlQ2NBAS",
+            "IkMKBUxhYmVsEhIKDkxBQkVMX09QVElPTkFMEAESEgoOTEFCRUxfUkVRVUlS",
+            "RUQQAhISCg5MQUJFTF9SRVBFQVRFRBADIlQKFE9uZW9mRGVzY3JpcHRvclBy",
+            "b3RvEgwKBG5hbWUYASABKAkSLgoHb3B0aW9ucxgCIAEoCzIdLmdvb2dsZS5w",
+            "cm90b2J1Zi5PbmVvZk9wdGlvbnMijAEKE0VudW1EZXNjcmlwdG9yUHJvdG8S",
+            "DAoEbmFtZRgBIAEoCRI4CgV2YWx1ZRgCIAMoCzIpLmdvb2dsZS5wcm90b2J1",
+            "Zi5FbnVtVmFsdWVEZXNjcmlwdG9yUHJvdG8SLQoHb3B0aW9ucxgDIAEoCzIc",
+            "Lmdvb2dsZS5wcm90b2J1Zi5FbnVtT3B0aW9ucyJsChhFbnVtVmFsdWVEZXNj",
+            "cmlwdG9yUHJvdG8SDAoEbmFtZRgBIAEoCRIOCgZudW1iZXIYAiABKAUSMgoH",
+            "b3B0aW9ucxgDIAEoCzIhLmdvb2dsZS5wcm90b2J1Zi5FbnVtVmFsdWVPcHRp",
+            "b25zIpABChZTZXJ2aWNlRGVzY3JpcHRvclByb3RvEgwKBG5hbWUYASABKAkS",
+            "NgoGbWV0aG9kGAIgAygLMiYuZ29vZ2xlLnByb3RvYnVmLk1ldGhvZERlc2Ny",
+            "aXB0b3JQcm90bxIwCgdvcHRpb25zGAMgASgLMh8uZ29vZ2xlLnByb3RvYnVm",
+            "LlNlcnZpY2VPcHRpb25zIsEBChVNZXRob2REZXNjcmlwdG9yUHJvdG8SDAoE",
+            "bmFtZRgBIAEoCRISCgppbnB1dF90eXBlGAIgASgJEhMKC291dHB1dF90eXBl",
+            "GAMgASgJEi8KB29wdGlvbnMYBCABKAsyHi5nb29nbGUucHJvdG9idWYuTWV0",
+            "aG9kT3B0aW9ucxIfChBjbGllbnRfc3RyZWFtaW5nGAUgASgIOgVmYWxzZRIf",
+            "ChBzZXJ2ZXJfc3RyZWFtaW5nGAYgASgIOgVmYWxzZSLwBQoLRmlsZU9wdGlv",
+            "bnMSFAoMamF2YV9wYWNrYWdlGAEgASgJEhwKFGphdmFfb3V0ZXJfY2xhc3Nu",
+            "YW1lGAggASgJEiIKE2phdmFfbXVsdGlwbGVfZmlsZXMYCiABKAg6BWZhbHNl",
+            "EikKHWphdmFfZ2VuZXJhdGVfZXF1YWxzX2FuZF9oYXNoGBQgASgIQgIYARIl",
+            "ChZqYXZhX3N0cmluZ19jaGVja191dGY4GBsgASgIOgVmYWxzZRJGCgxvcHRp",
+            "bWl6ZV9mb3IYCSABKA4yKS5nb29nbGUucHJvdG9idWYuRmlsZU9wdGlvbnMu",
+            "T3B0aW1pemVNb2RlOgVTUEVFRBISCgpnb19wYWNrYWdlGAsgASgJEiIKE2Nj",
+            "X2dlbmVyaWNfc2VydmljZXMYECABKAg6BWZhbHNlEiQKFWphdmFfZ2VuZXJp",
+            "Y19zZXJ2aWNlcxgRIAEoCDoFZmFsc2USIgoTcHlfZ2VuZXJpY19zZXJ2aWNl",
+            "cxgSIAEoCDoFZmFsc2USIwoUcGhwX2dlbmVyaWNfc2VydmljZXMYEyABKAg6",
+            "BWZhbHNlEhkKCmRlcHJlY2F0ZWQYFyABKAg6BWZhbHNlEh8KEGNjX2VuYWJs",
+            "ZV9hcmVuYXMYHyABKAg6BWZhbHNlEhkKEW9iamNfY2xhc3NfcHJlZml4GCQg",
+            "ASgJEhgKEGNzaGFycF9uYW1lc3BhY2UYJSABKAkSFAoMc3dpZnRfcHJlZml4",
+            "GCcgASgJEhgKEHBocF9jbGFzc19wcmVmaXgYKCABKAkSFQoNcGhwX25hbWVz",
+            "cGFjZRgpIAEoCRJDChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5n",
+            "b29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbiI6CgxPcHRpbWl6",
+            "ZU1vZGUSCQoFU1BFRUQQARINCglDT0RFX1NJWkUQAhIQCgxMSVRFX1JVTlRJ",
+            "TUUQAyoJCOgHEICAgIACSgQIJhAnIvIBCg5NZXNzYWdlT3B0aW9ucxImChdt",
+            "ZXNzYWdlX3NldF93aXJlX2Zvcm1hdBgBIAEoCDoFZmFsc2USLgofbm9fc3Rh",
+            "bmRhcmRfZGVzY3JpcHRvcl9hY2Nlc3NvchgCIAEoCDoFZmFsc2USGQoKZGVw",
+            "cmVjYXRlZBgDIAEoCDoFZmFsc2USEQoJbWFwX2VudHJ5GAcgASgIEkMKFHVu",
             "aW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5V",
             "aW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5V",
-            "bmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIirQIKDU1ldGhvZE9wdGlv",
-            "bnMSGQoKZGVwcmVjYXRlZBghIAEoCDoFZmFsc2USXwoRaWRlbXBvdGVuY3lf",
-            "bGV2ZWwYIiABKA4yLy5nb29nbGUucHJvdG9idWYuTWV0aG9kT3B0aW9ucy5J",
-            "ZGVtcG90ZW5jeUxldmVsOhNJREVNUE9URU5DWV9VTktOT1dOEkMKFHVuaW50",
-            "ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5Vbmlu",
-            "dGVycHJldGVkT3B0aW9uIlAKEElkZW1wb3RlbmN5TGV2ZWwSFwoTSURFTVBP",
-            "VEVOQ1lfVU5LTk9XThAAEhMKD05PX1NJREVfRUZGRUNUUxABEg4KCklERU1Q",
-            "T1RFTlQQAioJCOgHEICAgIACIp4CChNVbmludGVycHJldGVkT3B0aW9uEjsK",
-            "BG5hbWUYAiADKAsyLS5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9w",
-            "dGlvbi5OYW1lUGFydBIYChBpZGVudGlmaWVyX3ZhbHVlGAMgASgJEhoKEnBv",
-            "c2l0aXZlX2ludF92YWx1ZRgEIAEoBBIaChJuZWdhdGl2ZV9pbnRfdmFsdWUY",
-            "BSABKAMSFAoMZG91YmxlX3ZhbHVlGAYgASgBEhQKDHN0cmluZ192YWx1ZRgH",
-            "IAEoDBIXCg9hZ2dyZWdhdGVfdmFsdWUYCCABKAkaMwoITmFtZVBhcnQSEQoJ",
-            "bmFtZV9wYXJ0GAEgAigJEhQKDGlzX2V4dGVuc2lvbhgCIAIoCCLVAQoOU291",
-            "cmNlQ29kZUluZm8SOgoIbG9jYXRpb24YASADKAsyKC5nb29nbGUucHJvdG9i",
-            "dWYuU291cmNlQ29kZUluZm8uTG9jYXRpb24ahgEKCExvY2F0aW9uEhAKBHBh",
-            "dGgYASADKAVCAhABEhAKBHNwYW4YAiADKAVCAhABEhgKEGxlYWRpbmdfY29t",
-            "bWVudHMYAyABKAkSGQoRdHJhaWxpbmdfY29tbWVudHMYBCABKAkSIQoZbGVh",
-            "ZGluZ19kZXRhY2hlZF9jb21tZW50cxgGIAMoCSKnAQoRR2VuZXJhdGVkQ29k",
-            "ZUluZm8SQQoKYW5ub3RhdGlvbhgBIAMoCzItLmdvb2dsZS5wcm90b2J1Zi5H",
-            "ZW5lcmF0ZWRDb2RlSW5mby5Bbm5vdGF0aW9uGk8KCkFubm90YXRpb24SEAoE",
-            "cGF0aBgBIAMoBUICEAESEwoLc291cmNlX2ZpbGUYAiABKAkSDQoFYmVnaW4Y",
-            "AyABKAUSCwoDZW5kGAQgASgFQowBChNjb20uZ29vZ2xlLnByb3RvYnVmQhBE",
-            "ZXNjcmlwdG9yUHJvdG9zSAFaPmdpdGh1Yi5jb20vZ29sYW5nL3Byb3RvYnVm",
-            "L3Byb3RvYy1nZW4tZ28vZGVzY3JpcHRvcjtkZXNjcmlwdG9yogIDR1BCqgIa",
-            "R29vZ2xlLlByb3RvYnVmLlJlZmxlY3Rpb24="));
+            "bmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAJKBAgIEAlKBAgJEAoingMK",
+            "DEZpZWxkT3B0aW9ucxI6CgVjdHlwZRgBIAEoDjIjLmdvb2dsZS5wcm90b2J1",
+            "Zi5GaWVsZE9wdGlvbnMuQ1R5cGU6BlNUUklORxIOCgZwYWNrZWQYAiABKAgS",
+            "PwoGanN0eXBlGAYgASgOMiQuZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9u",
+            "cy5KU1R5cGU6CUpTX05PUk1BTBITCgRsYXp5GAUgASgIOgVmYWxzZRIZCgpk",
+            "ZXByZWNhdGVkGAMgASgIOgVmYWxzZRITCgR3ZWFrGAogASgIOgVmYWxzZRJD",
+            "ChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9i",
+            "dWYuVW5pbnRlcnByZXRlZE9wdGlvbiIvCgVDVHlwZRIKCgZTVFJJTkcQABII",
+            "CgRDT1JEEAESEAoMU1RSSU5HX1BJRUNFEAIiNQoGSlNUeXBlEg0KCUpTX05P",
+            "Uk1BTBAAEg0KCUpTX1NUUklORxABEg0KCUpTX05VTUJFUhACKgkI6AcQgICA",
+            "gAJKBAgEEAUiXgoMT25lb2ZPcHRpb25zEkMKFHVuaW50ZXJwcmV0ZWRfb3B0",
+            "aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0",
+            "aW9uKgkI6AcQgICAgAIikwEKC0VudW1PcHRpb25zEhMKC2FsbG93X2FsaWFz",
+            "GAIgASgIEhkKCmRlcHJlY2F0ZWQYAyABKAg6BWZhbHNlEkMKFHVuaW50ZXJw",
+            "cmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVy",
+            "cHJldGVkT3B0aW9uKgkI6AcQgICAgAJKBAgFEAYifQoQRW51bVZhbHVlT3B0",
+            "aW9ucxIZCgpkZXByZWNhdGVkGAEgASgIOgVmYWxzZRJDChR1bmludGVycHJl",
+            "dGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnBy",
+            "ZXRlZE9wdGlvbioJCOgHEICAgIACInsKDlNlcnZpY2VPcHRpb25zEhkKCmRl",
+            "cHJlY2F0ZWQYISABKAg6BWZhbHNlEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9u",
+            "GOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9u",
+            "KgkI6AcQgICAgAIirQIKDU1ldGhvZE9wdGlvbnMSGQoKZGVwcmVjYXRlZBgh",
+            "IAEoCDoFZmFsc2USXwoRaWRlbXBvdGVuY3lfbGV2ZWwYIiABKA4yLy5nb29n",
+            "bGUucHJvdG9idWYuTWV0aG9kT3B0aW9ucy5JZGVtcG90ZW5jeUxldmVsOhNJ",
+            "REVNUE9URU5DWV9VTktOT1dOEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcH",
+            "IAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uIlAK",
+            "EElkZW1wb3RlbmN5TGV2ZWwSFwoTSURFTVBPVEVOQ1lfVU5LTk9XThAAEhMK",
+            "D05PX1NJREVfRUZGRUNUUxABEg4KCklERU1QT1RFTlQQAioJCOgHEICAgIAC",
+            "Ip4CChNVbmludGVycHJldGVkT3B0aW9uEjsKBG5hbWUYAiADKAsyLS5nb29n",
+            "bGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbi5OYW1lUGFydBIYChBp",
+            "ZGVudGlmaWVyX3ZhbHVlGAMgASgJEhoKEnBvc2l0aXZlX2ludF92YWx1ZRgE",
+            "IAEoBBIaChJuZWdhdGl2ZV9pbnRfdmFsdWUYBSABKAMSFAoMZG91YmxlX3Zh",
+            "bHVlGAYgASgBEhQKDHN0cmluZ192YWx1ZRgHIAEoDBIXCg9hZ2dyZWdhdGVf",
+            "dmFsdWUYCCABKAkaMwoITmFtZVBhcnQSEQoJbmFtZV9wYXJ0GAEgAigJEhQK",
+            "DGlzX2V4dGVuc2lvbhgCIAIoCCLVAQoOU291cmNlQ29kZUluZm8SOgoIbG9j",
+            "YXRpb24YASADKAsyKC5nb29nbGUucHJvdG9idWYuU291cmNlQ29kZUluZm8u",
+            "TG9jYXRpb24ahgEKCExvY2F0aW9uEhAKBHBhdGgYASADKAVCAhABEhAKBHNw",
+            "YW4YAiADKAVCAhABEhgKEGxlYWRpbmdfY29tbWVudHMYAyABKAkSGQoRdHJh",
+            "aWxpbmdfY29tbWVudHMYBCABKAkSIQoZbGVhZGluZ19kZXRhY2hlZF9jb21t",
+            "ZW50cxgGIAMoCSKnAQoRR2VuZXJhdGVkQ29kZUluZm8SQQoKYW5ub3RhdGlv",
+            "bhgBIAMoCzItLmdvb2dsZS5wcm90b2J1Zi5HZW5lcmF0ZWRDb2RlSW5mby5B",
+            "bm5vdGF0aW9uGk8KCkFubm90YXRpb24SEAoEcGF0aBgBIAMoBUICEAESEwoL",
+            "c291cmNlX2ZpbGUYAiABKAkSDQoFYmVnaW4YAyABKAUSCwoDZW5kGAQgASgF",
+            "QowBChNjb20uZ29vZ2xlLnByb3RvYnVmQhBEZXNjcmlwdG9yUHJvdG9zSAFa",
+            "PmdpdGh1Yi5jb20vZ29sYW5nL3Byb3RvYnVmL3Byb3RvYy1nZW4tZ28vZGVz",
+            "Y3JpcHRvcjtkZXNjcmlwdG9yogIDR1BCqgIaR29vZ2xlLlByb3RvYnVmLlJl",
+            "ZmxlY3Rpb24="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
           new pbr::FileDescriptor[] { },
           new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
           new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileDescriptorSet), global::Google.Protobuf.Reflection.FileDescriptorSet.Parser, new[]{ "File" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileDescriptorSet), global::Google.Protobuf.Reflection.FileDescriptorSet.Parser, new[]{ "File" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileDescriptorProto), global::Google.Protobuf.Reflection.FileDescriptorProto.Parser, new[]{ "Name", "Package", "Dependency", "PublicDependency", "WeakDependency", "MessageType", "EnumType", "Service", "Extension", "Options", "SourceCodeInfo", "Syntax" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileDescriptorProto), global::Google.Protobuf.Reflection.FileDescriptorProto.Parser, new[]{ "Name", "Package", "Dependency", "PublicDependency", "WeakDependency", "MessageType", "EnumType", "Service", "Extension", "Options", "SourceCodeInfo", "Syntax" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto), global::Google.Protobuf.Reflection.DescriptorProto.Parser, new[]{ "Name", "Field", "Extension", "NestedType", "EnumType", "ExtensionRange", "OneofDecl", "Options", "ReservedRange", "ReservedName" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto.Types.ExtensionRange), global::Google.Protobuf.Reflection.DescriptorProto.Types.ExtensionRange.Parser, new[]{ "Start", "End" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto), global::Google.Protobuf.Reflection.DescriptorProto.Parser, new[]{ "Name", "Field", "Extension", "NestedType", "EnumType", "ExtensionRange", "OneofDecl", "Options", "ReservedRange", "ReservedName" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto.Types.ExtensionRange), global::Google.Protobuf.Reflection.DescriptorProto.Types.ExtensionRange.Parser, new[]{ "Start", "End", "Options" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto.Types.ReservedRange), global::Google.Protobuf.Reflection.DescriptorProto.Types.ReservedRange.Parser, new[]{ "Start", "End" }, null, null, null)}),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto.Types.ReservedRange), global::Google.Protobuf.Reflection.DescriptorProto.Types.ReservedRange.Parser, new[]{ "Start", "End" }, null, null, null)}),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.ExtensionRangeOptions), global::Google.Protobuf.Reflection.ExtensionRangeOptions.Parser, new[]{ "UninterpretedOption" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto), global::Google.Protobuf.Reflection.FieldDescriptorProto.Parser, new[]{ "Name", "Number", "Label", "Type", "TypeName", "Extendee", "DefaultValue", "OneofIndex", "JsonName", "Options" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type), typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label) }, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto), global::Google.Protobuf.Reflection.FieldDescriptorProto.Parser, new[]{ "Name", "Number", "Label", "Type", "TypeName", "Extendee", "DefaultValue", "OneofIndex", "JsonName", "Options" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type), typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label) }, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.OneofDescriptorProto), global::Google.Protobuf.Reflection.OneofDescriptorProto.Parser, new[]{ "Name", "Options" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.OneofDescriptorProto), global::Google.Protobuf.Reflection.OneofDescriptorProto.Parser, new[]{ "Name", "Options" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumDescriptorProto), global::Google.Protobuf.Reflection.EnumDescriptorProto.Parser, new[]{ "Name", "Value", "Options" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumDescriptorProto), global::Google.Protobuf.Reflection.EnumDescriptorProto.Parser, new[]{ "Name", "Value", "Options" }, null, null, null),
@@ -1052,6 +1057,7 @@ namespace Google.Protobuf.Reflection {
         public ExtensionRange(ExtensionRange other) : this() {
         public ExtensionRange(ExtensionRange other) : this() {
           start_ = other.start_;
           start_ = other.start_;
           end_ = other.end_;
           end_ = other.end_;
+          Options = other.options_ != null ? other.Options.Clone() : null;
         }
         }
 
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1081,6 +1087,17 @@ namespace Google.Protobuf.Reflection {
           }
           }
         }
         }
 
 
+        /// <summary>Field number for the "options" field.</summary>
+        public const int OptionsFieldNumber = 3;
+        private global::Google.Protobuf.Reflection.ExtensionRangeOptions options_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        public global::Google.Protobuf.Reflection.ExtensionRangeOptions Options {
+          get { return options_; }
+          set {
+            options_ = value;
+          }
+        }
+
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public override bool Equals(object other) {
         public override bool Equals(object other) {
           return Equals(other as ExtensionRange);
           return Equals(other as ExtensionRange);
@@ -1096,6 +1113,7 @@ namespace Google.Protobuf.Reflection {
           }
           }
           if (Start != other.Start) return false;
           if (Start != other.Start) return false;
           if (End != other.End) return false;
           if (End != other.End) return false;
+          if (!object.Equals(Options, other.Options)) return false;
           return true;
           return true;
         }
         }
 
 
@@ -1104,6 +1122,7 @@ namespace Google.Protobuf.Reflection {
           int hash = 1;
           int hash = 1;
           if (Start != 0) hash ^= Start.GetHashCode();
           if (Start != 0) hash ^= Start.GetHashCode();
           if (End != 0) hash ^= End.GetHashCode();
           if (End != 0) hash ^= End.GetHashCode();
+          if (options_ != null) hash ^= Options.GetHashCode();
           return hash;
           return hash;
         }
         }
 
 
@@ -1122,6 +1141,10 @@ namespace Google.Protobuf.Reflection {
             output.WriteRawTag(16);
             output.WriteRawTag(16);
             output.WriteInt32(End);
             output.WriteInt32(End);
           }
           }
+          if (options_ != null) {
+            output.WriteRawTag(26);
+            output.WriteMessage(Options);
+          }
         }
         }
 
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1133,6 +1156,9 @@ namespace Google.Protobuf.Reflection {
           if (End != 0) {
           if (End != 0) {
             size += 1 + pb::CodedOutputStream.ComputeInt32Size(End);
             size += 1 + pb::CodedOutputStream.ComputeInt32Size(End);
           }
           }
+          if (options_ != null) {
+            size += 1 + pb::CodedOutputStream.ComputeMessageSize(Options);
+          }
           return size;
           return size;
         }
         }
 
 
@@ -1147,6 +1173,12 @@ namespace Google.Protobuf.Reflection {
           if (other.End != 0) {
           if (other.End != 0) {
             End = other.End;
             End = other.End;
           }
           }
+          if (other.options_ != null) {
+            if (options_ == null) {
+              options_ = new global::Google.Protobuf.Reflection.ExtensionRangeOptions();
+            }
+            Options.MergeFrom(other.Options);
+          }
         }
         }
 
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1165,6 +1197,13 @@ namespace Google.Protobuf.Reflection {
                 End = input.ReadInt32();
                 End = input.ReadInt32();
                 break;
                 break;
               }
               }
+              case 26: {
+                if (options_ == null) {
+                  options_ = new global::Google.Protobuf.Reflection.ExtensionRangeOptions();
+                }
+                input.ReadMessage(options_);
+                break;
+              }
             }
             }
           }
           }
         }
         }
@@ -1332,6 +1371,118 @@ namespace Google.Protobuf.Reflection {
 
 
   }
   }
 
 
+  internal sealed partial class ExtensionRangeOptions : pb::IMessage<ExtensionRangeOptions> {
+    private static readonly pb::MessageParser<ExtensionRangeOptions> _parser = new pb::MessageParser<ExtensionRangeOptions>(() => new ExtensionRangeOptions());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<ExtensionRangeOptions> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ExtensionRangeOptions() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ExtensionRangeOptions(ExtensionRangeOptions other) : this() {
+      uninterpretedOption_ = other.uninterpretedOption_.Clone();
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ExtensionRangeOptions Clone() {
+      return new ExtensionRangeOptions(this);
+    }
+
+    /// <summary>Field number for the "uninterpreted_option" field.</summary>
+    public const int UninterpretedOptionFieldNumber = 999;
+    private static readonly pb::FieldCodec<global::Google.Protobuf.Reflection.UninterpretedOption> _repeated_uninterpretedOption_codec
+        = pb::FieldCodec.ForMessage(7994, global::Google.Protobuf.Reflection.UninterpretedOption.Parser);
+    private readonly pbc::RepeatedField<global::Google.Protobuf.Reflection.UninterpretedOption> uninterpretedOption_ = new pbc::RepeatedField<global::Google.Protobuf.Reflection.UninterpretedOption>();
+    /// <summary>
+    /// The parser stores options it doesn't recognize here. See above.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public pbc::RepeatedField<global::Google.Protobuf.Reflection.UninterpretedOption> UninterpretedOption {
+      get { return uninterpretedOption_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as ExtensionRangeOptions);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(ExtensionRangeOptions other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if(!uninterpretedOption_.Equals(other.uninterpretedOption_)) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      hash ^= uninterpretedOption_.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      uninterpretedOption_.WriteTo(output, _repeated_uninterpretedOption_codec);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      size += uninterpretedOption_.CalculateSize(_repeated_uninterpretedOption_codec);
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(ExtensionRangeOptions other) {
+      if (other == null) {
+        return;
+      }
+      uninterpretedOption_.Add(other.uninterpretedOption_);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 7994: {
+            uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
   /// <summary>
   /// <summary>
   /// Describes a field within a message.
   /// Describes a field within a message.
   /// </summary>
   /// </summary>
@@ -1342,7 +1493,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[3]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[4]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1819,7 +1970,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[4]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[5]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1973,7 +2124,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[5]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[6]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2147,7 +2298,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[6]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[7]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2329,7 +2480,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[7]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[8]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2503,7 +2654,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[8]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[9]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2776,7 +2927,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[9]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[10]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -3503,7 +3654,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[10]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[11]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -3783,7 +3934,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[11]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[12]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -3857,13 +4008,15 @@ namespace Google.Protobuf.Reflection {
     /// <summary>
     /// <summary>
     /// The jstype option determines the JavaScript type used for values of the
     /// The jstype option determines the JavaScript type used for values of the
     /// field.  The option is permitted only for 64 bit integral and fixed types
     /// field.  The option is permitted only for 64 bit integral and fixed types
-    /// (int64, uint64, sint64, fixed64, sfixed64).  By default these types are
-    /// represented as JavaScript strings.  This avoids loss of precision that can
-    /// happen when a large value is converted to a floating point JavaScript
-    /// numbers.  Specifying JS_NUMBER for the jstype causes the generated
-    /// JavaScript code to use the JavaScript "number" type instead of strings.
-    /// This option is an enum to permit additional types to be added,
-    /// e.g. goog.math.Integer.
+    /// (int64, uint64, sint64, fixed64, sfixed64).  A field with jstype JS_STRING
+    /// is represented as JavaScript string, which avoids loss of precision that
+    /// can happen when a large value is converted to a floating point JavaScript.
+    /// Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
+    /// use the JavaScript "number" type.  The behavior of the default option
+    /// JS_NORMAL is implementation dependent.
+    ///
+    /// This option is an enum to permit additional types to be added, e.g.
+    /// goog.math.Integer.
     /// </summary>
     /// </summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::Google.Protobuf.Reflection.FieldOptions.Types.JSType Jstype {
     public global::Google.Protobuf.Reflection.FieldOptions.Types.JSType Jstype {
@@ -4158,7 +4311,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[12]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[13]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -4272,7 +4425,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[13]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[14]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -4452,7 +4605,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[14]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[15]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -4600,7 +4753,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[15]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[16]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -4748,7 +4901,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[16]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[17]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -4956,7 +5109,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[17]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[18]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -5400,7 +5553,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[18]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[19]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -5854,7 +6007,7 @@ namespace Google.Protobuf.Reflection {
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pbr::MessageDescriptor Descriptor {
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[19]; }
+      get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[20]; }
     }
     }
 
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]

+ 10 - 0
csharp/src/Google.Protobuf/WellKnownTypes/Any.cs

@@ -73,6 +73,16 @@ namespace Google.Protobuf.WellKnownTypes {
   ///       any.Unpack(foo)
   ///       any.Unpack(foo)
   ///       ...
   ///       ...
   ///
   ///
+  ///  Example 4: Pack and unpack a message in Go
+  ///
+  ///      foo := &amp;pb.Foo{...}
+  ///      any, err := ptypes.MarshalAny(foo)
+  ///      ...
+  ///      foo := &amp;pb.Foo{}
+  ///      if err := ptypes.UnmarshalAny(any, foo); err != nil {
+  ///        ...
+  ///      }
+  ///
   /// The pack methods provided by protobuf library will by default use
   /// The pack methods provided by protobuf library will by default use
   /// 'type.googleapis.com/full.type.name' as the type URL and the unpack
   /// 'type.googleapis.com/full.type.name' as the type URL and the unpack
   /// methods only use the fully qualified type name after the last '/'
   /// methods only use the fully qualified type name after the last '/'

+ 29 - 21
csharp/src/Google.Protobuf/WellKnownTypes/Api.cs

@@ -52,7 +52,15 @@ namespace Google.Protobuf.WellKnownTypes {
   }
   }
   #region Messages
   #region Messages
   /// <summary>
   /// <summary>
-  /// Api is a light-weight descriptor for a protocol buffer service.
+  /// Api is a light-weight descriptor for an API Interface.
+  ///
+  /// Interfaces are also described as "protocol buffer services" in some contexts,
+  /// such as by the "service" keyword in a .proto file, but they are different
+  /// from API Services, which represent a concrete implementation of an interface
+  /// as opposed to simply a description of methods and bindings. They are also
+  /// sometimes simply referred to as "APIs" in other contexts, such as the name of
+  /// this message itself. See https://cloud.google.com/apis/design/glossary for
+  /// detailed terminology.
   /// </summary>
   /// </summary>
   public sealed partial class Api : pb::IMessage<Api> {
   public sealed partial class Api : pb::IMessage<Api> {
     private static readonly pb::MessageParser<Api> _parser = new pb::MessageParser<Api>(() => new Api());
     private static readonly pb::MessageParser<Api> _parser = new pb::MessageParser<Api>(() => new Api());
@@ -96,8 +104,8 @@ namespace Google.Protobuf.WellKnownTypes {
     public const int NameFieldNumber = 1;
     public const int NameFieldNumber = 1;
     private string name_ = "";
     private string name_ = "";
     /// <summary>
     /// <summary>
-    /// The fully qualified name of this api, including package name
-    /// followed by the api's simple name.
+    /// The fully qualified name of this interface, including package name
+    /// followed by the interface's simple name.
     /// </summary>
     /// </summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public string Name {
     public string Name {
@@ -113,7 +121,7 @@ namespace Google.Protobuf.WellKnownTypes {
         = pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Method.Parser);
         = pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Method.Parser);
     private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Method> methods_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Method>();
     private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Method> methods_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Method>();
     /// <summary>
     /// <summary>
-    /// The methods of this api, in unspecified order.
+    /// The methods of this interface, in unspecified order.
     /// </summary>
     /// </summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Method> Methods {
     public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Method> Methods {
@@ -126,7 +134,7 @@ namespace Google.Protobuf.WellKnownTypes {
         = pb::FieldCodec.ForMessage(26, global::Google.Protobuf.WellKnownTypes.Option.Parser);
         = pb::FieldCodec.ForMessage(26, global::Google.Protobuf.WellKnownTypes.Option.Parser);
     private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> options_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option>();
     private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> options_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option>();
     /// <summary>
     /// <summary>
-    /// Any metadata attached to the API.
+    /// Any metadata attached to the interface.
     /// </summary>
     /// </summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> Options {
     public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> Options {
@@ -137,13 +145,12 @@ namespace Google.Protobuf.WellKnownTypes {
     public const int VersionFieldNumber = 4;
     public const int VersionFieldNumber = 4;
     private string version_ = "";
     private string version_ = "";
     /// <summary>
     /// <summary>
-    /// A version string for this api. If specified, must have the form
-    /// `major-version.minor-version`, as in `1.10`. If the minor version
-    /// is omitted, it defaults to zero. If the entire version field is
-    /// empty, the major version is derived from the package name, as
-    /// outlined below. If the field is not empty, the version in the
-    /// package name will be verified to be consistent with what is
-    /// provided here.
+    /// A version string for this interface. If specified, must have the form
+    /// `major-version.minor-version`, as in `1.10`. If the minor version is
+    /// omitted, it defaults to zero. If the entire version field is empty, the
+    /// major version is derived from the package name, as outlined below. If the
+    /// field is not empty, the version in the package name will be verified to be
+    /// consistent with what is provided here.
     ///
     ///
     /// The versioning schema uses [semantic
     /// The versioning schema uses [semantic
     /// versioning](http://semver.org) where the major version number
     /// versioning](http://semver.org) where the major version number
@@ -153,10 +160,10 @@ namespace Google.Protobuf.WellKnownTypes {
     /// chosen based on the product plan.
     /// chosen based on the product plan.
     ///
     ///
     /// The major version is also reflected in the package name of the
     /// The major version is also reflected in the package name of the
-    /// API, which must end in `v&lt;major-version>`, as in
+    /// interface, which must end in `v&lt;major-version>`, as in
     /// `google.feature.v1`. For major versions 0 and 1, the suffix can
     /// `google.feature.v1`. For major versions 0 and 1, the suffix can
     /// be omitted. Zero major versions must only be used for
     /// be omitted. Zero major versions must only be used for
-    /// experimental, none-GA apis.
+    /// experimental, non-GA interfaces.
     /// </summary>
     /// </summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public string Version {
     public string Version {
@@ -187,7 +194,7 @@ namespace Google.Protobuf.WellKnownTypes {
         = pb::FieldCodec.ForMessage(50, global::Google.Protobuf.WellKnownTypes.Mixin.Parser);
         = pb::FieldCodec.ForMessage(50, global::Google.Protobuf.WellKnownTypes.Mixin.Parser);
     private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Mixin> mixins_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Mixin>();
     private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Mixin> mixins_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Mixin>();
     /// <summary>
     /// <summary>
-    /// Included APIs. See [Mixin][].
+    /// Included interfaces. See [Mixin][].
     /// </summary>
     /// </summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Mixin> Mixins {
     public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Mixin> Mixins {
@@ -364,7 +371,7 @@ namespace Google.Protobuf.WellKnownTypes {
   }
   }
 
 
   /// <summary>
   /// <summary>
-  /// Method represents a method of an api.
+  /// Method represents a method of an API interface.
   /// </summary>
   /// </summary>
   public sealed partial class Method : pb::IMessage<Method> {
   public sealed partial class Method : pb::IMessage<Method> {
     private static readonly pb::MessageParser<Method> _parser = new pb::MessageParser<Method>(() => new Method());
     private static readonly pb::MessageParser<Method> _parser = new pb::MessageParser<Method>(() => new Method());
@@ -665,9 +672,9 @@ namespace Google.Protobuf.WellKnownTypes {
   }
   }
 
 
   /// <summary>
   /// <summary>
-  /// Declares an API to be included in this API. The including API must
-  /// redeclare all the methods from the included API, but documentation
-  /// and options are inherited as follows:
+  /// Declares an API Interface to be included in this interface. The including
+  /// interface must redeclare all the methods from the included interface, but
+  /// documentation and options are inherited as follows:
   ///
   ///
   /// - If after comment and whitespace stripping, the documentation
   /// - If after comment and whitespace stripping, the documentation
   ///   string of the redeclared method is empty, it will be inherited
   ///   string of the redeclared method is empty, it will be inherited
@@ -679,7 +686,8 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   ///
   /// - If an http annotation is inherited, the path pattern will be
   /// - If an http annotation is inherited, the path pattern will be
   ///   modified as follows. Any version prefix will be replaced by the
   ///   modified as follows. Any version prefix will be replaced by the
-  ///   version of the including API plus the [root][] path if specified.
+  ///   version of the including interface plus the [root][] path if
+  ///   specified.
   ///
   ///
   /// Example of a simple mixin:
   /// Example of a simple mixin:
   ///
   ///
@@ -780,7 +788,7 @@ namespace Google.Protobuf.WellKnownTypes {
     public const int NameFieldNumber = 1;
     public const int NameFieldNumber = 1;
     private string name_ = "";
     private string name_ = "";
     /// <summary>
     /// <summary>
-    /// The fully qualified name of the API which is included.
+    /// The fully qualified name of the interface which is included.
     /// </summary>
     /// </summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public string Name {
     public string Name {

+ 26 - 9
generate_descriptor_proto.sh

@@ -42,12 +42,23 @@ declare -a RUNTIME_PROTO_FILES=(\
   google/protobuf/wrappers.proto)
   google/protobuf/wrappers.proto)
 
 
 declare -a COMPILER_PROTO_FILES=(\
 declare -a COMPILER_PROTO_FILES=(\
-  google/protobuf/compiler/plugin.proto \
-  google/protobuf/compiler/profile.proto \
-)
+  google/protobuf/compiler/plugin.proto)
 
 
 CORE_PROTO_IS_CORRECT=0
 CORE_PROTO_IS_CORRECT=0
 PROCESS_ROUND=1
 PROCESS_ROUND=1
+BOOTSTRAP_PROTOC=""
+while [ $# -gt 0 ]; do
+  case $1 in
+    --bootstrap_protoc)
+      BOOTSTRAP_PROTOC=$2
+      shift
+      ;;
+    *)
+      break
+      ;;
+  esac
+  shift
+done
 TMP=$(mktemp -d)
 TMP=$(mktemp -d)
 echo "Updating descriptor protos..."
 echo "Updating descriptor protos..."
 while [ $CORE_PROTO_IS_CORRECT -ne 1 ]
 while [ $CORE_PROTO_IS_CORRECT -ne 1 ]
@@ -55,14 +66,20 @@ do
   echo "Round $PROCESS_ROUND"
   echo "Round $PROCESS_ROUND"
   CORE_PROTO_IS_CORRECT=1
   CORE_PROTO_IS_CORRECT=1
 
 
-  make $@ protoc
-  if test $? -ne 0; then
-    echo "Failed to build protoc."
-    exit 1
+  if [ "$BOOTSTRAP_PROTOC" != "" ]; then
+    PROTOC=$BOOTSTRAP_PROTOC
+    BOOTSTRAP_PROTOC=""
+  else
+    make $@ protoc
+    if test $? -ne 0; then
+      echo "Failed to build protoc."
+      exit 1
+    fi
+    PROTOC="./protoc"
   fi
   fi
 
 
-  ./protoc --cpp_out=dllexport_decl=LIBPROTOBUF_EXPORT:$TMP ${RUNTIME_PROTO_FILES[@]} && \
-  ./protoc --cpp_out=dllexport_decl=LIBPROTOC_EXPORT:$TMP ${COMPILER_PROTO_FILES[@]}
+  $PROTOC --cpp_out=dllexport_decl=LIBPROTOBUF_EXPORT:$TMP ${RUNTIME_PROTO_FILES[@]} && \
+  $PROTOC --cpp_out=dllexport_decl=LIBPROTOC_EXPORT:$TMP ${COMPILER_PROTO_FILES[@]}
 
 
   for PROTO_FILE in ${RUNTIME_PROTO_FILES[@]} ${COMPILER_PROTO_FILES[@]}; do
   for PROTO_FILE in ${RUNTIME_PROTO_FILES[@]} ${COMPILER_PROTO_FILES[@]}; do
     BASE_NAME=${PROTO_FILE%.*}
     BASE_NAME=${PROTO_FILE%.*}

+ 2 - 1
java/core/generate-test-sources-build.xml

@@ -5,6 +5,7 @@
         <arg value="--proto_path=${protobuf.source.dir}"/>
         <arg value="--proto_path=${protobuf.source.dir}"/>
         <arg value="--proto_path=${test.proto.dir}"/>
         <arg value="--proto_path=${test.proto.dir}"/>
         <arg value="${protobuf.source.dir}/google/protobuf/unittest.proto"/>
         <arg value="${protobuf.source.dir}/google/protobuf/unittest.proto"/>
+        <arg value="${protobuf.source.dir}/google/protobuf/unittest_proto3.proto"/>
         <arg value="${protobuf.source.dir}/google/protobuf/unittest_import.proto"/>
         <arg value="${protobuf.source.dir}/google/protobuf/unittest_import.proto"/>
         <arg value="${protobuf.source.dir}/google/protobuf/unittest_import_public.proto"/>
         <arg value="${protobuf.source.dir}/google/protobuf/unittest_import_public.proto"/>
         <arg value="${protobuf.source.dir}/google/protobuf/unittest_mset.proto"/>
         <arg value="${protobuf.source.dir}/google/protobuf/unittest_mset.proto"/>
@@ -40,4 +41,4 @@
         <arg value="${test.proto.dir}/com/google/protobuf/map_test.proto"/>
         <arg value="${test.proto.dir}/com/google/protobuf/map_test.proto"/>
         <arg value="${test.proto.dir}/com/google/protobuf/map_initialization_order_test.proto"/>
         <arg value="${test.proto.dir}/com/google/protobuf/map_initialization_order_test.proto"/>
     </exec>
     </exec>
-</project>
+</project>

+ 1 - 1
java/core/pom.xml

@@ -6,7 +6,7 @@
   <parent>
   <parent>
     <groupId>com.google.protobuf</groupId>
     <groupId>com.google.protobuf</groupId>
     <artifactId>protobuf-parent</artifactId>
     <artifactId>protobuf-parent</artifactId>
-    <version>3.3.2</version>
+    <version>3.4.0</version>
   </parent>
   </parent>
 
 
   <artifactId>protobuf-java</artifactId>
   <artifactId>protobuf-java</artifactId>

+ 17 - 10
java/core/src/main/java/com/google/protobuf/AbstractMessage.java

@@ -32,6 +32,7 @@ package com.google.protobuf;
 
 
 import com.google.protobuf.Descriptors.EnumValueDescriptor;
 import com.google.protobuf.Descriptors.EnumValueDescriptor;
 import com.google.protobuf.Descriptors.FieldDescriptor;
 import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.Descriptors.FileDescriptor.Syntax;
 import com.google.protobuf.Descriptors.OneofDescriptor;
 import com.google.protobuf.Descriptors.OneofDescriptor;
 import com.google.protobuf.Internal.EnumLite;
 import com.google.protobuf.Internal.EnumLite;
 import java.io.IOException;
 import java.io.IOException;
@@ -162,7 +163,7 @@ public abstract class AbstractMessage
     }
     }
     return hash;
     return hash;
   }
   }
-  
+
   private static ByteString toByteString(Object value) {
   private static ByteString toByteString(Object value) {
     if (value instanceof byte[]) {
     if (value instanceof byte[]) {
       return ByteString.copyFrom((byte[]) value);
       return ByteString.copyFrom((byte[]) value);
@@ -170,7 +171,7 @@ public abstract class AbstractMessage
       return (ByteString) value;
       return (ByteString) value;
     }
     }
   }
   }
- 
+
   /**
   /**
    * Compares two bytes fields. The parameters must be either a byte array or a
    * Compares two bytes fields. The parameters must be either a byte array or a
    * ByteString object. They can be of different type though.
    * ByteString object. They can be of different type though.
@@ -181,7 +182,7 @@ public abstract class AbstractMessage
     }
     }
     return toByteString(a).equals(toByteString(b));
     return toByteString(a).equals(toByteString(b));
   }
   }
-  
+
   /**
   /**
    * Converts a list of MapEntry messages into a Map used for equals() and
    * Converts a list of MapEntry messages into a Map used for equals() and
    * hashCode().
    * hashCode().
@@ -212,7 +213,7 @@ public abstract class AbstractMessage
     }
     }
     return result;
     return result;
   }
   }
-  
+
   /**
   /**
    * Compares two map fields. The parameters must be a list of MapEntry
    * Compares two map fields. The parameters must be a list of MapEntry
    * messages.
    * messages.
@@ -223,13 +224,13 @@ public abstract class AbstractMessage
     Map mb = convertMapEntryListToMap((List) b);
     Map mb = convertMapEntryListToMap((List) b);
     return MapFieldLite.equals(ma, mb);
     return MapFieldLite.equals(ma, mb);
   }
   }
-  
+
   /**
   /**
    * Compares two set of fields.
    * Compares two set of fields.
    * This method is used to implement {@link AbstractMessage#equals(Object)}
    * This method is used to implement {@link AbstractMessage#equals(Object)}
    * and {@link AbstractMutableMessage#equals(Object)}. It takes special care
    * and {@link AbstractMutableMessage#equals(Object)}. It takes special care
    * of bytes fields because immutable messages and mutable messages use
    * of bytes fields because immutable messages and mutable messages use
-   * different Java type to reprensent a bytes field and this method should be
+   * different Java type to represent a bytes field and this method should be
    * able to compare immutable messages, mutable messages and also an immutable
    * able to compare immutable messages, mutable messages and also an immutable
    * message to a mutable message.
    * message to a mutable message.
    */
    */
@@ -275,7 +276,7 @@ public abstract class AbstractMessage
     }
     }
     return true;
     return true;
   }
   }
-  
+
   /**
   /**
    * Calculates the hash code of a map field. {@code value} must be a list of
    * Calculates the hash code of a map field. {@code value} must be a list of
    * MapEntry messages.
    * MapEntry messages.
@@ -371,7 +372,7 @@ public abstract class AbstractMessage
     public String getInitializationErrorString() {
     public String getInitializationErrorString() {
       return MessageReflection.delimitWithCommas(findInitializationErrors());
       return MessageReflection.delimitWithCommas(findInitializationErrors());
     }
     }
-    
+
     @Override
     @Override
     protected BuilderType internalMergeFrom(AbstractMessageLite other) {
     protected BuilderType internalMergeFrom(AbstractMessageLite other) {
       return mergeFrom((Message) other);
       return mergeFrom((Message) other);
@@ -432,8 +433,12 @@ public abstract class AbstractMessage
         final CodedInputStream input,
         final CodedInputStream input,
         final ExtensionRegistryLite extensionRegistry)
         final ExtensionRegistryLite extensionRegistry)
         throws IOException {
         throws IOException {
+      boolean discardUnknown =
+          getDescriptorForType().getFile().getSyntax() == Syntax.PROTO3
+              ? input.shouldDiscardUnknownFieldsProto3()
+              : input.shouldDiscardUnknownFields();
       final UnknownFieldSet.Builder unknownFields =
       final UnknownFieldSet.Builder unknownFields =
-        UnknownFieldSet.newBuilder(getUnknownFields());
+          discardUnknown ? null : UnknownFieldSet.newBuilder(getUnknownFields());
       while (true) {
       while (true) {
         final int tag = input.readTag();
         final int tag = input.readTag();
         if (tag == 0) {
         if (tag == 0) {
@@ -451,7 +456,9 @@ public abstract class AbstractMessage
           break;
           break;
         }
         }
       }
       }
-      setUnknownFields(unknownFields.build());
+      if (unknownFields != null) {
+        setUnknownFields(unknownFields.build());
+      }
       return (BuilderType) this;
       return (BuilderType) this;
     }
     }
 
 

+ 60 - 24
java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java

@@ -36,7 +36,9 @@ import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collection;
+import java.util.List;
 
 
 /**
 /**
  * A partial implementation of the {@link MessageLite} interface which
  * A partial implementation of the {@link MessageLite} interface which
@@ -118,8 +120,13 @@ public abstract class AbstractMessageLite<
     }
     }
   }
   }
 
 
-  protected static <T> void addAll(final Iterable<T> values,
-      final Collection<? super T> list) {
+  // For binary compatibility
+  @Deprecated
+  protected static <T> void addAll(final Iterable<T> values, final Collection<? super T> list) {
+    Builder.addAll(values, (List) list);
+  }
+
+  protected static <T> void addAll(final Iterable<T> values, final List<? super T> list) {
     Builder.addAll(values, list);
     Builder.addAll(values, list);
   }
   }
 
 
@@ -334,6 +341,25 @@ public abstract class AbstractMessageLite<
           + " threw an IOException (should never happen).";
           + " threw an IOException (should never happen).";
     }
     }
 
 
+    // We check nulls as we iterate to avoid iterating over values twice.
+    private static <T> void addAllCheckingNulls(Iterable<T> values, List<? super T> list) {
+      if (list instanceof ArrayList && values instanceof Collection) {
+        ((ArrayList<T>) list).ensureCapacity(list.size() + ((Collection<T>) values).size());
+      }
+      int begin = list.size();
+      for (T value : values) {
+        if (value == null) {
+          // encountered a null value so we must undo our modifications prior to throwing
+          String message = "Element at index " + (list.size() - begin) + " is null.";
+          for (int i = list.size() - 1; i >= begin; i--) {
+            list.remove(i);
+          }
+          throw new NullPointerException(message);
+        }
+        list.add(value);
+      }
+    }
+
     /**
     /**
      * Construct an UninitializedMessageException reporting missing fields in
      * Construct an UninitializedMessageException reporting missing fields in
      * the given message.
      * the given message.
@@ -343,16 +369,20 @@ public abstract class AbstractMessageLite<
       return new UninitializedMessageException(message);
       return new UninitializedMessageException(message);
     }
     }
 
 
+    // For binary compatibility.
+    @Deprecated
+    protected static <T> void addAll(final Iterable<T> values, final Collection<? super T> list) {
+      addAll(values, (List<T>) list);
+    }
+
     /**
     /**
-     * Adds the {@code values} to the {@code list}.  This is a helper method
-     * used by generated code.  Users should ignore it.
+     * Adds the {@code values} to the {@code list}. This is a helper method used by generated code.
+     * Users should ignore it.
      *
      *
-     * @throws NullPointerException if {@code values} or any of the elements of
-     * {@code values} is null. When that happens, some elements of
-     * {@code values} may have already been added to the result {@code list}.
+     * @throws NullPointerException if {@code values} or any of the elements of {@code values} is
+     *     null.
      */
      */
-    protected static <T> void addAll(final Iterable<T> values,
-                                     final Collection<? super T> list) {
+    protected static <T> void addAll(final Iterable<T> values, final List<? super T> list) {
       checkNotNull(values);
       checkNotNull(values);
       if (values instanceof LazyStringList) {
       if (values instanceof LazyStringList) {
         // For StringOrByteStringLists, check the underlying elements to avoid
         // For StringOrByteStringLists, check the underlying elements to avoid
@@ -360,25 +390,31 @@ public abstract class AbstractMessageLite<
         // TODO(dweis): Could we just prohibit nulls in all protobuf lists and get rid of this? Is
         // TODO(dweis): Could we just prohibit nulls in all protobuf lists and get rid of this? Is
         // if even possible to hit this condition as all protobuf methods check for null first,
         // if even possible to hit this condition as all protobuf methods check for null first,
         // right?
         // right?
-        checkForNullValues(((LazyStringList) values).getUnderlyingElements());
-        list.addAll((Collection<T>) values);
-      } else if (values instanceof Collection) {
-        if (!(values instanceof PrimitiveNonBoxingCollection)) {
-          checkForNullValues(values);
+        List<?> lazyValues = ((LazyStringList) values).getUnderlyingElements();
+        LazyStringList lazyList = (LazyStringList) list;
+        int begin = list.size();
+        for (Object value : lazyValues) {
+          if (value == null) {
+            // encountered a null value so we must undo our modifications prior to throwing
+            String message = "Element at index " + (lazyList.size() - begin) + " is null.";
+            for (int i = lazyList.size() - 1; i >= begin; i--) {
+              lazyList.remove(i);
+            }
+            throw new NullPointerException(message);
+          }
+          if (value instanceof ByteString) {
+            lazyList.add((ByteString) value);
+          } else {
+            lazyList.add((String) value);
+          }
         }
         }
-        list.addAll((Collection<T>) values);
       } else {
       } else {
-        for (final T value : values) {
-          checkNotNull(value);
-          list.add(value);
+        if (values instanceof PrimitiveNonBoxingCollection) {
+          list.addAll((Collection<T>) values);
+        } else {
+          addAllCheckingNulls(values, list);
         }
         }
       }
       }
     }
     }
-
-    private static void checkForNullValues(final Iterable<?> values) {
-      for (final Object value : values) {
-        checkNotNull(value);
-      }
-    }
   }
   }
 }
 }

+ 214 - 85
java/core/src/main/java/com/google/protobuf/CodedInputStream.java

@@ -34,8 +34,8 @@ import static com.google.protobuf.Internal.EMPTY_BYTE_ARRAY;
 import static com.google.protobuf.Internal.EMPTY_BYTE_BUFFER;
 import static com.google.protobuf.Internal.EMPTY_BYTE_BUFFER;
 import static com.google.protobuf.Internal.UTF_8;
 import static com.google.protobuf.Internal.UTF_8;
 import static com.google.protobuf.Internal.checkNotNull;
 import static com.google.protobuf.Internal.checkNotNull;
-import static com.google.protobuf.WireFormat.FIXED_32_SIZE;
-import static com.google.protobuf.WireFormat.FIXED_64_SIZE;
+import static com.google.protobuf.WireFormat.FIXED32_SIZE;
+import static com.google.protobuf.WireFormat.FIXED64_SIZE;
 import static com.google.protobuf.WireFormat.MAX_VARINT_SIZE;
 import static com.google.protobuf.WireFormat.MAX_VARINT_SIZE;
 
 
 import java.io.ByteArrayOutputStream;
 import java.io.ByteArrayOutputStream;
@@ -372,6 +372,64 @@ public abstract class CodedInputStream {
     return oldLimit;
     return oldLimit;
   }
   }
 
 
+
+  private boolean explicitDiscardUnknownFields = false;
+
+  /** TODO(liujisi): flip the default.*/
+  private static volatile boolean proto3DiscardUnknownFieldsDefault = true;
+
+  static void setProto3DiscardUnknownsByDefaultForTest() {
+    proto3DiscardUnknownFieldsDefault = true;
+  }
+
+  static void setProto3KeepUnknownsByDefaultForTest() {
+    proto3DiscardUnknownFieldsDefault = false;
+  }
+
+  static boolean getProto3DiscardUnknownFieldsDefault() {
+    return proto3DiscardUnknownFieldsDefault;
+  }
+
+  /**
+   * Sets this {@code CodedInputStream} to discard unknown fields. Only applies to full runtime
+   * messages; lite messages will always preserve unknowns.
+   *
+   * <p>Note calling this function alone will have NO immediate effect on the underlying input data.
+   * The unknown fields will be discarded during parsing. This affects both Proto2 and Proto3 full
+   * runtime.
+   */
+  final void discardUnknownFields() {
+    explicitDiscardUnknownFields = true;
+  }
+
+  /**
+   * Reverts the unknown fields preservation behavior for Proto2 and Proto3 full runtime to their
+   * default.
+   */
+  final void unsetDiscardUnknownFields() {
+    explicitDiscardUnknownFields = false;
+  }
+
+  /**
+   * Whether unknown fields in this input stream should be discarded during parsing into full
+   * runtime messages.
+   */
+  final boolean shouldDiscardUnknownFields() {
+    return explicitDiscardUnknownFields;
+  }
+
+  /**
+   * Whether unknown fields in this input stream should be discarded during parsing for proto3 full
+   * runtime messages.
+   *
+   * <p>This function was temporarily introduced before proto3 unknown fields behavior is changed.
+   * TODO(liujisi): remove this and related code in GeneratedMessage after proto3 unknown
+   * fields migration is done.
+   */
+  final boolean shouldDiscardUnknownFieldsProto3() {
+    return explicitDiscardUnknownFields ? true : proto3DiscardUnknownFieldsDefault;
+  }
+
   /**
   /**
    * Resets the current size counter to zero (see {@link #setSizeLimit(int)}). Only valid for {@link
    * Resets the current size counter to zero (see {@link #setSizeLimit(int)}). Only valid for {@link
    * InputStream}-backed streams.
    * InputStream}-backed streams.
@@ -572,7 +630,7 @@ public abstract class CodedInputStream {
           skipRawVarint();
           skipRawVarint();
           return true;
           return true;
         case WireFormat.WIRETYPE_FIXED64:
         case WireFormat.WIRETYPE_FIXED64:
-          skipRawBytes(FIXED_64_SIZE);
+          skipRawBytes(FIXED64_SIZE);
           return true;
           return true;
         case WireFormat.WIRETYPE_LENGTH_DELIMITED:
         case WireFormat.WIRETYPE_LENGTH_DELIMITED:
           skipRawBytes(readRawVarint32());
           skipRawBytes(readRawVarint32());
@@ -585,7 +643,7 @@ public abstract class CodedInputStream {
         case WireFormat.WIRETYPE_END_GROUP:
         case WireFormat.WIRETYPE_END_GROUP:
           return false;
           return false;
         case WireFormat.WIRETYPE_FIXED32:
         case WireFormat.WIRETYPE_FIXED32:
-          skipRawBytes(FIXED_32_SIZE);
+          skipRawBytes(FIXED32_SIZE);
           return true;
           return true;
         default:
         default:
           throw InvalidProtocolBufferException.invalidWireType();
           throw InvalidProtocolBufferException.invalidWireType();
@@ -1064,12 +1122,12 @@ public abstract class CodedInputStream {
     public int readRawLittleEndian32() throws IOException {
     public int readRawLittleEndian32() throws IOException {
       int tempPos = pos;
       int tempPos = pos;
 
 
-      if (limit - tempPos < FIXED_32_SIZE) {
+      if (limit - tempPos < FIXED32_SIZE) {
         throw InvalidProtocolBufferException.truncatedMessage();
         throw InvalidProtocolBufferException.truncatedMessage();
       }
       }
 
 
       final byte[] buffer = this.buffer;
       final byte[] buffer = this.buffer;
-      pos = tempPos + FIXED_32_SIZE;
+      pos = tempPos + FIXED32_SIZE;
       return (((buffer[tempPos] & 0xff))
       return (((buffer[tempPos] & 0xff))
           | ((buffer[tempPos + 1] & 0xff) << 8)
           | ((buffer[tempPos + 1] & 0xff) << 8)
           | ((buffer[tempPos + 2] & 0xff) << 16)
           | ((buffer[tempPos + 2] & 0xff) << 16)
@@ -1080,12 +1138,12 @@ public abstract class CodedInputStream {
     public long readRawLittleEndian64() throws IOException {
     public long readRawLittleEndian64() throws IOException {
       int tempPos = pos;
       int tempPos = pos;
 
 
-      if (limit - tempPos < FIXED_64_SIZE) {
+      if (limit - tempPos < FIXED64_SIZE) {
         throw InvalidProtocolBufferException.truncatedMessage();
         throw InvalidProtocolBufferException.truncatedMessage();
       }
       }
 
 
       final byte[] buffer = this.buffer;
       final byte[] buffer = this.buffer;
-      pos = tempPos + FIXED_64_SIZE;
+      pos = tempPos + FIXED64_SIZE;
       return (((buffer[tempPos] & 0xffL))
       return (((buffer[tempPos] & 0xffL))
           | ((buffer[tempPos + 1] & 0xffL) << 8)
           | ((buffer[tempPos + 1] & 0xffL) << 8)
           | ((buffer[tempPos + 2] & 0xffL) << 16)
           | ((buffer[tempPos + 2] & 0xffL) << 16)
@@ -1290,7 +1348,7 @@ public abstract class CodedInputStream {
           skipRawVarint();
           skipRawVarint();
           return true;
           return true;
         case WireFormat.WIRETYPE_FIXED64:
         case WireFormat.WIRETYPE_FIXED64:
-          skipRawBytes(FIXED_64_SIZE);
+          skipRawBytes(FIXED64_SIZE);
           return true;
           return true;
         case WireFormat.WIRETYPE_LENGTH_DELIMITED:
         case WireFormat.WIRETYPE_LENGTH_DELIMITED:
           skipRawBytes(readRawVarint32());
           skipRawBytes(readRawVarint32());
@@ -1303,7 +1361,7 @@ public abstract class CodedInputStream {
         case WireFormat.WIRETYPE_END_GROUP:
         case WireFormat.WIRETYPE_END_GROUP:
           return false;
           return false;
         case WireFormat.WIRETYPE_FIXED32:
         case WireFormat.WIRETYPE_FIXED32:
-          skipRawBytes(FIXED_32_SIZE);
+          skipRawBytes(FIXED32_SIZE);
           return true;
           return true;
         default:
         default:
           throw InvalidProtocolBufferException.invalidWireType();
           throw InvalidProtocolBufferException.invalidWireType();
@@ -1429,7 +1487,9 @@ public abstract class CodedInputStream {
       final int size = readRawVarint32();
       final int size = readRawVarint32();
       if (size > 0 && size <= remaining()) {
       if (size > 0 && size <= remaining()) {
         // TODO(nathanmittler): Is there a way to avoid this copy?
         // TODO(nathanmittler): Is there a way to avoid this copy?
-        byte[] bytes = copyToArray(pos, pos + size);
+        // The same as readBytes' logic
+        byte[] bytes = new byte[size];
+        UnsafeUtil.copyMemory(pos, bytes, 0, size);
         String result = new String(bytes, UTF_8);
         String result = new String(bytes, UTF_8);
         pos += size;
         pos += size;
         return result;
         return result;
@@ -1449,7 +1509,9 @@ public abstract class CodedInputStream {
       final int size = readRawVarint32();
       final int size = readRawVarint32();
       if (size >= 0 && size <= remaining()) {
       if (size >= 0 && size <= remaining()) {
         // TODO(nathanmittler): Is there a way to avoid this copy?
         // TODO(nathanmittler): Is there a way to avoid this copy?
-        byte[] bytes = copyToArray(pos, pos + size);
+        // The same as readBytes' logic
+        byte[] bytes = new byte[size];
+        UnsafeUtil.copyMemory(pos, bytes, 0, size);
         // TODO(martinrb): We could save a pass by validating while decoding.
         // TODO(martinrb): We could save a pass by validating while decoding.
         if (!Utf8.isValidUtf8(bytes)) {
         if (!Utf8.isValidUtf8(bytes)) {
           throw InvalidProtocolBufferException.invalidUtf8();
           throw InvalidProtocolBufferException.invalidUtf8();
@@ -1545,14 +1607,17 @@ public abstract class CodedInputStream {
     public ByteString readBytes() throws IOException {
     public ByteString readBytes() throws IOException {
       final int size = readRawVarint32();
       final int size = readRawVarint32();
       if (size > 0 && size <= remaining()) {
       if (size > 0 && size <= remaining()) {
-        ByteBuffer result;
         if (immutable && enableAliasing) {
         if (immutable && enableAliasing) {
-          result = slice(pos, pos + size);
+          final ByteBuffer result = slice(pos, pos + size);
+          pos += size;
+          return ByteString.wrap(result);
         } else {
         } else {
-          result = copy(pos, pos + size);
+          // Use UnsafeUtil to copy the memory to bytes instead of using ByteBuffer ways.
+          byte[] bytes = new byte[size];
+          UnsafeUtil.copyMemory(pos, bytes, 0, size);
+          pos += size;
+          return ByteString.wrap(bytes);
         }
         }
-        pos += size;
-        return ByteString.wrap(result);
       }
       }
 
 
       if (size == 0) {
       if (size == 0) {
@@ -1573,18 +1638,21 @@ public abstract class CodedInputStream {
     public ByteBuffer readByteBuffer() throws IOException {
     public ByteBuffer readByteBuffer() throws IOException {
       final int size = readRawVarint32();
       final int size = readRawVarint32();
       if (size > 0 && size <= remaining()) {
       if (size > 0 && size <= remaining()) {
-        ByteBuffer result;
         // "Immutable" implies that buffer is backing a ByteString.
         // "Immutable" implies that buffer is backing a ByteString.
         // Disallow slicing in this case to prevent the caller from modifying the contents
         // Disallow slicing in this case to prevent the caller from modifying the contents
         // of the ByteString.
         // of the ByteString.
         if (!immutable && enableAliasing) {
         if (!immutable && enableAliasing) {
-          result = slice(pos, pos + size);
+          final ByteBuffer result = slice(pos, pos + size);
+          pos += size;
+          return result;
         } else {
         } else {
-          result = copy(pos, pos + size);
+          // The same as readBytes' logic
+          byte[] bytes = new byte[size];
+          UnsafeUtil.copyMemory(pos, bytes, 0, size);
+          pos += size;
+          return ByteBuffer.wrap(bytes);
         }
         }
-        pos += size;
         // TODO(nathanmittler): Investigate making the ByteBuffer be made read-only
         // TODO(nathanmittler): Investigate making the ByteBuffer be made read-only
-        return result;
       }
       }
 
 
       if (size == 0) {
       if (size == 0) {
@@ -1785,11 +1853,11 @@ public abstract class CodedInputStream {
     public int readRawLittleEndian32() throws IOException {
     public int readRawLittleEndian32() throws IOException {
       long tempPos = pos;
       long tempPos = pos;
 
 
-      if (limit - tempPos < FIXED_32_SIZE) {
+      if (limit - tempPos < FIXED32_SIZE) {
         throw InvalidProtocolBufferException.truncatedMessage();
         throw InvalidProtocolBufferException.truncatedMessage();
       }
       }
 
 
-      pos = tempPos + FIXED_32_SIZE;
+      pos = tempPos + FIXED32_SIZE;
       return (((UnsafeUtil.getByte(tempPos) & 0xff))
       return (((UnsafeUtil.getByte(tempPos) & 0xff))
           | ((UnsafeUtil.getByte(tempPos + 1) & 0xff) << 8)
           | ((UnsafeUtil.getByte(tempPos + 1) & 0xff) << 8)
           | ((UnsafeUtil.getByte(tempPos + 2) & 0xff) << 16)
           | ((UnsafeUtil.getByte(tempPos + 2) & 0xff) << 16)
@@ -1800,11 +1868,11 @@ public abstract class CodedInputStream {
     public long readRawLittleEndian64() throws IOException {
     public long readRawLittleEndian64() throws IOException {
       long tempPos = pos;
       long tempPos = pos;
 
 
-      if (limit - tempPos < FIXED_64_SIZE) {
+      if (limit - tempPos < FIXED64_SIZE) {
         throw InvalidProtocolBufferException.truncatedMessage();
         throw InvalidProtocolBufferException.truncatedMessage();
       }
       }
 
 
-      pos = tempPos + FIXED_64_SIZE;
+      pos = tempPos + FIXED64_SIZE;
       return (((UnsafeUtil.getByte(tempPos) & 0xffL))
       return (((UnsafeUtil.getByte(tempPos) & 0xffL))
           | ((UnsafeUtil.getByte(tempPos + 1) & 0xffL) << 8)
           | ((UnsafeUtil.getByte(tempPos + 1) & 0xffL) << 8)
           | ((UnsafeUtil.getByte(tempPos + 2) & 0xffL) << 16)
           | ((UnsafeUtil.getByte(tempPos + 2) & 0xffL) << 16)
@@ -1943,27 +2011,6 @@ public abstract class CodedInputStream {
         buffer.limit(prevLimit);
         buffer.limit(prevLimit);
       }
       }
     }
     }
-
-    private ByteBuffer copy(long begin, long end) throws IOException {
-      return ByteBuffer.wrap(copyToArray(begin, end));
-    }
-
-    private byte[] copyToArray(long begin, long end) throws IOException {
-      int prevPos = buffer.position();
-      int prevLimit = buffer.limit();
-      try {
-        buffer.position(bufferPos(begin));
-        buffer.limit(bufferPos(end));
-        byte[] bytes = new byte[(int) (end - begin)];
-        buffer.get(bytes);
-        return bytes;
-      } catch (IllegalArgumentException e) {
-        throw InvalidProtocolBufferException.truncatedMessage();
-      } finally {
-        buffer.position(prevPos);
-        buffer.limit(prevLimit);
-      }
-    }
   }
   }
 
 
   /**
   /**
@@ -2034,7 +2081,7 @@ public abstract class CodedInputStream {
           skipRawVarint();
           skipRawVarint();
           return true;
           return true;
         case WireFormat.WIRETYPE_FIXED64:
         case WireFormat.WIRETYPE_FIXED64:
-          skipRawBytes(FIXED_64_SIZE);
+          skipRawBytes(FIXED64_SIZE);
           return true;
           return true;
         case WireFormat.WIRETYPE_LENGTH_DELIMITED:
         case WireFormat.WIRETYPE_LENGTH_DELIMITED:
           skipRawBytes(readRawVarint32());
           skipRawBytes(readRawVarint32());
@@ -2047,7 +2094,7 @@ public abstract class CodedInputStream {
         case WireFormat.WIRETYPE_END_GROUP:
         case WireFormat.WIRETYPE_END_GROUP:
           return false;
           return false;
         case WireFormat.WIRETYPE_FIXED32:
         case WireFormat.WIRETYPE_FIXED32:
-          skipRawBytes(FIXED_32_SIZE);
+          skipRawBytes(FIXED32_SIZE);
           return true;
           return true;
         default:
         default:
           throw InvalidProtocolBufferException.invalidWireType();
           throw InvalidProtocolBufferException.invalidWireType();
@@ -2332,8 +2379,7 @@ public abstract class CodedInputStream {
       if (size == 0) {
       if (size == 0) {
         return ByteString.EMPTY;
         return ByteString.EMPTY;
       }
       }
-      // Slow path:  Build a byte array first then copy it.
-      return ByteString.wrap(readRawBytesSlowPath(size));
+      return readBytesSlowPath(size);
     }
     }
 
 
     @Override
     @Override
@@ -2558,13 +2604,13 @@ public abstract class CodedInputStream {
     public int readRawLittleEndian32() throws IOException {
     public int readRawLittleEndian32() throws IOException {
       int tempPos = pos;
       int tempPos = pos;
 
 
-      if (bufferSize - tempPos < FIXED_32_SIZE) {
-        refillBuffer(FIXED_32_SIZE);
+      if (bufferSize - tempPos < FIXED32_SIZE) {
+        refillBuffer(FIXED32_SIZE);
         tempPos = pos;
         tempPos = pos;
       }
       }
 
 
       final byte[] buffer = this.buffer;
       final byte[] buffer = this.buffer;
-      pos = tempPos + FIXED_32_SIZE;
+      pos = tempPos + FIXED32_SIZE;
       return (((buffer[tempPos] & 0xff))
       return (((buffer[tempPos] & 0xff))
           | ((buffer[tempPos + 1] & 0xff) << 8)
           | ((buffer[tempPos + 1] & 0xff) << 8)
           | ((buffer[tempPos + 2] & 0xff) << 16)
           | ((buffer[tempPos + 2] & 0xff) << 16)
@@ -2575,13 +2621,13 @@ public abstract class CodedInputStream {
     public long readRawLittleEndian64() throws IOException {
     public long readRawLittleEndian64() throws IOException {
       int tempPos = pos;
       int tempPos = pos;
 
 
-      if (bufferSize - tempPos < FIXED_64_SIZE) {
-        refillBuffer(FIXED_64_SIZE);
+      if (bufferSize - tempPos < FIXED64_SIZE) {
+        refillBuffer(FIXED64_SIZE);
         tempPos = pos;
         tempPos = pos;
       }
       }
 
 
       final byte[] buffer = this.buffer;
       final byte[] buffer = this.buffer;
-      pos = tempPos + FIXED_64_SIZE;
+      pos = tempPos + FIXED64_SIZE;
       return (((buffer[tempPos] & 0xffL))
       return (((buffer[tempPos] & 0xffL))
           | ((buffer[tempPos + 1] & 0xffL) << 8)
           | ((buffer[tempPos + 1] & 0xffL) << 8)
           | ((buffer[tempPos + 2] & 0xffL) << 16)
           | ((buffer[tempPos + 2] & 0xffL) << 16)
@@ -2675,7 +2721,13 @@ public abstract class CodedInputStream {
      */
      */
     private void refillBuffer(int n) throws IOException {
     private void refillBuffer(int n) throws IOException {
       if (!tryRefillBuffer(n)) {
       if (!tryRefillBuffer(n)) {
-        throw InvalidProtocolBufferException.truncatedMessage();
+        // We have to distinguish the exception between sizeLimitExceeded and truncatedMessage. So
+        // we just throw an sizeLimitExceeded exception here if it exceeds the sizeLimit
+        if (n > sizeLimit - totalBytesRetired - pos) {
+          throw InvalidProtocolBufferException.sizeLimitExceeded();
+        } else {
+          throw InvalidProtocolBufferException.truncatedMessage();
+        }
       }
       }
     }
     }
 
 
@@ -2684,8 +2736,8 @@ public abstract class CodedInputStream {
      * buffer. Caller must ensure that the requested space is not yet available, and that the
      * buffer. Caller must ensure that the requested space is not yet available, and that the
      * requested space is less than BUFFER_SIZE.
      * requested space is less than BUFFER_SIZE.
      *
      *
-     * @return {@code true} if the bytes could be made available; {@code false} if the end of the
-     *     stream or the current limit was reached.
+     * @return {@code true} If the bytes could be made available; {@code false} 1. Current at the
+     *     end of the stream 2. The current limit was reached 3. The total size limit was reached
      */
      */
     private boolean tryRefillBuffer(int n) throws IOException {
     private boolean tryRefillBuffer(int n) throws IOException {
       if (pos + n <= bufferSize) {
       if (pos + n <= bufferSize) {
@@ -2693,6 +2745,14 @@ public abstract class CodedInputStream {
             "refillBuffer() called when " + n + " bytes were already available in buffer");
             "refillBuffer() called when " + n + " bytes were already available in buffer");
       }
       }
 
 
+      // Check whether the size of total message needs to read is bigger than the size limit.
+      // We shouldn't throw an exception here as isAtEnd() function needs to get this function's
+      // return as the result.
+      if (n > sizeLimit - totalBytesRetired - pos) {
+        return false;
+      }
+
+      // Shouldn't throw the exception here either.
       if (totalBytesRetired + pos + n > currentLimit) {
       if (totalBytesRetired + pos + n > currentLimit) {
         // Oops, we hit a limit.
         // Oops, we hit a limit.
         return false;
         return false;
@@ -2712,7 +2772,16 @@ public abstract class CodedInputStream {
         pos = 0;
         pos = 0;
       }
       }
 
 
-      int bytesRead = input.read(buffer, bufferSize, buffer.length - bufferSize);
+      // Here we should refill the buffer as many bytes as possible.
+      int bytesRead =
+          input.read(
+              buffer,
+              bufferSize,
+              Math.min(
+                  //  the size of allocated but unused bytes in the buffer
+                  buffer.length - bufferSize,
+                  //  do not exceed the total bytes limit
+                  sizeLimit - totalBytesRetired - bufferSize));
       if (bytesRead == 0 || bytesRead < -1 || bytesRead > buffer.length) {
       if (bytesRead == 0 || bytesRead < -1 || bytesRead > buffer.length) {
         throw new IllegalStateException(
         throw new IllegalStateException(
             "InputStream#read(byte[]) returned invalid result: "
             "InputStream#read(byte[]) returned invalid result: "
@@ -2721,10 +2790,6 @@ public abstract class CodedInputStream {
       }
       }
       if (bytesRead > 0) {
       if (bytesRead > 0) {
         bufferSize += bytesRead;
         bufferSize += bytesRead;
-        // Integer-overflow-conscious check against sizeLimit
-        if (totalBytesRetired + n - sizeLimit > 0) {
-          throw InvalidProtocolBufferException.sizeLimitExceeded();
-        }
         recomputeBufferSizeAfterLimit();
         recomputeBufferSizeAfterLimit();
         return (bufferSize >= n) ? true : tryRefillBuffer(n);
         return (bufferSize >= n) ? true : tryRefillBuffer(n);
       }
       }
@@ -2756,6 +2821,49 @@ public abstract class CodedInputStream {
      * (bufferSize - pos) && size > 0)
      * (bufferSize - pos) && size > 0)
      */
      */
     private byte[] readRawBytesSlowPath(final int size) throws IOException {
     private byte[] readRawBytesSlowPath(final int size) throws IOException {
+      // Attempt to read the data in one byte array when it's safe to do.
+      byte[] result = readRawBytesSlowPathOneChunk(size);
+      if (result != null) {
+        return result;
+      }
+
+      final int originalBufferPos = pos;
+      final int bufferedBytes = bufferSize - pos;
+
+      // Mark the current buffer consumed.
+      totalBytesRetired += bufferSize;
+      pos = 0;
+      bufferSize = 0;
+
+      // Determine the number of bytes we need to read from the input stream.
+      int sizeLeft = size - bufferedBytes;
+
+      // The size is very large. For security reasons we read them in small
+      // chunks.
+      List<byte[]> chunks = readRawBytesSlowPathRemainingChunks(sizeLeft);
+
+      // OK, got everything.  Now concatenate it all into one buffer.
+      final byte[] bytes = new byte[size];
+
+      // Start by copying the leftover bytes from this.buffer.
+      System.arraycopy(buffer, originalBufferPos, bytes, 0, bufferedBytes);
+
+      // And now all the chunks.
+      int tempPos = bufferedBytes;
+      for (final byte[] chunk : chunks) {
+        System.arraycopy(chunk, 0, bytes, tempPos, chunk.length);
+        tempPos += chunk.length;
+      }
+
+      // Done.
+      return bytes;
+    }
+
+    /**
+     * Attempts to read the data in one byte array when it's safe to do. Returns null if the size to
+     * read is too large and needs to be allocated in smaller chunks for security reasons.
+     */
+    private byte[] readRawBytesSlowPathOneChunk(final int size) throws IOException {
       if (size == 0) {
       if (size == 0) {
         return Internal.EMPTY_BYTE_ARRAY;
         return Internal.EMPTY_BYTE_ARRAY;
       }
       }
@@ -2776,14 +2884,7 @@ public abstract class CodedInputStream {
         throw InvalidProtocolBufferException.truncatedMessage();
         throw InvalidProtocolBufferException.truncatedMessage();
       }
       }
 
 
-      final int originalBufferPos = pos;
       final int bufferedBytes = bufferSize - pos;
       final int bufferedBytes = bufferSize - pos;
-
-      // Mark the current buffer consumed.
-      totalBytesRetired += bufferSize;
-      pos = 0;
-      bufferSize = 0;
-
       // Determine the number of bytes we need to read from the input stream.
       // Determine the number of bytes we need to read from the input stream.
       int sizeLeft = size - bufferedBytes;
       int sizeLeft = size - bufferedBytes;
       // TODO(nathanmittler): Consider using a value larger than DEFAULT_BUFFER_SIZE.
       // TODO(nathanmittler): Consider using a value larger than DEFAULT_BUFFER_SIZE.
@@ -2793,7 +2894,10 @@ public abstract class CodedInputStream {
         final byte[] bytes = new byte[size];
         final byte[] bytes = new byte[size];
 
 
         // Copy all of the buffered bytes to the result buffer.
         // Copy all of the buffered bytes to the result buffer.
-        System.arraycopy(buffer, originalBufferPos, bytes, 0, bufferedBytes);
+        System.arraycopy(buffer, pos, bytes, 0, bufferedBytes);
+        totalBytesRetired += bufferSize;
+        pos = 0;
+        bufferSize = 0;
 
 
         // Fill the remaining bytes from the input stream.
         // Fill the remaining bytes from the input stream.
         int tempPos = bufferedBytes;
         int tempPos = bufferedBytes;
@@ -2809,6 +2913,11 @@ public abstract class CodedInputStream {
         return bytes;
         return bytes;
       }
       }
 
 
+      return null;
+    }
+
+    /** Reads the remaining data in small chunks from the input stream. */
+    private List<byte[]> readRawBytesSlowPathRemainingChunks(int sizeLeft) throws IOException {
       // The size is very large.  For security reasons, we can't allocate the
       // The size is very large.  For security reasons, we can't allocate the
       // entire byte array yet.  The size comes directly from the input, so a
       // entire byte array yet.  The size comes directly from the input, so a
       // maliciously-crafted message could provide a bogus very large size in
       // maliciously-crafted message could provide a bogus very large size in
@@ -2834,21 +2943,41 @@ public abstract class CodedInputStream {
         chunks.add(chunk);
         chunks.add(chunk);
       }
       }
 
 
-      // OK, got everything.  Now concatenate it all into one buffer.
-      final byte[] bytes = new byte[size];
-
-      // Start by copying the leftover bytes from this.buffer.
-      System.arraycopy(buffer, originalBufferPos, bytes, 0, bufferedBytes);
+      return chunks;
+    }
 
 
-      // And now all the chunks.
-      int tempPos = bufferedBytes;
-      for (final byte[] chunk : chunks) {
-        System.arraycopy(chunk, 0, bytes, tempPos, chunk.length);
-        tempPos += chunk.length;
+    /**
+     * Like readBytes, but caller must have already checked the fast path: (size <= (bufferSize -
+     * pos) && size > 0 || size == 0)
+     */
+    private ByteString readBytesSlowPath(final int size) throws IOException {
+      final byte[] result = readRawBytesSlowPathOneChunk(size);
+      if (result != null) {
+        return ByteString.wrap(result);
       }
       }
 
 
-      // Done.
-      return bytes;
+      final int originalBufferPos = pos;
+      final int bufferedBytes = bufferSize - pos;
+
+      // Mark the current buffer consumed.
+      totalBytesRetired += bufferSize;
+      pos = 0;
+      bufferSize = 0;
+
+      // Determine the number of bytes we need to read from the input stream.
+      int sizeLeft = size - bufferedBytes;
+
+      // The size is very large. For security reasons we read them in small
+      // chunks.
+      List<byte[]> chunks = readRawBytesSlowPathRemainingChunks(sizeLeft);
+
+      // Wrap the byte arrays into a single ByteString.
+      List<ByteString> byteStrings = new ArrayList<ByteString>(1 + chunks.size());
+      byteStrings.add(ByteString.copyFrom(buffer, originalBufferPos, bufferedBytes));
+      for (byte[] chunk : chunks) {
+        byteStrings.add(ByteString.wrap(chunk));
+      }
+      return ByteString.copyFrom(byteStrings);
     }
     }
 
 
     @Override
     @Override

+ 35 - 47
java/core/src/main/java/com/google/protobuf/CodedOutputStream.java

@@ -30,8 +30,8 @@
 
 
 package com.google.protobuf;
 package com.google.protobuf;
 
 
-import static com.google.protobuf.WireFormat.FIXED_32_SIZE;
-import static com.google.protobuf.WireFormat.FIXED_64_SIZE;
+import static com.google.protobuf.WireFormat.FIXED32_SIZE;
+import static com.google.protobuf.WireFormat.FIXED64_SIZE;
 import static com.google.protobuf.WireFormat.MAX_VARINT_SIZE;
 import static com.google.protobuf.WireFormat.MAX_VARINT_SIZE;
 import static java.lang.Math.max;
 import static java.lang.Math.max;
 
 
@@ -59,13 +59,12 @@ import java.util.logging.Logger;
 public abstract class CodedOutputStream extends ByteOutput {
 public abstract class CodedOutputStream extends ByteOutput {
   private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName());
   private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName());
   private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = UnsafeUtil.hasUnsafeArrayOperations();
   private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = UnsafeUtil.hasUnsafeArrayOperations();
-  private static final long ARRAY_BASE_OFFSET = UnsafeUtil.getArrayBaseOffset();
 
 
   /**
   /**
    * @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead.
    * @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead.
    */
    */
   @Deprecated
   @Deprecated
-  public static final int LITTLE_ENDIAN_32_SIZE = FIXED_32_SIZE;
+  public static final int LITTLE_ENDIAN_32_SIZE = FIXED32_SIZE;
 
 
   /**
   /**
    * The buffer size used in {@link #newInstance(OutputStream)}.
    * The buffer size used in {@link #newInstance(OutputStream)}.
@@ -755,7 +754,7 @@ public abstract class CodedOutputStream extends ByteOutput {
    * {@code fixed32} field.
    * {@code fixed32} field.
    */
    */
   public static int computeFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) {
   public static int computeFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) {
-    return FIXED_32_SIZE;
+    return FIXED32_SIZE;
   }
   }
 
 
   /**
   /**
@@ -763,7 +762,7 @@ public abstract class CodedOutputStream extends ByteOutput {
    * {@code sfixed32} field.
    * {@code sfixed32} field.
    */
    */
   public static int computeSFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) {
   public static int computeSFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) {
-    return FIXED_32_SIZE;
+    return FIXED32_SIZE;
   }
   }
 
 
   /**
   /**
@@ -813,7 +812,7 @@ public abstract class CodedOutputStream extends ByteOutput {
    * {@code fixed64} field.
    * {@code fixed64} field.
    */
    */
   public static int computeFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) {
   public static int computeFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) {
-    return FIXED_64_SIZE;
+    return FIXED64_SIZE;
   }
   }
 
 
   /**
   /**
@@ -821,7 +820,7 @@ public abstract class CodedOutputStream extends ByteOutput {
    * {@code sfixed64} field.
    * {@code sfixed64} field.
    */
    */
   public static int computeSFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) {
   public static int computeSFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) {
-    return FIXED_64_SIZE;
+    return FIXED64_SIZE;
   }
   }
 
 
   /**
   /**
@@ -829,7 +828,7 @@ public abstract class CodedOutputStream extends ByteOutput {
    * {@code float} field, including tag.
    * {@code float} field, including tag.
    */
    */
   public static int computeFloatSizeNoTag(@SuppressWarnings("unused") final float unused) {
   public static int computeFloatSizeNoTag(@SuppressWarnings("unused") final float unused) {
-    return FIXED_32_SIZE;
+    return FIXED32_SIZE;
   }
   }
 
 
   /**
   /**
@@ -837,7 +836,7 @@ public abstract class CodedOutputStream extends ByteOutput {
    * {@code double} field, including tag.
    * {@code double} field, including tag.
    */
    */
   public static int computeDoubleSizeNoTag(@SuppressWarnings("unused") final double unused) {
   public static int computeDoubleSizeNoTag(@SuppressWarnings("unused") final double unused) {
-    return FIXED_64_SIZE;
+    return FIXED64_SIZE;
   }
   }
 
 
   /**
   /**
@@ -1321,15 +1320,12 @@ public abstract class CodedOutputStream extends ByteOutput {
     @Override
     @Override
     public final void writeUInt32NoTag(int value) throws IOException {
     public final void writeUInt32NoTag(int value) throws IOException {
       if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) {
       if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) {
-        long pos = ARRAY_BASE_OFFSET + position;
         while (true) {
         while (true) {
           if ((value & ~0x7F) == 0) {
           if ((value & ~0x7F) == 0) {
-            UnsafeUtil.putByte(buffer, pos++, (byte) value);
-            position++;
+            UnsafeUtil.putByte(buffer, position++, (byte) value);
             return;
             return;
           } else {
           } else {
-            UnsafeUtil.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
-            position++;
+            UnsafeUtil.putByte(buffer, position++, (byte) ((value & 0x7F) | 0x80));
             value >>>= 7;
             value >>>= 7;
           }
           }
         }
         }
@@ -1367,15 +1363,12 @@ public abstract class CodedOutputStream extends ByteOutput {
     @Override
     @Override
     public final void writeUInt64NoTag(long value) throws IOException {
     public final void writeUInt64NoTag(long value) throws IOException {
       if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) {
       if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) {
-        long pos = ARRAY_BASE_OFFSET + position;
         while (true) {
         while (true) {
           if ((value & ~0x7FL) == 0) {
           if ((value & ~0x7FL) == 0) {
-            UnsafeUtil.putByte(buffer, pos++, (byte) value);
-            position++;
+            UnsafeUtil.putByte(buffer, position++, (byte) value);
             return;
             return;
           } else {
           } else {
-            UnsafeUtil.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
-            position++;
+            UnsafeUtil.putByte(buffer, position++, (byte) (((int) value & 0x7F) | 0x80));
             value >>>= 7;
             value >>>= 7;
           }
           }
         }
         }
@@ -1854,7 +1847,7 @@ public abstract class CodedOutputStream extends ByteOutput {
     }
     }
 
 
     static boolean isSupported() {
     static boolean isSupported() {
-      return UnsafeUtil.hasUnsafeByteBufferOperations() && UnsafeUtil.hasUnsafeCopyMemory();
+      return UnsafeUtil.hasUnsafeByteBufferOperations();
     }
     }
 
 
     @Override
     @Override
@@ -2030,7 +2023,7 @@ public abstract class CodedOutputStream extends ByteOutput {
     @Override
     @Override
     public void writeFixed32NoTag(int value) throws IOException {
     public void writeFixed32NoTag(int value) throws IOException {
       buffer.putInt(bufferPos(position), value);
       buffer.putInt(bufferPos(position), value);
-      position += FIXED_32_SIZE;
+      position += FIXED32_SIZE;
     }
     }
 
 
     @Override
     @Override
@@ -2064,7 +2057,7 @@ public abstract class CodedOutputStream extends ByteOutput {
     @Override
     @Override
     public void writeFixed64NoTag(long value) throws IOException {
     public void writeFixed64NoTag(long value) throws IOException {
       buffer.putLong(bufferPos(position), value);
       buffer.putLong(bufferPos(position), value);
-      position += FIXED_64_SIZE;
+      position += FIXED64_SIZE;
     }
     }
 
 
     @Override
     @Override
@@ -2081,8 +2074,7 @@ public abstract class CodedOutputStream extends ByteOutput {
             String.format("Pos: %d, limit: %d, len: %d", position, limit, length));
             String.format("Pos: %d, limit: %d, len: %d", position, limit, length));
       }
       }
 
 
-      UnsafeUtil.copyMemory(
-          value, UnsafeUtil.getArrayBaseOffset() + offset, null, position, length);
+      UnsafeUtil.copyMemory(value, offset, position, length);
       position += length;
       position += length;
     }
     }
 
 
@@ -2249,19 +2241,17 @@ public abstract class CodedOutputStream extends ByteOutput {
      */
      */
     final void bufferUInt32NoTag(int value) {
     final void bufferUInt32NoTag(int value) {
       if (HAS_UNSAFE_ARRAY_OPERATIONS) {
       if (HAS_UNSAFE_ARRAY_OPERATIONS) {
-        final long originalPos = ARRAY_BASE_OFFSET + position;
-        long pos = originalPos;
+        final long originalPos = position;
         while (true) {
         while (true) {
           if ((value & ~0x7F) == 0) {
           if ((value & ~0x7F) == 0) {
-            UnsafeUtil.putByte(buffer, pos++, (byte) value);
+            UnsafeUtil.putByte(buffer, position++, (byte) value);
             break;
             break;
           } else {
           } else {
-            UnsafeUtil.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
+            UnsafeUtil.putByte(buffer, position++, (byte) ((value & 0x7F) | 0x80));
             value >>>= 7;
             value >>>= 7;
           }
           }
         }
         }
-        int delta = (int) (pos - originalPos);
-        position += delta;
+        int delta = (int) (position - originalPos);
         totalBytesWritten += delta;
         totalBytesWritten += delta;
       } else {
       } else {
         while (true) {
         while (true) {
@@ -2284,19 +2274,17 @@ public abstract class CodedOutputStream extends ByteOutput {
      */
      */
     final void bufferUInt64NoTag(long value) {
     final void bufferUInt64NoTag(long value) {
       if (HAS_UNSAFE_ARRAY_OPERATIONS) {
       if (HAS_UNSAFE_ARRAY_OPERATIONS) {
-        final long originalPos = ARRAY_BASE_OFFSET + position;
-        long pos = originalPos;
+        final long originalPos = position;
         while (true) {
         while (true) {
           if ((value & ~0x7FL) == 0) {
           if ((value & ~0x7FL) == 0) {
-            UnsafeUtil.putByte(buffer, pos++, (byte) value);
+            UnsafeUtil.putByte(buffer, position++, (byte) value);
             break;
             break;
           } else {
           } else {
-            UnsafeUtil.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
+            UnsafeUtil.putByte(buffer, position++, (byte) (((int) value & 0x7F) | 0x80));
             value >>>= 7;
             value >>>= 7;
           }
           }
         }
         }
-        int delta = (int) (pos - originalPos);
-        position += delta;
+        int delta = (int) (position - originalPos);
         totalBytesWritten += delta;
         totalBytesWritten += delta;
       } else {
       } else {
         while (true) {
         while (true) {
@@ -2322,7 +2310,7 @@ public abstract class CodedOutputStream extends ByteOutput {
       buffer[position++] = (byte) ((value >> 8) & 0xFF);
       buffer[position++] = (byte) ((value >> 8) & 0xFF);
       buffer[position++] = (byte) ((value >> 16) & 0xFF);
       buffer[position++] = (byte) ((value >> 16) & 0xFF);
       buffer[position++] = (byte) ((value >> 24) & 0xFF);
       buffer[position++] = (byte) ((value >> 24) & 0xFF);
-      totalBytesWritten += FIXED_32_SIZE;
+      totalBytesWritten += FIXED32_SIZE;
     }
     }
 
 
     /**
     /**
@@ -2338,7 +2326,7 @@ public abstract class CodedOutputStream extends ByteOutput {
       buffer[position++] = (byte) ((int) (value >> 40) & 0xFF);
       buffer[position++] = (byte) ((int) (value >> 40) & 0xFF);
       buffer[position++] = (byte) ((int) (value >> 48) & 0xFF);
       buffer[position++] = (byte) ((int) (value >> 48) & 0xFF);
       buffer[position++] = (byte) ((int) (value >> 56) & 0xFF);
       buffer[position++] = (byte) ((int) (value >> 56) & 0xFF);
-      totalBytesWritten += FIXED_64_SIZE;
+      totalBytesWritten += FIXED64_SIZE;
     }
     }
   }
   }
 
 
@@ -2379,7 +2367,7 @@ public abstract class CodedOutputStream extends ByteOutput {
 
 
     @Override
     @Override
     public void writeFixed32(final int fieldNumber, final int value) throws IOException {
     public void writeFixed32(final int fieldNumber, final int value) throws IOException {
-      flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_32_SIZE);
+      flushIfNotAvailable(MAX_VARINT_SIZE + FIXED32_SIZE);
       bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
       bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
       bufferFixed32NoTag(value);
       bufferFixed32NoTag(value);
     }
     }
@@ -2393,7 +2381,7 @@ public abstract class CodedOutputStream extends ByteOutput {
 
 
     @Override
     @Override
     public void writeFixed64(final int fieldNumber, final long value) throws IOException {
     public void writeFixed64(final int fieldNumber, final long value) throws IOException {
-      flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_64_SIZE);
+      flushIfNotAvailable(MAX_VARINT_SIZE + FIXED64_SIZE);
       bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
       bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
       bufferFixed64NoTag(value);
       bufferFixed64NoTag(value);
     }
     }
@@ -2519,7 +2507,7 @@ public abstract class CodedOutputStream extends ByteOutput {
 
 
     @Override
     @Override
     public void writeFixed32NoTag(final int value) throws IOException {
     public void writeFixed32NoTag(final int value) throws IOException {
-      flushIfNotAvailable(FIXED_32_SIZE);
+      flushIfNotAvailable(FIXED32_SIZE);
       bufferFixed32NoTag(value);
       bufferFixed32NoTag(value);
     }
     }
 
 
@@ -2531,7 +2519,7 @@ public abstract class CodedOutputStream extends ByteOutput {
 
 
     @Override
     @Override
     public void writeFixed64NoTag(final long value) throws IOException {
     public void writeFixed64NoTag(final long value) throws IOException {
-      flushIfNotAvailable(FIXED_64_SIZE);
+      flushIfNotAvailable(FIXED64_SIZE);
       bufferFixed64NoTag(value);
       bufferFixed64NoTag(value);
     }
     }
 
 
@@ -2682,7 +2670,7 @@ public abstract class CodedOutputStream extends ByteOutput {
 
 
     @Override
     @Override
     public void writeFixed32(final int fieldNumber, final int value) throws IOException {
     public void writeFixed32(final int fieldNumber, final int value) throws IOException {
-      flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_32_SIZE);
+      flushIfNotAvailable(MAX_VARINT_SIZE + FIXED32_SIZE);
       bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
       bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
       bufferFixed32NoTag(value);
       bufferFixed32NoTag(value);
     }
     }
@@ -2696,7 +2684,7 @@ public abstract class CodedOutputStream extends ByteOutput {
 
 
     @Override
     @Override
     public void writeFixed64(final int fieldNumber, final long value) throws IOException {
     public void writeFixed64(final int fieldNumber, final long value) throws IOException {
-      flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_64_SIZE);
+      flushIfNotAvailable(MAX_VARINT_SIZE + FIXED64_SIZE);
       bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
       bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
       bufferFixed64NoTag(value);
       bufferFixed64NoTag(value);
     }
     }
@@ -2822,7 +2810,7 @@ public abstract class CodedOutputStream extends ByteOutput {
 
 
     @Override
     @Override
     public void writeFixed32NoTag(final int value) throws IOException {
     public void writeFixed32NoTag(final int value) throws IOException {
-      flushIfNotAvailable(FIXED_32_SIZE);
+      flushIfNotAvailable(FIXED32_SIZE);
       bufferFixed32NoTag(value);
       bufferFixed32NoTag(value);
     }
     }
 
 
@@ -2834,7 +2822,7 @@ public abstract class CodedOutputStream extends ByteOutput {
 
 
     @Override
     @Override
     public void writeFixed64NoTag(final long value) throws IOException {
     public void writeFixed64NoTag(final long value) throws IOException {
-      flushIfNotAvailable(FIXED_64_SIZE);
+      flushIfNotAvailable(FIXED64_SIZE);
       bufferFixed64NoTag(value);
       bufferFixed64NoTag(value);
     }
     }
 
 

+ 71 - 0
java/core/src/main/java/com/google/protobuf/DiscardUnknownFieldsParser.java

@@ -0,0 +1,71 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+/**
+ * Parsers to discard unknown fields during parsing.
+ */
+public final class DiscardUnknownFieldsParser {
+
+  /**
+   * Warps a given {@link Parser} into a new {@link Parser} that discards unknown fields during
+   * parsing.
+   *
+   * <p>Usage example:
+   * <pre>{@code
+     * private final static Parser<Foo> FOO_PARSER = DiscardUnknownFieldsParser.wrap(Foo.parser());
+     * Foo parseFooDiscardUnknown(ByteBuffer input) throws IOException {
+     *   return FOO_PARSER.parseFrom(input);
+     * }
+   * }</pre>
+   *
+   * <p>Like all other implementations of {@code Parser}, this parser is stateless and thread-safe.
+   *
+   * @param parser The delegated parser that parses messages.
+   * @return a {@link Parser} that will discard unknown fields during parsing.
+   */
+  public static final <T extends Message> Parser<T> wrap(final Parser<T> parser) {
+    return new AbstractParser<T>() {
+      @Override
+      public T parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+          throws InvalidProtocolBufferException {
+        try {
+          input.discardUnknownFields();
+          return parser.parsePartialFrom(input, extensionRegistry);
+        } finally {
+          input.unsetDiscardUnknownFields();
+        }
+      }
+    };
+  }
+
+  private DiscardUnknownFieldsParser() {}
+}

+ 4 - 6
java/core/src/main/java/com/google/protobuf/DynamicMessage.java

@@ -590,9 +590,8 @@ public final class DynamicMessage extends AbstractMessage {
 
 
     @Override
     @Override
     public Builder setUnknownFields(UnknownFieldSet unknownFields) {
     public Builder setUnknownFields(UnknownFieldSet unknownFields) {
-      if (getDescriptorForType().getFile().getSyntax()
-          == Descriptors.FileDescriptor.Syntax.PROTO3) {
-        // Proto3 discards unknown fields.
+      if (getDescriptorForType().getFile().getSyntax() == Descriptors.FileDescriptor.Syntax.PROTO3
+          && CodedInputStream.getProto3DiscardUnknownFieldsDefault()) {
         return this;
         return this;
       }
       }
       this.unknownFields = unknownFields;
       this.unknownFields = unknownFields;
@@ -601,9 +600,8 @@ public final class DynamicMessage extends AbstractMessage {
 
 
     @Override
     @Override
     public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
     public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
-      if (getDescriptorForType().getFile().getSyntax()
-          == Descriptors.FileDescriptor.Syntax.PROTO3) {
-        // Proto3 discards unknown fields.
+      if (getDescriptorForType().getFile().getSyntax() == Descriptors.FileDescriptor.Syntax.PROTO3
+          && CodedInputStream.getProto3DiscardUnknownFieldsDefault()) {
         return this;
         return this;
       }
       }
       this.unknownFields =
       this.unknownFields =

+ 2 - 5
java/core/src/main/java/com/google/protobuf/Extension.java

@@ -58,10 +58,7 @@ public abstract class Extension<ContainingType extends MessageLite, Type>
     PROTO1,
     PROTO1,
   }
   }
 
 
-  protected ExtensionType getExtensionType() {
-    // TODO(liujisi): make this abstract after we fix proto1.
-    return ExtensionType.IMMUTABLE;
-  }
+  protected abstract ExtensionType getExtensionType();
 
 
   /**
   /**
    * Type of a message extension.
    * Type of a message extension.
@@ -70,7 +67,7 @@ public abstract class Extension<ContainingType extends MessageLite, Type>
     PROTO1,
     PROTO1,
     PROTO2,
     PROTO2,
   }
   }
-  
+
   /**
   /**
    * If the extension is a message extension (i.e., getLiteType() == MESSAGE),
    * If the extension is a message extension (i.e., getLiteType() == MESSAGE),
    * returns the type of the message, otherwise undefined.
    * returns the type of the message, otherwise undefined.

+ 3 - 2
java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java

@@ -34,7 +34,7 @@ import static com.google.protobuf.ExtensionRegistryLite.EMPTY_REGISTRY_LITE;
 
 
 /**
 /**
  * A factory object to create instances of {@link ExtensionRegistryLite}.
  * A factory object to create instances of {@link ExtensionRegistryLite}.
- * 
+ *
  * <p>
  * <p>
  * This factory detects (via reflection) if the full (non-Lite) protocol buffer libraries
  * This factory detects (via reflection) if the full (non-Lite) protocol buffer libraries
  * are available, and if so, the instances returned are actually {@link ExtensionRegistry}.
  * are available, and if so, the instances returned are actually {@link ExtensionRegistry}.
@@ -82,6 +82,7 @@ final class ExtensionRegistryFactory {
     return EMPTY_REGISTRY_LITE;
     return EMPTY_REGISTRY_LITE;
   }
   }
 
 
+
   static boolean isFullRegistry(ExtensionRegistryLite registry) {
   static boolean isFullRegistry(ExtensionRegistryLite registry) {
     return EXTENSION_REGISTRY_CLASS != null
     return EXTENSION_REGISTRY_CLASS != null
         && EXTENSION_REGISTRY_CLASS.isAssignableFrom(registry.getClass());
         && EXTENSION_REGISTRY_CLASS.isAssignableFrom(registry.getClass());
@@ -90,6 +91,6 @@ final class ExtensionRegistryFactory {
   private static final ExtensionRegistryLite invokeSubclassFactory(String methodName)
   private static final ExtensionRegistryLite invokeSubclassFactory(String methodName)
       throws Exception {
       throws Exception {
     return (ExtensionRegistryLite) EXTENSION_REGISTRY_CLASS
     return (ExtensionRegistryLite) EXTENSION_REGISTRY_CLASS
-        .getMethod(methodName).invoke(null);
+        .getDeclaredMethod(methodName).invoke(null);
   }
   }
 }
 }

+ 11 - 67
java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java

@@ -107,11 +107,12 @@ public abstract class GeneratedMessageLite<
   @SuppressWarnings("unchecked") // Guaranteed by runtime
   @SuppressWarnings("unchecked") // Guaranteed by runtime
   @Override
   @Override
   public int hashCode() {
   public int hashCode() {
-    if (memoizedHashCode == 0) {
-      HashCodeVisitor visitor = new HashCodeVisitor();
-      visit(visitor, (MessageType) this);
-      memoizedHashCode = visitor.hashCode;
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
     }
     }
+    HashCodeVisitor visitor = new HashCodeVisitor();
+    visit(visitor, (MessageType) this);
+    memoizedHashCode = visitor.hashCode;
     return memoizedHashCode;
     return memoizedHashCode;
   }
   }
 
 
@@ -331,7 +332,7 @@ public abstract class GeneratedMessageLite<
       if (isBuilt) {
       if (isBuilt) {
         MessageType newInstance =
         MessageType newInstance =
             (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE);
             (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE);
-        newInstance.visit(MergeFromVisitor.INSTANCE, instance);
+        mergeFromInstance(newInstance, instance);
         instance = newInstance;
         instance = newInstance;
         isBuilt = false;
         isBuilt = false;
       }
       }
@@ -386,10 +387,14 @@ public abstract class GeneratedMessageLite<
     /** All subclasses implement this. */
     /** All subclasses implement this. */
     public BuilderType mergeFrom(MessageType message) {
     public BuilderType mergeFrom(MessageType message) {
       copyOnWrite();
       copyOnWrite();
-      instance.visit(MergeFromVisitor.INSTANCE, message);
+      mergeFromInstance(instance, message);
       return (BuilderType) this;
       return (BuilderType) this;
     }
     }
 
 
+    private void mergeFromInstance(MessageType dest, MessageType src) {
+      dest.visit(MergeFromVisitor.INSTANCE, src);
+    }
+
     @Override
     @Override
     public MessageType getDefaultInstanceForType() {
     public MessageType getDefaultInstanceForType() {
       return defaultInstance;
       return defaultInstance;
@@ -1713,7 +1718,6 @@ public abstract class GeneratedMessageLite<
     Object visitOneofLong(boolean minePresent, Object mine, Object other);
     Object visitOneofLong(boolean minePresent, Object mine, Object other);
     Object visitOneofString(boolean minePresent, Object mine, Object other);
     Object visitOneofString(boolean minePresent, Object mine, Object other);
     Object visitOneofByteString(boolean minePresent, Object mine, Object other);
     Object visitOneofByteString(boolean minePresent, Object mine, Object other);
-    Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other);
     Object visitOneofMessage(boolean minePresent, Object mine, Object other);
     Object visitOneofMessage(boolean minePresent, Object mine, Object other);
     void visitOneofNotSet(boolean minePresent);
     void visitOneofNotSet(boolean minePresent);
 
 
@@ -1721,7 +1725,6 @@ public abstract class GeneratedMessageLite<
      * Message fields use null sentinals.
      * Message fields use null sentinals.
      */
      */
     <T extends MessageLite> T visitMessage(T mine, T other);
     <T extends MessageLite> T visitMessage(T mine, T other);
-    LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other);
 
 
     <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other);
     <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other);
     BooleanList visitBooleanList(BooleanList mine, BooleanList other);
     BooleanList visitBooleanList(BooleanList mine, BooleanList other);
@@ -1864,14 +1867,6 @@ public abstract class GeneratedMessageLite<
       throw NOT_EQUALS;
       throw NOT_EQUALS;
     }
     }
 
 
-    @Override
-    public Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other) {
-      if (minePresent && mine.equals(other)) {
-        return mine;
-      }
-      throw NOT_EQUALS;
-    }
-
     @Override
     @Override
     public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
     public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
       if (minePresent && ((GeneratedMessageLite<?, ?>) mine).equals(this, (MessageLite) other)) {
       if (minePresent && ((GeneratedMessageLite<?, ?>) mine).equals(this, (MessageLite) other)) {
@@ -1902,21 +1897,6 @@ public abstract class GeneratedMessageLite<
       return mine;
       return mine;
     }
     }
 
 
-    @Override
-    public LazyFieldLite visitLazyMessage(
-        LazyFieldLite mine, LazyFieldLite other) {
-      if (mine == null && other == null) {
-        return null;
-      }
-      if (mine == null || other == null) {
-        throw NOT_EQUALS;
-      }
-      if (mine.equals(other)) {
-        return mine;
-      }
-      throw NOT_EQUALS;
-    }
-
     @Override
     @Override
     public <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other) {
     public <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other) {
       if (!mine.equals(other)) {
       if (!mine.equals(other)) {
@@ -2093,12 +2073,6 @@ public abstract class GeneratedMessageLite<
       return mine;
       return mine;
     }
     }
 
 
-    @Override
-    public Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other) {
-      hashCode = (53 * hashCode) + mine.hashCode();
-      return mine;
-    }
-
     @Override
     @Override
     public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
     public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
       return visitMessage((MessageLite) mine, (MessageLite) other);
       return visitMessage((MessageLite) mine, (MessageLite) other);
@@ -2127,18 +2101,6 @@ public abstract class GeneratedMessageLite<
       return mine;
       return mine;
     }
     }
 
 
-    @Override
-    public LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other) {
-      final int protoHash;
-      if (mine != null) {
-        protoHash = mine.hashCode();
-      } else {
-        protoHash = 37;
-      }
-      hashCode = (53 * hashCode) + protoHash;
-      return mine;
-    }
-
     @Override
     @Override
     public <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other) {
     public <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other) {
       hashCode = (53 * hashCode) + mine.hashCode();
       hashCode = (53 * hashCode) + mine.hashCode();
@@ -2281,13 +2243,6 @@ public abstract class GeneratedMessageLite<
       return other;
       return other;
     }
     }
 
 
-    @Override
-    public Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other) {
-      LazyFieldLite lazy = minePresent ? (LazyFieldLite) mine : new LazyFieldLite();
-      lazy.merge((LazyFieldLite) other);
-      return lazy;
-    }
-
     @Override
     @Override
     public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
     public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
       if (minePresent) {
       if (minePresent) {
@@ -2311,17 +2266,6 @@ public abstract class GeneratedMessageLite<
       return mine != null ? mine : other;
       return mine != null ? mine : other;
     }
     }
 
 
-    @Override
-    public LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other) {
-      if (other != null) {
-        if (mine == null) {
-          mine = new LazyFieldLite();
-        }
-        mine.merge(other);
-      }
-      return mine;
-    }
-
     @Override
     @Override
     public <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other) {
     public <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other) {
       int size = mine.size();
       int size = mine.size();

+ 53 - 40
java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java

@@ -30,6 +30,8 @@
 
 
 package com.google.protobuf;
 package com.google.protobuf;
 
 
+import static com.google.protobuf.Internal.checkNotNull;
+
 import com.google.protobuf.Descriptors.Descriptor;
 import com.google.protobuf.Descriptors.Descriptor;
 import com.google.protobuf.Descriptors.EnumDescriptor;
 import com.google.protobuf.Descriptors.EnumDescriptor;
 import com.google.protobuf.Descriptors.EnumValueDescriptor;
 import com.google.protobuf.Descriptors.EnumValueDescriptor;
@@ -47,7 +49,6 @@ import com.google.protobuf.Descriptors.OneofDescriptor;
 // to be able to use GeneratedMessage.GeneratedExtension. The GeneratedExtension definition in
 // to be able to use GeneratedMessage.GeneratedExtension. The GeneratedExtension definition in
 // this file is also excluded from opensource to avoid conflict.
 // this file is also excluded from opensource to avoid conflict.
 import com.google.protobuf.GeneratedMessage.GeneratedExtension;
 import com.google.protobuf.GeneratedMessage.GeneratedExtension;
-
 import java.io.IOException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
 import java.io.ObjectStreamException;
 import java.io.ObjectStreamException;
@@ -277,13 +278,30 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
 
 
   /**
   /**
    * Called by subclasses to parse an unknown field.
    * Called by subclasses to parse an unknown field.
+   *
    * @return {@code true} unless the tag is an end-group tag.
    * @return {@code true} unless the tag is an end-group tag.
    */
    */
   protected boolean parseUnknownField(
   protected boolean parseUnknownField(
       CodedInputStream input,
       CodedInputStream input,
       UnknownFieldSet.Builder unknownFields,
       UnknownFieldSet.Builder unknownFields,
       ExtensionRegistryLite extensionRegistry,
       ExtensionRegistryLite extensionRegistry,
-      int tag) throws IOException {
+      int tag)
+      throws IOException {
+    if (input.shouldDiscardUnknownFields()) {
+      return input.skipField(tag);
+    }
+    return unknownFields.mergeFieldFrom(tag, input);
+  }
+
+  protected boolean parseUnknownFieldProto3(
+      CodedInputStream input,
+      UnknownFieldSet.Builder unknownFields,
+      ExtensionRegistryLite extensionRegistry,
+      int tag)
+      throws IOException {
+    if (input.shouldDiscardUnknownFieldsProto3()) {
+      return input.skipField(tag);
+    }
     return unknownFields.mergeFieldFrom(tag, input);
     return unknownFields.mergeFieldFrom(tag, input);
   }
   }
 
 
@@ -619,15 +637,22 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
       return (BuilderType) this;
       return (BuilderType) this;
     }
     }
 
 
+    protected BuilderType setUnknownFieldsProto3(final UnknownFieldSet unknownFields) {
+      if (CodedInputStream.getProto3DiscardUnknownFieldsDefault()) {
+        return (BuilderType) this;
+      }
+      this.unknownFields = unknownFields;
+      onChanged();
+      return (BuilderType) this;
+    }
+
     @Override
     @Override
     public BuilderType mergeUnknownFields(
     public BuilderType mergeUnknownFields(
         final UnknownFieldSet unknownFields) {
         final UnknownFieldSet unknownFields) {
-      this.unknownFields =
+      return setUnknownFields(
         UnknownFieldSet.newBuilder(this.unknownFields)
         UnknownFieldSet.newBuilder(this.unknownFields)
                        .mergeFrom(unknownFields)
                        .mergeFrom(unknownFields)
-                       .build();
-      onChanged();
-      return (BuilderType) this;
+                       .build());
     }
     }
 
 
     @Override
     @Override
@@ -665,18 +690,6 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
       return unknownFields;
       return unknownFields;
     }
     }
 
 
-    /**
-     * Called by subclasses to parse an unknown field.
-     * @return {@code true} unless the tag is an end-group tag.
-     */
-    protected boolean parseUnknownField(
-        final CodedInputStream input,
-        final UnknownFieldSet.Builder unknownFields,
-        final ExtensionRegistryLite extensionRegistry,
-        final int tag) throws IOException {
-      return unknownFields.mergeFieldFrom(tag, input);
-    }
-
     /**
     /**
      * Implementation of {@link BuilderParent} for giving to our children. This
      * Implementation of {@link BuilderParent} for giving to our children. This
      * small inner class makes it so we don't publicly expose the BuilderParent
      * small inner class makes it so we don't publicly expose the BuilderParent
@@ -987,8 +1000,23 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
         ExtensionRegistryLite extensionRegistry,
         ExtensionRegistryLite extensionRegistry,
         int tag) throws IOException {
         int tag) throws IOException {
       return MessageReflection.mergeFieldFrom(
       return MessageReflection.mergeFieldFrom(
-          input, unknownFields, extensionRegistry, getDescriptorForType(),
-          new MessageReflection.ExtensionAdapter(extensions), tag);
+          input, input.shouldDiscardUnknownFields() ? null : unknownFields, extensionRegistry,
+          getDescriptorForType(), new MessageReflection.ExtensionAdapter(extensions), tag);
+    }
+
+    @Override
+    protected boolean parseUnknownFieldProto3(
+        CodedInputStream input,
+        UnknownFieldSet.Builder unknownFields,
+        ExtensionRegistryLite extensionRegistry,
+        int tag) throws IOException {
+      return MessageReflection.mergeFieldFrom(
+          input,
+          input.shouldDiscardUnknownFieldsProto3() ? null : unknownFields,
+          extensionRegistry,
+          getDescriptorForType(),
+          new MessageReflection.ExtensionAdapter(extensions),
+          tag);
     }
     }
 
 
 
 
@@ -1458,21 +1486,6 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
       return super.isInitialized() && extensionsAreInitialized();
       return super.isInitialized() && extensionsAreInitialized();
     }
     }
 
 
-    /**
-     * Called by subclasses to parse an unknown field or an extension.
-     * @return {@code true} unless the tag is an end-group tag.
-     */
-    @Override
-    protected boolean parseUnknownField(
-        final CodedInputStream input,
-        final UnknownFieldSet.Builder unknownFields,
-        final ExtensionRegistryLite extensionRegistry,
-        final int tag) throws IOException {
-      return MessageReflection.mergeFieldFrom(
-          input, unknownFields, extensionRegistry, getDescriptorForType(),
-          new MessageReflection.BuilderAdapter(this), tag);
-    }
-
     // ---------------------------------------------------------------
     // ---------------------------------------------------------------
     // Reflection
     // Reflection
 
 
@@ -2277,7 +2290,7 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
       public Object getRepeatedRaw(Builder builder, int index) {
       public Object getRepeatedRaw(Builder builder, int index) {
         return getRepeated(builder, index);
         return getRepeated(builder, index);
       }
       }
-      
+
       @Override
       @Override
       public void setRepeated(Builder builder, int index, Object value) {
       public void setRepeated(Builder builder, int index, Object value) {
         getMutableMapField(builder).getMutableList().set(index, coerceType((Message) value));
         getMutableMapField(builder).getMutableList().set(index, coerceType((Message) value));
@@ -2678,7 +2691,7 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
 
 
     return (Extension<MessageType, T>) extension;
     return (Extension<MessageType, T>) extension;
   }
   }
-  
+
   protected static int computeStringSize(final int fieldNumber, final Object value) {
   protected static int computeStringSize(final int fieldNumber, final Object value) {
     if (value instanceof String) {
     if (value instanceof String) {
       return CodedOutputStream.computeStringSize(fieldNumber, (String) value);
       return CodedOutputStream.computeStringSize(fieldNumber, (String) value);
@@ -2686,7 +2699,7 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
       return CodedOutputStream.computeBytesSize(fieldNumber, (ByteString) value);
       return CodedOutputStream.computeBytesSize(fieldNumber, (ByteString) value);
     }
     }
   }
   }
-  
+
   protected static int computeStringSizeNoTag(final Object value) {
   protected static int computeStringSizeNoTag(final Object value) {
     if (value instanceof String) {
     if (value instanceof String) {
       return CodedOutputStream.computeStringSizeNoTag((String) value);
       return CodedOutputStream.computeStringSizeNoTag((String) value);
@@ -2694,7 +2707,7 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
       return CodedOutputStream.computeBytesSizeNoTag((ByteString) value);
       return CodedOutputStream.computeBytesSizeNoTag((ByteString) value);
     }
     }
   }
   }
-  
+
   protected static void writeString(
   protected static void writeString(
       CodedOutputStream output, final int fieldNumber, final Object value) throws IOException {
       CodedOutputStream output, final int fieldNumber, final Object value) throws IOException {
     if (value instanceof String) {
     if (value instanceof String) {
@@ -2703,7 +2716,7 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
       output.writeBytes(fieldNumber, (ByteString) value);
       output.writeBytes(fieldNumber, (ByteString) value);
     }
     }
   }
   }
-  
+
   protected static void writeStringNoTag(
   protected static void writeStringNoTag(
       CodedOutputStream output, final Object value) throws IOException {
       CodedOutputStream output, final Object value) throws IOException {
     if (value instanceof String) {
     if (value instanceof String) {

+ 2 - 3
java/core/src/main/java/com/google/protobuf/Internal.java

@@ -414,9 +414,8 @@ public final class Internal {
     }
     }
   }
   }
 
 
-  /**
-   * An empty byte array constant used in generated code.
-   */
+
+  /** An empty byte array constant used in generated code. */
   public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
   public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
 
 
   /**
   /**

+ 4 - 0
java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java

@@ -50,6 +50,10 @@ public class InvalidProtocolBufferException extends IOException {
     super(e.getMessage(), e);
     super(e.getMessage(), e);
   }
   }
 
 
+  public InvalidProtocolBufferException(final String description, IOException e) {
+    super(description, e);
+  }
+
   /**
   /**
    * Attaches an unfinished message to the exception to support best-effort
    * Attaches an unfinished message to the exception to support best-effort
    * parsing in {@code Parser} interface.
    * parsing in {@code Parser} interface.

+ 16 - 8
java/core/src/main/java/com/google/protobuf/MessageReflection.java

@@ -31,7 +31,6 @@
 package com.google.protobuf;
 package com.google.protobuf;
 
 
 import com.google.protobuf.Descriptors.FieldDescriptor;
 import com.google.protobuf.Descriptors.FieldDescriptor;
-
 import java.io.IOException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
@@ -714,12 +713,14 @@ class MessageReflection {
   }
   }
 
 
   /**
   /**
-   * Parses a single field into MergeTarget. The target can be Message.Builder,
-   * FieldSet or MutableMessage.
+   * Parses a single field into MergeTarget. The target can be Message.Builder, FieldSet or
+   * MutableMessage.
    *
    *
-   * Package-private because it is used by GeneratedMessage.ExtendableMessage.
+   * <p>Package-private because it is used by GeneratedMessage.ExtendableMessage.
    *
    *
    * @param tag The tag, which should have already been read.
    * @param tag The tag, which should have already been read.
+   * @param unknownFields If not null, unknown fields will be merged to this {@link
+   *     UnknownFieldSet}, otherwise unknown fields will be discarded.
    * @return {@code true} unless the tag is an end-group tag.
    * @return {@code true} unless the tag is an end-group tag.
    */
    */
   static boolean mergeFieldFrom(
   static boolean mergeFieldFrom(
@@ -728,7 +729,8 @@ class MessageReflection {
       ExtensionRegistryLite extensionRegistry,
       ExtensionRegistryLite extensionRegistry,
       Descriptors.Descriptor type,
       Descriptors.Descriptor type,
       MergeTarget target,
       MergeTarget target,
-      int tag) throws IOException {
+      int tag)
+      throws IOException {
     if (type.getOptions().getMessageSetWireFormat() &&
     if (type.getOptions().getMessageSetWireFormat() &&
         tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
         tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
       mergeMessageSetExtensionFromCodedStream(
       mergeMessageSetExtensionFromCodedStream(
@@ -792,7 +794,11 @@ class MessageReflection {
     }
     }
 
 
     if (unknown) {  // Unknown field or wrong wire type.  Skip.
     if (unknown) {  // Unknown field or wrong wire type.  Skip.
-      return unknownFields.mergeFieldFrom(tag, input);
+      if (unknownFields != null) {
+        return unknownFields.mergeFieldFrom(tag, input);
+      } else {
+        return input.skipField(tag);
+      }
     }
     }
 
 
     if (packed) {
     if (packed) {
@@ -844,7 +850,9 @@ class MessageReflection {
             // If the number isn't recognized as a valid value for this enum,
             // If the number isn't recognized as a valid value for this enum,
             // drop it.
             // drop it.
             if (value == null) {
             if (value == null) {
-              unknownFields.mergeVarintField(fieldNumber, rawValue);
+              if (unknownFields != null) {
+                unknownFields.mergeVarintField(fieldNumber, rawValue);
+              }
               return true;
               return true;
             }
             }
           }
           }
@@ -947,7 +955,7 @@ class MessageReflection {
         mergeMessageSetExtensionFromBytes(
         mergeMessageSetExtensionFromBytes(
             rawBytes, extension, extensionRegistry, target);
             rawBytes, extension, extensionRegistry, target);
       } else { // We don't know how to parse this. Ignore it.
       } else { // We don't know how to parse this. Ignore it.
-        if (rawBytes != null) {
+        if (rawBytes != null && unknownFields != null) {
           unknownFields.mergeField(typeId, UnknownFieldSet.Field.newBuilder()
           unknownFields.mergeField(typeId, UnknownFieldSet.Field.newBuilder()
               .addLengthDelimited(rawBytes).build());
               .addLengthDelimited(rawBytes).build());
         }
         }

+ 106 - 105
java/core/src/main/java/com/google/protobuf/TextFormat.java

@@ -34,7 +34,6 @@ import com.google.protobuf.Descriptors.Descriptor;
 import com.google.protobuf.Descriptors.EnumDescriptor;
 import com.google.protobuf.Descriptors.EnumDescriptor;
 import com.google.protobuf.Descriptors.EnumValueDescriptor;
 import com.google.protobuf.Descriptors.EnumValueDescriptor;
 import com.google.protobuf.Descriptors.FieldDescriptor;
 import com.google.protobuf.Descriptors.FieldDescriptor;
-
 import java.io.IOException;
 import java.io.IOException;
 import java.math.BigInteger;
 import java.math.BigInteger;
 import java.nio.CharBuffer;
 import java.nio.CharBuffer;
@@ -56,14 +55,7 @@ import java.util.regex.Pattern;
 public final class TextFormat {
 public final class TextFormat {
   private TextFormat() {}
   private TextFormat() {}
 
 
-  private static final Logger logger =
-      Logger.getLogger(TextFormat.class.getName());
-
-  private static final Printer DEFAULT_PRINTER = new Printer();
-  private static final Printer SINGLE_LINE_PRINTER =
-      (new Printer()).setSingleLineMode(true);
-  private static final Printer UNICODE_PRINTER =
-      (new Printer()).setEscapeNonAscii(false);
+  private static final Logger logger = Logger.getLogger(TextFormat.class.getName());
 
 
   /**
   /**
    * Outputs a textual representation of the Protocol Message supplied into
    * Outputs a textual representation of the Protocol Message supplied into
@@ -73,14 +65,14 @@ public final class TextFormat {
   public static void print(
   public static void print(
       final MessageOrBuilder message, final Appendable output)
       final MessageOrBuilder message, final Appendable output)
       throws IOException {
       throws IOException {
-    DEFAULT_PRINTER.print(message, new TextGenerator(output));
+    Printer.DEFAULT.print(message, multiLineOutput(output));
   }
   }
 
 
   /** Outputs a textual representation of {@code fields} to {@code output}. */
   /** Outputs a textual representation of {@code fields} to {@code output}. */
   public static void print(final UnknownFieldSet fields,
   public static void print(final UnknownFieldSet fields,
                            final Appendable output)
                            final Appendable output)
                            throws IOException {
                            throws IOException {
-    DEFAULT_PRINTER.printUnknownFields(fields, new TextGenerator(output));
+    Printer.DEFAULT.printUnknownFields(fields, multiLineOutput(output));
   }
   }
 
 
   /**
   /**
@@ -90,7 +82,7 @@ public final class TextFormat {
   public static void printUnicode(
   public static void printUnicode(
       final MessageOrBuilder message, final Appendable output)
       final MessageOrBuilder message, final Appendable output)
       throws IOException {
       throws IOException {
-    UNICODE_PRINTER.print(message, new TextGenerator(output));
+    Printer.UNICODE.print(message, multiLineOutput(output));
   }
   }
 
 
   /**
   /**
@@ -100,7 +92,7 @@ public final class TextFormat {
   public static void printUnicode(final UnknownFieldSet fields,
   public static void printUnicode(final UnknownFieldSet fields,
                                   final Appendable output)
                                   final Appendable output)
                                   throws IOException {
                                   throws IOException {
-    UNICODE_PRINTER.printUnknownFields(fields, new TextGenerator(output));
+    Printer.UNICODE.printUnknownFields(fields, multiLineOutput(output));
   }
   }
 
 
   /**
   /**
@@ -109,10 +101,9 @@ public final class TextFormat {
    */
    */
   public static String shortDebugString(final MessageOrBuilder message) {
   public static String shortDebugString(final MessageOrBuilder message) {
     try {
     try {
-      final StringBuilder sb = new StringBuilder();
-      SINGLE_LINE_PRINTER.print(message, new TextGenerator(sb));
-      // Single line mode currently might have an extra space at the end.
-      return sb.toString().trim();
+      final StringBuilder text = new StringBuilder();
+      Printer.DEFAULT.print(message, singleLineOutput(text));
+      return text.toString();
     } catch (IOException e) {
     } catch (IOException e) {
       throw new IllegalStateException(e);
       throw new IllegalStateException(e);
     }
     }
@@ -125,11 +116,11 @@ public final class TextFormat {
   public static String shortDebugString(final FieldDescriptor field,
   public static String shortDebugString(final FieldDescriptor field,
                                         final Object value) {
                                         final Object value) {
     try {
     try {
-      final StringBuilder sb = new StringBuilder();
-      SINGLE_LINE_PRINTER.printField(field, value, new TextGenerator(sb));
-      return sb.toString().trim();
+      final StringBuilder text = new StringBuilder();
+      Printer.DEFAULT.printField(field, value, singleLineOutput(text));
+      return text.toString();
     } catch (IOException e) {
     } catch (IOException e) {
-        throw new IllegalStateException(e);
+      throw new IllegalStateException(e);
     }
     }
   }
   }
 
 
@@ -139,10 +130,9 @@ public final class TextFormat {
    */
    */
   public static String shortDebugString(final UnknownFieldSet fields) {
   public static String shortDebugString(final UnknownFieldSet fields) {
     try {
     try {
-      final StringBuilder sb = new StringBuilder();
-      SINGLE_LINE_PRINTER.printUnknownFields(fields, new TextGenerator(sb));
-      // Single line mode currently might have an extra space at the end.
-      return sb.toString().trim();
+      final StringBuilder text = new StringBuilder();
+      Printer.DEFAULT.printUnknownFields(fields, singleLineOutput(text));
+      return text.toString();
     } catch (IOException e) {
     } catch (IOException e) {
       throw new IllegalStateException(e);
       throw new IllegalStateException(e);
     }
     }
@@ -183,7 +173,7 @@ public final class TextFormat {
   public static String printToUnicodeString(final MessageOrBuilder message) {
   public static String printToUnicodeString(final MessageOrBuilder message) {
     try {
     try {
       final StringBuilder text = new StringBuilder();
       final StringBuilder text = new StringBuilder();
-      UNICODE_PRINTER.print(message, new TextGenerator(text));
+      Printer.UNICODE.print(message, multiLineOutput(text));
       return text.toString();
       return text.toString();
     } catch (IOException e) {
     } catch (IOException e) {
       throw new IllegalStateException(e);
       throw new IllegalStateException(e);
@@ -197,7 +187,7 @@ public final class TextFormat {
   public static String printToUnicodeString(final UnknownFieldSet fields) {
   public static String printToUnicodeString(final UnknownFieldSet fields) {
     try {
     try {
       final StringBuilder text = new StringBuilder();
       final StringBuilder text = new StringBuilder();
-      UNICODE_PRINTER.printUnknownFields(fields, new TextGenerator(text));
+      Printer.UNICODE.printUnknownFields(fields, multiLineOutput(text));
       return text.toString();
       return text.toString();
     } catch (IOException e) {
     } catch (IOException e) {
       throw new IllegalStateException(e);
       throw new IllegalStateException(e);
@@ -208,7 +198,7 @@ public final class TextFormat {
                                 final Object value,
                                 final Object value,
                                 final Appendable output)
                                 final Appendable output)
                                 throws IOException {
                                 throws IOException {
-    DEFAULT_PRINTER.printField(field, value, new TextGenerator(output));
+    Printer.DEFAULT.printField(field, value, multiLineOutput(output));
   }
   }
 
 
   public static String printFieldToString(final FieldDescriptor field,
   public static String printFieldToString(final FieldDescriptor field,
@@ -222,6 +212,23 @@ public final class TextFormat {
     }
     }
   }
   }
 
 
+  /**
+   * Outputs a unicode textual representation of the value of given field value.
+   *
+   * <p>Same as {@code printFieldValue()}, except that non-ASCII characters in string type fields
+   * are not escaped in backslash+octals.
+   *
+   * @param field the descriptor of the field
+   * @param value the value of the field
+   * @param output the output to which to append the formatted value
+   * @throws ClassCastException if the value is not appropriate for the given field descriptor
+   * @throws IOException if there is an exception writing to the output
+   */
+  public static void printUnicodeFieldValue(
+      final FieldDescriptor field, final Object value, final Appendable output) throws IOException {
+    Printer.UNICODE.printFieldValue(field, value, multiLineOutput(output));
+  }
+
   /**
   /**
    * Outputs a textual representation of the value of given field value.
    * Outputs a textual representation of the value of given field value.
    *
    *
@@ -236,7 +243,7 @@ public final class TextFormat {
                                      final Object value,
                                      final Object value,
                                      final Appendable output)
                                      final Appendable output)
                                      throws IOException {
                                      throws IOException {
-    DEFAULT_PRINTER.printFieldValue(field, value, new TextGenerator(output));
+    Printer.DEFAULT.printFieldValue(field, value, multiLineOutput(output));
   }
   }
 
 
   /**
   /**
@@ -253,7 +260,7 @@ public final class TextFormat {
                                             final Object value,
                                             final Object value,
                                             final Appendable output)
                                             final Appendable output)
                                             throws IOException {
                                             throws IOException {
-    printUnknownFieldValue(tag, value, new TextGenerator(output));
+    printUnknownFieldValue(tag, value, multiLineOutput(output));
   }
   }
 
 
   private static void printUnknownFieldValue(final int tag,
   private static void printUnknownFieldValue(final int tag,
@@ -277,7 +284,7 @@ public final class TextFormat {
         generator.print("\"");
         generator.print("\"");
         break;
         break;
       case WireFormat.WIRETYPE_START_GROUP:
       case WireFormat.WIRETYPE_START_GROUP:
-        DEFAULT_PRINTER.printUnknownFields((UnknownFieldSet) value, generator);
+        Printer.DEFAULT.printUnknownFields((UnknownFieldSet) value, generator);
         break;
         break;
       default:
       default:
         throw new IllegalArgumentException("Bad tag: " + tag);
         throw new IllegalArgumentException("Bad tag: " + tag);
@@ -286,24 +293,16 @@ public final class TextFormat {
 
 
   /** Helper class for converting protobufs to text. */
   /** Helper class for converting protobufs to text. */
   private static final class Printer {
   private static final class Printer {
-    /** Whether to omit newlines from the output. */
-    boolean singleLineMode = false;
+    // Printer instance which escapes non-ASCII characters.
+    static final Printer DEFAULT = new Printer(true);
+    // Printer instance which emits Unicode (it still escapes newlines and quotes in strings).
+    static final Printer UNICODE = new Printer(false);
 
 
     /** Whether to escape non ASCII characters with backslash and octal. */
     /** Whether to escape non ASCII characters with backslash and octal. */
-    boolean escapeNonAscii = true;
-
-    private Printer() {}
+    private final boolean escapeNonAscii;
 
 
-    /** Setter of singleLineMode */
-    private Printer setSingleLineMode(boolean singleLineMode) {
-      this.singleLineMode = singleLineMode;
-      return this;
-    }
-
-    /** Setter of escapeNonAscii */
-    private Printer setEscapeNonAscii(boolean escapeNonAscii) {
+    private Printer(boolean escapeNonAscii) {
       this.escapeNonAscii = escapeNonAscii;
       this.escapeNonAscii = escapeNonAscii;
-      return this;
     }
     }
 
 
     private void print(
     private void print(
@@ -355,12 +354,9 @@ public final class TextFormat {
       }
       }
 
 
       if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
       if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
-        if (singleLineMode) {
-          generator.print(" { ");
-        } else {
-          generator.print(" {\n");
-          generator.indent();
-        }
+        generator.print(" {");
+        generator.eol();
+        generator.indent();
       } else {
       } else {
         generator.print(": ");
         generator.print(": ");
       }
       }
@@ -368,19 +364,10 @@ public final class TextFormat {
       printFieldValue(field, value, generator);
       printFieldValue(field, value, generator);
 
 
       if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
       if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
-        if (singleLineMode) {
-          generator.print("} ");
-        } else {
-          generator.outdent();
-          generator.print("}\n");
-        }
-      } else {
-        if (singleLineMode) {
-          generator.print(" ");
-        } else {
-          generator.print("\n");
-        }
+        generator.outdent();
+        generator.print("}");
       }
       }
+      generator.eol();
     }
     }
 
 
     private void printFieldValue(final FieldDescriptor field,
     private void printFieldValue(final FieldDescriptor field,
@@ -469,19 +456,13 @@ public final class TextFormat {
             field.getLengthDelimitedList(), generator);
             field.getLengthDelimitedList(), generator);
         for (final UnknownFieldSet value : field.getGroupList()) {
         for (final UnknownFieldSet value : field.getGroupList()) {
           generator.print(entry.getKey().toString());
           generator.print(entry.getKey().toString());
-          if (singleLineMode) {
-            generator.print(" { ");
-          } else {
-            generator.print(" {\n");
-            generator.indent();
-          }
+          generator.print(" {");
+          generator.eol();
+          generator.indent();
           printUnknownFields(value, generator);
           printUnknownFields(value, generator);
-          if (singleLineMode) {
-            generator.print("} ");
-          } else {
-            generator.outdent();
-            generator.print("}\n");
-          }
+          generator.outdent();
+          generator.print("}");
+          generator.eol();
         }
         }
       }
       }
     }
     }
@@ -495,7 +476,7 @@ public final class TextFormat {
         generator.print(String.valueOf(number));
         generator.print(String.valueOf(number));
         generator.print(": ");
         generator.print(": ");
         printUnknownFieldValue(wireType, value, generator);
         printUnknownFieldValue(wireType, value, generator);
-        generator.print(singleLineMode ? " " : "\n");
+        generator.eol();
       }
       }
     }
     }
   }
   }
@@ -521,16 +502,29 @@ public final class TextFormat {
     }
     }
   }
   }
 
 
-  /**
+  private static TextGenerator multiLineOutput(Appendable output) {
+    return new TextGenerator(output, false);
+  }
+
+  private static TextGenerator singleLineOutput(Appendable output) {
+    return new TextGenerator(output, true);
+  }
+
+ /**
    * An inner class for writing text to the output stream.
    * An inner class for writing text to the output stream.
    */
    */
   private static final class TextGenerator {
   private static final class TextGenerator {
     private final Appendable output;
     private final Appendable output;
     private final StringBuilder indent = new StringBuilder();
     private final StringBuilder indent = new StringBuilder();
-    private boolean atStartOfLine = true;
+    private final boolean singleLineMode;
+    // While technically we are "at the start of a line" at the very beginning of the output, all
+    // we would do in response to this is emit the (zero length) indentation, so it has no effect.
+    // Setting it false here does however suppress an unwanted leading space in single-line mode.
+    private boolean atStartOfLine = false;
 
 
-    private TextGenerator(final Appendable output) {
+    private TextGenerator(final Appendable output, boolean singleLineMode) {
       this.output = output;
       this.output = output;
+      this.singleLineMode = singleLineMode;
     }
     }
 
 
     /**
     /**
@@ -552,35 +546,31 @@ public final class TextFormat {
         throw new IllegalArgumentException(
         throw new IllegalArgumentException(
             " Outdent() without matching Indent().");
             " Outdent() without matching Indent().");
       }
       }
-      indent.delete(length - 2, length);
+      indent.setLength(length - 2);
     }
     }
 
 
     /**
     /**
-     * Print text to the output stream.
+     * Print text to the output stream. Bare newlines are never expected to be passed to this
+     * method; to indicate the end of a line, call "eol()".
      */
      */
     public void print(final CharSequence text) throws IOException {
     public void print(final CharSequence text) throws IOException {
-      final int size = text.length();
-      int pos = 0;
-
-      for (int i = 0; i < size; i++) {
-        if (text.charAt(i) == '\n') {
-          write(text.subSequence(pos, i + 1));
-          pos = i + 1;
-          atStartOfLine = true;
-        }
+      if (atStartOfLine) {
+        atStartOfLine = false;
+        output.append(singleLineMode ? " " : indent);
       }
       }
-      write(text.subSequence(pos, size));
+      output.append(text);
     }
     }
 
 
-    private void write(final CharSequence data) throws IOException {
-      if (data.length() == 0) {
-        return;
-      }
-      if (atStartOfLine) {
-        atStartOfLine = false;
-        output.append(indent);
+    /**
+     * Signifies reaching the "end of the current line" in the output. In single-line mode, this
+     * does not result in a newline being emitted, but ensures that a separating space is written
+     * before the next output.
+     */
+    public void eol() throws IOException {
+      if (!singleLineMode) {
+        output.append("\n");
       }
       }
-      output.append(data);
+      atStartOfLine = true;
     }
     }
   }
   }
 
 
@@ -1469,9 +1459,15 @@ public final class TextFormat {
             extensionRegistry, name.toString());
             extensionRegistry, name.toString());
 
 
         if (extension == null) {
         if (extension == null) {
-          unknownFields.add((tokenizer.getPreviousLine() + 1) + ":" +
-              (tokenizer.getPreviousColumn() + 1) + ":\t" +
-              type.getFullName() + ".[" + name + "]");
+          unknownFields.add(
+              (tokenizer.getPreviousLine() + 1)
+                  + ":"
+                  + (tokenizer.getPreviousColumn() + 1)
+                  + ":\t"
+                  + type.getFullName()
+                  + ".["
+                  + name
+                  + "]");
         } else {
         } else {
           if (extension.descriptor.getContainingType() != type) {
           if (extension.descriptor.getContainingType() != type) {
             throw tokenizer.parseExceptionPreviousToken(
             throw tokenizer.parseExceptionPreviousToken(
@@ -1506,9 +1502,14 @@ public final class TextFormat {
         }
         }
 
 
         if (field == null) {
         if (field == null) {
-          unknownFields.add((tokenizer.getPreviousLine() + 1) + ":" +
-              (tokenizer.getPreviousColumn() + 1) + ":\t" +
-              type.getFullName() + "." + name);
+          unknownFields.add(
+              (tokenizer.getPreviousLine() + 1)
+                  + ":"
+                  + (tokenizer.getPreviousColumn() + 1)
+                  + ":\t"
+                  + type.getFullName()
+                  + "."
+                  + name);
         }
         }
       }
       }
 
 

+ 1 - 1
java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java

@@ -715,7 +715,7 @@ public final class UnknownFieldSet implements MessageLite {
    * @see UnknownFieldSet
    * @see UnknownFieldSet
    */
    */
   public static final class Field {
   public static final class Field {
-    Field() {}
+    private Field() {}
 
 
     /** Construct a new {@link Builder}. */
     /** Construct a new {@link Builder}. */
     public static Builder newBuilder() {
     public static Builder newBuilder() {

+ 125 - 48
java/core/src/main/java/com/google/protobuf/UnsafeUtil.java

@@ -47,8 +47,29 @@ final class UnsafeUtil {
   private static final boolean HAS_UNSAFE_BYTEBUFFER_OPERATIONS =
   private static final boolean HAS_UNSAFE_BYTEBUFFER_OPERATIONS =
       supportsUnsafeByteBufferOperations();
       supportsUnsafeByteBufferOperations();
   private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations();
   private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations();
-  private static final boolean HAS_UNSAFE_COPY_MEMORY = supportsUnsafeCopyMemory();
-  private static final long ARRAY_BASE_OFFSET = byteArrayBaseOffset();
+
+  private static final long BYTE_ARRAY_BASE_OFFSET = arrayBaseOffset(byte[].class);
+  // Micro-optimization: we can assume a scale of 1 and skip the multiply
+  // private static final long BYTE_ARRAY_INDEX_SCALE = 1;
+
+  private static final long BOOLEAN_ARRAY_BASE_OFFSET = arrayBaseOffset(boolean[].class);
+  private static final long BOOLEAN_ARRAY_INDEX_SCALE = arrayIndexScale(boolean[].class);
+
+  private static final long INT_ARRAY_BASE_OFFSET = arrayBaseOffset(int[].class);
+  private static final long INT_ARRAY_INDEX_SCALE = arrayIndexScale(int[].class);
+
+  private static final long LONG_ARRAY_BASE_OFFSET = arrayBaseOffset(long[].class);
+  private static final long LONG_ARRAY_INDEX_SCALE = arrayIndexScale(long[].class);
+
+  private static final long FLOAT_ARRAY_BASE_OFFSET = arrayBaseOffset(float[].class);
+  private static final long FLOAT_ARRAY_INDEX_SCALE = arrayIndexScale(float[].class);
+
+  private static final long DOUBLE_ARRAY_BASE_OFFSET = arrayBaseOffset(double[].class);
+  private static final long DOUBLE_ARRAY_INDEX_SCALE = arrayIndexScale(double[].class);
+
+  private static final long OBJECT_ARRAY_BASE_OFFSET = arrayBaseOffset(Object[].class);
+  private static final long OBJECT_ARRAY_INDEX_SCALE = arrayIndexScale(Object[].class);
+
   private static final long BUFFER_ADDRESS_OFFSET = fieldOffset(bufferAddressField());
   private static final long BUFFER_ADDRESS_OFFSET = fieldOffset(bufferAddressField());
 
 
   private UnsafeUtil() {}
   private UnsafeUtil() {}
@@ -57,10 +78,6 @@ final class UnsafeUtil {
     return HAS_UNSAFE_ARRAY_OPERATIONS;
     return HAS_UNSAFE_ARRAY_OPERATIONS;
   }
   }
 
 
-  static boolean hasUnsafeCopyMemory() {
-    return HAS_UNSAFE_COPY_MEMORY;
-  }
-
   static boolean hasUnsafeByteBufferOperations() {
   static boolean hasUnsafeByteBufferOperations() {
     return HAS_UNSAFE_BYTEBUFFER_OPERATIONS;
     return HAS_UNSAFE_BYTEBUFFER_OPERATIONS;
   }
   }
@@ -69,8 +86,12 @@ final class UnsafeUtil {
     return MEMORY_ACCESSOR.objectFieldOffset(field);
     return MEMORY_ACCESSOR.objectFieldOffset(field);
   }
   }
 
 
-  static long getArrayBaseOffset() {
-    return ARRAY_BASE_OFFSET;
+  private static int arrayBaseOffset(Class<?> clazz) {
+    return HAS_UNSAFE_ARRAY_OPERATIONS ? MEMORY_ACCESSOR.arrayBaseOffset(clazz) : -1;
+  }
+
+  private static int arrayIndexScale(Class<?> clazz) {
+    return HAS_UNSAFE_ARRAY_OPERATIONS ? MEMORY_ACCESSOR.arrayIndexScale(clazz) : -1;
   }
   }
 
 
   static byte getByte(Object target, long offset) {
   static byte getByte(Object target, long offset) {
@@ -129,9 +150,82 @@ final class UnsafeUtil {
     MEMORY_ACCESSOR.putObject(target, offset, value);
     MEMORY_ACCESSOR.putObject(target, offset, value);
   }
   }
 
 
-  static void copyMemory(
-      Object src, long srcOffset, Object target, long targetOffset, long length) {
-    MEMORY_ACCESSOR.copyMemory(src, srcOffset, target, targetOffset, length);
+  static byte getByte(byte[] target, long index) {
+    return MEMORY_ACCESSOR.getByte(target, BYTE_ARRAY_BASE_OFFSET + index);
+  }
+
+  static void putByte(byte[] target, long index, byte value) {
+    MEMORY_ACCESSOR.putByte(target, BYTE_ARRAY_BASE_OFFSET + index, value);
+  }
+
+  static int getInt(int[] target, long index) {
+    return MEMORY_ACCESSOR.getInt(target, INT_ARRAY_BASE_OFFSET + (index * INT_ARRAY_INDEX_SCALE));
+  }
+
+  static void putInt(int[] target, long index, int value) {
+    MEMORY_ACCESSOR.putInt(target, INT_ARRAY_BASE_OFFSET + (index * INT_ARRAY_INDEX_SCALE), value);
+  }
+
+  static long getLong(long[] target, long index) {
+    return MEMORY_ACCESSOR.getLong(
+        target, LONG_ARRAY_BASE_OFFSET + (index * LONG_ARRAY_INDEX_SCALE));
+  }
+
+  static void putLong(long[] target, long index, long value) {
+    MEMORY_ACCESSOR.putLong(
+        target, LONG_ARRAY_BASE_OFFSET + (index * LONG_ARRAY_INDEX_SCALE), value);
+  }
+
+  static boolean getBoolean(boolean[] target, long index) {
+    return MEMORY_ACCESSOR.getBoolean(
+        target, BOOLEAN_ARRAY_BASE_OFFSET + (index * BOOLEAN_ARRAY_INDEX_SCALE));
+  }
+
+  static void putBoolean(boolean[] target, long index, boolean value) {
+    MEMORY_ACCESSOR.putBoolean(
+        target, BOOLEAN_ARRAY_BASE_OFFSET + (index * BOOLEAN_ARRAY_INDEX_SCALE), value);
+  }
+
+  static float getFloat(float[] target, long index) {
+    return MEMORY_ACCESSOR.getFloat(
+        target, FLOAT_ARRAY_BASE_OFFSET + (index * FLOAT_ARRAY_INDEX_SCALE));
+  }
+
+  static void putFloat(float[] target, long index, float value) {
+    MEMORY_ACCESSOR.putFloat(
+        target, FLOAT_ARRAY_BASE_OFFSET + (index * FLOAT_ARRAY_INDEX_SCALE), value);
+  }
+
+  static double getDouble(double[] target, long index) {
+    return MEMORY_ACCESSOR.getDouble(
+        target, DOUBLE_ARRAY_BASE_OFFSET + (index * DOUBLE_ARRAY_INDEX_SCALE));
+  }
+
+  static void putDouble(double[] target, long index, double value) {
+    MEMORY_ACCESSOR.putDouble(
+        target, DOUBLE_ARRAY_BASE_OFFSET + (index * DOUBLE_ARRAY_INDEX_SCALE), value);
+  }
+
+  static Object getObject(Object[] target, long index) {
+    return MEMORY_ACCESSOR.getObject(
+        target, OBJECT_ARRAY_BASE_OFFSET + (index * OBJECT_ARRAY_INDEX_SCALE));
+  }
+
+  static void putObject(Object[] target, long index, Object value) {
+    MEMORY_ACCESSOR.putObject(
+        target, OBJECT_ARRAY_BASE_OFFSET + (index * OBJECT_ARRAY_INDEX_SCALE), value);
+  }
+
+  static void copyMemory(byte[] src, long srcIndex, long targetOffset, long length) {
+    MEMORY_ACCESSOR.copyMemory(src, srcIndex, targetOffset, length);
+  }
+
+  static void copyMemory(long srcOffset, byte[] target, long targetIndex, long length) {
+    MEMORY_ACCESSOR.copyMemory(srcOffset, target, targetIndex, length);
+  }
+
+  static void copyMemory(byte[] src, long srcIndex, byte[] target, long targetIndex, long length) {
+    System.arraycopy(src, (int) srcIndex, target, (int) targetIndex, (int) length); 
   }
   }
 
 
   static byte getByte(long address) {
   static byte getByte(long address) {
@@ -221,6 +315,7 @@ final class UnsafeUtil {
       Class<?> clazz = UNSAFE.getClass();
       Class<?> clazz = UNSAFE.getClass();
       clazz.getMethod("objectFieldOffset", Field.class);
       clazz.getMethod("objectFieldOffset", Field.class);
       clazz.getMethod("arrayBaseOffset", Class.class);
       clazz.getMethod("arrayBaseOffset", Class.class);
+      clazz.getMethod("arrayIndexScale", Class.class);
       clazz.getMethod("getInt", Object.class, long.class);
       clazz.getMethod("getInt", Object.class, long.class);
       clazz.getMethod("putInt", Object.class, long.class, int.class);
       clazz.getMethod("putInt", Object.class, long.class, int.class);
       clazz.getMethod("getLong", Object.class, long.class);
       clazz.getMethod("getLong", Object.class, long.class);
@@ -245,27 +340,6 @@ final class UnsafeUtil {
     return false;
     return false;
   }
   }
 
 
-  /**
-   * Indicates whether or not unsafe copyMemory(object, long, object, long, long) operations are
-   * supported on this platform.
-   */
-  private static boolean supportsUnsafeCopyMemory() {
-    if (UNSAFE == null) {
-      return false;
-    }
-    try {
-      Class<?> clazz = UNSAFE.getClass();
-      clazz.getMethod("copyMemory", Object.class, long.class, Object.class, long.class, long.class);
-
-      return true;
-    } catch (Throwable e) {
-      logger.log(
-          Level.WARNING,
-          "copyMemory is missing from platform - proto runtime falling back to safer methods.");
-    }
-    return false;
-  }
-
   private static boolean supportsUnsafeByteBufferOperations() {
   private static boolean supportsUnsafeByteBufferOperations() {
     if (UNSAFE == null) {
     if (UNSAFE == null) {
       return false;
       return false;
@@ -283,6 +357,7 @@ final class UnsafeUtil {
       clazz.getMethod("getLong", long.class);
       clazz.getMethod("getLong", long.class);
       clazz.getMethod("putLong", long.class, long.class);
       clazz.getMethod("putLong", long.class, long.class);
       clazz.getMethod("copyMemory", long.class, long.class, long.class);
       clazz.getMethod("copyMemory", long.class, long.class, long.class);
+      clazz.getMethod("copyMemory", Object.class, long.class, Object.class, long.class, long.class);
       return true;
       return true;
     } catch (Throwable e) {
     } catch (Throwable e) {
       logger.log(
       logger.log(
@@ -307,13 +382,6 @@ final class UnsafeUtil {
     return field(Buffer.class, "address");
     return field(Buffer.class, "address");
   }
   }
 
 
-  /**
-   * Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not available.
-   */
-  private static int byteArrayBaseOffset() {
-    return HAS_UNSAFE_ARRAY_OPERATIONS ? MEMORY_ACCESSOR.arrayBaseOffset(byte[].class) : -1;
-  }
-
   /**
   /**
    * Returns the offset of the provided field, or {@code -1} if {@code sun.misc.Unsafe} is not
    * Returns the offset of the provided field, or {@code -1} if {@code sun.misc.Unsafe} is not
    * available.
    * available.
@@ -394,6 +462,10 @@ final class UnsafeUtil {
       return unsafe.arrayBaseOffset(clazz);
       return unsafe.arrayBaseOffset(clazz);
     }
     }
 
 
+    public final int arrayIndexScale(Class<?> clazz) {
+      return unsafe.arrayIndexScale(clazz);
+    }
+
     public abstract byte getByte(long address);
     public abstract byte getByte(long address);
 
 
     public abstract void putByte(long address, byte value);
     public abstract void putByte(long address, byte value);
@@ -408,10 +480,11 @@ final class UnsafeUtil {
 
 
     public abstract void copyMemory(long srcAddress, long targetAddress, long length);
     public abstract void copyMemory(long srcAddress, long targetAddress, long length);
 
 
-    public abstract void copyMemory(
-        Object src, long srcOffset, Object target, long targetOffset, long length);
-
     public abstract Object getStaticObject(Field field);
     public abstract Object getStaticObject(Field field);
+    
+    public abstract void copyMemory(long srcOffset, byte[] target, long targetIndex, long length);
+    
+    public abstract void copyMemory(byte[] src, long srcIndex, long targetOffset, long length);
   }
   }
 
 
   private static final class JvmMemoryAccessor extends MemoryAccessor {
   private static final class JvmMemoryAccessor extends MemoryAccessor {
@@ -490,16 +563,20 @@ final class UnsafeUtil {
       unsafe.putDouble(target, offset, value);
       unsafe.putDouble(target, offset, value);
     }
     }
 
 
-    @Override
-    public void copyMemory(
-        Object src, long srcOffset, Object target, long targetOffset, long length) {
-      unsafe.copyMemory(src, srcOffset, target, targetOffset, length);
-    }
-
     @Override
     @Override
     public void copyMemory(long srcAddress, long targetAddress, long length) {
     public void copyMemory(long srcAddress, long targetAddress, long length) {
       unsafe.copyMemory(srcAddress, targetAddress, length);
       unsafe.copyMemory(srcAddress, targetAddress, length);
     }
     }
+    
+    @Override 
+    public void copyMemory(long srcOffset, byte[] target, long targetIndex, long length) {
+      unsafe.copyMemory(null, srcOffset, target, BYTE_ARRAY_BASE_OFFSET + targetIndex, length);
+    }
+    
+    @Override
+    public void copyMemory(byte[] src, long srcIndex, long targetOffset, long length) {
+      unsafe.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex, null, targetOffset, length);
+    }
 
 
     @Override
     @Override
     public Object getStaticObject(Field field) {
     public Object getStaticObject(Field field) {

+ 9 - 24
java/core/src/main/java/com/google/protobuf/Utf8.java

@@ -31,7 +31,6 @@
 package com.google.protobuf;
 package com.google.protobuf;
 
 
 import static com.google.protobuf.UnsafeUtil.addressOffset;
 import static com.google.protobuf.UnsafeUtil.addressOffset;
-import static com.google.protobuf.UnsafeUtil.getArrayBaseOffset;
 import static com.google.protobuf.UnsafeUtil.hasUnsafeArrayOperations;
 import static com.google.protobuf.UnsafeUtil.hasUnsafeArrayOperations;
 import static com.google.protobuf.UnsafeUtil.hasUnsafeByteBufferOperations;
 import static com.google.protobuf.UnsafeUtil.hasUnsafeByteBufferOperations;
 import static java.lang.Character.MAX_SURROGATE;
 import static java.lang.Character.MAX_SURROGATE;
@@ -1001,8 +1000,8 @@ final class Utf8 {
         throw new ArrayIndexOutOfBoundsException(
         throw new ArrayIndexOutOfBoundsException(
             String.format("Array length=%d, index=%d, limit=%d", bytes.length, index, limit));
             String.format("Array length=%d, index=%d, limit=%d", bytes.length, index, limit));
       }
       }
-      long offset = getArrayBaseOffset() + index;
-      final long offsetLimit = getArrayBaseOffset() + limit;
+      long offset = index;
+      final long offsetLimit = limit;
       if (state != COMPLETE) {
       if (state != COMPLETE) {
         // The previous decoding operation was incomplete (or malformed).
         // The previous decoding operation was incomplete (or malformed).
         // We look for a well-formed sequence consisting of bytes from
         // We look for a well-formed sequence consisting of bytes from
@@ -1187,7 +1186,7 @@ final class Utf8 {
 
 
     @Override
     @Override
     int encodeUtf8(final CharSequence in, final byte[] out, final int offset, final int length) {
     int encodeUtf8(final CharSequence in, final byte[] out, final int offset, final int length) {
-      long outIx = getArrayBaseOffset() + offset;
+      long outIx = offset;
       final long outLimit = outIx + length;
       final long outLimit = outIx + length;
       final int inLimit = in.length();
       final int inLimit = in.length();
       if (inLimit > length || out.length - length < offset) {
       if (inLimit > length || out.length - length < offset) {
@@ -1204,7 +1203,7 @@ final class Utf8 {
       }
       }
       if (inIx == inLimit) {
       if (inIx == inLimit) {
         // We're done, it was ASCII encoded.
         // We're done, it was ASCII encoded.
-        return (int) (outIx - getArrayBaseOffset());
+        return (int) outIx;
       }
       }
 
 
       for (char c; inIx < inLimit; ++inIx) {
       for (char c; inIx < inLimit; ++inIx) {
@@ -1243,7 +1242,7 @@ final class Utf8 {
       }
       }
 
 
       // All bytes have been encoded.
       // All bytes have been encoded.
-      return (int) (outIx - getArrayBaseOffset());
+      return (int) outIx;
     }
     }
 
 
     @Override
     @Override
@@ -1321,31 +1320,17 @@ final class Utf8 {
      */
      */
     private static int unsafeEstimateConsecutiveAscii(
     private static int unsafeEstimateConsecutiveAscii(
         byte[] bytes, long offset, final int maxChars) {
         byte[] bytes, long offset, final int maxChars) {
-      int remaining = maxChars;
-      if (remaining < UNSAFE_COUNT_ASCII_THRESHOLD) {
+      if (maxChars < UNSAFE_COUNT_ASCII_THRESHOLD) {
         // Don't bother with small strings.
         // Don't bother with small strings.
         return 0;
         return 0;
       }
       }
 
 
-      // Read bytes until 8-byte aligned so that we can read longs in the loop below.
-      // Byte arrays are already either 8 or 16-byte aligned, so we just need to make sure that
-      // the index (relative to the start of the array) is also 8-byte aligned. We do this by
-      // ANDing the index with 7 to determine the number of bytes that need to be read before
-      // we're 8-byte aligned.
-      final int unaligned = 8 - ((int) offset & 7);
-      for (int j = unaligned; j > 0; j--) {
+      for (int i = 0; i < maxChars; i++) {
         if (UnsafeUtil.getByte(bytes, offset++) < 0) {
         if (UnsafeUtil.getByte(bytes, offset++) < 0) {
-          return unaligned - j;
+          return i;
         }
         }
       }
       }
-
-      // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
-      // To speed things up further, we're reading longs instead of bytes so we use a mask to
-      // determine if any byte in the current long is non-ASCII.
-      remaining -= unaligned;
-      for (; remaining >= 8 && (UnsafeUtil.getLong(bytes, offset) & ASCII_MASK_LONG) == 0;
-          offset += 8, remaining -= 8) {}
-      return maxChars - remaining;
+      return maxChars;
     }
     }
 
 
     /**
     /**

+ 4 - 2
java/core/src/main/java/com/google/protobuf/WireFormat.java

@@ -47,8 +47,10 @@ public final class WireFormat {
   // Do not allow instantiation.
   // Do not allow instantiation.
   private WireFormat() {}
   private WireFormat() {}
 
 
-  static final int FIXED_32_SIZE = 4;
-  static final int FIXED_64_SIZE = 8;
+  static final int FIXED32_SIZE = 4;
+  static final int FIXED64_SIZE = 8;
+  static final int MAX_VARINT32_SIZE = 5;
+  static final int MAX_VARINT64_SIZE = 10;
   static final int MAX_VARINT_SIZE = 10;
   static final int MAX_VARINT_SIZE = 10;
 
 
   public static final int WIRETYPE_VARINT           = 0;
   public static final int WIRETYPE_VARINT           = 0;

+ 123 - 0
java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java

@@ -41,6 +41,7 @@ import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.nio.ByteBuffer;
+import java.util.Arrays;
 import junit.framework.TestCase;
 import junit.framework.TestCase;
 
 
 /**
 /**
@@ -613,6 +614,82 @@ public class CodedInputStreamTest extends TestCase {
       checkSizeLimitExceeded(expected);
       checkSizeLimitExceeded(expected);
     }
     }
   }
   }
+  
+  public void testRefillBufferWithCorrectSize() throws Exception {
+    // NOTE: refillBuffer only applies to the stream-backed CIS.
+    byte[] bytes = "123456789".getBytes("UTF-8");
+    ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
+    CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
+
+    int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+    output.writeRawVarint32(tag);
+    output.writeRawVarint32(bytes.length);
+    output.writeRawBytes(bytes);
+    output.writeRawVarint32(tag);
+    output.writeRawVarint32(bytes.length);
+    output.writeRawBytes(bytes);
+    output.writeRawByte(4);
+    output.flush();
+
+    // Input is two string with length 9 and one raw byte.
+    byte[] rawInput = rawOutput.toByteArray(); 
+    for (int inputStreamBufferLength = 8; 
+        inputStreamBufferLength <= rawInput.length + 1; inputStreamBufferLength++) {
+      CodedInputStream input = CodedInputStream.newInstance(
+              new ByteArrayInputStream(rawInput), inputStreamBufferLength);
+      input.setSizeLimit(rawInput.length - 1);
+      input.readString();
+      input.readString(); 
+      try {
+        input.readRawByte(); // Hits limit.
+        fail("Should have thrown an exception!");
+      } catch (InvalidProtocolBufferException expected) {
+        checkSizeLimitExceeded(expected);
+      }
+    }
+  }
+
+  public void testIsAtEnd() throws Exception {
+    CodedInputStream input = CodedInputStream.newInstance(
+        new ByteArrayInputStream(new byte[5]));
+    try {   
+      for (int i = 0; i < 5; i++) {
+        assertEquals(false, input.isAtEnd());
+        input.readRawByte();
+      }
+      assertEquals(true, input.isAtEnd());
+    } catch (Exception e) {
+      fail("Catch exception in the testIsAtEnd");
+    }
+  }
+
+  public void testCurrentLimitExceeded() throws Exception {
+    byte[] bytes = "123456789".getBytes("UTF-8");
+    ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
+    CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
+
+    int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+    output.writeRawVarint32(tag);
+    output.writeRawVarint32(bytes.length);
+    output.writeRawBytes(bytes);
+    output.flush();
+
+    byte[] rawInput = rawOutput.toByteArray();
+    CodedInputStream input = CodedInputStream.newInstance(
+              new ByteArrayInputStream(rawInput));
+    // The length of the whole rawInput
+    input.setSizeLimit(11);
+    // Some number that is smaller than the rawInput's length
+    // but larger than 2 
+    input.pushLimit(5);
+    try {
+      input.readString();
+      fail("Should have thrown an exception");
+    } catch (InvalidProtocolBufferException expected) {
+      assertEquals(expected.getMessage(), 
+          InvalidProtocolBufferException.truncatedMessage().getMessage());
+    }
+  }
 
 
   public void testSizeLimitMultipleMessages() throws Exception {
   public void testSizeLimitMultipleMessages() throws Exception {
     // NOTE: Size limit only applies to the stream-backed CIS.
     // NOTE: Size limit only applies to the stream-backed CIS.
@@ -807,6 +884,52 @@ public class CodedInputStreamTest extends TestCase {
     }
     }
   }
   }
 
 
+  public void testReadLargeByteStringFromInputStream() throws Exception {
+    byte[] bytes = new byte[1024 * 1024];
+    for (int i = 0; i < bytes.length; i++) {
+      bytes[i] = (byte) (i & 0xFF);
+    }
+    ByteString.Output rawOutput = ByteString.newOutput();
+    CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+    output.writeRawVarint32(bytes.length);
+    output.writeRawBytes(bytes);
+    output.flush();
+    byte[] data = rawOutput.toByteString().toByteArray();
+
+    CodedInputStream input = CodedInputStream.newInstance(
+        new ByteArrayInputStream(data) {
+          @Override
+          public synchronized int available() {
+            return 0;
+          }
+        });
+    ByteString result = input.readBytes();
+    assertEquals(ByteString.copyFrom(bytes), result);
+  }
+
+  public void testReadLargeByteArrayFromInputStream() throws Exception {
+    byte[] bytes = new byte[1024 * 1024];
+    for (int i = 0; i < bytes.length; i++) {
+      bytes[i] = (byte) (i & 0xFF);
+    }
+    ByteString.Output rawOutput = ByteString.newOutput();
+    CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+    output.writeRawVarint32(bytes.length);
+    output.writeRawBytes(bytes);
+    output.flush();
+    byte[] data = rawOutput.toByteString().toByteArray();
+
+    CodedInputStream input = CodedInputStream.newInstance(
+        new ByteArrayInputStream(data) {
+          @Override
+          public synchronized int available() {
+            return 0;
+          }
+        });
+    byte[] result = input.readByteArray();
+    assertTrue(Arrays.equals(bytes, result));
+  }
+
   public void testReadByteBuffer() throws Exception {
   public void testReadByteBuffer() throws Exception {
     ByteString.Output rawOutput = ByteString.newOutput();
     ByteString.Output rawOutput = ByteString.newOutput();
     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);

+ 157 - 0
java/core/src/test/java/com/google/protobuf/DiscardUnknownFieldsTest.java

@@ -0,0 +1,157 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import static org.junit.Assert.assertEquals;
+
+import protobuf_unittest.UnittestProto;
+import proto3_unittest.UnittestProto3;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for discard or preserve unknown fields. */
+@RunWith(JUnit4.class)
+public class DiscardUnknownFieldsTest {
+  @Test
+  public void testProto2() throws Exception {
+    testProto2Message(
+          UnittestProto.TestEmptyMessage.getDefaultInstance());
+    testProto2Message(
+          UnittestProto.TestEmptyMessageWithExtensions.getDefaultInstance());
+    testProto2Message(
+          DynamicMessage.getDefaultInstance(UnittestProto.TestEmptyMessage.getDescriptor()));
+    testProto2Message(
+          DynamicMessage.getDefaultInstance(
+              UnittestProto.TestEmptyMessageWithExtensions.getDescriptor()));
+  }
+
+  @Test
+  public void testProto3() throws Exception {
+    testProto3Message(UnittestProto3.TestEmptyMessage.getDefaultInstance());
+    testProto3Message(
+        DynamicMessage.getDefaultInstance(UnittestProto3.TestEmptyMessage.getDescriptor()));
+  }
+
+  private static void testProto2Message(Message message) throws Exception {
+    assertUnknownFieldsDefaultPreserved(message);
+    assertUnknownFieldsExplicitlyDiscarded(message);
+    assertReuseCodedInputStreamPreserve(message);
+    assertUnknownFieldsInUnknownFieldSetArePreserve(message);
+  }
+
+  private static void testProto3Message(Message message) throws Exception {
+    CodedInputStream.setProto3KeepUnknownsByDefaultForTest();
+    assertUnknownFieldsDefaultPreserved(message);
+    assertUnknownFieldsExplicitlyDiscarded(message);
+    assertReuseCodedInputStreamPreserve(message);
+    assertUnknownFieldsInUnknownFieldSetArePreserve(message);
+    CodedInputStream.setProto3DiscardUnknownsByDefaultForTest();
+    assertUnknownFieldsDefaultDiscarded(message);
+    assertUnknownFieldsExplicitlyDiscarded(message);
+    assertUnknownFieldsInUnknownFieldSetAreDiscarded(message);
+  }
+
+  private static void assertReuseCodedInputStreamPreserve(Message message) throws Exception {
+    final int messageSize = payload.size();
+    byte[] copied = new byte[messageSize * 2];
+    payload.copyTo(copied, 0);
+    payload.copyTo(copied, messageSize);
+    CodedInputStream input = CodedInputStream.newInstance(copied);
+    {
+      // Use DiscardUnknownFieldsParser to parse the first payload.
+      int oldLimit = input.pushLimit(messageSize);
+      Message parsed = DiscardUnknownFieldsParser.wrap(message.getParserForType()).parseFrom(input);
+      assertEquals(message.getClass().getName(), 0, parsed.getSerializedSize());
+      input.popLimit(oldLimit);
+    }
+    {
+      // Use the normal parser to parse the remaining payload should have unknown fields preserved.
+      Message parsed = message.getParserForType().parseFrom(input);
+      assertEquals(message.getClass().getName(), payload, parsed.toByteString());
+    }
+  }
+
+  /**
+   * {@link Message.Builder#setUnknownFields(UnknownFieldSet)} and {@link
+   * Message.Builder#mergeUnknownFields(UnknownFieldSet)} should preserve the unknown fields.
+   */
+  private static void assertUnknownFieldsInUnknownFieldSetArePreserve(Message message)
+      throws Exception {
+    UnknownFieldSet unknownFields = UnknownFieldSet.newBuilder().mergeFrom(payload).build();
+    Message built = message.newBuilderForType().setUnknownFields(unknownFields).build();
+    assertEquals(message.getClass().getName(), payload, built.toByteString());
+
+  }
+  /**
+   * {@link Message.Builder#setUnknownFields(UnknownFieldSet)} and {@link
+   * Message.Builder#mergeUnknownFields(UnknownFieldSet)} should discard the unknown fields.
+   */
+  private static void assertUnknownFieldsInUnknownFieldSetAreDiscarded(Message message)
+      throws Exception {
+    UnknownFieldSet unknownFields = UnknownFieldSet.newBuilder().mergeFrom(payload).build();
+    Message built = message.newBuilderForType().setUnknownFields(unknownFields).build();
+    assertEquals(message.getClass().getName(), 0, built.getSerializedSize());
+  }
+
+  private static void assertUnknownFieldsDefaultPreserved(MessageLite message) throws Exception {
+    {
+      MessageLite parsed = message.getParserForType().parseFrom(payload);
+      assertEquals(message.getClass().getName(), payload, parsed.toByteString());
+    }
+
+    {
+      MessageLite parsed = message.newBuilderForType().mergeFrom(payload).build();
+      assertEquals(message.getClass().getName(), payload, parsed.toByteString());
+    }
+  }
+
+  private static void assertUnknownFieldsDefaultDiscarded(MessageLite message) throws Exception {
+    {
+      MessageLite parsed = message.getParserForType().parseFrom(payload);
+      assertEquals(message.getClass().getName(), 0, parsed.getSerializedSize());
+    }
+
+    {
+      MessageLite parsed = message.newBuilderForType().mergeFrom(payload).build();
+      assertEquals(message.getClass().getName(), 0, parsed.getSerializedSize());
+    }
+  }
+
+  private static void assertUnknownFieldsExplicitlyDiscarded(Message message) throws Exception {
+    Message parsed =
+        DiscardUnknownFieldsParser.wrap(message.getParserForType()).parseFrom(payload);
+    assertEquals(message.getClass().getName(), 0, parsed.getSerializedSize());
+  }
+
+  private static final ByteString payload =
+      TestUtilLite.getAllLiteSetBuilder().build().toByteString();
+}

+ 3 - 9
java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java

@@ -108,7 +108,7 @@ public class FieldPresenceTest extends TestCase {
     assertFalse(TestAllTypes.newBuilder().build().hasOptionalNestedMessage());
     assertFalse(TestAllTypes.newBuilder().build().hasOptionalNestedMessage());
     assertFalse(TestAllTypes.newBuilder().hasOptionalNestedMessage());
     assertFalse(TestAllTypes.newBuilder().hasOptionalNestedMessage());
 
 
-    // oneof fields don't have hasFoo() methods (even for message types).
+    // oneof fields don't have hasFoo() methods for non-message types.
     assertHasMethodRemoved(
     assertHasMethodRemoved(
         UnittestProto.TestAllTypes.class,
         UnittestProto.TestAllTypes.class,
         TestAllTypes.class,
         TestAllTypes.class,
@@ -121,10 +121,8 @@ public class FieldPresenceTest extends TestCase {
         UnittestProto.TestAllTypes.class,
         UnittestProto.TestAllTypes.class,
         TestAllTypes.class,
         TestAllTypes.class,
         "OneofBytes");
         "OneofBytes");
-    assertHasMethodRemoved(
-        UnittestProto.TestAllTypes.class,
-        TestAllTypes.class,
-        "OneofNestedMessage");
+    assertFalse(TestAllTypes.newBuilder().build().hasOneofNestedMessage());
+    assertFalse(TestAllTypes.newBuilder().hasOneofNestedMessage());
 
 
     assertHasMethodRemoved(
     assertHasMethodRemoved(
         UnittestProto.TestAllTypes.Builder.class,
         UnittestProto.TestAllTypes.Builder.class,
@@ -138,10 +136,6 @@ public class FieldPresenceTest extends TestCase {
         UnittestProto.TestAllTypes.Builder.class,
         UnittestProto.TestAllTypes.Builder.class,
         TestAllTypes.Builder.class,
         TestAllTypes.Builder.class,
         "OneofBytes");
         "OneofBytes");
-    assertHasMethodRemoved(
-        UnittestProto.TestAllTypes.Builder.class,
-        TestAllTypes.Builder.class,
-        "OneofNestedMessage");
   }
   }
 
 
   public void testOneofEquals() throws Exception {
   public void testOneofEquals() throws Exception {

+ 36 - 42
java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java

@@ -923,15 +923,9 @@ public class GeneratedMessageTest extends TestCase {
   }
   }
 
 
   public void testEnumValues() {
   public void testEnumValues() {
-     assertEquals(
-         TestAllTypes.NestedEnum.BAR.getNumber(),
-         TestAllTypes.NestedEnum.BAR_VALUE);
-    assertEquals(
-        TestAllTypes.NestedEnum.BAZ.getNumber(),
-        TestAllTypes.NestedEnum.BAZ_VALUE);
-    assertEquals(
-        TestAllTypes.NestedEnum.FOO.getNumber(),
-        TestAllTypes.NestedEnum.FOO_VALUE);
+    assertEquals(TestAllTypes.NestedEnum.BAR_VALUE, TestAllTypes.NestedEnum.BAR.getNumber());
+    assertEquals(TestAllTypes.NestedEnum.BAZ_VALUE, TestAllTypes.NestedEnum.BAZ.getNumber());
+    assertEquals(TestAllTypes.NestedEnum.FOO_VALUE, TestAllTypes.NestedEnum.FOO.getNumber());
   }
   }
 
 
   public void testNonNestedExtensionInitialization() {
   public void testNonNestedExtensionInitialization() {
@@ -1319,51 +1313,51 @@ public class GeneratedMessageTest extends TestCase {
       assertFalse(builder.clearFooInt().hasFooInt());
       assertFalse(builder.clearFooInt().hasFooInt());
       TestOneof2 message2 = builder.build();
       TestOneof2 message2 = builder.build();
       assertFalse(message2.hasFooInt());
       assertFalse(message2.hasFooInt());
-      assertEquals(message2.getFooInt(), 0);
+      assertEquals(0, message2.getFooInt());
     }
     }
 
 
     // Enum
     // Enum
     {
     {
       TestOneof2.Builder builder = TestOneof2.newBuilder();
       TestOneof2.Builder builder = TestOneof2.newBuilder();
-      assertEquals(builder.getFooEnum(), TestOneof2.NestedEnum.FOO);
+      assertEquals(TestOneof2.NestedEnum.FOO, builder.getFooEnum());
       assertTrue(builder.setFooEnum(TestOneof2.NestedEnum.BAR).hasFooEnum());
       assertTrue(builder.setFooEnum(TestOneof2.NestedEnum.BAR).hasFooEnum());
-      assertEquals(builder.getFooEnum(), TestOneof2.NestedEnum.BAR);
+      assertEquals(TestOneof2.NestedEnum.BAR, builder.getFooEnum());
       TestOneof2 message = builder.buildPartial();
       TestOneof2 message = builder.buildPartial();
       assertTrue(message.hasFooEnum());
       assertTrue(message.hasFooEnum());
-      assertEquals(message.getFooEnum(), TestOneof2.NestedEnum.BAR);
+      assertEquals(TestOneof2.NestedEnum.BAR, message.getFooEnum());
 
 
       assertFalse(builder.clearFooEnum().hasFooEnum());
       assertFalse(builder.clearFooEnum().hasFooEnum());
       TestOneof2 message2 = builder.build();
       TestOneof2 message2 = builder.build();
       assertFalse(message2.hasFooEnum());
       assertFalse(message2.hasFooEnum());
-      assertEquals(message2.getFooEnum(), TestOneof2.NestedEnum.FOO);
+      assertEquals(TestOneof2.NestedEnum.FOO, message2.getFooEnum());
     }
     }
 
 
     // String
     // String
     {
     {
       TestOneof2.Builder builder = TestOneof2.newBuilder();
       TestOneof2.Builder builder = TestOneof2.newBuilder();
-      assertEquals(builder.getFooString(), "");
+      assertEquals("", builder.getFooString());
       builder.setFooString("foo");
       builder.setFooString("foo");
       assertTrue(builder.hasFooString());
       assertTrue(builder.hasFooString());
-      assertEquals(builder.getFooString(), "foo");
+      assertEquals("foo", builder.getFooString());
       TestOneof2 message = builder.buildPartial();
       TestOneof2 message = builder.buildPartial();
       assertTrue(message.hasFooString());
       assertTrue(message.hasFooString());
-      assertEquals(message.getFooString(), "foo");
+      assertEquals("foo", message.getFooString());
       assertEquals(message.getFooStringBytes(), TestUtil.toBytes("foo"));
       assertEquals(message.getFooStringBytes(), TestUtil.toBytes("foo"));
 
 
       assertFalse(builder.clearFooString().hasFooString());
       assertFalse(builder.clearFooString().hasFooString());
       TestOneof2 message2 = builder.buildPartial();
       TestOneof2 message2 = builder.buildPartial();
       assertFalse(message2.hasFooString());
       assertFalse(message2.hasFooString());
-      assertEquals(message2.getFooString(), "");
+      assertEquals("", message2.getFooString());
       assertEquals(message2.getFooStringBytes(), TestUtil.toBytes(""));
       assertEquals(message2.getFooStringBytes(), TestUtil.toBytes(""));
 
 
       // Get method should not change the oneof value.
       // Get method should not change the oneof value.
       builder.setFooInt(123);
       builder.setFooInt(123);
-      assertEquals(builder.getFooString(), "");
+      assertEquals("", builder.getFooString());
       assertEquals(builder.getFooStringBytes(), TestUtil.toBytes(""));
       assertEquals(builder.getFooStringBytes(), TestUtil.toBytes(""));
       assertEquals(123, builder.getFooInt());
       assertEquals(123, builder.getFooInt());
 
 
       message = builder.build();
       message = builder.build();
-      assertEquals(message.getFooString(), "");
+      assertEquals("", message.getFooString());
       assertEquals(message.getFooStringBytes(), TestUtil.toBytes(""));
       assertEquals(message.getFooStringBytes(), TestUtil.toBytes(""));
       assertEquals(123, message.getFooInt());
       assertEquals(123, message.getFooInt());
     }
     }
@@ -1371,38 +1365,38 @@ public class GeneratedMessageTest extends TestCase {
     // Cord
     // Cord
     {
     {
       TestOneof2.Builder builder = TestOneof2.newBuilder();
       TestOneof2.Builder builder = TestOneof2.newBuilder();
-      assertEquals(builder.getFooCord(), "");
+      assertEquals("", builder.getFooCord());
       builder.setFooCord("foo");
       builder.setFooCord("foo");
       assertTrue(builder.hasFooCord());
       assertTrue(builder.hasFooCord());
-      assertEquals(builder.getFooCord(), "foo");
+      assertEquals("foo", builder.getFooCord());
       TestOneof2 message = builder.buildPartial();
       TestOneof2 message = builder.buildPartial();
       assertTrue(message.hasFooCord());
       assertTrue(message.hasFooCord());
-      assertEquals(message.getFooCord(), "foo");
+      assertEquals("foo", message.getFooCord());
       assertEquals(message.getFooCordBytes(), TestUtil.toBytes("foo"));
       assertEquals(message.getFooCordBytes(), TestUtil.toBytes("foo"));
 
 
       assertFalse(builder.clearFooCord().hasFooCord());
       assertFalse(builder.clearFooCord().hasFooCord());
       TestOneof2 message2 = builder.build();
       TestOneof2 message2 = builder.build();
       assertFalse(message2.hasFooCord());
       assertFalse(message2.hasFooCord());
-      assertEquals(message2.getFooCord(), "");
+      assertEquals("", message2.getFooCord());
       assertEquals(message2.getFooCordBytes(), TestUtil.toBytes(""));
       assertEquals(message2.getFooCordBytes(), TestUtil.toBytes(""));
     }
     }
 
 
     // StringPiece
     // StringPiece
     {
     {
       TestOneof2.Builder builder = TestOneof2.newBuilder();
       TestOneof2.Builder builder = TestOneof2.newBuilder();
-      assertEquals(builder.getFooStringPiece(), "");
+      assertEquals("", builder.getFooStringPiece());
       builder.setFooStringPiece("foo");
       builder.setFooStringPiece("foo");
       assertTrue(builder.hasFooStringPiece());
       assertTrue(builder.hasFooStringPiece());
-      assertEquals(builder.getFooStringPiece(), "foo");
+      assertEquals("foo", builder.getFooStringPiece());
       TestOneof2 message = builder.buildPartial();
       TestOneof2 message = builder.buildPartial();
       assertTrue(message.hasFooStringPiece());
       assertTrue(message.hasFooStringPiece());
-      assertEquals(message.getFooStringPiece(), "foo");
+      assertEquals("foo", message.getFooStringPiece());
       assertEquals(message.getFooStringPieceBytes(), TestUtil.toBytes("foo"));
       assertEquals(message.getFooStringPieceBytes(), TestUtil.toBytes("foo"));
 
 
       assertFalse(builder.clearFooStringPiece().hasFooStringPiece());
       assertFalse(builder.clearFooStringPiece().hasFooStringPiece());
       TestOneof2 message2 = builder.build();
       TestOneof2 message2 = builder.build();
       assertFalse(message2.hasFooStringPiece());
       assertFalse(message2.hasFooStringPiece());
-      assertEquals(message2.getFooStringPiece(), "");
+      assertEquals("", message2.getFooStringPiece());
       assertEquals(message2.getFooStringPieceBytes(), TestUtil.toBytes(""));
       assertEquals(message2.getFooStringPieceBytes(), TestUtil.toBytes(""));
     }
     }
 
 
@@ -1410,20 +1404,20 @@ public class GeneratedMessageTest extends TestCase {
     {
     {
       // set
       // set
       TestOneof2.Builder builder = TestOneof2.newBuilder();
       TestOneof2.Builder builder = TestOneof2.newBuilder();
-      assertEquals(builder.getFooMessage().getQuxInt(), 0);
+      assertEquals(0, builder.getFooMessage().getQuxInt());
       builder.setFooMessage(
       builder.setFooMessage(
           TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build());
           TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build());
       assertTrue(builder.hasFooMessage());
       assertTrue(builder.hasFooMessage());
-      assertEquals(builder.getFooMessage().getQuxInt(), 234);
+      assertEquals(234, builder.getFooMessage().getQuxInt());
       TestOneof2 message = builder.buildPartial();
       TestOneof2 message = builder.buildPartial();
       assertTrue(message.hasFooMessage());
       assertTrue(message.hasFooMessage());
-      assertEquals(message.getFooMessage().getQuxInt(), 234);
+      assertEquals(234, message.getFooMessage().getQuxInt());
 
 
       // clear
       // clear
       assertFalse(builder.clearFooMessage().hasFooString());
       assertFalse(builder.clearFooMessage().hasFooString());
       message = builder.build();
       message = builder.build();
       assertFalse(message.hasFooMessage());
       assertFalse(message.hasFooMessage());
-      assertEquals(message.getFooMessage().getQuxInt(), 0);
+      assertEquals(0, message.getFooMessage().getQuxInt());
 
 
       // nested builder
       // nested builder
       builder = TestOneof2.newBuilder();
       builder = TestOneof2.newBuilder();
@@ -1432,10 +1426,10 @@ public class GeneratedMessageTest extends TestCase {
       assertFalse(builder.hasFooMessage());
       assertFalse(builder.hasFooMessage());
       builder.getFooMessageBuilder().setQuxInt(123);
       builder.getFooMessageBuilder().setQuxInt(123);
       assertTrue(builder.hasFooMessage());
       assertTrue(builder.hasFooMessage());
-      assertEquals(builder.getFooMessage().getQuxInt(), 123);
+      assertEquals(123, builder.getFooMessage().getQuxInt());
       message = builder.build();
       message = builder.build();
       assertTrue(message.hasFooMessage());
       assertTrue(message.hasFooMessage());
-      assertEquals(message.getFooMessage().getQuxInt(), 123);
+      assertEquals(123, message.getFooMessage().getQuxInt());
     }
     }
 
 
     // LazyMessage is tested in LazyMessageLiteTest.java
     // LazyMessage is tested in LazyMessageLiteTest.java
@@ -1448,7 +1442,7 @@ public class GeneratedMessageTest extends TestCase {
       TestOneof2 message = builder.setFooInt(123).build();
       TestOneof2 message = builder.setFooInt(123).build();
       TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
       TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
       assertTrue(message2.hasFooInt());
       assertTrue(message2.hasFooInt());
-      assertEquals(message2.getFooInt(), 123);
+      assertEquals(123, message2.getFooInt());
     }
     }
 
 
     // String
     // String
@@ -1457,7 +1451,7 @@ public class GeneratedMessageTest extends TestCase {
       TestOneof2 message = builder.setFooString("foo").build();
       TestOneof2 message = builder.setFooString("foo").build();
       TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
       TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
       assertTrue(message2.hasFooString());
       assertTrue(message2.hasFooString());
-      assertEquals(message2.getFooString(), "foo");
+      assertEquals("foo", message2.getFooString());
     }
     }
 
 
     // Enum
     // Enum
@@ -1466,7 +1460,7 @@ public class GeneratedMessageTest extends TestCase {
       TestOneof2 message = builder.setFooEnum(TestOneof2.NestedEnum.BAR).build();
       TestOneof2 message = builder.setFooEnum(TestOneof2.NestedEnum.BAR).build();
       TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
       TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
       assertTrue(message2.hasFooEnum());
       assertTrue(message2.hasFooEnum());
-      assertEquals(message2.getFooEnum(), TestOneof2.NestedEnum.BAR);
+      assertEquals(TestOneof2.NestedEnum.BAR, message2.getFooEnum());
     }
     }
 
 
     // Message
     // Message
@@ -1476,7 +1470,7 @@ public class GeneratedMessageTest extends TestCase {
           TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build()).build();
           TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build()).build();
       TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
       TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
       assertTrue(message2.hasFooMessage());
       assertTrue(message2.hasFooMessage());
-      assertEquals(message2.getFooMessage().getQuxInt(), 234);
+      assertEquals(234, message2.getFooMessage().getQuxInt());
     }
     }
   }
   }
 
 
@@ -1488,7 +1482,7 @@ public class GeneratedMessageTest extends TestCase {
       ByteString serialized = message.toByteString();
       ByteString serialized = message.toByteString();
       TestOneof2 message2 = TestOneof2.parseFrom(serialized);
       TestOneof2 message2 = TestOneof2.parseFrom(serialized);
       assertTrue(message2.hasFooInt());
       assertTrue(message2.hasFooInt());
-      assertEquals(message2.getFooInt(), 123);
+      assertEquals(123, message2.getFooInt());
     }
     }
 
 
     // String
     // String
@@ -1498,7 +1492,7 @@ public class GeneratedMessageTest extends TestCase {
       ByteString serialized = message.toByteString();
       ByteString serialized = message.toByteString();
       TestOneof2 message2 = TestOneof2.parseFrom(serialized);
       TestOneof2 message2 = TestOneof2.parseFrom(serialized);
       assertTrue(message2.hasFooString());
       assertTrue(message2.hasFooString());
-      assertEquals(message2.getFooString(), "foo");
+      assertEquals("foo", message2.getFooString());
     }
     }
 
 
     // Enum
     // Enum
@@ -1508,7 +1502,7 @@ public class GeneratedMessageTest extends TestCase {
       ByteString serialized = message.toByteString();
       ByteString serialized = message.toByteString();
       TestOneof2 message2 = TestOneof2.parseFrom(serialized);
       TestOneof2 message2 = TestOneof2.parseFrom(serialized);
       assertTrue(message2.hasFooEnum());
       assertTrue(message2.hasFooEnum());
-      assertEquals(message2.getFooEnum(), TestOneof2.NestedEnum.BAR);
+      assertEquals(TestOneof2.NestedEnum.BAR, message2.getFooEnum());
     }
     }
 
 
     // Message
     // Message
@@ -1519,7 +1513,7 @@ public class GeneratedMessageTest extends TestCase {
       ByteString serialized = message.toByteString();
       ByteString serialized = message.toByteString();
       TestOneof2 message2 = TestOneof2.parseFrom(serialized);
       TestOneof2 message2 = TestOneof2.parseFrom(serialized);
       assertTrue(message2.hasFooMessage());
       assertTrue(message2.hasFooMessage());
-      assertEquals(message2.getFooMessage().getQuxInt(), 234);
+      assertEquals(234, message2.getFooMessage().getQuxInt());
     }
     }
   }
   }
 
 

+ 214 - 0
java/core/src/test/java/com/google/protobuf/LiteTest.java

@@ -33,6 +33,7 @@ package com.google.protobuf;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 import static java.util.Collections.singletonList;
 
 
+import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
 import com.google.protobuf.UnittestImportLite.ImportEnumLite;
 import com.google.protobuf.UnittestImportLite.ImportEnumLite;
 import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite;
 import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite;
 import com.google.protobuf.UnittestLite.ForeignEnumLite;
 import com.google.protobuf.UnittestLite.ForeignEnumLite;
@@ -52,7 +53,12 @@ import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.BarPrime;
 import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo;
 import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo;
 import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestOneofEquals;
 import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestOneofEquals;
 import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestRecursiveOneof;
 import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestRecursiveOneof;
+import java.lang.reflect.Field;
 import java.nio.ByteBuffer;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
 import junit.framework.TestCase;
 import junit.framework.TestCase;
 
 
 /**
 /**
@@ -155,6 +161,31 @@ public class LiteTest extends TestCase {
       // expected.
       // expected.
     }
     }
   }
   }
+
+  public void testMemoization() throws Exception {
+    TestAllExtensionsLite message = TestUtilLite.getAllLiteExtensionsSet();
+
+    // Test serialized size is memoized
+    message.memoizedSerializedSize = -1;
+    int size = message.getSerializedSize();
+    assertTrue(size > 0);
+    assertEquals(size, message.memoizedSerializedSize);
+
+    // Test hashCode is memoized
+    assertEquals(0, message.memoizedHashCode);
+    int hashCode = message.hashCode();
+    assertTrue(hashCode != 0);
+    assertEquals(hashCode, message.memoizedHashCode);
+
+    // Test isInitialized is memoized
+    Field memo = message.getClass().getDeclaredField("memoizedIsInitialized");
+    memo.setAccessible(true);
+    memo.set(message, (byte) -1);
+    boolean initialized = message.isInitialized();
+    assertTrue(initialized);
+    // We have to cast to Byte first. Casting to byte causes a type error
+    assertEquals(1, ((Byte) memo.get(message)).intValue());
+  }
   
   
   public void testSanityCopyOnWrite() throws InvalidProtocolBufferException {
   public void testSanityCopyOnWrite() throws InvalidProtocolBufferException {
     // Since builders are implemented as a thin wrapper around a message
     // Since builders are implemented as a thin wrapper around a message
@@ -2378,4 +2409,187 @@ public class LiteTest extends TestCase {
           expected.getUnfinishedMessage());
           expected.getUnfinishedMessage());
     }
     }
   }
   }
+
+  // Make sure we haven't screwed up the code generation for packing fields by default.
+  public void testPackedSerialization() throws Exception {
+    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+    builder.addRepeatedInt32(4321);
+    builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.BAZ);
+    TestAllTypes message = builder.build();
+
+    CodedInputStream in = CodedInputStream.newInstance(message.toByteArray());
+
+    while (!in.isAtEnd()) {
+      int tag = in.readTag();
+      assertEquals(WireFormat.WIRETYPE_LENGTH_DELIMITED, WireFormat.getTagWireType(tag));
+      in.skipField(tag);
+    }
+  }
+
+  public void testAddAllIteratesOnce() {
+    TestAllTypesLite message =
+        TestAllTypesLite.newBuilder()
+            .addAllRepeatedBool(new OneTimeIterableList(false))
+            .addAllRepeatedInt32(new OneTimeIterableList(0))
+            .addAllRepeatedInt64(new OneTimeIterableList(0L))
+            .addAllRepeatedFloat(new OneTimeIterableList(0f))
+            .addAllRepeatedDouble(new OneTimeIterableList(0d))
+            .addAllRepeatedBytes(new OneTimeIterableList(ByteString.EMPTY))
+            .addAllRepeatedString(new OneTimeIterableList(""))
+            .addAllRepeatedNestedMessage(
+                new OneTimeIterableList(NestedMessage.getDefaultInstance()))
+            .addAllRepeatedBool(new OneTimeIterable(false))
+            .addAllRepeatedInt32(new OneTimeIterable(0))
+            .addAllRepeatedInt64(new OneTimeIterable(0L))
+            .addAllRepeatedFloat(new OneTimeIterable(0f))
+            .addAllRepeatedDouble(new OneTimeIterable(0d))
+            .addAllRepeatedBytes(new OneTimeIterable(ByteString.EMPTY))
+            .addAllRepeatedString(new OneTimeIterable(""))
+            .addAllRepeatedNestedMessage(new OneTimeIterable(NestedMessage.getDefaultInstance()))
+            .build();
+  }
+
+  public void testAddAllIteratesOnce_throwsOnNull() {
+    TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder();
+    try {
+      builder.addAllRepeatedBool(new OneTimeIterableList(true, false, (Boolean) null));
+      fail();
+    } catch (NullPointerException expected) {
+      assertEquals("Element at index 2 is null.", expected.getMessage());
+      assertEquals(0, builder.getRepeatedBoolCount());
+    }
+
+    try {
+      builder.addAllRepeatedBool(new OneTimeIterable(true, false, (Boolean) null));
+      fail();
+    } catch (NullPointerException expected) {
+      assertEquals("Element at index 2 is null.", expected.getMessage());
+      assertEquals(0, builder.getRepeatedBoolCount());
+    }
+
+    try {
+      builder = TestAllTypesLite.newBuilder();
+      builder.addAllRepeatedBool(new OneTimeIterableList((Boolean) null));
+      fail();
+    } catch (NullPointerException expected) {
+      assertEquals("Element at index 0 is null.", expected.getMessage());
+      assertEquals(0, builder.getRepeatedBoolCount());
+    }
+
+    try {
+      builder = TestAllTypesLite.newBuilder();
+      builder.addAllRepeatedInt32(new OneTimeIterableList((Integer) null));
+      fail();
+    } catch (NullPointerException expected) {
+      assertEquals("Element at index 0 is null.", expected.getMessage());
+      assertEquals(0, builder.getRepeatedInt32Count());
+    }
+
+    try {
+      builder = TestAllTypesLite.newBuilder();
+      builder.addAllRepeatedInt64(new OneTimeIterableList((Long) null));
+      fail();
+    } catch (NullPointerException expected) {
+      assertEquals("Element at index 0 is null.", expected.getMessage());
+      assertEquals(0, builder.getRepeatedInt64Count());
+    }
+
+    try {
+      builder = TestAllTypesLite.newBuilder();
+      builder.addAllRepeatedFloat(new OneTimeIterableList((Float) null));
+      fail();
+    } catch (NullPointerException expected) {
+      assertEquals("Element at index 0 is null.", expected.getMessage());
+      assertEquals(0, builder.getRepeatedFloatCount());
+    }
+
+    try {
+      builder = TestAllTypesLite.newBuilder();
+      builder.addAllRepeatedDouble(new OneTimeIterableList((Double) null));
+      fail();
+    } catch (NullPointerException expected) {
+      assertEquals("Element at index 0 is null.", expected.getMessage());
+      assertEquals(0, builder.getRepeatedDoubleCount());
+    }
+
+    try {
+      builder = TestAllTypesLite.newBuilder();
+      builder.addAllRepeatedBytes(new OneTimeIterableList((ByteString) null));
+      fail();
+    } catch (NullPointerException expected) {
+      assertEquals("Element at index 0 is null.", expected.getMessage());
+      assertEquals(0, builder.getRepeatedBytesCount());
+    }
+
+    try {
+      builder = TestAllTypesLite.newBuilder();
+      builder.addAllRepeatedString(new OneTimeIterableList("", "", (String) null, ""));
+      fail();
+    } catch (NullPointerException expected) {
+      assertEquals("Element at index 2 is null.", expected.getMessage());
+      assertEquals(0, builder.getRepeatedStringCount());
+    }
+
+    try {
+      builder = TestAllTypesLite.newBuilder();
+      builder.addAllRepeatedString(new OneTimeIterable("", "", (String) null, ""));
+      fail();
+    } catch (NullPointerException expected) {
+      assertEquals("Element at index 2 is null.", expected.getMessage());
+      assertEquals(0, builder.getRepeatedStringCount());
+    }
+
+    try {
+      builder = TestAllTypesLite.newBuilder();
+      builder.addAllRepeatedString(new OneTimeIterableList((String) null));
+      fail();
+    } catch (NullPointerException expected) {
+      assertEquals("Element at index 0 is null.", expected.getMessage());
+      assertEquals(0, builder.getRepeatedStringCount());
+    }
+
+    try {
+      builder = TestAllTypesLite.newBuilder();
+      builder.addAllRepeatedNestedMessage(new OneTimeIterableList((NestedMessage) null));
+      fail();
+    } catch (NullPointerException expected) {
+      assertEquals("Element at index 0 is null.", expected.getMessage());
+      assertEquals(0, builder.getRepeatedNestedMessageCount());
+    }
+  }
+
+  private static final class OneTimeIterableList<T> extends ArrayList<T> {
+    private boolean wasIterated = false;
+
+    OneTimeIterableList(T... contents) {
+      addAll(Arrays.asList(contents));
+    }
+
+    @Override
+    public Iterator<T> iterator() {
+      if (wasIterated) {
+        fail();
+      }
+      wasIterated = true;
+      return super.iterator();
+    }
+  }
+
+  private static final class OneTimeIterable<T> implements Iterable<T> {
+    private final List<T> list;
+    private boolean wasIterated = false;
+
+    OneTimeIterable(T... contents) {
+      list = Arrays.asList(contents);
+    }
+
+    @Override
+    public Iterator<T> iterator() {
+      if (wasIterated) {
+        fail();
+      }
+      wasIterated = true;
+      return list.iterator();
+    }
+  }
 }
 }

+ 4 - 4
java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java

@@ -408,12 +408,12 @@ public final class MapForProto2LiteTest extends TestCase {
     TestMap map = tryParseTestMap(BizarroTestMap.newBuilder()
     TestMap map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putInt32ToInt32Field(5, bytes)
         .putInt32ToInt32Field(5, bytes)
         .build());
         .build());
-    assertEquals(map.getInt32ToInt32FieldOrDefault(5, -1), 0);
+    assertEquals(0, map.getInt32ToInt32FieldOrDefault(5, -1));
 
 
     map = tryParseTestMap(BizarroTestMap.newBuilder()
     map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putInt32ToStringField(stringKey, 5)
         .putInt32ToStringField(stringKey, 5)
         .build());
         .build());
-    assertEquals(map.getInt32ToStringFieldOrDefault(0, null), "");
+    assertEquals("", map.getInt32ToStringFieldOrDefault(0, null));
 
 
     map = tryParseTestMap(BizarroTestMap.newBuilder()
     map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putInt32ToBytesField(stringKey, 5)
         .putInt32ToBytesField(stringKey, 5)
@@ -423,7 +423,7 @@ public final class MapForProto2LiteTest extends TestCase {
     map = tryParseTestMap(BizarroTestMap.newBuilder()
     map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putInt32ToEnumField(stringKey, bytes)
         .putInt32ToEnumField(stringKey, bytes)
         .build());
         .build());
-    assertEquals(map.getInt32ToEnumFieldOrDefault(0, null), TestMap.EnumValue.FOO);
+    assertEquals(TestMap.EnumValue.FOO, map.getInt32ToEnumFieldOrDefault(0, null));
 
 
     try {
     try {
       tryParseTestMap(BizarroTestMap.newBuilder()
       tryParseTestMap(BizarroTestMap.newBuilder()
@@ -439,7 +439,7 @@ public final class MapForProto2LiteTest extends TestCase {
     map = tryParseTestMap(BizarroTestMap.newBuilder()
     map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putStringToInt32Field(stringKey, bytes)
         .putStringToInt32Field(stringKey, bytes)
         .build());
         .build());
-    assertEquals(map.getStringToInt32FieldOrDefault(stringKey, -1), 0);
+    assertEquals(0, map.getStringToInt32FieldOrDefault(stringKey, -1));
   }
   }
 
 
   public void testMergeFrom() throws Exception {
   public void testMergeFrom() throws Exception {

+ 4 - 4
java/core/src/test/java/com/google/protobuf/MapForProto2Test.java

@@ -546,12 +546,12 @@ public class MapForProto2Test extends TestCase {
     TestMap map = tryParseTestMap(BizarroTestMap.newBuilder()
     TestMap map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putInt32ToInt32Field(5, bytes)
         .putInt32ToInt32Field(5, bytes)
         .build());
         .build());
-    assertEquals(map.getInt32ToInt32FieldOrDefault(5, -1), 0);
+    assertEquals(0, map.getInt32ToInt32FieldOrDefault(5, -1));
 
 
     map = tryParseTestMap(BizarroTestMap.newBuilder()
     map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putInt32ToStringField(stringKey, 5)
         .putInt32ToStringField(stringKey, 5)
         .build());
         .build());
-    assertEquals(map.getInt32ToStringFieldOrDefault(0, null), "");
+    assertEquals("", map.getInt32ToStringFieldOrDefault(0, null));
 
 
     map = tryParseTestMap(BizarroTestMap.newBuilder()
     map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putInt32ToBytesField(stringKey, 5)
         .putInt32ToBytesField(stringKey, 5)
@@ -561,7 +561,7 @@ public class MapForProto2Test extends TestCase {
     map = tryParseTestMap(BizarroTestMap.newBuilder()
     map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putInt32ToEnumField(stringKey, bytes)
         .putInt32ToEnumField(stringKey, bytes)
         .build());
         .build());
-    assertEquals(map.getInt32ToEnumFieldOrDefault(0, null), TestMap.EnumValue.FOO);
+    assertEquals(TestMap.EnumValue.FOO, map.getInt32ToEnumFieldOrDefault(0, null));
 
 
     try {
     try {
       tryParseTestMap(BizarroTestMap.newBuilder()
       tryParseTestMap(BizarroTestMap.newBuilder()
@@ -577,7 +577,7 @@ public class MapForProto2Test extends TestCase {
     map = tryParseTestMap(BizarroTestMap.newBuilder()
     map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putStringToInt32Field(stringKey, bytes)
         .putStringToInt32Field(stringKey, bytes)
         .build());
         .build());
-    assertEquals(map.getStringToInt32FieldOrDefault(stringKey, -1), 0);
+    assertEquals(0, map.getStringToInt32FieldOrDefault(stringKey, -1));
   }
   }
 
 
   public void testMergeFrom() throws Exception {
   public void testMergeFrom() throws Exception {

+ 4 - 4
java/core/src/test/java/com/google/protobuf/MapTest.java

@@ -576,12 +576,12 @@ public class MapTest extends TestCase {
     TestMap map = tryParseTestMap(BizarroTestMap.newBuilder()
     TestMap map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putInt32ToInt32Field(5, bytes)
         .putInt32ToInt32Field(5, bytes)
         .build());
         .build());
-    assertEquals(map.getInt32ToInt32FieldOrDefault(5, -1), 0);
+    assertEquals(0, map.getInt32ToInt32FieldOrDefault(5, -1));
 
 
     map = tryParseTestMap(BizarroTestMap.newBuilder()
     map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putInt32ToStringField(stringKey, 5)
         .putInt32ToStringField(stringKey, 5)
         .build());
         .build());
-    assertEquals(map.getInt32ToStringFieldOrDefault(0, null), "");
+    assertEquals("", map.getInt32ToStringFieldOrDefault(0, null));
 
 
     map = tryParseTestMap(BizarroTestMap.newBuilder()
     map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putInt32ToBytesField(stringKey, 5)
         .putInt32ToBytesField(stringKey, 5)
@@ -591,7 +591,7 @@ public class MapTest extends TestCase {
     map = tryParseTestMap(BizarroTestMap.newBuilder()
     map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putInt32ToEnumField(stringKey, bytes)
         .putInt32ToEnumField(stringKey, bytes)
         .build());
         .build());
-    assertEquals(map.getInt32ToEnumFieldOrDefault(0, null), TestMap.EnumValue.FOO);
+    assertEquals(TestMap.EnumValue.FOO, map.getInt32ToEnumFieldOrDefault(0, null));
 
 
     try {
     try {
       tryParseTestMap(BizarroTestMap.newBuilder()
       tryParseTestMap(BizarroTestMap.newBuilder()
@@ -607,7 +607,7 @@ public class MapTest extends TestCase {
     map = tryParseTestMap(BizarroTestMap.newBuilder()
     map = tryParseTestMap(BizarroTestMap.newBuilder()
         .putStringToInt32Field(stringKey, bytes)
         .putStringToInt32Field(stringKey, bytes)
         .build());
         .build());
-    assertEquals(map.getStringToInt32FieldOrDefault(stringKey, -1), 0);
+    assertEquals(0, map.getStringToInt32FieldOrDefault(stringKey, -1));
   }
   }
 
 
   public void testMergeFrom() throws Exception {
   public void testMergeFrom() throws Exception {

+ 8 - 4
java/core/src/test/java/com/google/protobuf/MessageTest.java

@@ -321,8 +321,10 @@ public class MessageTest extends TestCase {
 
 
     assertTrue(result.getField(result.getDescriptorForType()
     assertTrue(result.getField(result.getDescriptorForType()
         .findFieldByName("repeated_foreign_message")) instanceof List<?>);
         .findFieldByName("repeated_foreign_message")) instanceof List<?>);
-    assertEquals(result.getRepeatedFieldCount(result.getDescriptorForType()
-        .findFieldByName("repeated_foreign_message")), 0);
+    assertEquals(
+        0,
+        result.getRepeatedFieldCount(
+            result.getDescriptorForType().findFieldByName("repeated_foreign_message")));
   }
   }
   
   
   /** Test reading repeated message from DynamicMessage. */
   /** Test reading repeated message from DynamicMessage. */
@@ -345,7 +347,9 @@ public class MessageTest extends TestCase {
 
 
     assertTrue(result.getField(result.getDescriptorForType()
     assertTrue(result.getField(result.getDescriptorForType()
         .findFieldByName("repeated_foreign_message")) instanceof List<?>);
         .findFieldByName("repeated_foreign_message")) instanceof List<?>);
-    assertEquals(result.getRepeatedFieldCount(result.getDescriptorForType()
-        .findFieldByName("repeated_foreign_message")), 2);
+    assertEquals(
+        2,
+        result.getRepeatedFieldCount(
+            result.getDescriptorForType().findFieldByName("repeated_foreign_message")));
   }
   }
 }
 }

+ 26 - 0
java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java

@@ -92,5 +92,31 @@ public class TestBadIdentifiers extends TestCase {
     assertEquals(0L, message.getExtension(
     assertEquals(0L, message.getExtension(
         TestBadIdentifiersProto.TestConflictingFieldNames.int64FieldList).longValue());
         TestBadIdentifiersProto.TestConflictingFieldNames.int64FieldList).longValue());
 
 
+    assertEquals("", message.getFieldName32());
+    assertEquals("", message.getFieldName33());
+    assertEquals(0, message.get2Conflict34());
+    assertEquals(0, message.get2Conflict35());
+
+  }
+
+  public void testNumberFields() throws Exception {
+    TestBadIdentifiersProto.TestLeadingNumberFields message =
+        TestBadIdentifiersProto.TestLeadingNumberFields.getDefaultInstance();
+    // Make sure generated accessors are properly named.
+    assertFalse(message.has30DayImpressions());
+    assertEquals(0, message.get30DayImpressions());
+    assertEquals(0, message.get60DayImpressionsCount());
+    assertEquals(0, message.get60DayImpressionsList().size());
+
+    assertFalse(message.has2Underscores());
+    assertEquals("", message.get2Underscores());
+    assertEquals(0, message.get2RepeatedUnderscoresCount());
+    assertEquals(0, message.get2RepeatedUnderscoresList().size());
+
+    assertFalse(message.has32());
+    assertEquals(0, message.get32());
+    assertEquals(0, message.get64Count());
+    assertEquals(0, message.get64List().size());
+
   }
   }
 }
 }

+ 83 - 0
java/core/src/test/java/com/google/protobuf/TestBadIdentifiersLite.java

@@ -0,0 +1,83 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests that proto2 api generation doesn't cause compile errors when compiling protocol buffers
+ * that have names that would otherwise conflict if not fully qualified (like @Deprecated
+ * and @Override).
+ *
+ * <p>Forked from {@link TestBadIdentifiers}.
+ *
+ * @author jonp@google.com (Jon Perlow)
+ */
+public final class TestBadIdentifiersLite extends TestCase {
+
+  public void testCompilation() {
+    // If this compiles, it means the generation was correct.
+    TestBadIdentifiersProto.Deprecated.newBuilder();
+    TestBadIdentifiersProto.Override.newBuilder();
+  }
+
+  public void testConflictingFieldNames() throws Exception {
+    TestBadIdentifiersProto.TestConflictingFieldNames message =
+        TestBadIdentifiersProto.TestConflictingFieldNames.getDefaultInstance();
+    // Make sure generated accessors are properly named.
+    assertEquals(0, message.getInt32Field1Count());
+    assertEquals(0, message.getEnumField2Count());
+    assertEquals(0, message.getStringField3Count());
+    assertEquals(0, message.getBytesField4Count());
+    assertEquals(0, message.getMessageField5Count());
+
+    assertEquals(0, message.getInt32FieldCount11());
+    assertEquals(0, message.getEnumFieldCount12().getNumber());
+    assertEquals("", message.getStringFieldCount13());
+    assertEquals(ByteString.EMPTY, message.getBytesFieldCount14());
+    assertEquals(0, message.getMessageFieldCount15().getSerializedSize());
+
+    assertEquals(0, message.getInt32Field21Count());
+    assertEquals(0, message.getEnumField22Count());
+    assertEquals(0, message.getStringField23Count());
+    assertEquals(0, message.getBytesField24Count());
+    assertEquals(0, message.getMessageField25Count());
+
+    assertEquals(0, message.getInt32Field1List().size());
+    assertEquals(0, message.getInt32FieldList31());
+
+    assertEquals(0, message.getInt64FieldCount());
+    assertEquals(0L, message.getExtension(
+        TestBadIdentifiersProto.TestConflictingFieldNames.int64FieldCount).longValue());
+    assertEquals(0L, message.getExtension(
+        TestBadIdentifiersProto.TestConflictingFieldNames.int64FieldList).longValue());
+  }
+}

+ 5 - 9
java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java

@@ -36,7 +36,6 @@ import com.google.protobuf.Descriptors.EnumValueDescriptor;
 import com.google.protobuf.Descriptors.FieldDescriptor;
 import com.google.protobuf.Descriptors.FieldDescriptor;
 import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
 import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
 import com.google.protobuf.TextFormat.ParseException;
 import com.google.protobuf.TextFormat.ParseException;
-
 import junit.framework.TestCase;
 import junit.framework.TestCase;
 
 
 /**
 /**
@@ -151,18 +150,15 @@ public class UnknownEnumValueTest extends TestCase {
     assertEquals(4321, unknown4321.getNumber());
     assertEquals(4321, unknown4321.getNumber());
     assertEquals(5432, unknown5432.getNumber());
     assertEquals(5432, unknown5432.getNumber());
     assertEquals(6543, unknown6543.getNumber());
     assertEquals(6543, unknown6543.getNumber());
-    
+
     // Unknown EnumValueDescriptor will map to UNRECOGNIZED.
     // Unknown EnumValueDescriptor will map to UNRECOGNIZED.
     assertEquals(
     assertEquals(
-        TestAllTypes.NestedEnum.valueOf(unknown4321),
-        TestAllTypes.NestedEnum.UNRECOGNIZED);
+        TestAllTypes.NestedEnum.UNRECOGNIZED, TestAllTypes.NestedEnum.valueOf(unknown4321));
     assertEquals(
     assertEquals(
-        TestAllTypes.NestedEnum.valueOf(unknown5432),
-        TestAllTypes.NestedEnum.UNRECOGNIZED);
+        TestAllTypes.NestedEnum.UNRECOGNIZED, TestAllTypes.NestedEnum.valueOf(unknown5432));
     assertEquals(
     assertEquals(
-        TestAllTypes.NestedEnum.valueOf(unknown6543),
-        TestAllTypes.NestedEnum.UNRECOGNIZED);
-    
+        TestAllTypes.NestedEnum.UNRECOGNIZED, TestAllTypes.NestedEnum.valueOf(unknown6543));
+
     // Setters also accept unknown EnumValueDescriptor.
     // Setters also accept unknown EnumValueDescriptor.
     builder.setField(optionalNestedEnumField, unknown6543);
     builder.setField(optionalNestedEnumField, unknown6543);
     builder.setRepeatedField(repeatedNestedEnumField, 0, unknown4321);
     builder.setRepeatedField(repeatedNestedEnumField, 0, unknown4321);

+ 17 - 0
java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto

@@ -148,6 +148,12 @@ message TestConflictingFieldNames {
   // the method getInt32FieldList().
   // the method getInt32FieldList().
   required int32 int32_field_list = 31;  // NO_PROTO3
   required int32 int32_field_list = 31;  // NO_PROTO3
 
 
+  // These field pairs have the same Java converted name
+  optional string field_name = 32; // NO_PROTO3
+  optional string field__name = 33; // NO_PROTO3
+  optional int32 _2conflict = 34; // NO_PROTO3
+  optional int32 __2conflict = 35;
+
   extensions 1000 to max;  // NO_PROTO3
   extensions 1000 to max;  // NO_PROTO3
 
 
   repeated int64 int64_field = 41;
   repeated int64 int64_field = 41;
@@ -166,3 +172,14 @@ message TestMapField {
 
 
   map<int32, int32> map_field = 1;
   map<int32, int32> map_field = 1;
 }
 }
+
+message TestLeadingNumberFields {
+  optional int32 _30day_impressions = 1;
+  repeated string _60day_impressions = 2;
+
+  optional string __2_underscores = 3;
+  repeated string __2repeated_underscores = 4;
+
+  optional int32 _32 = 32;
+  repeated int64 _64 = 64;
+}

+ 1 - 1
java/pom.xml

@@ -11,7 +11,7 @@
 
 
   <groupId>com.google.protobuf</groupId>
   <groupId>com.google.protobuf</groupId>
   <artifactId>protobuf-parent</artifactId>
   <artifactId>protobuf-parent</artifactId>
-  <version>3.3.2</version>
+  <version>3.4.0</version>
   <packaging>pom</packaging>
   <packaging>pom</packaging>
 
 
   <name>Protocol Buffers [Parent]</name>
   <name>Protocol Buffers [Parent]</name>

+ 1 - 1
java/util/pom.xml

@@ -6,7 +6,7 @@
   <parent>
   <parent>
     <groupId>com.google.protobuf</groupId>
     <groupId>com.google.protobuf</groupId>
     <artifactId>protobuf-parent</artifactId>
     <artifactId>protobuf-parent</artifactId>
-    <version>3.3.2</version>
+    <version>3.4.0</version>
   </parent>
   </parent>
 
 
   <artifactId>protobuf-java-util</artifactId>
   <artifactId>protobuf-java-util</artifactId>

+ 16 - 7
java/util/src/main/java/com/google/protobuf/util/Durations.java

@@ -30,7 +30,6 @@
 
 
 package com.google.protobuf.util;
 package com.google.protobuf.util;
 
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.math.IntMath.checkedAdd;
 import static com.google.common.math.IntMath.checkedAdd;
 import static com.google.common.math.IntMath.checkedSubtract;
 import static com.google.common.math.IntMath.checkedSubtract;
 import static com.google.common.math.LongMath.checkedAdd;
 import static com.google.common.math.LongMath.checkedAdd;
@@ -83,6 +82,17 @@ public final class Durations {
     return COMPARATOR;
     return COMPARATOR;
   }
   }
 
 
+  /**
+   * Compares two durations. The value returned is identical to what would be returned by:
+   * {@code Durations.comparator().compare(x, y)}.
+   *
+   * @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if {@code x < y};
+   *     and a value greater than {@code 0} if {@code x > y}
+   */
+  public static int compare(Duration x, Duration y) {
+    return COMPARATOR.compare(x, y);
+  }
+
   /**
   /**
    * Returns true if the given {@link Duration} is valid. The {@code seconds} value must be in the
    * Returns true if the given {@link Duration} is valid. The {@code seconds} value must be in the
    * range [-315,576,000,000, +315,576,000,000]. The {@code nanos} value must be in the range
    * range [-315,576,000,000, +315,576,000,000]. The {@code nanos} value must be in the range
@@ -124,14 +134,13 @@ public final class Durations {
   public static Duration checkValid(Duration duration) {
   public static Duration checkValid(Duration duration) {
     long seconds = duration.getSeconds();
     long seconds = duration.getSeconds();
     int nanos = duration.getNanos();
     int nanos = duration.getNanos();
-    checkArgument(
-        isValid(seconds, nanos),
-        "Duration is not valid. See proto definition for valid values. "
+    if (!isValid(seconds, nanos)) {
+        throw new IllegalArgumentException(String.format(
+            "Duration is not valid. See proto definition for valid values. "
             + "Seconds (%s) must be in range [-315,576,000,000, +315,576,000,000]. "
             + "Seconds (%s) must be in range [-315,576,000,000, +315,576,000,000]. "
             + "Nanos (%s) must be in range [-999,999,999, +999,999,999]. "
             + "Nanos (%s) must be in range [-999,999,999, +999,999,999]. "
-            + "Nanos must have the same sign as seconds",
-        seconds,
-        nanos);
+            + "Nanos must have the same sign as seconds", seconds, nanos));
+    }
     return duration;
     return duration;
   }
   }
 
 

+ 6 - 3
java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java

@@ -311,16 +311,19 @@ public class FieldMaskUtil {
       return replacePrimitiveFields;
       return replacePrimitiveFields;
     }
     }
 
 
-    public void setReplaceMessageFields(boolean value) {
+    public MergeOptions setReplaceMessageFields(boolean value) {
       replaceMessageFields = value;
       replaceMessageFields = value;
+      return this;
     }
     }
 
 
-    public void setReplaceRepeatedFields(boolean value) {
+    public MergeOptions setReplaceRepeatedFields(boolean value) {
       replaceRepeatedFields = value;
       replaceRepeatedFields = value;
+      return this;
     }
     }
 
 
-    public void setReplacePrimitiveFields(boolean value) {
+    public MergeOptions setReplacePrimitiveFields(boolean value) {
       replacePrimitiveFields = value;
       replacePrimitiveFields = value;
+      return this;
     }
     }
   }
   }
 
 

+ 5 - 1
java/util/src/main/java/com/google/protobuf/util/JsonFormat.java

@@ -1686,7 +1686,11 @@ public class JsonFormat {
     }
     }
 
 
     private ByteString parseBytes(JsonElement json) throws InvalidProtocolBufferException {
     private ByteString parseBytes(JsonElement json) throws InvalidProtocolBufferException {
-      return ByteString.copyFrom(BaseEncoding.base64().decode(json.getAsString()));
+      try {
+        return ByteString.copyFrom(BaseEncoding.base64().decode(json.getAsString()));
+      } catch (IllegalArgumentException e) {
+        return ByteString.copyFrom(BaseEncoding.base64Url().decode(json.getAsString()));
+      }
     }
     }
 
 
     private EnumValueDescriptor parseEnum(EnumDescriptor enumDescriptor, JsonElement json)
     private EnumValueDescriptor parseEnum(EnumDescriptor enumDescriptor, JsonElement json)

+ 16 - 7
java/util/src/main/java/com/google/protobuf/util/Timestamps.java

@@ -30,7 +30,6 @@
 
 
 package com.google.protobuf.util;
 package com.google.protobuf.util;
 
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.math.IntMath.checkedAdd;
 import static com.google.common.math.IntMath.checkedAdd;
 import static com.google.common.math.IntMath.checkedSubtract;
 import static com.google.common.math.IntMath.checkedSubtract;
 import static com.google.common.math.LongMath.checkedAdd;
 import static com.google.common.math.LongMath.checkedAdd;
@@ -114,6 +113,17 @@ public final class Timestamps {
     return COMPARATOR;
     return COMPARATOR;
   }
   }
 
 
+  /**
+   * Compares two timestamps. The value returned is identical to what would be returned by:
+   * {@code Timestamps.comparator().compare(x, y)}.
+   *
+   * @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if {@code x < y};
+   *     and a value greater than {@code 0} if {@code x > y}
+   */
+  public static int compare(Timestamp x, Timestamp y) {
+    return COMPARATOR.compare(x, y);
+  }
+
   /**
   /**
    * Returns true if the given {@link Timestamp} is valid. The {@code seconds} value must be in the
    * Returns true if the given {@link Timestamp} is valid. The {@code seconds} value must be in the
    * range [-62,135,596,800, +253,402,300,799] (i.e., between 0001-01-01T00:00:00Z and
    * range [-62,135,596,800, +253,402,300,799] (i.e., between 0001-01-01T00:00:00Z and
@@ -149,13 +159,12 @@ public final class Timestamps {
   public static Timestamp checkValid(Timestamp timestamp) {
   public static Timestamp checkValid(Timestamp timestamp) {
     long seconds = timestamp.getSeconds();
     long seconds = timestamp.getSeconds();
     int nanos = timestamp.getNanos();
     int nanos = timestamp.getNanos();
-    checkArgument(
-        isValid(seconds, nanos),
-        "Timestamp is not valid. See proto definition for valid values. "
+    if (!isValid(seconds, nanos)) {
+        throw new IllegalArgumentException(String.format(
+            "Timestamp is not valid. See proto definition for valid values. "
             + "Seconds (%s) must be in range [-62,135,596,800, +253,402,300,799]. "
             + "Seconds (%s) must be in range [-62,135,596,800, +253,402,300,799]. "
-            + "Nanos (%s) must be in range [0, +999,999,999].",
-        seconds,
-        nanos);
+            + "Nanos (%s) must be in range [0, +999,999,999].", seconds, nanos));
+    }
     return timestamp;
     return timestamp;
   }
   }
 
 

+ 2 - 2
java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java

@@ -73,7 +73,6 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Set;
 import java.util.Set;
 import junit.framework.TestCase;
 import junit.framework.TestCase;
 
 
@@ -1144,7 +1143,8 @@ public class JsonFormatTest extends TestCase {
   }
   }
 
 
   public void testParserAcceptBase64Variants() throws Exception {
   public void testParserAcceptBase64Variants() throws Exception {
-    assertAccepts("optionalBytes", "AQI");
+    assertAccepts("optionalBytes", "AQI");  // No padding
+    assertAccepts("optionalBytes", "-_w");  // base64Url, no padding
   }
   }
 
 
   public void testParserRejectInvalidEnumValue() throws Exception {
   public void testParserRejectInvalidEnumValue() throws Exception {

+ 5 - 3
js/binary/encoder.js

@@ -390,11 +390,13 @@ jspb.BinaryEncoder.prototype.writeDouble = function(value) {
 
 
 
 
 /**
 /**
- * Writes a boolean value to the buffer as a varint.
- * @param {boolean} value The value to write.
+ * Writes a boolean value to the buffer as a varint. We allow numbers as input
+ * because the JSPB code generator uses 0/1 instead of true/false to save space
+ * in the string representation of the proto.
+ * @param {boolean|number} value The value to write.
  */
  */
 jspb.BinaryEncoder.prototype.writeBool = function(value) {
 jspb.BinaryEncoder.prototype.writeBool = function(value) {
-  goog.asserts.assert(goog.isBoolean(value));
+  goog.asserts.assert(goog.isBoolean(value) || goog.isNumber(value));
   this.buffer_.push(value ? 1 : 0);
   this.buffer_.push(value ? 1 : 0);
 };
 };
 
 

+ 6 - 4
js/binary/writer.js

@@ -235,7 +235,7 @@ jspb.BinaryWriter.prototype.getResultBuffer = function() {
 
 
 
 
 /**
 /**
- * Converts the encoded data into a bas64-encoded string.
+ * Converts the encoded data into a base64-encoded string.
  * @return {string}
  * @return {string}
  */
  */
 jspb.BinaryWriter.prototype.getResultBase64String = function() {
 jspb.BinaryWriter.prototype.getResultBase64String = function() {
@@ -716,13 +716,15 @@ jspb.BinaryWriter.prototype.writeDouble = function(field, value) {
 
 
 
 
 /**
 /**
- * Writes a boolean field to the buffer.
+ * Writes a boolean field to the buffer. We allow numbers as input
+ * because the JSPB code generator uses 0/1 instead of true/false to save space
+ * in the string representation of the proto.
  * @param {number} field The field number.
  * @param {number} field The field number.
- * @param {boolean?} value The value to write.
+ * @param {boolean?|number?} value The value to write.
  */
  */
 jspb.BinaryWriter.prototype.writeBool = function(field, value) {
 jspb.BinaryWriter.prototype.writeBool = function(field, value) {
   if (value == null) return;
   if (value == null) return;
-  goog.asserts.assert(goog.isBoolean(value));
+  goog.asserts.assert(goog.isBoolean(value) || goog.isNumber(value));
   this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
   this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
   this.encoder_.writeBool(value);
   this.encoder_.writeBool(value);
 };
 };

+ 355 - 0
js/compatibility_tests/v3.1.0/binary/arith_test.js

@@ -0,0 +1,355 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Test cases for Int64-manipulation functions.
+ *
+ * Test suite is written using Jasmine -- see http://jasmine.github.io/
+ *
+ * @author cfallin@google.com (Chris Fallin)
+ */
+
+goog.require('goog.testing.asserts');
+goog.require('jspb.arith.Int64');
+goog.require('jspb.arith.UInt64');
+
+
+describe('binaryArithTest', function() {
+  /**
+   * Tests comparison operations.
+   */
+  it('testCompare', function() {
+    var a = new jspb.arith.UInt64(1234, 5678);
+    var b = new jspb.arith.UInt64(1234, 5678);
+    assertEquals(a.cmp(b), 0);
+    assertEquals(b.cmp(a), 0);
+    b.lo -= 1;
+    assertEquals(a.cmp(b), 1);
+    assertEquals(b.cmp(a), -1);
+    b.lo += 2;
+    assertEquals(a.cmp(b), -1);
+    assertEquals(b.cmp(a), 1);
+    b.lo = a.lo;
+    b.hi = a.hi - 1;
+    assertEquals(a.cmp(b), 1);
+    assertEquals(b.cmp(a), -1);
+
+    assertEquals(a.zero(), false);
+    assertEquals(a.msb(), false);
+    assertEquals(a.lsb(), false);
+    a.hi = 0;
+    a.lo = 0;
+    assertEquals(a.zero(), true);
+    a.hi = 0x80000000;
+    assertEquals(a.zero(), false);
+    assertEquals(a.msb(), true);
+    a.lo = 0x00000001;
+    assertEquals(a.lsb(), true);
+  });
+
+
+  /**
+   * Tests shifts.
+   */
+  it('testShifts', function() {
+    var a = new jspb.arith.UInt64(1, 0);
+    assertEquals(a.lo, 1);
+    assertEquals(a.hi, 0);
+    var orig = a;
+    a = a.leftShift();
+    assertEquals(orig.lo, 1);  // original unmodified.
+    assertEquals(orig.hi, 0);
+    assertEquals(a.lo, 2);
+    assertEquals(a.hi, 0);
+    a = a.leftShift();
+    assertEquals(a.lo, 4);
+    assertEquals(a.hi, 0);
+    for (var i = 0; i < 29; i++) {
+      a = a.leftShift();
+    }
+    assertEquals(a.lo, 0x80000000);
+    assertEquals(a.hi, 0);
+    a = a.leftShift();
+    assertEquals(a.lo, 0);
+    assertEquals(a.hi, 1);
+    a = a.leftShift();
+    assertEquals(a.lo, 0);
+    assertEquals(a.hi, 2);
+    a = a.rightShift();
+    a = a.rightShift();
+    assertEquals(a.lo, 0x80000000);
+    assertEquals(a.hi, 0);
+    a = a.rightShift();
+    assertEquals(a.lo, 0x40000000);
+    assertEquals(a.hi, 0);
+  });
+
+
+  /**
+   * Tests additions.
+   */
+  it('testAdd', function() {
+    var a = new jspb.arith.UInt64(/* lo = */ 0x89abcdef,
+                                         /* hi = */ 0x01234567);
+    var b = new jspb.arith.UInt64(/* lo = */ 0xff52ab91,
+                                         /* hi = */ 0x92fa2123);
+    // Addition with carry.
+    var c = a.add(b);
+    assertEquals(a.lo, 0x89abcdef);  // originals unmodified.
+    assertEquals(a.hi, 0x01234567);
+    assertEquals(b.lo, 0xff52ab91);
+    assertEquals(b.hi, 0x92fa2123);
+    assertEquals(c.lo, 0x88fe7980);
+    assertEquals(c.hi, 0x941d668b);
+
+    // Simple addition without carry.
+    a.lo = 2;
+    a.hi = 0;
+    b.lo = 3;
+    b.hi = 0;
+    c = a.add(b);
+    assertEquals(c.lo, 5);
+    assertEquals(c.hi, 0);
+  });
+
+
+  /**
+   * Test subtractions.
+   */
+  it('testSub', function() {
+    var kLength = 10;
+    var hiValues = [0x1682ef32,
+                    0x583902f7,
+                    0xb62f5955,
+                    0x6ea99bbf,
+                    0x25a39c20,
+                    0x0700a08b,
+                    0x00f7304d,
+                    0x91a5b5af,
+                    0x89077fd2,
+                    0xe09e347c];
+    var loValues = [0xe1538b18,
+                    0xbeacd556,
+                    0x74100758,
+                    0x96e3cb26,
+                    0x56c37c3f,
+                    0xe00b3f7d,
+                    0x859f25d7,
+                    0xc2ee614a,
+                    0xe1d21cd7,
+                    0x30aae6a4];
+    for (var i = 0; i < kLength; i++) {
+      for (var j = 0; j < kLength; j++) {
+        var a = new jspb.arith.UInt64(loValues[i], hiValues[j]);
+        var b = new jspb.arith.UInt64(loValues[j], hiValues[i]);
+        var c = a.add(b).sub(b);
+        assertEquals(c.hi, a.hi);
+        assertEquals(c.lo, a.lo);
+      }
+    }
+  });
+
+
+  /**
+   * Tests 32-by-32 multiplication.
+   */
+  it('testMul32x32', function() {
+    var testData = [
+      // a        b          low(a*b)   high(a*b)
+      [0xc0abe2f8, 0x1607898a, 0x5de711b0, 0x109471b8],
+      [0x915eb3cb, 0x4fb66d0e, 0xbd0d441a, 0x2d43d0bc],
+      [0xfe4efe70, 0x80b48c37, 0xbcddea10, 0x7fdada0c],
+      [0xe222fd4a, 0xe43d524a, 0xd5e0eb64, 0xc99d549c],
+      [0xd171f469, 0xb94ebd01, 0x4be17969, 0x979bc4fa],
+      [0x829cc1df, 0xe2598b38, 0xf4157dc8, 0x737c12ad],
+      [0xf10c3767, 0x8382881e, 0x942b3612, 0x7bd428b8],
+      [0xb0f6dd24, 0x232597e1, 0x079c98a4, 0x184bbce7],
+      [0xfcdb05a7, 0x902f55bc, 0x636199a4, 0x8e69f412],
+      [0x0dd0bfa9, 0x916e27b1, 0x6e2542d9, 0x07d92e65]
+    ];
+
+    for (var i = 0; i < testData.length; i++) {
+      var a = testData[i][0] >>> 0;
+      var b = testData[i][1] >>> 0;
+      var cLow = testData[i][2] >>> 0;
+      var cHigh = testData[i][3] >>> 0;
+      var c = jspb.arith.UInt64.mul32x32(a, b);
+      assertEquals(c.lo, cLow);
+      assertEquals(c.hi, cHigh);
+    }
+  });
+
+
+  /**
+   * Tests 64-by-32 multiplication.
+   */
+  it('testMul', function() {
+    // 64x32 bits produces 96 bits of product. The multiplication function under
+    // test truncates the top 32 bits, so we compare against a 64-bit expected
+    // product.
+    var testData = [
+      // low(a)   high(a)               low(a*b)   high(a*b)
+      [0xec10955b, 0x360eb168, 0x4b7f3f5b, 0xbfcb7c59, 0x9517da5f],
+      [0x42b000fc, 0x9d101642, 0x6fa1ab72, 0x2584c438, 0x6a9e6d2b],
+      [0xf42d4fb4, 0xae366403, 0xa65a1000, 0x92434000, 0x1ff978df],
+      [0x17e2f56b, 0x25487693, 0xf13f98c7, 0x73794e2d, 0xa96b0c6a],
+      [0x492f241f, 0x76c0eb67, 0x7377ac44, 0xd4336c3c, 0xfc4b1ebe],
+      [0xd6b92321, 0xe184fa48, 0xd6e76904, 0x93141584, 0xcbf44da1],
+      [0x4bf007ea, 0x968c0a9e, 0xf5e4026a, 0x4fdb1ae4, 0x61b9fb7d],
+      [0x10a83be7, 0x2d685ba6, 0xc9e5fb7f, 0x2ad43499, 0x3742473d],
+      [0x2f261829, 0x1aca681a, 0x3d3494e3, 0x8213205b, 0x283719f8],
+      [0xe4f2ce21, 0x2e74b7bd, 0xd801b38b, 0xbc17feeb, 0xc6c44e0f]
+    ];
+
+    for (var i = 0; i < testData.length; i++) {
+      var a = new jspb.arith.UInt64(testData[i][0], testData[i][1]);
+      var prod = a.mul(testData[i][2]);
+      assertEquals(prod.lo, testData[i][3]);
+      assertEquals(prod.hi, testData[i][4]);
+    }
+  });
+
+
+  /**
+   * Tests 64-div-by-32 division.
+   */
+  it('testDiv', function() {
+    // Compute a/b, yielding quot = a/b and rem = a%b.
+    var testData = [
+      // --- divisors in (0, 2^32-1) to test full divisor range
+      // low(a)   high(a)    b          low(quot)  high(quot) rem
+      [0x712443f1, 0xe85cefcc, 0xc1a7050b, 0x332c79ad, 0x00000001, 0x92ffa882],
+      [0x11912915, 0xb2699eb5, 0x30467cbe, 0xb21b4be4, 0x00000003, 0x283465dd],
+      [0x0d917982, 0x201f2a6e, 0x3f35bf03, 0x8217c8e4, 0x00000000, 0x153402d6],
+      [0xa072c108, 0x74020c96, 0xc60568fd, 0x95f9613e, 0x00000000, 0x3f4676c2],
+      [0xd845d5d8, 0xcdd235c4, 0x20426475, 0x6154e78b, 0x00000006, 0x202fb751],
+      [0xa4dbf71f, 0x9e90465e, 0xf08e022f, 0xa8be947f, 0x00000000, 0xbe43b5ce],
+      [0x3dbe627f, 0xa791f4b9, 0x28a5bd89, 0x1f5dfe93, 0x00000004, 0x02bf9ed4],
+      [0x5c1c53ee, 0xccf5102e, 0x198576e7, 0x07e3ae31, 0x00000008, 0x02ea8fb7],
+      [0xfef1e581, 0x04714067, 0xca6540c1, 0x059e73ec, 0x00000000, 0x31658095],
+      [0x1e2dd90c, 0x13dd6667, 0x8b2184c3, 0x248d1a42, 0x00000000, 0x4ca6d0c6],
+      // --- divisors in (0, 2^16-1) to test larger quotient high-words
+      // low(a)   high(a)    b          low(quot)  high(quot) rem
+      [0x86722b47, 0x2cd57c9a, 0x00003123, 0x2ae41b7a, 0x0000e995, 0x00000f99],
+      [0x1dd7884c, 0xf5e839bc, 0x00009eeb, 0x5c886242, 0x00018c21, 0x000099b6],
+      [0x5c53d625, 0x899fc7e5, 0x000087d7, 0xd625007a, 0x0001035c, 0x000019af],
+      [0x6932d932, 0x9d0a5488, 0x000051fb, 0x9d976143, 0x0001ea63, 0x00004981],
+      [0x4d18bb85, 0x0c92fb31, 0x00001d9f, 0x03265ab4, 0x00006cac, 0x000001b9],
+      [0xbe756768, 0xdea67ccb, 0x00008a03, 0x58add442, 0x00019cff, 0x000056a2],
+      [0xe2466f9a, 0x2521f114, 0x0000c350, 0xa0c0860d, 0x000030ab, 0x0000a48a],
+      [0xf00ddad1, 0xe2f5446a, 0x00002cfc, 0x762697a6, 0x00050b96, 0x00000b69],
+      [0xa879152a, 0x0a70e0a5, 0x00007cdf, 0xb44151b3, 0x00001567, 0x0000363d],
+      [0x7179a74c, 0x46083fff, 0x0000253c, 0x4d39ba6e, 0x0001e17f, 0x00000f84]
+    ];
+
+    for (var i = 0; i < testData.length; i++) {
+      var a = new jspb.arith.UInt64(testData[i][0], testData[i][1]);
+      var result = a.div(testData[i][2]);
+      var quotient = result[0];
+      var remainder = result[1];
+      assertEquals(quotient.lo, testData[i][3]);
+      assertEquals(quotient.hi, testData[i][4]);
+      assertEquals(remainder.lo, testData[i][5]);
+    }
+  });
+
+
+  /**
+   * Tests .toString() and .fromString().
+   */
+  it('testStrings', function() {
+    var testData = [
+        [0x5e84c935, 0xcae33d0e, '14619595947299359029'],
+        [0x62b3b8b8, 0x93480544, '10612738313170434232'],
+        [0x319bfb13, 0xc01c4172, '13843011313344445203'],
+        [0x5b8a65fb, 0xa5885b31, '11927883880638080507'],
+        [0x6bdb80f1, 0xb0d1b16b, '12741159895737008369'],
+        [0x4b82b442, 0x2e0d8c97, '3318463081876730946'],
+        [0x780d5208, 0x7d76752c, '9040542135845999112'],
+        [0x2e46800f, 0x0993778d, '690026616168284175'],
+        [0xf00a7e32, 0xcd8e3931, '14811839111111540274'],
+        [0x1baeccd6, 0x923048c4, '10533999535534820566'],
+        [0x03669d29, 0xbff3ab72, '13831587386756603177'],
+        [0x2526073e, 0x01affc81, '121593346566522686'],
+        [0xc24244e0, 0xd7f40d0e, '15561076969511732448'],
+        [0xc56a341e, 0xa68b66a7, '12000798502816461854'],
+        [0x8738d64d, 0xbfe78604, '13828168534871037517'],
+        [0x5baff03b, 0xd7572aea, '15516918227177304123'],
+        [0x4a843d8a, 0x864e132b, '9677693725920476554'],
+        [0x25b4e94d, 0x22b54dc6, '2500990681505655117'],
+        [0x6bbe664b, 0x55a5cc0e, '6171563226690381387'],
+        [0xee916c81, 0xb00aabb3, '12685140089732426881']
+    ];
+
+    for (var i = 0; i < testData.length; i++) {
+      var a = new jspb.arith.UInt64(testData[i][0], testData[i][1]);
+      var roundtrip = jspb.arith.UInt64.fromString(a.toString());
+      assertEquals(roundtrip.lo, a.lo);
+      assertEquals(roundtrip.hi, a.hi);
+      assertEquals(a.toString(), testData[i][2]);
+    }
+  });
+
+
+  /**
+   * Tests signed Int64s. These are built on UInt64s, so we only need to test
+   * the explicit overrides: .toString() and .fromString().
+   */
+  it('testSignedInt64', function() {
+    var testStrings = [
+        '-7847499644178593666',
+        '3771946501229139523',
+        '2872856549054995060',
+        '-5780049594274350904',
+        '3383785956695105201',
+        '2973055184857072610',
+        '-3879428459215627206',
+        '4589812431064156631',
+        '8484075557333689940',
+        '1075325817098092407',
+        '-4346697501012292314',
+        '2488620459718316637',
+        '6112655187423520672',
+        '-3655278273928612104',
+        '3439154019435803196',
+        '1004112478843763757',
+        '-6587790776614368413',
+        '664320065099714586',
+        '4760412909973292912',
+        '-7911903989602274672'
+    ];
+
+    for (var i = 0; i < testStrings.length; i++) {
+      var roundtrip =
+          jspb.arith.Int64.fromString(testStrings[i]).toString();
+      assertEquals(roundtrip, testStrings[i]);
+    }
+  });
+});

+ 334 - 0
js/compatibility_tests/v3.1.0/binary/decoder_test.js

@@ -0,0 +1,334 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Test cases for jspb's binary protocol buffer decoder.
+ *
+ * There are two particular magic numbers that need to be pointed out -
+ * 2^64-1025 is the largest number representable as both a double and an
+ * unsigned 64-bit integer, and 2^63-513 is the largest number representable as
+ * both a double and a signed 64-bit integer.
+ *
+ * Test suite is written using Jasmine -- see http://jasmine.github.io/
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.require('goog.testing.asserts');
+goog.require('jspb.BinaryConstants');
+goog.require('jspb.BinaryDecoder');
+goog.require('jspb.BinaryEncoder');
+
+
+/**
+ * Tests encoding and decoding of unsigned types.
+ * @param {Function} readValue
+ * @param {Function} writeValue
+ * @param {number} epsilon
+ * @param {number} upperLimit
+ * @param {Function} filter
+ * @suppress {missingProperties|visibility}
+ */
+function doTestUnsignedValue(readValue,
+    writeValue, epsilon, upperLimit, filter) {
+  var encoder = new jspb.BinaryEncoder();
+
+  // Encode zero and limits.
+  writeValue.call(encoder, filter(0));
+  writeValue.call(encoder, filter(epsilon));
+  writeValue.call(encoder, filter(upperLimit));
+
+  // Encode positive values.
+  for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
+    writeValue.call(encoder, filter(cursor));
+  }
+
+  var decoder = jspb.BinaryDecoder.alloc(encoder.end());
+
+  // Check zero and limits.
+  assertEquals(filter(0), readValue.call(decoder));
+  assertEquals(filter(epsilon), readValue.call(decoder));
+  assertEquals(filter(upperLimit), readValue.call(decoder));
+
+  // Check positive values.
+  for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
+    if (filter(cursor) != readValue.call(decoder)) throw 'fail!';
+  }
+
+  // Encoding values outside the valid range should assert.
+  assertThrows(function() {writeValue.call(encoder, -1);});
+  assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);});
+}
+
+
+/**
+ * Tests encoding and decoding of signed types.
+ * @param {Function} readValue
+ * @param {Function} writeValue
+ * @param {number} epsilon
+ * @param {number} lowerLimit
+ * @param {number} upperLimit
+ * @param {Function} filter
+ * @suppress {missingProperties}
+ */
+function doTestSignedValue(readValue,
+    writeValue, epsilon, lowerLimit, upperLimit, filter) {
+  var encoder = new jspb.BinaryEncoder();
+
+  // Encode zero and limits.
+  writeValue.call(encoder, filter(lowerLimit));
+  writeValue.call(encoder, filter(-epsilon));
+  writeValue.call(encoder, filter(0));
+  writeValue.call(encoder, filter(epsilon));
+  writeValue.call(encoder, filter(upperLimit));
+
+  var inputValues = [];
+
+  // Encode negative values.
+  for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) {
+    var val = filter(cursor);
+    writeValue.call(encoder, val);
+    inputValues.push(val);
+  }
+
+  // Encode positive values.
+  for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
+    var val = filter(cursor);
+    writeValue.call(encoder, val);
+    inputValues.push(val);
+  }
+
+  var decoder = jspb.BinaryDecoder.alloc(encoder.end());
+
+  // Check zero and limits.
+  assertEquals(filter(lowerLimit), readValue.call(decoder));
+  assertEquals(filter(-epsilon), readValue.call(decoder));
+  assertEquals(filter(0), readValue.call(decoder));
+  assertEquals(filter(epsilon), readValue.call(decoder));
+  assertEquals(filter(upperLimit), readValue.call(decoder));
+
+  // Verify decoded values.
+  for (var i = 0; i < inputValues.length; i++) {
+    assertEquals(inputValues[i], readValue.call(decoder));
+  }
+
+  // Encoding values outside the valid range should assert.
+  assertThrows(function() {writeValue.call(encoder, lowerLimit * 1.1);});
+  assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);});
+}
+
+describe('binaryDecoderTest', function() {
+  /**
+   * Tests the decoder instance cache.
+   */
+  it('testInstanceCache', /** @suppress {visibility} */ function() {
+    // Empty the instance caches.
+    jspb.BinaryDecoder.instanceCache_ = [];
+
+    // Allocating and then freeing a decoder should put it in the instance
+    // cache.
+    jspb.BinaryDecoder.alloc().free();
+
+    assertEquals(1, jspb.BinaryDecoder.instanceCache_.length);
+
+    // Allocating and then freeing three decoders should leave us with three in
+    // the cache.
+
+    var decoder1 = jspb.BinaryDecoder.alloc();
+    var decoder2 = jspb.BinaryDecoder.alloc();
+    var decoder3 = jspb.BinaryDecoder.alloc();
+    decoder1.free();
+    decoder2.free();
+    decoder3.free();
+
+    assertEquals(3, jspb.BinaryDecoder.instanceCache_.length);
+  });
+
+
+  /**
+   * Tests reading 64-bit integers as hash strings.
+   */
+  it('testHashStrings', function() {
+    var encoder = new jspb.BinaryEncoder();
+
+    var hashA = String.fromCharCode(0x00, 0x00, 0x00, 0x00,
+                                    0x00, 0x00, 0x00, 0x00);
+    var hashB = String.fromCharCode(0x12, 0x34, 0x00, 0x00,
+                                    0x00, 0x00, 0x00, 0x00);
+    var hashC = String.fromCharCode(0x12, 0x34, 0x56, 0x78,
+                                    0x87, 0x65, 0x43, 0x21);
+    var hashD = String.fromCharCode(0xFF, 0xFF, 0xFF, 0xFF,
+                                    0xFF, 0xFF, 0xFF, 0xFF);
+
+    encoder.writeVarintHash64(hashA);
+    encoder.writeVarintHash64(hashB);
+    encoder.writeVarintHash64(hashC);
+    encoder.writeVarintHash64(hashD);
+
+    encoder.writeFixedHash64(hashA);
+    encoder.writeFixedHash64(hashB);
+    encoder.writeFixedHash64(hashC);
+    encoder.writeFixedHash64(hashD);
+
+    var decoder = jspb.BinaryDecoder.alloc(encoder.end());
+
+    assertEquals(hashA, decoder.readVarintHash64());
+    assertEquals(hashB, decoder.readVarintHash64());
+    assertEquals(hashC, decoder.readVarintHash64());
+    assertEquals(hashD, decoder.readVarintHash64());
+
+    assertEquals(hashA, decoder.readFixedHash64());
+    assertEquals(hashB, decoder.readFixedHash64());
+    assertEquals(hashC, decoder.readFixedHash64());
+    assertEquals(hashD, decoder.readFixedHash64());
+  });
+
+
+  /**
+   * Verifies that misuse of the decoder class triggers assertions.
+   * @suppress {checkTypes|visibility}
+   */
+  it('testDecodeErrors', function() {
+    // Reading a value past the end of the stream should trigger an assertion.
+    var decoder = jspb.BinaryDecoder.alloc([0, 1, 2]);
+    assertThrows(function() {decoder.readUint64()});
+
+    // Overlong varints should trigger assertions.
+    decoder.setBlock([255, 255, 255, 255, 255, 255,
+                      255, 255, 255, 255, 255, 0]);
+    assertThrows(function() {decoder.readUnsignedVarint64()});
+    decoder.reset();
+    assertThrows(function() {decoder.readSignedVarint64()});
+    decoder.reset();
+    assertThrows(function() {decoder.readZigzagVarint64()});
+
+    // Positive 32-bit varints encoded with 1 bits in positions 33 through 35
+    // should trigger assertions.
+    decoder.setBlock([255, 255, 255, 255, 0x1F]);
+    assertThrows(function() {decoder.readUnsignedVarint32()});
+
+    decoder.setBlock([255, 255, 255, 255, 0x2F]);
+    assertThrows(function() {decoder.readUnsignedVarint32()});
+
+    decoder.setBlock([255, 255, 255, 255, 0x4F]);
+    assertThrows(function() {decoder.readUnsignedVarint32()});
+
+    // Negative 32-bit varints encoded with non-1 bits in the high dword should
+    // trigger assertions.
+    decoder.setBlock([255, 255, 255, 255, 255, 255, 0, 255, 255, 1]);
+    assertThrows(function() {decoder.readUnsignedVarint32()});
+
+    decoder.setBlock([255, 255, 255, 255, 255, 255, 255, 255, 255, 0]);
+    assertThrows(function() {decoder.readUnsignedVarint32()});
+  });
+
+
+  /**
+   * Tests encoding and decoding of unsigned integers.
+   */
+  it('testUnsignedIntegers', function() {
+    doTestUnsignedValue(
+        jspb.BinaryDecoder.prototype.readUint8,
+        jspb.BinaryEncoder.prototype.writeUint8,
+        1, 0xFF, Math.round);
+
+    doTestUnsignedValue(
+        jspb.BinaryDecoder.prototype.readUint16,
+        jspb.BinaryEncoder.prototype.writeUint16,
+        1, 0xFFFF, Math.round);
+
+    doTestUnsignedValue(
+        jspb.BinaryDecoder.prototype.readUint32,
+        jspb.BinaryEncoder.prototype.writeUint32,
+        1, 0xFFFFFFFF, Math.round);
+
+    doTestUnsignedValue(
+        jspb.BinaryDecoder.prototype.readUint64,
+        jspb.BinaryEncoder.prototype.writeUint64,
+        1, Math.pow(2, 64) - 1025, Math.round);
+  });
+
+
+  /**
+   * Tests encoding and decoding of signed integers.
+   */
+  it('testSignedIntegers', function() {
+    doTestSignedValue(
+        jspb.BinaryDecoder.prototype.readInt8,
+        jspb.BinaryEncoder.prototype.writeInt8,
+        1, -0x80, 0x7F, Math.round);
+
+    doTestSignedValue(
+        jspb.BinaryDecoder.prototype.readInt16,
+        jspb.BinaryEncoder.prototype.writeInt16,
+        1, -0x8000, 0x7FFF, Math.round);
+
+    doTestSignedValue(
+        jspb.BinaryDecoder.prototype.readInt32,
+        jspb.BinaryEncoder.prototype.writeInt32,
+        1, -0x80000000, 0x7FFFFFFF, Math.round);
+
+    doTestSignedValue(
+        jspb.BinaryDecoder.prototype.readInt64,
+        jspb.BinaryEncoder.prototype.writeInt64,
+        1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
+  });
+
+
+  /**
+   * Tests encoding and decoding of floats.
+   */
+  it('testFloats', function() {
+    /**
+     * @param {number} x
+     * @return {number}
+     */
+    function truncate(x) {
+      var temp = new Float32Array(1);
+      temp[0] = x;
+      return temp[0];
+    }
+    doTestSignedValue(
+        jspb.BinaryDecoder.prototype.readFloat,
+        jspb.BinaryEncoder.prototype.writeFloat,
+        jspb.BinaryConstants.FLOAT32_EPS,
+        -jspb.BinaryConstants.FLOAT32_MAX,
+        jspb.BinaryConstants.FLOAT32_MAX,
+        truncate);
+
+    doTestSignedValue(
+        jspb.BinaryDecoder.prototype.readDouble,
+        jspb.BinaryEncoder.prototype.writeDouble,
+        jspb.BinaryConstants.FLOAT64_EPS * 10,
+        -jspb.BinaryConstants.FLOAT64_MAX,
+        jspb.BinaryConstants.FLOAT64_MAX,
+        function(x) { return x; });
+  });
+});

+ 628 - 0
js/compatibility_tests/v3.1.0/binary/proto_test.js

@@ -0,0 +1,628 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test suite is written using Jasmine -- see http://jasmine.github.io/
+
+goog.require('goog.crypt.base64');
+goog.require('goog.testing.asserts');
+goog.require('jspb.Message');
+
+// CommonJS-LoadFromFile: ../testbinary_pb proto.jspb.test
+goog.require('proto.jspb.test.ExtendsWithMessage');
+goog.require('proto.jspb.test.ForeignEnum');
+goog.require('proto.jspb.test.ForeignMessage');
+goog.require('proto.jspb.test.TestAllTypes');
+goog.require('proto.jspb.test.TestExtendable');
+goog.require('proto.jspb.test.extendOptionalBool');
+goog.require('proto.jspb.test.extendOptionalBytes');
+goog.require('proto.jspb.test.extendOptionalDouble');
+goog.require('proto.jspb.test.extendOptionalFixed32');
+goog.require('proto.jspb.test.extendOptionalFixed64');
+goog.require('proto.jspb.test.extendOptionalFloat');
+goog.require('proto.jspb.test.extendOptionalForeignEnum');
+goog.require('proto.jspb.test.extendOptionalInt32');
+goog.require('proto.jspb.test.extendOptionalInt64');
+goog.require('proto.jspb.test.extendOptionalSfixed32');
+goog.require('proto.jspb.test.extendOptionalSfixed64');
+goog.require('proto.jspb.test.extendOptionalSint32');
+goog.require('proto.jspb.test.extendOptionalSint64');
+goog.require('proto.jspb.test.extendOptionalString');
+goog.require('proto.jspb.test.extendOptionalUint32');
+goog.require('proto.jspb.test.extendOptionalUint64');
+goog.require('proto.jspb.test.extendPackedRepeatedBoolList');
+goog.require('proto.jspb.test.extendPackedRepeatedDoubleList');
+goog.require('proto.jspb.test.extendPackedRepeatedFixed32List');
+goog.require('proto.jspb.test.extendPackedRepeatedFixed64List');
+goog.require('proto.jspb.test.extendPackedRepeatedFloatList');
+goog.require('proto.jspb.test.extendPackedRepeatedForeignEnumList');
+goog.require('proto.jspb.test.extendPackedRepeatedInt32List');
+goog.require('proto.jspb.test.extendPackedRepeatedInt64List');
+goog.require('proto.jspb.test.extendPackedRepeatedSfixed32List');
+goog.require('proto.jspb.test.extendPackedRepeatedSfixed64List');
+goog.require('proto.jspb.test.extendPackedRepeatedSint32List');
+goog.require('proto.jspb.test.extendPackedRepeatedSint64List');
+goog.require('proto.jspb.test.extendPackedRepeatedUint32List');
+goog.require('proto.jspb.test.extendPackedRepeatedUint64List');
+goog.require('proto.jspb.test.extendRepeatedBoolList');
+goog.require('proto.jspb.test.extendRepeatedBytesList');
+goog.require('proto.jspb.test.extendRepeatedDoubleList');
+goog.require('proto.jspb.test.extendRepeatedFixed32List');
+goog.require('proto.jspb.test.extendRepeatedFixed64List');
+goog.require('proto.jspb.test.extendRepeatedFloatList');
+goog.require('proto.jspb.test.extendRepeatedForeignEnumList');
+goog.require('proto.jspb.test.extendRepeatedInt32List');
+goog.require('proto.jspb.test.extendRepeatedInt64List');
+goog.require('proto.jspb.test.extendRepeatedSfixed32List');
+goog.require('proto.jspb.test.extendRepeatedSfixed64List');
+goog.require('proto.jspb.test.extendRepeatedSint32List');
+goog.require('proto.jspb.test.extendRepeatedSint64List');
+goog.require('proto.jspb.test.extendRepeatedStringList');
+goog.require('proto.jspb.test.extendRepeatedUint32List');
+goog.require('proto.jspb.test.extendRepeatedUint64List');
+
+
+var suite = {};
+
+var BYTES = new Uint8Array([1, 2, 8, 9]);
+
+var BYTES_B64 = goog.crypt.base64.encodeByteArray(BYTES);
+
+
+/**
+ * Helper: fill all fields on a TestAllTypes message.
+ * @param {proto.jspb.test.TestAllTypes} msg
+ */
+function fillAllFields(msg) {
+  msg.setOptionalInt32(-42);
+  // can be exactly represented by JS number (64-bit double, i.e., 52-bit
+  // mantissa).
+  msg.setOptionalInt64(-0x7fffffff00000000);
+  msg.setOptionalUint32(0x80000000);
+  msg.setOptionalUint64(0xf000000000000000);
+  msg.setOptionalSint32(-100);
+  msg.setOptionalSint64(-0x8000000000000000);
+  msg.setOptionalFixed32(1234);
+  msg.setOptionalFixed64(0x1234567800000000);
+  msg.setOptionalSfixed32(-1234);
+  msg.setOptionalSfixed64(-0x1234567800000000);
+  msg.setOptionalFloat(1.5);
+  msg.setOptionalDouble(-1.5);
+  msg.setOptionalBool(true);
+  msg.setOptionalString('hello world');
+  msg.setOptionalBytes(BYTES);
+  msg.setOptionalGroup(new proto.jspb.test.TestAllTypes.OptionalGroup());
+  msg.getOptionalGroup().setA(100);
+  var submsg = new proto.jspb.test.ForeignMessage();
+  submsg.setC(16);
+  msg.setOptionalForeignMessage(submsg);
+  msg.setOptionalForeignEnum(proto.jspb.test.ForeignEnum.FOREIGN_FOO);
+  msg.setOneofString('oneof');
+
+
+  msg.setRepeatedInt32List([-42]);
+  msg.setRepeatedInt64List([-0x7fffffff00000000]);
+  msg.setRepeatedUint32List([0x80000000]);
+  msg.setRepeatedUint64List([0xf000000000000000]);
+  msg.setRepeatedSint32List([-100]);
+  msg.setRepeatedSint64List([-0x8000000000000000]);
+  msg.setRepeatedFixed32List([1234]);
+  msg.setRepeatedFixed64List([0x1234567800000000]);
+  msg.setRepeatedSfixed32List([-1234]);
+  msg.setRepeatedSfixed64List([-0x1234567800000000]);
+  msg.setRepeatedFloatList([1.5]);
+  msg.setRepeatedDoubleList([-1.5]);
+  msg.setRepeatedBoolList([true]);
+  msg.setRepeatedStringList(['hello world']);
+  msg.setRepeatedBytesList([BYTES, BYTES]);
+  msg.setRepeatedGroupList([new proto.jspb.test.TestAllTypes.RepeatedGroup()]);
+  msg.getRepeatedGroupList()[0].setA(100);
+  submsg = new proto.jspb.test.ForeignMessage();
+  submsg.setC(1000);
+  msg.setRepeatedForeignMessageList([submsg]);
+  msg.setRepeatedForeignEnumList([proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
+
+  msg.setPackedRepeatedInt32List([-42]);
+  msg.setPackedRepeatedInt64List([-0x7fffffff00000000]);
+  msg.setPackedRepeatedUint32List([0x80000000]);
+  msg.setPackedRepeatedUint64List([0xf000000000000000]);
+  msg.setPackedRepeatedSint32List([-100]);
+  msg.setPackedRepeatedSint64List([-0x8000000000000000]);
+  msg.setPackedRepeatedFixed32List([1234]);
+  msg.setPackedRepeatedFixed64List([0x1234567800000000]);
+  msg.setPackedRepeatedSfixed32List([-1234]);
+  msg.setPackedRepeatedSfixed64List([-0x1234567800000000]);
+  msg.setPackedRepeatedFloatList([1.5]);
+  msg.setPackedRepeatedDoubleList([-1.5]);
+  msg.setPackedRepeatedBoolList([true]);
+
+}
+
+
+/**
+ * Helper: compare a bytes field to an expected value
+ * @param {Uint8Array|string} arr
+ * @param {Uint8Array} expected
+ * @return {boolean}
+ */
+function bytesCompare(arr, expected) {
+  if (goog.isString(arr)) {
+    arr = goog.crypt.base64.decodeStringToUint8Array(arr);
+  }
+  if (arr.length != expected.length) {
+    return false;
+  }
+  for (var i = 0; i < arr.length; i++) {
+    if (arr[i] != expected[i]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+
+/**
+ * Helper: verify contents of given TestAllTypes message as set by
+ * fillAllFields().
+ * @param {proto.jspb.test.TestAllTypes} original
+ * @param {proto.jspb.test.TestAllTypes} copy
+ */
+function checkAllFields(original, copy) {
+  assertTrue(jspb.Message.equals(original, copy));
+
+  assertEquals(copy.getOptionalInt32(), -42);
+  assertEquals(copy.getOptionalInt64(), -0x7fffffff00000000);
+  assertEquals(copy.getOptionalUint32(), 0x80000000);
+  assertEquals(copy.getOptionalUint64(), 0xf000000000000000);
+  assertEquals(copy.getOptionalSint32(), -100);
+  assertEquals(copy.getOptionalSint64(), -0x8000000000000000);
+  assertEquals(copy.getOptionalFixed32(), 1234);
+  assertEquals(copy.getOptionalFixed64(), 0x1234567800000000);
+  assertEquals(copy.getOptionalSfixed32(), -1234);
+  assertEquals(copy.getOptionalSfixed64(), -0x1234567800000000);
+  assertEquals(copy.getOptionalFloat(), 1.5);
+  assertEquals(copy.getOptionalDouble(), -1.5);
+  assertEquals(copy.getOptionalBool(), true);
+  assertEquals(copy.getOptionalString(), 'hello world');
+  assertEquals(true, bytesCompare(copy.getOptionalBytes(), BYTES));
+  assertEquals(true, bytesCompare(copy.getOptionalBytes_asU8(), BYTES));
+  assertEquals(
+      copy.getOptionalBytes_asB64(), goog.crypt.base64.encodeByteArray(BYTES));
+
+  assertEquals(copy.getOptionalGroup().getA(), 100);
+  assertEquals(copy.getOptionalForeignMessage().getC(), 16);
+  assertEquals(copy.getOptionalForeignEnum(),
+      proto.jspb.test.ForeignEnum.FOREIGN_FOO);
+
+
+  assertEquals(copy.getOneofString(), 'oneof');
+  assertEquals(copy.getOneofFieldCase(),
+      proto.jspb.test.TestAllTypes.OneofFieldCase.ONEOF_STRING);
+
+  assertElementsEquals(copy.getRepeatedInt32List(), [-42]);
+  assertElementsEquals(copy.getRepeatedInt64List(), [-0x7fffffff00000000]);
+  assertElementsEquals(copy.getRepeatedUint32List(), [0x80000000]);
+  assertElementsEquals(copy.getRepeatedUint64List(), [0xf000000000000000]);
+  assertElementsEquals(copy.getRepeatedSint32List(), [-100]);
+  assertElementsEquals(copy.getRepeatedSint64List(), [-0x8000000000000000]);
+  assertElementsEquals(copy.getRepeatedFixed32List(), [1234]);
+  assertElementsEquals(copy.getRepeatedFixed64List(), [0x1234567800000000]);
+  assertElementsEquals(copy.getRepeatedSfixed32List(), [-1234]);
+  assertElementsEquals(copy.getRepeatedSfixed64List(), [-0x1234567800000000]);
+  assertElementsEquals(copy.getRepeatedFloatList(), [1.5]);
+  assertElementsEquals(copy.getRepeatedDoubleList(), [-1.5]);
+  assertElementsEquals(copy.getRepeatedBoolList(), [true]);
+  assertElementsEquals(copy.getRepeatedStringList(), ['hello world']);
+  assertEquals(copy.getRepeatedBytesList().length, 2);
+  assertEquals(true, bytesCompare(copy.getRepeatedBytesList_asU8()[0], BYTES));
+  assertEquals(true, bytesCompare(copy.getRepeatedBytesList()[0], BYTES));
+  assertEquals(true, bytesCompare(copy.getRepeatedBytesList_asU8()[1], BYTES));
+  assertEquals(copy.getRepeatedBytesList_asB64()[0], BYTES_B64);
+  assertEquals(copy.getRepeatedBytesList_asB64()[1], BYTES_B64);
+  assertEquals(copy.getRepeatedGroupList().length, 1);
+  assertEquals(copy.getRepeatedGroupList()[0].getA(), 100);
+  assertEquals(copy.getRepeatedForeignMessageList().length, 1);
+  assertEquals(copy.getRepeatedForeignMessageList()[0].getC(), 1000);
+  assertElementsEquals(copy.getRepeatedForeignEnumList(),
+      [proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
+
+  assertElementsEquals(copy.getPackedRepeatedInt32List(), [-42]);
+  assertElementsEquals(copy.getPackedRepeatedInt64List(),
+      [-0x7fffffff00000000]);
+  assertElementsEquals(copy.getPackedRepeatedUint32List(), [0x80000000]);
+  assertElementsEquals(copy.getPackedRepeatedUint64List(),
+      [0xf000000000000000]);
+  assertElementsEquals(copy.getPackedRepeatedSint32List(), [-100]);
+  assertElementsEquals(copy.getPackedRepeatedSint64List(),
+      [-0x8000000000000000]);
+  assertElementsEquals(copy.getPackedRepeatedFixed32List(), [1234]);
+  assertElementsEquals(copy.getPackedRepeatedFixed64List(),
+      [0x1234567800000000]);
+  assertElementsEquals(copy.getPackedRepeatedSfixed32List(), [-1234]);
+  assertElementsEquals(copy.getPackedRepeatedSfixed64List(),
+      [-0x1234567800000000]);
+  assertElementsEquals(copy.getPackedRepeatedFloatList(), [1.5]);
+  assertElementsEquals(copy.getPackedRepeatedDoubleList(), [-1.5]);
+
+}
+
+
+/**
+ * Helper: verify that all expected extensions are present.
+ * @param {!proto.jspb.test.TestExtendable} msg
+ */
+function checkExtensions(msg) {
+  assertEquals(-42,
+      msg.getExtension(proto.jspb.test.extendOptionalInt32));
+  assertEquals(-0x7fffffff00000000,
+      msg.getExtension(proto.jspb.test.extendOptionalInt64));
+  assertEquals(0x80000000,
+      msg.getExtension(proto.jspb.test.extendOptionalUint32));
+  assertEquals(0xf000000000000000,
+      msg.getExtension(proto.jspb.test.extendOptionalUint64));
+  assertEquals(-100,
+      msg.getExtension(proto.jspb.test.extendOptionalSint32));
+  assertEquals(-0x8000000000000000,
+      msg.getExtension(proto.jspb.test.extendOptionalSint64));
+  assertEquals(1234,
+      msg.getExtension(proto.jspb.test.extendOptionalFixed32));
+  assertEquals(0x1234567800000000,
+      msg.getExtension(proto.jspb.test.extendOptionalFixed64));
+  assertEquals(-1234,
+      msg.getExtension(proto.jspb.test.extendOptionalSfixed32));
+  assertEquals(-0x1234567800000000,
+      msg.getExtension(proto.jspb.test.extendOptionalSfixed64));
+  assertEquals(1.5,
+      msg.getExtension(proto.jspb.test.extendOptionalFloat));
+  assertEquals(-1.5,
+      msg.getExtension(proto.jspb.test.extendOptionalDouble));
+  assertEquals(true,
+      msg.getExtension(proto.jspb.test.extendOptionalBool));
+  assertEquals('hello world',
+      msg.getExtension(proto.jspb.test.extendOptionalString));
+  assertEquals(
+      true, bytesCompare(
+                msg.getExtension(proto.jspb.test.extendOptionalBytes), BYTES));
+  assertEquals(16,
+      msg.getExtension(
+          proto.jspb.test.ExtendsWithMessage.optionalExtension).getFoo());
+
+
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedInt32List),
+      [-42]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedInt64List),
+      [-0x7fffffff00000000]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedUint32List),
+      [0x80000000]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedUint64List),
+      [0xf000000000000000]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedSint32List),
+      [-100]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedSint64List),
+      [-0x8000000000000000]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedFixed32List),
+      [1234]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedFixed64List),
+      [0x1234567800000000]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedSfixed32List),
+      [-1234]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedSfixed64List),
+      [-0x1234567800000000]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedFloatList),
+      [1.5]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedDoubleList),
+      [-1.5]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedBoolList),
+      [true]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedStringList),
+      ['hello world']);
+  assertEquals(
+      true,
+      bytesCompare(
+          msg.getExtension(proto.jspb.test.extendRepeatedBytesList)[0], BYTES));
+  assertEquals(1000,
+      msg.getExtension(
+          proto.jspb.test.ExtendsWithMessage.repeatedExtensionList)[0]
+      .getFoo());
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendRepeatedForeignEnumList),
+      [proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
+
+
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedInt32List),
+      [-42]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedInt64List),
+      [-0x7fffffff00000000]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedUint32List),
+      [0x80000000]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedUint64List),
+      [0xf000000000000000]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedSint32List),
+      [-100]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedSint64List),
+      [-0x8000000000000000]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedFixed32List),
+      [1234]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedFixed64List),
+      [0x1234567800000000]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedSfixed32List),
+      [-1234]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedSfixed64List),
+      [-0x1234567800000000]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedFloatList),
+      [1.5]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedDoubleList),
+      [-1.5]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedBoolList),
+      [true]);
+  assertElementsEquals(
+      msg.getExtension(proto.jspb.test.extendPackedRepeatedForeignEnumList),
+      [proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
+
+}
+
+
+describe('protoBinaryTest', function() {
+  /**
+   * Tests a basic serialization-deserializaton round-trip with all supported
+   * field types (on the TestAllTypes message type).
+   */
+  it('testRoundTrip', function() {
+    var msg = new proto.jspb.test.TestAllTypes();
+    fillAllFields(msg);
+    var encoded = msg.serializeBinary();
+    var decoded = proto.jspb.test.TestAllTypes.deserializeBinary(encoded);
+    checkAllFields(msg, decoded);
+  });
+
+  /**
+   * Test that base64 string and Uint8Array are interchangeable in bytes fields.
+   */
+  it('testBytesFieldsGettersInterop', function() {
+    var msg = new proto.jspb.test.TestAllTypes();
+    // Set from a base64 string and check all the getters work.
+    msg.setOptionalBytes(BYTES_B64);
+    assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES));
+    assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES));
+    assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
+
+    // Test binary serialize round trip doesn't break it.
+    msg = proto.jspb.test.TestAllTypes.deserializeBinary(msg.serializeBinary());
+    assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES));
+    assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES));
+    assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
+
+    msg = new proto.jspb.test.TestAllTypes();
+    // Set from a Uint8Array and check all the getters work.
+    msg.setOptionalBytes(BYTES);
+    assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES));
+    assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES));
+    assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
+
+  });
+
+  /**
+   * Test that bytes setters will receive result of any of the getters.
+   */
+  it('testBytesFieldsSettersInterop', function() {
+    var msg = new proto.jspb.test.TestAllTypes();
+    msg.setOptionalBytes(BYTES);
+    assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
+
+    msg.setOptionalBytes(msg.getOptionalBytes());
+    assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
+    msg.setOptionalBytes(msg.getOptionalBytes_asB64());
+    assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
+    msg.setOptionalBytes(msg.getOptionalBytes_asU8());
+    assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
+  });
+
+  /**
+   * Test that bytes setters will receive result of any of the getters.
+   */
+  it('testRepeatedBytesGetters', function() {
+    var msg = new proto.jspb.test.TestAllTypes();
+
+    function assertGetters() {
+      assertTrue(goog.isString(msg.getRepeatedBytesList_asB64()[0]));
+      assertTrue(goog.isString(msg.getRepeatedBytesList_asB64()[1]));
+      assertTrue(msg.getRepeatedBytesList_asU8()[0] instanceof Uint8Array);
+      assertTrue(msg.getRepeatedBytesList_asU8()[1] instanceof Uint8Array);
+
+      assertTrue(bytesCompare(msg.getRepeatedBytesList()[0], BYTES));
+      assertTrue(bytesCompare(msg.getRepeatedBytesList()[1], BYTES));
+      assertTrue(bytesCompare(msg.getRepeatedBytesList_asB64()[0], BYTES));
+      assertTrue(bytesCompare(msg.getRepeatedBytesList_asB64()[1], BYTES));
+      assertTrue(bytesCompare(msg.getRepeatedBytesList_asU8()[0], BYTES));
+      assertTrue(bytesCompare(msg.getRepeatedBytesList_asU8()[1], BYTES));
+    }
+
+    msg.setRepeatedBytesList([BYTES, BYTES]);
+    assertGetters();
+
+    msg.setRepeatedBytesList([BYTES_B64, BYTES_B64]);
+    assertGetters();
+
+    msg.setRepeatedBytesList([]);
+    assertEquals(0, msg.getRepeatedBytesList().length);
+    assertEquals(0, msg.getRepeatedBytesList_asB64().length);
+    assertEquals(0, msg.getRepeatedBytesList_asU8().length);
+  });
+
+  /**
+   * Helper: fill all extension values.
+   * @param {proto.jspb.test.TestExtendable} msg
+   */
+  function fillExtensions(msg) {
+    msg.setExtension(
+        proto.jspb.test.extendOptionalInt32, -42);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalInt64, -0x7fffffff00000000);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalUint32, 0x80000000);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalUint64, 0xf000000000000000);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalSint32, -100);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalSint64, -0x8000000000000000);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalFixed32, 1234);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalFixed64, 0x1234567800000000);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalSfixed32, -1234);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalSfixed64, -0x1234567800000000);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalFloat, 1.5);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalDouble, -1.5);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalBool, true);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalString, 'hello world');
+    msg.setExtension(proto.jspb.test.extendOptionalBytes, BYTES);
+    var submsg = new proto.jspb.test.ExtendsWithMessage();
+    submsg.setFoo(16);
+    msg.setExtension(
+        proto.jspb.test.ExtendsWithMessage.optionalExtension, submsg);
+    msg.setExtension(
+        proto.jspb.test.extendOptionalForeignEnum,
+        proto.jspb.test.ForeignEnum.FOREIGN_FOO);
+
+
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedInt32List, [-42]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedInt64List, [-0x7fffffff00000000]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedUint32List, [0x80000000]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedUint64List, [0xf000000000000000]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedSint32List, [-100]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedSint64List, [-0x8000000000000000]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedFixed32List, [1234]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedFixed64List, [0x1234567800000000]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedSfixed32List, [-1234]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedSfixed64List, [-0x1234567800000000]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedFloatList, [1.5]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedDoubleList, [-1.5]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedBoolList, [true]);
+    msg.setExtension(
+        proto.jspb.test.extendRepeatedStringList, ['hello world']);
+    msg.setExtension(proto.jspb.test.extendRepeatedBytesList, [BYTES]);
+    submsg = new proto.jspb.test.ExtendsWithMessage();
+    submsg.setFoo(1000);
+    msg.setExtension(
+        proto.jspb.test.ExtendsWithMessage.repeatedExtensionList, [submsg]);
+    msg.setExtension(proto.jspb.test.extendRepeatedForeignEnumList,
+        [proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
+
+
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedInt32List, [-42]);
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedInt64List, [-0x7fffffff00000000]);
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedUint32List, [0x80000000]);
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedUint64List, [0xf000000000000000]);
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedSint32List, [-100]);
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedSint64List, [-0x8000000000000000]);
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedFixed32List, [1234]);
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedFixed64List, [0x1234567800000000]);
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedSfixed32List, [-1234]);
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedSfixed64List,
+        [-0x1234567800000000]);
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedFloatList, [1.5]);
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedDoubleList, [-1.5]);
+    msg.setExtension(
+        proto.jspb.test.extendPackedRepeatedBoolList, [true]);
+    msg.setExtension(proto.jspb.test.extendPackedRepeatedForeignEnumList,
+        [proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
+
+  }
+
+
+  /**
+   * Tests extension serialization and deserialization.
+   */
+  it('testExtensions', function() {
+    var msg = new proto.jspb.test.TestExtendable();
+    fillExtensions(msg);
+    var encoded = msg.serializeBinary();
+    var decoded = proto.jspb.test.TestExtendable.deserializeBinary(encoded);
+    checkExtensions(decoded);
+  });
+});

+ 922 - 0
js/compatibility_tests/v3.1.0/binary/reader_test.js

@@ -0,0 +1,922 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Test cases for jspb's binary protocol buffer reader.
+ *
+ * There are two particular magic numbers that need to be pointed out -
+ * 2^64-1025 is the largest number representable as both a double and an
+ * unsigned 64-bit integer, and 2^63-513 is the largest number representable as
+ * both a double and a signed 64-bit integer.
+ *
+ * Test suite is written using Jasmine -- see http://jasmine.github.io/
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.require('goog.testing.asserts');
+goog.require('jspb.BinaryConstants');
+goog.require('jspb.BinaryDecoder');
+goog.require('jspb.BinaryReader');
+goog.require('jspb.BinaryWriter');
+
+
+
+describe('binaryReaderTest', function() {
+  /**
+   * Tests the reader instance cache.
+   */
+  it('testInstanceCaches', /** @suppress {visibility} */ function() {
+    var writer = new jspb.BinaryWriter();
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    writer.writeMessage(1, dummyMessage, goog.nullFunction);
+    writer.writeMessage(2, dummyMessage, goog.nullFunction);
+
+    var buffer = writer.getResultBuffer();
+
+    // Empty the instance caches.
+    jspb.BinaryReader.instanceCache_ = [];
+
+    // Allocating and then freeing three decoders should leave us with three in
+    // the cache.
+
+    var decoder1 = jspb.BinaryDecoder.alloc();
+    var decoder2 = jspb.BinaryDecoder.alloc();
+    var decoder3 = jspb.BinaryDecoder.alloc();
+    decoder1.free();
+    decoder2.free();
+    decoder3.free();
+
+    assertEquals(3, jspb.BinaryDecoder.instanceCache_.length);
+    assertEquals(0, jspb.BinaryReader.instanceCache_.length);
+
+    // Allocating and then freeing a reader should remove one decoder from its
+    // cache, but it should stay stuck to the reader afterwards since we can't
+    // have a reader without a decoder.
+    jspb.BinaryReader.alloc().free();
+
+    assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
+    assertEquals(1, jspb.BinaryReader.instanceCache_.length);
+
+    // Allocating a reader should remove a reader from the cache.
+    var reader = jspb.BinaryReader.alloc(buffer);
+
+    assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
+    assertEquals(0, jspb.BinaryReader.instanceCache_.length);
+
+    // Processing the message reuses the current reader.
+    reader.nextField();
+    assertEquals(1, reader.getFieldNumber());
+    reader.readMessage(dummyMessage, function() {
+      assertEquals(0, jspb.BinaryReader.instanceCache_.length);
+    });
+
+    reader.nextField();
+    assertEquals(2, reader.getFieldNumber());
+    reader.readMessage(dummyMessage, function() {
+      assertEquals(0, jspb.BinaryReader.instanceCache_.length);
+    });
+
+    assertEquals(false, reader.nextField());
+
+    assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
+    assertEquals(0, jspb.BinaryReader.instanceCache_.length);
+
+    // Freeing the reader should put it back into the cache.
+    reader.free();
+
+    assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
+    assertEquals(1, jspb.BinaryReader.instanceCache_.length);
+  });
+
+
+  /**
+   * @param {number} x
+   * @return {number}
+   */
+  function truncate(x) {
+    var temp = new Float32Array(1);
+    temp[0] = x;
+    return temp[0];
+  }
+
+
+  /**
+   * Verifies that misuse of the reader class triggers assertions.
+   */
+  it('testReadErrors', /** @suppress {checkTypes|visibility} */ function() {
+    // Calling readMessage on a non-delimited field should trigger an
+    // assertion.
+    var reader = jspb.BinaryReader.alloc([8, 1]);
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    reader.nextField();
+    assertThrows(function() {
+      reader.readMessage(dummyMessage, goog.nullFunction);
+    });
+
+    // Reading past the end of the stream should trigger an assertion.
+    reader = jspb.BinaryReader.alloc([9, 1]);
+    reader.nextField();
+    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()});
+    });
+
+    // Skipping an invalid field should trigger an assertion.
+    reader = jspb.BinaryReader.alloc([12, 1]);
+    reader.nextWireType_ = 1000;
+    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()});
+
+    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.readString()});
+    assertThrows(function() {reader.readBytes()});
+  });
+
+
+  /**
+   * Tests encoding and decoding of unsigned field types.
+   * @param {Function} readField
+   * @param {Function} writeField
+   * @param {number} epsilon
+   * @param {number} upperLimit
+   * @param {Function} filter
+   * @private
+   * @suppress {missingProperties}
+   */
+  var doTestUnsignedField_ = function(readField,
+      writeField, epsilon, upperLimit, filter) {
+    assertNotNull(readField);
+    assertNotNull(writeField);
+
+    var writer = new jspb.BinaryWriter();
+
+    // Encode zero and limits.
+    writeField.call(writer, 1, filter(0));
+    writeField.call(writer, 2, filter(epsilon));
+    writeField.call(writer, 3, filter(upperLimit));
+
+    // Encode positive values.
+    for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
+      writeField.call(writer, 4, filter(cursor));
+    }
+
+    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+    // Check zero and limits.
+    reader.nextField();
+    assertEquals(1, reader.getFieldNumber());
+    assertEquals(filter(0), readField.call(reader));
+
+    reader.nextField();
+    assertEquals(2, reader.getFieldNumber());
+    assertEquals(filter(epsilon), readField.call(reader));
+
+    reader.nextField();
+    assertEquals(3, reader.getFieldNumber());
+    assertEquals(filter(upperLimit), readField.call(reader));
+
+    // Check positive values.
+    for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
+      reader.nextField();
+      if (4 != reader.getFieldNumber()) throw 'fail!';
+      if (filter(cursor) != readField.call(reader)) throw 'fail!';
+    }
+  };
+
+
+  /**
+   * Tests encoding and decoding of signed field types.
+   * @param {Function} readField
+   * @param {Function} writeField
+   * @param {number} epsilon
+   * @param {number} lowerLimit
+   * @param {number} upperLimit
+   * @param {Function} filter
+   * @private
+   * @suppress {missingProperties}
+   */
+  var doTestSignedField_ = function(readField,
+      writeField, epsilon, lowerLimit, upperLimit, filter) {
+    var writer = new jspb.BinaryWriter();
+
+    // Encode zero and limits.
+    writeField.call(writer, 1, filter(lowerLimit));
+    writeField.call(writer, 2, filter(-epsilon));
+    writeField.call(writer, 3, filter(0));
+    writeField.call(writer, 4, filter(epsilon));
+    writeField.call(writer, 5, filter(upperLimit));
+
+    var inputValues = [];
+
+    // Encode negative values.
+    for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) {
+      var val = filter(cursor);
+      writeField.call(writer, 6, 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
+      });
+    }
+
+    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+    // Check zero and limits.
+    reader.nextField();
+    assertEquals(1, reader.getFieldNumber());
+    assertEquals(filter(lowerLimit), readField.call(reader));
+
+    reader.nextField();
+    assertEquals(2, reader.getFieldNumber());
+    assertEquals(filter(-epsilon), readField.call(reader));
+
+    reader.nextField();
+    assertEquals(3, reader.getFieldNumber());
+    assertEquals(filter(0), readField.call(reader));
+
+    reader.nextField();
+    assertEquals(4, reader.getFieldNumber());
+    assertEquals(filter(epsilon), readField.call(reader));
+
+    reader.nextField();
+    assertEquals(5, reader.getFieldNumber());
+    assertEquals(filter(upperLimit), readField.call(reader));
+
+    for (var i = 0; i < inputValues.length; i++) {
+      var expected = inputValues[i];
+      reader.nextField();
+      assertEquals(expected.fieldNumber, reader.getFieldNumber());
+      assertEquals(expected.value, readField.call(reader));
+    }
+  };
+
+
+  /**
+   * Tests fields that use varint encoding.
+   */
+  it('testVarintFields', function() {
+    assertNotUndefined(jspb.BinaryReader.prototype.readUint32);
+    assertNotUndefined(jspb.BinaryWriter.prototype.writeUint32);
+    assertNotUndefined(jspb.BinaryReader.prototype.readUint64);
+    assertNotUndefined(jspb.BinaryWriter.prototype.writeUint64);
+    assertNotUndefined(jspb.BinaryReader.prototype.readBool);
+    assertNotUndefined(jspb.BinaryWriter.prototype.writeBool);
+    doTestUnsignedField_(
+        jspb.BinaryReader.prototype.readUint32,
+        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);
+
+    doTestSignedField_(
+        jspb.BinaryReader.prototype.readInt32,
+        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);
+
+    doTestSignedField_(
+        jspb.BinaryReader.prototype.readEnum,
+        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; });
+  });
+
+
+  /**
+   * Tests reading a field from hexadecimal string (format: '08 BE EF').
+   * @param {Function} readField
+   * @param {number} expected
+   * @param {string} hexString
+   */
+  function doTestHexStringVarint_(readField, expected, hexString) {
+    var bytesCount = (hexString.length + 1) / 3;
+    var bytes = new Uint8Array(bytesCount);
+    for (var i = 0; i < bytesCount; i++) {
+      bytes[i] = parseInt(hexString.substring(i * 3, i * 3 + 2), 16);
+    }
+    var reader = jspb.BinaryReader.alloc(bytes);
+    reader.nextField();
+    assertEquals(expected, readField.call(reader));
+  }
+
+
+  /**
+   * Tests non-canonical redundant varint decoding.
+   */
+  it('testRedundantVarintFields', function() {
+    assertNotNull(jspb.BinaryReader.prototype.readUint32);
+    assertNotNull(jspb.BinaryReader.prototype.readUint64);
+    assertNotNull(jspb.BinaryReader.prototype.readSint32);
+    assertNotNull(jspb.BinaryReader.prototype.readSint64);
+
+    // 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');
+
+    // 11 stands for -6 in zigzag encoding
+    doTestHexStringVarint_(
+      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');
+
+    // 11 stands for -6 in zigzag encoding
+    doTestHexStringVarint_(
+      jspb.BinaryReader.prototype.readSint64,
+      -6, '08 8B 80 80 80 80 80 80 80 80 00');
+  });
+
+
+  /**
+   * Tests 64-bit fields that are handled as strings.
+   */
+  it('testStringInt64Fields', function() {
+    var writer = new jspb.BinaryWriter();
+
+    var testSignedData = [
+      '2730538252207801776',
+      '-2688470994844604560',
+      '3398529779486536359',
+      '3568577411627971000',
+      '272477188847484900',
+      '-6649058714086158188',
+      '-7695254765712060806',
+      '-4525541438037104029',
+      '-4993706538836508568',
+      '4990160321893729138'
+    ];
+    var testUnsignedData = [
+      '7822732630241694882',
+      '6753602971916687352',
+      '2399935075244442116',
+      '8724292567325338867',
+      '16948784802625696584',
+      '4136275908516066934',
+      '3575388346793700364',
+      '5167142028379259461',
+      '1557573948689737699',
+      '17100725280812548567'
+    ];
+
+    for (var i = 0; i < testSignedData.length; i++) {
+      writer.writeInt64String(2 * i + 1, testSignedData[i]);
+      writer.writeUint64String(2 * i + 2, testUnsignedData[i]);
+    }
+
+    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+    for (var i = 0; i < testSignedData.length; i++) {
+      reader.nextField();
+      assertEquals(2 * i + 1, reader.getFieldNumber());
+      assertEquals(testSignedData[i], reader.readInt64String());
+      reader.nextField();
+      assertEquals(2 * i + 2, reader.getFieldNumber());
+      assertEquals(testUnsignedData[i], reader.readUint64String());
+    }
+  });
+
+
+  /**
+   * Tests fields that use zigzag encoding.
+   */
+  it('testZigzagFields', function() {
+    doTestSignedField_(
+        jspb.BinaryReader.prototype.readSint32,
+        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);
+  });
+
+
+  /**
+   * Tests fields that use fixed-length encoding.
+   */
+  it('testFixedFields', function() {
+    doTestUnsignedField_(
+        jspb.BinaryReader.prototype.readFixed32,
+        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);
+
+    doTestSignedField_(
+        jspb.BinaryReader.prototype.readSfixed32,
+        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);
+  });
+
+
+  /**
+   * Tests floating point fields.
+   */
+  it('testFloatFields', function() {
+    doTestSignedField_(
+        jspb.BinaryReader.prototype.readFloat,
+        jspb.BinaryWriter.prototype.writeFloat,
+        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; });
+  });
+
+
+  /**
+   * Tests length-delimited string fields.
+   */
+  it('testStringFields', function() {
+    var s1 = 'The quick brown fox jumps over the lazy dog.';
+    var s2 = '人人生而自由,在尊嚴和權利上一律平等。';
+
+    var writer = new jspb.BinaryWriter();
+
+    writer.writeString(1, s1);
+    writer.writeString(2, s2);
+
+    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+    reader.nextField();
+    assertEquals(1, reader.getFieldNumber());
+    assertEquals(s1, reader.readString());
+
+    reader.nextField();
+    assertEquals(2, reader.getFieldNumber());
+    assertEquals(s2, reader.readString());
+  });
+
+
+  /**
+   * Tests length-delimited byte fields.
+   */
+  it('testByteFields', function() {
+    var message = [];
+    var lowerLimit = 1;
+    var upperLimit = 256;
+    var scale = 1.1;
+
+    var writer = new jspb.BinaryWriter();
+
+    for (var cursor = lowerLimit; cursor < upperLimit; cursor *= 1.1) {
+      var len = Math.round(cursor);
+      var bytes = [];
+      for (var i = 0; i < len; i++) bytes.push(i % 256);
+
+      writer.writeBytes(len, bytes);
+    }
+
+    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+    for (var cursor = lowerLimit; reader.nextField(); cursor *= 1.1) {
+      var len = Math.round(cursor);
+      if (len != reader.getFieldNumber()) throw 'fail!';
+
+      var bytes = reader.readBytes();
+      if (len != bytes.length) throw 'fail!';
+      for (var i = 0; i < bytes.length; i++) {
+        if (i % 256 != bytes[i]) throw 'fail!';
+      }
+    }
+  });
+
+
+  /**
+   * Tests nested messages.
+   */
+  it('testNesting', function() {
+    var writer = new jspb.BinaryWriter();
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+
+    writer.writeInt32(1, 100);
+
+    // Add one message with 3 int fields.
+    writer.writeMessage(2, dummyMessage, function() {
+      writer.writeInt32(3, 300);
+      writer.writeInt32(4, 400);
+      writer.writeInt32(5, 500);
+    });
+
+    // Add one empty message.
+    writer.writeMessage(6, dummyMessage, goog.nullFunction);
+
+    writer.writeInt32(7, 700);
+
+    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+    // Validate outermost message.
+
+    reader.nextField();
+    assertEquals(1, reader.getFieldNumber());
+    assertEquals(100, reader.readInt32());
+
+    reader.nextField();
+    assertEquals(2, reader.getFieldNumber());
+    reader.readMessage(dummyMessage, function() {
+      // Validate embedded message 1.
+      reader.nextField();
+      assertEquals(3, reader.getFieldNumber());
+      assertEquals(300, reader.readInt32());
+
+      reader.nextField();
+      assertEquals(4, reader.getFieldNumber());
+      assertEquals(400, reader.readInt32());
+
+      reader.nextField();
+      assertEquals(5, reader.getFieldNumber());
+      assertEquals(500, reader.readInt32());
+
+      assertEquals(false, reader.nextField());
+    });
+
+    reader.nextField();
+    assertEquals(6, reader.getFieldNumber());
+    reader.readMessage(dummyMessage, function() {
+      // Validate embedded message 2.
+
+      assertEquals(false, reader.nextField());
+    });
+
+    reader.nextField();
+    assertEquals(7, reader.getFieldNumber());
+    assertEquals(700, reader.readInt32());
+
+    assertEquals(false, reader.nextField());
+  });
+
+  /**
+   * Tests skipping fields of each type by interleaving them with sentinel
+   * values and skipping everything that's not a sentinel.
+   */
+  it('testSkipField', function() {
+    var writer = new jspb.BinaryWriter();
+
+    var sentinel = 123456789;
+
+    // Write varint fields of different sizes.
+    writer.writeInt32(1, sentinel);
+    writer.writeInt32(1, 1);
+    writer.writeInt32(1, 1000);
+    writer.writeInt32(1, 1000000);
+    writer.writeInt32(1, 1000000000);
+
+    // Write fixed 64-bit encoded fields.
+    writer.writeInt32(2, sentinel);
+    writer.writeDouble(2, 1);
+    writer.writeFixed64(2, 1);
+    writer.writeSfixed64(2, 1);
+
+    // Write fixed 32-bit encoded fields.
+    writer.writeInt32(3, sentinel);
+    writer.writeFloat(3, 1);
+    writer.writeFixed32(3, 1);
+    writer.writeSfixed32(3, 1);
+
+    // Write delimited fields.
+    writer.writeInt32(4, sentinel);
+    writer.writeBytes(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+    writer.writeString(4, 'The quick brown fox jumps over the lazy dog');
+
+    // Write a group with a nested group inside.
+    writer.writeInt32(5, sentinel);
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+    writer.writeGroup(5, dummyMessage, function() {
+      writer.writeInt64(42, 42);
+      writer.writeGroup(6, dummyMessage, function() {
+        writer.writeInt64(84, 42);
+      });
+    });
+
+    // Write final sentinel.
+    writer.writeInt32(6, sentinel);
+
+    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+    function skip(field, count) {
+      for (var i = 0; i < count; i++) {
+        reader.nextField();
+        if (field != reader.getFieldNumber()) throw 'fail!';
+        reader.skipField();
+      }
+    }
+
+    reader.nextField();
+    assertEquals(1, reader.getFieldNumber());
+    assertEquals(sentinel, reader.readInt32());
+    skip(1, 4);
+
+    reader.nextField();
+    assertEquals(2, reader.getFieldNumber());
+    assertEquals(sentinel, reader.readInt32());
+    skip(2, 3);
+
+    reader.nextField();
+    assertEquals(3, reader.getFieldNumber());
+    assertEquals(sentinel, reader.readInt32());
+    skip(3, 3);
+
+    reader.nextField();
+    assertEquals(4, reader.getFieldNumber());
+    assertEquals(sentinel, reader.readInt32());
+    skip(4, 2);
+
+    reader.nextField();
+    assertEquals(5, reader.getFieldNumber());
+    assertEquals(sentinel, reader.readInt32());
+    skip(5, 1);
+
+    reader.nextField();
+    assertEquals(6, reader.getFieldNumber());
+    assertEquals(sentinel, reader.readInt32());
+  });
+
+
+  /**
+   * Tests packed fields.
+   */
+  it('testPackedFields', function() {
+    var writer = new jspb.BinaryWriter();
+
+    var sentinel = 123456789;
+
+    var unsignedData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+    var signedData = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10];
+    var floatData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10];
+    var doubleData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10];
+    var boolData = [true, false, true, true, false, false, true, false];
+
+    for (var i = 0; i < floatData.length; i++) {
+      floatData[i] = truncate(floatData[i]);
+    }
+
+    writer.writeInt32(1, sentinel);
+
+    writer.writePackedInt32(2, signedData);
+    writer.writePackedInt64(2, signedData);
+    writer.writePackedUint32(2, unsignedData);
+    writer.writePackedUint64(2, unsignedData);
+    writer.writePackedSint32(2, signedData);
+    writer.writePackedSint64(2, signedData);
+    writer.writePackedFixed32(2, unsignedData);
+    writer.writePackedFixed64(2, unsignedData);
+    writer.writePackedSfixed32(2, signedData);
+    writer.writePackedSfixed64(2, signedData);
+    writer.writePackedFloat(2, floatData);
+    writer.writePackedDouble(2, doubleData);
+    writer.writePackedBool(2, boolData);
+    writer.writePackedEnum(2, unsignedData);
+
+    writer.writeInt32(3, sentinel);
+
+    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+    reader.nextField();
+    assertEquals(sentinel, reader.readInt32());
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedInt32(), signedData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedInt64(), signedData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedUint32(), unsignedData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedUint64(), unsignedData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedSint32(), signedData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedSint64(), signedData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedFixed32(), unsignedData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedFixed64(), unsignedData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedSfixed32(), signedData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedSfixed64(), signedData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedFloat(), floatData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedDouble(), doubleData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedBool(), boolData);
+
+    reader.nextField();
+    assertElementsEquals(reader.readPackedEnum(), unsignedData);
+
+    reader.nextField();
+    assertEquals(sentinel, reader.readInt32());
+  });
+
+
+  /**
+   * Byte blobs inside nested messages should always have their byte offset set
+   * relative to the start of the outermost blob, not the start of their parent
+   * blob.
+   */
+  it('testNestedBlobs', function() {
+    // Create a proto consisting of two nested messages, with the inner one
+    // containing a blob of bytes.
+
+    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} */({});
+
+    writer.writeMessage(1, dummyMessage, function() {
+      writer.writeMessage(1, dummyMessage, function() {
+        writer.writeBytes(1, blob);
+      });
+    });
+
+    // Peel off the outer two message layers. Each layer should have two bytes
+    // of overhead, one for the field tag and one for the length of the inner
+    // blob.
+
+    var decoder1 = new jspb.BinaryDecoder(writer.getResultBuffer());
+    assertEquals(fieldTag, decoder1.readUnsignedVarint32());
+    assertEquals(blob.length + 4, decoder1.readUnsignedVarint32());
+
+    var decoder2 = new jspb.BinaryDecoder(decoder1.readBytes(blob.length + 4));
+    assertEquals(fieldTag, decoder2.readUnsignedVarint32());
+    assertEquals(blob.length + 2, decoder2.readUnsignedVarint32());
+
+    assertEquals(fieldTag, decoder2.readUnsignedVarint32());
+    assertEquals(blob.length, decoder2.readUnsignedVarint32());
+    var bytes = decoder2.readBytes(blob.length);
+
+    assertElementsEquals(bytes, blob);
+  });
+
+
+  /**
+   * Tests read callbacks.
+   */
+  it('testReadCallbacks', function() {
+    var writer = new jspb.BinaryWriter();
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+
+    // Add an int, a submessage, and another int.
+    writer.writeInt32(1, 100);
+
+    writer.writeMessage(2, dummyMessage, function() {
+      writer.writeInt32(3, 300);
+      writer.writeInt32(4, 400);
+      writer.writeInt32(5, 500);
+    });
+
+    writer.writeInt32(7, 700);
+
+    // Create the reader and register a custom read callback.
+    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+    /**
+     * @param {!jspb.BinaryReader} reader
+     * @return {*}
+     */
+    function readCallback(reader) {
+      reader.nextField();
+      assertEquals(3, reader.getFieldNumber());
+      assertEquals(300, reader.readInt32());
+
+      reader.nextField();
+      assertEquals(4, reader.getFieldNumber());
+      assertEquals(400, reader.readInt32());
+
+      reader.nextField();
+      assertEquals(5, reader.getFieldNumber());
+      assertEquals(500, reader.readInt32());
+
+      assertEquals(false, reader.nextField());
+    };
+
+    reader.registerReadCallback('readCallback', readCallback);
+
+    // Read the container message.
+    reader.nextField();
+    assertEquals(1, reader.getFieldNumber());
+    assertEquals(100, reader.readInt32());
+
+    reader.nextField();
+    assertEquals(2, reader.getFieldNumber());
+    reader.readMessage(dummyMessage, function() {
+      // Decode the embedded message using the registered callback.
+      reader.runReadCallback('readCallback');
+    });
+
+    reader.nextField();
+    assertEquals(7, reader.getFieldNumber());
+    assertEquals(700, reader.readInt32());
+
+    assertEquals(false, reader.nextField());
+  });
+});

+ 668 - 0
js/compatibility_tests/v3.1.0/binary/utils_test.js

@@ -0,0 +1,668 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Test cases for jspb's helper functions.
+ *
+ * Test suite is written using Jasmine -- see http://jasmine.github.io/
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.require('goog.crypt.base64');
+goog.require('goog.testing.asserts');
+goog.require('jspb.BinaryConstants');
+goog.require('jspb.BinaryWriter');
+goog.require('jspb.utils');
+
+
+/**
+ * @param {number} x
+ * @return {number}
+ */
+function truncate(x) {
+  var temp = new Float32Array(1);
+  temp[0] = x;
+  return temp[0];
+}
+
+
+/**
+ * Converts an 64-bit integer in split representation to a 64-bit hash string
+ * (8 bits encoded per character).
+ * @param {number} bitsLow The low 32 bits of the split 64-bit integer.
+ * @param {number} bitsHigh The high 32 bits of the split 64-bit integer.
+ * @return {string} The encoded hash string, 8 bits per character.
+ */
+function toHashString(bitsLow, bitsHigh) {
+  return String.fromCharCode((bitsLow >>> 0) & 0xFF,
+                             (bitsLow >>> 8) & 0xFF,
+                             (bitsLow >>> 16) & 0xFF,
+                             (bitsLow >>> 24) & 0xFF,
+                             (bitsHigh >>> 0) & 0xFF,
+                             (bitsHigh >>> 8) & 0xFF,
+                             (bitsHigh >>> 16) & 0xFF,
+                             (bitsHigh >>> 24) & 0xFF);
+}
+
+
+describe('binaryUtilsTest', function() {
+  /**
+   * Tests lossless binary-to-decimal conversion.
+   */
+  it('testDecimalConversion', function() {
+    // Check some magic numbers.
+    var result =
+        jspb.utils.joinUnsignedDecimalString(0x89e80001, 0x8ac72304);
+    assertEquals('10000000000000000001', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0xacd05f15, 0x1b69b4b);
+    assertEquals('123456789123456789', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0xeb1f0ad2, 0xab54a98c);
+    assertEquals('12345678901234567890', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0xe3b70cb1, 0x891087b8);
+    assertEquals('9876543210987654321', result);
+
+    // Check limits.
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00000000);
+    assertEquals('0', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0xFFFFFFFF, 0xFFFFFFFF);
+    assertEquals('18446744073709551615', result);
+
+    // Check each bit of the low dword.
+    for (var i = 0; i < 32; i++) {
+      var low = (1 << i) >>> 0;
+      result = jspb.utils.joinUnsignedDecimalString(low, 0);
+      assertEquals('' + Math.pow(2, i), result);
+    }
+
+    // Check the first 20 bits of the high dword.
+    for (var i = 0; i < 20; i++) {
+      var high = (1 << i) >>> 0;
+      result = jspb.utils.joinUnsignedDecimalString(0, high);
+      assertEquals('' + Math.pow(2, 32 + i), result);
+    }
+
+    // V8's internal double-to-string conversion is inaccurate for values above
+    // 2^52, even if they're representable integers - check the rest of the bits
+    // manually against the correct string representations of 2^N.
+
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00100000);
+    assertEquals('4503599627370496', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00200000);
+    assertEquals('9007199254740992', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00400000);
+    assertEquals('18014398509481984', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00800000);
+    assertEquals('36028797018963968', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x01000000);
+    assertEquals('72057594037927936', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x02000000);
+    assertEquals('144115188075855872', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x04000000);
+    assertEquals('288230376151711744', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x08000000);
+    assertEquals('576460752303423488', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x10000000);
+    assertEquals('1152921504606846976', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x20000000);
+    assertEquals('2305843009213693952', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x40000000);
+    assertEquals('4611686018427387904', result);
+
+    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x80000000);
+    assertEquals('9223372036854775808', result);
+  });
+
+
+  /**
+   * Going from hash strings to decimal strings should also be lossless.
+   */
+  it('testHashToDecimalConversion', function() {
+    var result;
+    var convert = jspb.utils.hash64ToDecimalString;
+
+    result = convert(toHashString(0x00000000, 0x00000000), false);
+    assertEquals('0', result);
+
+    result = convert(toHashString(0x00000000, 0x00000000), true);
+    assertEquals('0', result);
+
+    result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF), false);
+    assertEquals('18446744073709551615', result);
+
+    result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF), true);
+    assertEquals('-1', result);
+
+    result = convert(toHashString(0x00000000, 0x80000000), false);
+    assertEquals('9223372036854775808', result);
+
+    result = convert(toHashString(0x00000000, 0x80000000), true);
+    assertEquals('-9223372036854775808', result);
+
+    result = convert(toHashString(0xacd05f15, 0x01b69b4b), false);
+    assertEquals('123456789123456789', result);
+
+    result = convert(toHashString(~0xacd05f15 + 1, ~0x01b69b4b), true);
+    assertEquals('-123456789123456789', result);
+
+    // And converting arrays of hashes should work the same way.
+    result = jspb.utils.hash64ArrayToDecimalStrings([
+      toHashString(0xFFFFFFFF, 0xFFFFFFFF),
+      toHashString(0x00000000, 0x80000000),
+      toHashString(0xacd05f15, 0x01b69b4b)], false);
+    assertEquals(3, result.length);
+    assertEquals('18446744073709551615', result[0]);
+    assertEquals('9223372036854775808', result[1]);
+    assertEquals('123456789123456789', result[2]);
+  });
+
+  /*
+   * Going from decimal strings to hash strings should be lossless.
+   */
+  it('testDecimalToHashConversion', function() {
+    var result;
+    var convert = jspb.utils.decimalStringToHash64;
+
+    result = convert('0');
+    assertEquals(String.fromCharCode.apply(null,
+      [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), result);
+
+    result = convert('-1');
+    assertEquals(String.fromCharCode.apply(null,
+      [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result);
+
+    result = convert('18446744073709551615');
+    assertEquals(String.fromCharCode.apply(null,
+      [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result);
+
+    result = convert('9223372036854775808');
+    assertEquals(String.fromCharCode.apply(null,
+      [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), result);
+
+    result = convert('-9223372036854775808');
+    assertEquals(String.fromCharCode.apply(null,
+      [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), result);
+
+    result = convert('123456789123456789');
+    assertEquals(String.fromCharCode.apply(null,
+      [0x15, 0x5F, 0xD0, 0xAC, 0x4B, 0x9B, 0xB6, 0x01]), result);
+
+    result = convert('-123456789123456789');
+    assertEquals(String.fromCharCode.apply(null,
+      [0xEB, 0xA0, 0x2F, 0x53, 0xB4, 0x64, 0x49, 0xFE]), result);
+  });
+
+  /**
+   * Going from hash strings to hex strings should be lossless.
+   */
+  it('testHashToHexConversion', function() {
+    var result;
+    var convert = jspb.utils.hash64ToHexString;
+
+    result = convert(toHashString(0x00000000, 0x00000000));
+    assertEquals('0x0000000000000000', result);
+
+    result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF));
+    assertEquals('0xffffffffffffffff', result);
+
+    result = convert(toHashString(0x12345678, 0x9ABCDEF0));
+    assertEquals('0x9abcdef012345678', result);
+  });
+
+
+  /**
+   * Going from hex strings to hash strings should be lossless.
+   */
+  it('testHexToHashConversion', function() {
+    var result;
+    var convert = jspb.utils.hexStringToHash64;
+
+    result = convert('0x0000000000000000');
+    assertEquals(String.fromCharCode.apply(null,
+        [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), result);
+
+    result = convert('0xffffffffffffffff');
+    assertEquals(String.fromCharCode.apply(null,
+        [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result);
+
+    // Hex string is big-endian, hash string is little-endian.
+    result = convert('0x123456789ABCDEF0');
+    assertEquals(String.fromCharCode.apply(null,
+        [0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]), result);
+
+    // Capitalization should not matter.
+    result = convert('0x0000abcdefABCDEF');
+    assertEquals(String.fromCharCode.apply(null,
+        [0xEF, 0xCD, 0xAB, 0xEF, 0xCD, 0xAB, 0x00, 0x00]), result);
+  });
+
+
+  /**
+   * Going from numbers to hash strings should be lossless for up to 53 bits of
+   * precision.
+   */
+  it('testNumberToHashConversion', function() {
+    var result;
+    var convert = jspb.utils.numberToHash64;
+
+    result = convert(0x0000000000000);
+    assertEquals('0x0000000000000000', jspb.utils.hash64ToHexString(result));
+
+    result = convert(0xFFFFFFFFFFFFF);
+    assertEquals('0x000fffffffffffff', jspb.utils.hash64ToHexString(result));
+
+    result = convert(0x123456789ABCD);
+    assertEquals('0x000123456789abcd', jspb.utils.hash64ToHexString(result));
+
+    result = convert(0xDCBA987654321);
+    assertEquals('0x000dcba987654321', jspb.utils.hash64ToHexString(result));
+
+    // 53 bits of precision should not be truncated.
+    result = convert(0x10000000000001);
+    assertEquals('0x0010000000000001', jspb.utils.hash64ToHexString(result));
+
+    // 54 bits of precision should be truncated.
+    result = convert(0x20000000000001);
+    assertNotEquals(
+        '0x0020000000000001', jspb.utils.hash64ToHexString(result));
+  });
+
+
+  /**
+   * Sanity check the behavior of Javascript's strings when doing funny things
+   * with unicode characters.
+   */
+  it('sanityCheckUnicodeStrings', function() {
+    var strings = new Array(65536);
+
+    // All possible unsigned 16-bit values should be storable in a string, they
+    // shouldn't do weird things with the length of the string, and they should
+    // come back out of the string unchanged.
+    for (var i = 0; i < 65536; i++) {
+      strings[i] = 'a' + String.fromCharCode(i) + 'a';
+      if (3 != strings[i].length) throw 'fail!';
+      if (i != strings[i].charCodeAt(1)) throw 'fail!';
+    }
+
+    // Each unicode character should compare equal to itself and not equal to a
+    // different unicode character.
+    for (var i = 0; i < 65536; i++) {
+      if (strings[i] != strings[i]) throw 'fail!';
+      if (strings[i] == strings[(i + 1) % 65536]) throw 'fail!';
+    }
+  });
+
+
+  /**
+   * Tests conversion from 32-bit floating point numbers to split64 numbers.
+   */
+  it('testFloat32ToSplit64', function() {
+    var f32_eps = jspb.BinaryConstants.FLOAT32_EPS;
+    var f32_min = jspb.BinaryConstants.FLOAT32_MIN;
+    var f32_max = jspb.BinaryConstants.FLOAT32_MAX;
+
+    // NaN.
+    jspb.utils.splitFloat32(NaN);
+    if (!isNaN(jspb.utils.joinFloat32(jspb.utils.split64Low,
+                                      jspb.utils.split64High))) {
+      throw 'fail!';
+    }
+
+    /**
+     * @param {number} x
+     * @param {number=} opt_bits
+     */
+    function test(x, opt_bits) {
+      jspb.utils.splitFloat32(x);
+      if (goog.isDef(opt_bits)) {
+        if (opt_bits != jspb.utils.split64Low) throw 'fail!';
+      }
+      if (truncate(x) != jspb.utils.joinFloat32(jspb.utils.split64Low,
+          jspb.utils.split64High)) {
+        throw 'fail!';
+      }
+    }
+
+    // Positive and negative infinity.
+    test(Infinity, 0x7f800000);
+    test(-Infinity, 0xff800000);
+
+    // Positive and negative zero.
+    test(0, 0x00000000);
+    test(-0, 0x80000000);
+
+    // Positive and negative epsilon.
+    test(f32_eps, 0x00000001);
+    test(-f32_eps, 0x80000001);
+
+    // Positive and negative min.
+    test(f32_min, 0x00800000);
+    test(-f32_min, 0x80800000);
+
+    // Positive and negative max.
+    test(f32_max, 0x7F7FFFFF);
+    test(-f32_max, 0xFF7FFFFF);
+
+    // Various positive values.
+    var cursor = f32_eps * 10;
+    while (cursor != Infinity) {
+      test(cursor);
+      cursor *= 1.1;
+    }
+
+    // Various negative values.
+    cursor = -f32_eps * 10;
+    while (cursor != -Infinity) {
+      test(cursor);
+      cursor *= 1.1;
+    }
+  });
+
+
+  /**
+   * Tests conversion from 64-bit floating point numbers to split64 numbers.
+   */
+  it('testFloat64ToSplit64', function() {
+    var f64_eps = jspb.BinaryConstants.FLOAT64_EPS;
+    var f64_min = jspb.BinaryConstants.FLOAT64_MIN;
+    var f64_max = jspb.BinaryConstants.FLOAT64_MAX;
+
+    // NaN.
+    jspb.utils.splitFloat64(NaN);
+    if (!isNaN(jspb.utils.joinFloat64(jspb.utils.split64Low,
+        jspb.utils.split64High))) {
+      throw 'fail!';
+    }
+
+    /**
+     * @param {number} x
+     * @param {number=} opt_highBits
+     * @param {number=} opt_lowBits
+     */
+    function test(x, opt_highBits, opt_lowBits) {
+      jspb.utils.splitFloat64(x);
+      if (goog.isDef(opt_highBits)) {
+        if (opt_highBits != jspb.utils.split64High) throw 'fail!';
+      }
+      if (goog.isDef(opt_lowBits)) {
+        if (opt_lowBits != jspb.utils.split64Low) throw 'fail!';
+      }
+      if (x != jspb.utils.joinFloat64(jspb.utils.split64Low,
+          jspb.utils.split64High)) {
+        throw 'fail!';
+      }
+    }
+
+    // Positive and negative infinity.
+    test(Infinity, 0x7ff00000, 0x00000000);
+    test(-Infinity, 0xfff00000, 0x00000000);
+
+    // Positive and negative zero.
+    test(0, 0x00000000, 0x00000000);
+    test(-0, 0x80000000, 0x00000000);
+
+    // Positive and negative epsilon.
+    test(f64_eps, 0x00000000, 0x00000001);
+    test(-f64_eps, 0x80000000, 0x00000001);
+
+    // Positive and negative min.
+    test(f64_min, 0x00100000, 0x00000000);
+    test(-f64_min, 0x80100000, 0x00000000);
+
+    // Positive and negative max.
+    test(f64_max, 0x7FEFFFFF, 0xFFFFFFFF);
+    test(-f64_max, 0xFFEFFFFF, 0xFFFFFFFF);
+
+    // Various positive values.
+    var cursor = f64_eps * 10;
+    while (cursor != Infinity) {
+      test(cursor);
+      cursor *= 1.1;
+    }
+
+    // Various negative values.
+    cursor = -f64_eps * 10;
+    while (cursor != -Infinity) {
+      test(cursor);
+      cursor *= 1.1;
+    }
+  });
+
+
+  /**
+   * Tests counting packed varints.
+   */
+  it('testCountVarints', function() {
+    var values = [];
+    for (var i = 1; i < 1000000000; i *= 1.1) {
+      values.push(Math.floor(i));
+    }
+
+    var writer = new jspb.BinaryWriter();
+    writer.writePackedUint64(1, values);
+
+    var buffer = new Uint8Array(writer.getResultBuffer());
+
+    // We should have two more varints than we started with - one for the field
+    // tag, one for the packed length.
+    assertEquals(values.length + 2,
+                 jspb.utils.countVarints(buffer, 0, buffer.length));
+  });
+
+
+  /**
+   * Tests counting matching varint fields.
+   */
+  it('testCountVarintFields', function() {
+    var writer = new jspb.BinaryWriter();
+
+    var count = 0;
+    for (var i = 1; i < 1000000000; i *= 1.1) {
+      writer.writeUint64(1, Math.floor(i));
+      count++;
+    }
+    writer.writeString(2, 'terminator');
+
+    var buffer = new Uint8Array(writer.getResultBuffer());
+    assertEquals(count,
+        jspb.utils.countVarintFields(buffer, 0, buffer.length, 1));
+
+    writer = new jspb.BinaryWriter();
+
+    count = 0;
+    for (var i = 1; i < 1000000000; i *= 1.1) {
+      writer.writeUint64(123456789, Math.floor(i));
+      count++;
+    }
+    writer.writeString(2, 'terminator');
+
+    buffer = new Uint8Array(writer.getResultBuffer());
+    assertEquals(count,
+        jspb.utils.countVarintFields(buffer, 0, buffer.length, 123456789));
+  });
+
+
+  /**
+   * Tests counting matching fixed32 fields.
+   */
+  it('testCountFixed32Fields', function() {
+    var writer = new jspb.BinaryWriter();
+
+    var count = 0;
+    for (var i = 1; i < 1000000000; i *= 1.1) {
+      writer.writeFixed32(1, Math.floor(i));
+      count++;
+    }
+    writer.writeString(2, 'terminator');
+
+    var buffer = new Uint8Array(writer.getResultBuffer());
+    assertEquals(count,
+        jspb.utils.countFixed32Fields(buffer, 0, buffer.length, 1));
+
+    writer = new jspb.BinaryWriter();
+
+    count = 0;
+    for (var i = 1; i < 1000000000; i *= 1.1) {
+      writer.writeFixed32(123456789, Math.floor(i));
+      count++;
+    }
+    writer.writeString(2, 'terminator');
+
+    buffer = new Uint8Array(writer.getResultBuffer());
+    assertEquals(count,
+        jspb.utils.countFixed32Fields(buffer, 0, buffer.length, 123456789));
+  });
+
+
+  /**
+   * Tests counting matching fixed64 fields.
+   */
+  it('testCountFixed64Fields', function() {
+    var writer = new jspb.BinaryWriter();
+
+    var count = 0;
+    for (var i = 1; i < 1000000000; i *= 1.1) {
+      writer.writeDouble(1, i);
+      count++;
+    }
+    writer.writeString(2, 'terminator');
+
+    var buffer = new Uint8Array(writer.getResultBuffer());
+    assertEquals(count,
+        jspb.utils.countFixed64Fields(buffer, 0, buffer.length, 1));
+
+    writer = new jspb.BinaryWriter();
+
+    count = 0;
+    for (var i = 1; i < 1000000000; i *= 1.1) {
+      writer.writeDouble(123456789, i);
+      count++;
+    }
+    writer.writeString(2, 'terminator');
+
+    buffer = new Uint8Array(writer.getResultBuffer());
+    assertEquals(count,
+        jspb.utils.countFixed64Fields(buffer, 0, buffer.length, 123456789));
+  });
+
+
+  /**
+   * Tests counting matching delimited fields.
+   */
+  it('testCountDelimitedFields', function() {
+    var writer = new jspb.BinaryWriter();
+
+    var count = 0;
+    for (var i = 1; i < 1000; i *= 1.1) {
+      writer.writeBytes(1, [Math.floor(i)]);
+      count++;
+    }
+    writer.writeString(2, 'terminator');
+
+    var buffer = new Uint8Array(writer.getResultBuffer());
+    assertEquals(count,
+        jspb.utils.countDelimitedFields(buffer, 0, buffer.length, 1));
+
+    writer = new jspb.BinaryWriter();
+
+    count = 0;
+    for (var i = 1; i < 1000; i *= 1.1) {
+      writer.writeBytes(123456789, [Math.floor(i)]);
+      count++;
+    }
+    writer.writeString(2, 'terminator');
+
+    buffer = new Uint8Array(writer.getResultBuffer());
+    assertEquals(count,
+        jspb.utils.countDelimitedFields(buffer, 0, buffer.length, 123456789));
+  });
+
+
+  /**
+   * Tests byte format for debug strings.
+   */
+  it('testDebugBytesToTextFormat', function() {
+    assertEquals('""', jspb.utils.debugBytesToTextFormat(null));
+    assertEquals('"\\x00\\x10\\xff"',
+        jspb.utils.debugBytesToTextFormat([0, 16, 255]));
+  });
+
+
+  /**
+   * Tests converting byte blob sources into byte blobs.
+   */
+  it('testByteSourceToUint8Array', function() {
+    var convert = jspb.utils.byteSourceToUint8Array;
+
+    var sourceData = [];
+    for (var i = 0; i < 256; i++) {
+      sourceData.push(i);
+    }
+
+    var sourceBytes = new Uint8Array(sourceData);
+    var sourceBuffer = sourceBytes.buffer;
+    var sourceBase64 = goog.crypt.base64.encodeByteArray(sourceData);
+    var sourceString = String.fromCharCode.apply(null, sourceData);
+
+    function check(result) {
+      assertEquals(Uint8Array, result.constructor);
+      assertEquals(sourceData.length, result.length);
+      for (var i = 0; i < result.length; i++) {
+        assertEquals(sourceData[i], result[i]);
+      }
+    }
+
+    // Converting Uint8Arrays into Uint8Arrays should be a no-op.
+    assertEquals(sourceBytes, convert(sourceBytes));
+
+    // Converting Array.<numbers> into Uint8Arrays should work.
+    check(convert(sourceData));
+
+    // Converting ArrayBuffers into Uint8Arrays should work.
+    check(convert(sourceBuffer));
+
+    // Converting base64-encoded strings into Uint8Arrays should work.
+    check(convert(sourceBase64));
+  });
+});

+ 122 - 0
js/compatibility_tests/v3.1.0/binary/writer_test.js

@@ -0,0 +1,122 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Test cases for jspb's binary protocol buffer writer. In
+ * practice BinaryWriter is used to drive the Decoder and Reader test cases,
+ * so only writer-specific tests are here.
+ *
+ * Test suite is written using Jasmine -- see http://jasmine.github.io/
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.require('goog.crypt');
+goog.require('goog.testing.asserts');
+goog.require('jspb.BinaryWriter');
+
+
+/**
+ * @param {function()} func This function should throw an error when run.
+ */
+function assertFails(func) {
+  var e = assertThrows(func);
+  //assertNotNull(e.toString().match(/Error/));
+}
+
+
+describe('binaryWriterTest', function() {
+  /**
+   * Verifies that misuse of the writer class triggers assertions.
+   */
+  it('testWriteErrors', function() {
+    // Submessages with invalid field indices should assert.
+    var writer = new jspb.BinaryWriter();
+    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+
+    assertFails(function() {
+      writer.writeMessage(-1, dummyMessage, goog.nullFunction);
+    });
+
+    // Writing invalid field indices should assert.
+    writer = new jspb.BinaryWriter();
+    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.writeInt64(1, -Infinity);});
+    assertFails(function() {writer.writeInt64(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.writeSint32(1, -Infinity);});
+    assertFails(function() {writer.writeSint32(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.writeFixed64(1, -1);});
+    assertFails(function() {writer.writeFixed64(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);});
+  });
+
+
+  /**
+   * Basic test of retrieving the result as a Uint8Array buffer
+   */
+  it('testGetResultBuffer', function() {
+    var expected = '0864120b48656c6c6f20776f726c641a0301020320c801';
+
+    var writer = new jspb.BinaryWriter();
+    writer.writeUint32(1, 100);
+    writer.writeString(2, 'Hello world');
+    writer.writeBytes(3, new Uint8Array([1, 2, 3]));
+    writer.writeUint32(4, 200);
+
+    var buffer = writer.getResultBuffer();
+    assertEquals(expected, goog.crypt.byteArrayToHex(buffer));
+  });
+});

+ 40 - 0
js/compatibility_tests/v3.1.0/commonjs/test6/test6.proto

@@ -0,0 +1,40 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+package jspb.test.importing;
+
+message ImportedMessage {
+  string string_value = 1;
+}

+ 42 - 0
js/compatibility_tests/v3.1.0/commonjs/test7/test7.proto

@@ -0,0 +1,42 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+package jspb.test.framing;
+
+import "test6/test6.proto";
+
+message FramingMessage {
+  jspb.test.importing.ImportedMessage imported_message = 1;
+}

+ 51 - 0
js/compatibility_tests/v3.1.0/data.proto

@@ -0,0 +1,51 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: mwr@google.com (Mark Rawling)
+
+syntax = "proto2";
+
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+package jspb.test;
+
+// legacy data, must be nested
+message data {
+  message NestedData {
+    required string str = 1;
+  }
+}
+
+// new data, does not require nesting
+message UnnestedData {
+  required string str = 1;
+}
+

+ 105 - 0
js/compatibility_tests/v3.1.0/debug_test.js

@@ -0,0 +1,105 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+goog.setTestOnly();
+
+goog.require('goog.testing.asserts');
+
+// CommonJS-LoadFromFile: google-protobuf
+goog.require('jspb.debug');
+
+// CommonJS-LoadFromFile: test_pb
+goog.require('proto.jspb.test.HasExtensions');
+goog.require('proto.jspb.test.IsExtension');
+goog.require('proto.jspb.test.Simple1');
+
+
+
+describe('debugTest', function() {
+  it('testSimple1', function() {
+    if (COMPILED) {
+      return;
+    }
+    var message = new proto.jspb.test.Simple1();
+    message.setAString('foo');
+    assertObjectEquals({
+      $name: 'proto.jspb.test.Simple1',
+      'aString': 'foo',
+      'aRepeatedStringList': []
+    }, jspb.debug.dump(message));
+
+    message.setABoolean(true);
+    message.setARepeatedStringList(['1', '2']);
+
+    assertObjectEquals({
+      $name: 'proto.jspb.test.Simple1',
+      'aString': 'foo',
+      'aRepeatedStringList': ['1', '2'],
+      'aBoolean': true
+    }, jspb.debug.dump(message));
+
+    message.clearAString();
+
+    assertObjectEquals({
+      $name: 'proto.jspb.test.Simple1',
+      'aRepeatedStringList': ['1', '2'],
+      'aBoolean': true
+    }, jspb.debug.dump(message));
+  });
+
+
+  it('testExtensions', function() {
+    if (COMPILED) {
+      return;
+    }
+    var extension = new proto.jspb.test.IsExtension();
+    extension.setExt1('ext1field');
+    var extendable = new proto.jspb.test.HasExtensions();
+    extendable.setStr1('v1');
+    extendable.setStr2('v2');
+    extendable.setStr3('v3');
+    extendable.setExtension(proto.jspb.test.IsExtension.extField, extension);
+
+    assertObjectEquals({
+      '$name': 'proto.jspb.test.HasExtensions',
+      'str1': 'v1',
+      'str2': 'v2',
+      'str3': 'v3',
+      '$extensions': {
+        'extField': {
+          '$name': 'proto.jspb.test.IsExtension',
+          'ext1': 'ext1field'
+        },
+        'repeatedSimpleList': []
+      }
+    }, jspb.debug.dump(extendable));
+  });
+
+});

+ 301 - 0
js/compatibility_tests/v3.1.0/maps_test.js

@@ -0,0 +1,301 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+goog.require('goog.testing.asserts');
+goog.require('goog.userAgent');
+
+// CommonJS-LoadFromFile: testbinary_pb proto.jspb.test
+goog.require('proto.jspb.test.MapValueEnum');
+goog.require('proto.jspb.test.MapValueMessage');
+goog.require('proto.jspb.test.TestMapFields');
+
+// CommonJS-LoadFromFile: test_pb proto.jspb.test
+goog.require('proto.jspb.test.MapValueMessageNoBinary');
+goog.require('proto.jspb.test.TestMapFieldsNoBinary');
+
+/**
+ * Helper: check that the given map has exactly this set of (sorted) entries.
+ * @param {!jspb.Map} map
+ * @param {!Array<!Array<?>>} entries
+ */
+function checkMapEquals(map, entries) {
+  var arr = map.toArray();
+  assertEquals(arr.length, entries.length);
+  for (var i = 0; i < arr.length; i++) {
+    assertElementsEquals(arr[i], entries[i]);
+  }
+}
+
+/**
+ * Converts an ES6 iterator to an array.
+ * @template T
+ * @param {!Iterator<T>} iter an iterator
+ * @return {!Array<T>}
+ */
+function toArray(iter) {
+  var arr = [];
+  while (true) {
+    var val = iter.next();
+    if (val.done) {
+      break;
+    }
+    arr.push(val.value);
+  }
+  return arr;
+}
+
+
+/**
+ * Helper: generate test methods for this TestMapFields class.
+ * @param {?} msgInfo
+ * @param {?} submessageCtor
+ * @param {!string} suffix
+ */
+function makeTests(msgInfo, submessageCtor, suffix) {
+  /**
+   * Helper: fill all maps on a TestMapFields.
+   * @param {?} msg
+   */
+  var fillMapFields = function(msg) {
+    msg.getMapStringStringMap().set('asdf', 'jkl;').set('key 2', 'hello world');
+    msg.getMapStringInt32Map().set('a', 1).set('b', -2);
+    msg.getMapStringInt64Map().set('c', 0x100000000).set('d', 0x200000000);
+    msg.getMapStringBoolMap().set('e', true).set('f', false);
+    msg.getMapStringDoubleMap().set('g', 3.14159).set('h', 2.71828);
+    msg.getMapStringEnumMap()
+        .set('i', proto.jspb.test.MapValueEnum.MAP_VALUE_BAR)
+        .set('j', proto.jspb.test.MapValueEnum.MAP_VALUE_BAZ);
+    msg.getMapStringMsgMap()
+        .set('k', new submessageCtor())
+        .set('l', new submessageCtor());
+    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.getMapBoolStringMap().set(false, 'e').set(true, 'f');
+  };
+
+  /**
+   * Helper: check all maps on a TestMapFields.
+   * @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.getMapStringEnumMap(), [
+          ['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']
+    ]);
+
+    assertEquals(msg.getMapStringMsgMap().getLength(), 2);
+    assertEquals(msg.getMapStringMsgMap().get('k').getFoo(), 42);
+    assertEquals(msg.getMapStringMsgMap().get('l').getFoo(), 84);
+
+    var entries = toArray(msg.getMapStringMsgMap().entries());
+    assertEquals(entries.length, 2);
+    entries.forEach(function(entry) {
+      var key = entry[0];
+      var val = entry[1];
+      assert(val === msg.getMapStringMsgMap().get(key));
+    });
+
+    msg.getMapStringMsgMap().forEach(function(val, key) {
+      assert(val === msg.getMapStringMsgMap().get(key));
+    });
+  };
+
+  it('testMapStringStringField' + suffix, function() {
+    var msg = new msgInfo.constructor();
+    assertEquals(msg.getMapStringStringMap().getLength(), 0);
+    assertEquals(msg.getMapStringInt32Map().getLength(), 0);
+    assertEquals(msg.getMapStringInt64Map().getLength(), 0);
+    assertEquals(msg.getMapStringBoolMap().getLength(), 0);
+    assertEquals(msg.getMapStringDoubleMap().getLength(), 0);
+    assertEquals(msg.getMapStringEnumMap().getLength(), 0);
+    assertEquals(msg.getMapStringMsgMap().getLength(), 0);
+
+    // Re-create to clear out any internally-cached wrappers, etc.
+    msg = new msgInfo.constructor();
+    var m = msg.getMapStringStringMap();
+    assertEquals(m.has('asdf'), false);
+    assertEquals(m.get('asdf'), undefined);
+    m.set('asdf', 'hello world');
+    assertEquals(m.has('asdf'), true);
+    assertEquals(m.get('asdf'), 'hello world');
+    m.set('jkl;', 'key 2');
+    assertEquals(m.has('jkl;'), true);
+    assertEquals(m.get('jkl;'), 'key 2');
+    assertEquals(m.getLength(), 2);
+    var it = m.entries();
+    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']
+    ]);
+    m.del('jkl;');
+    assertEquals(m.has('jkl;'), false);
+    assertEquals(m.get('jkl;'), undefined);
+    assertEquals(m.getLength(), 1);
+    it = m.keys();
+    assertEquals(it.next().value, 'asdf');
+    assertEquals(it.next().done, true);
+    it = m.values();
+    assertEquals(it.next().value, 'hello world');
+    assertEquals(it.next().done, true);
+
+    var count = 0;
+    m.forEach(function(value, key, map) {
+      assertEquals(map, m);
+      assertEquals(key, 'asdf');
+      assertEquals(value, 'hello world');
+      count++;
+    });
+    assertEquals(count, 1);
+
+    m.clear();
+    assertEquals(m.getLength(), 0);
+  });
+
+
+  /**
+   * Tests operations on maps with all key and value types.
+   */
+  it('testAllMapTypes' + suffix, function() {
+    var msg = new msgInfo.constructor();
+    fillMapFields(msg);
+    checkMapFields(msg);
+  });
+
+
+  if (msgInfo.deserializeBinary) {
+    /**
+     * Tests serialization and deserialization in binary format.
+     */
+    it('testBinaryFormat' + suffix, function() {
+      if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
+        // IE8/9 currently doesn't support binary format because they lack
+        // TypedArray.
+        return;
+      }
+
+      // Check that the format is correct.
+      var msg = new msgInfo.constructor();
+      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'
+      ];
+      assertEquals(serialized.length, expectedSerialized.length);
+      for (var i = 0; i < serialized.length; i++) {
+        assertEquals(serialized[i], expectedSerialized[i]);
+      }
+
+      // Check that all map fields successfully round-trip.
+      msg = new msgInfo.constructor();
+      fillMapFields(msg);
+      serialized = msg.serializeBinary();
+      var decoded = msgInfo.deserializeBinary(serialized);
+      checkMapFields(decoded);
+    });
+  }
+
+  /**
+   * Exercises the lazy map<->underlying array sync.
+   */
+  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 msg = new msgInfo.constructor([entries]);
+    assertEquals(entries.length, 3);
+    assertEquals(entries[0][0], 'a');
+    assertEquals(entries[1][0], 'c');
+    assertEquals(entries[2][0], 'b');
+    msg.getMapStringStringMap().del('a');
+    assertEquals(entries.length, 3);  // not yet sync'd
+    msg.toArray();                // force a sync
+    assertEquals(entries.length, 2);
+    assertEquals(entries[0][0], 'b'); // now in sorted order
+    assertEquals(entries[1][0], 'c');
+
+    var a = msg.toArray();
+    assertEquals(a[0], entries);  // retains original reference
+  });
+}
+
+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");
+});

+ 1037 - 0
js/compatibility_tests/v3.1.0/message_test.js

@@ -0,0 +1,1037 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test suite is written using Jasmine -- see http://jasmine.github.io/
+
+goog.setTestOnly();
+
+goog.require('goog.json');
+goog.require('goog.testing.asserts');
+goog.require('goog.userAgent');
+
+// CommonJS-LoadFromFile: google-protobuf jspb
+goog.require('jspb.Message');
+
+// CommonJS-LoadFromFile: test5_pb proto.jspb.exttest.beta
+goog.require('proto.jspb.exttest.beta.floatingStrField');
+
+// CommonJS-LoadFromFile: test3_pb proto.jspb.exttest
+goog.require('proto.jspb.exttest.floatingMsgField');
+
+// CommonJS-LoadFromFile: test4_pb proto.jspb.exttest
+goog.require('proto.jspb.exttest.floatingMsgFieldTwo');
+
+// CommonJS-LoadFromFile: test_pb proto.jspb.test
+goog.require('proto.jspb.test.CloneExtension');
+goog.require('proto.jspb.test.Complex');
+goog.require('proto.jspb.test.DefaultValues');
+goog.require('proto.jspb.test.Empty');
+goog.require('proto.jspb.test.EnumContainer');
+goog.require('proto.jspb.test.floatingMsgField');
+goog.require('proto.jspb.test.FloatingPointFields');
+goog.require('proto.jspb.test.floatingStrField');
+goog.require('proto.jspb.test.HasExtensions');
+goog.require('proto.jspb.test.IndirectExtension');
+goog.require('proto.jspb.test.IsExtension');
+goog.require('proto.jspb.test.OptionalFields');
+goog.require('proto.jspb.test.OuterEnum');
+goog.require('proto.jspb.test.OuterMessage.Complex');
+goog.require('proto.jspb.test.Simple1');
+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');
+goog.require('proto.jspb.test.TestReservedNames');
+goog.require('proto.jspb.test.TestReservedNamesExtension');
+
+// CommonJS-LoadFromFile: test2_pb proto.jspb.test
+goog.require('proto.jspb.test.ExtensionMessage');
+goog.require('proto.jspb.test.TestExtensionsMessage');
+
+
+
+
+describe('Message test suite', function() {
+  it('testEmptyProto', function() {
+    var empty1 = new proto.jspb.test.Empty([]);
+    var empty2 = new proto.jspb.test.Empty([]);
+    assertObjectEquals({}, empty1.toObject());
+    assertObjectEquals('Message should not be corrupted:', empty2, empty1);
+  });
+
+  it('testTopLevelEnum', function() {
+    var response = new proto.jspb.test.EnumContainer([]);
+    response.setOuterEnum(proto.jspb.test.OuterEnum.FOO);
+    assertEquals(proto.jspb.test.OuterEnum.FOO, response.getOuterEnum());
+  });
+
+  it('testByteStrings', function() {
+    var data = new proto.jspb.test.DefaultValues([]);
+    data.setBytesField('some_bytes');
+    assertEquals('some_bytes', data.getBytesField());
+  });
+
+  it('testComplexConversion', function() {
+    var data1 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1];
+    var data2 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1];
+    var foo = new proto.jspb.test.Complex(data1);
+    var bar = new proto.jspb.test.Complex(data2);
+    var result = foo.toObject();
+    assertObjectEquals({
+      aString: 'a',
+      anOutOfOrderBool: 1,
+      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: 1,
+      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]);
+    var bar = new proto.jspb.test.Complex([
+        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);
+
+  });
+
+  it('testNestedComplexMessage', function() {
+    // Instantiate the message and set a unique field, just to ensure that we
+    // are not getting jspb.test.Complex instead.
+    var msg = new proto.jspb.test.OuterMessage.Complex();
+    msg.setInnerComplexField(5);
+  });
+
+  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 result = special.toObject();
+    assertObjectEquals({
+      normal: 'normal',
+      pb_default: 'default',
+      pb_function: 'function',
+      pb_var: 'var'
+    }, result);
+  });
+
+  it('testDefaultValues', function() {
+    var defaultString = "default<>\'\"abc";
+    var response = new proto.jspb.test.DefaultValues();
+
+    // Test toObject
+    var expectedObject = {
+      stringField: defaultString,
+      boolField: true,
+      intField: 11,
+      enumField: 13,
+      emptyField: '',
+      bytesField: 'bW9v'
+    };
+    assertObjectEquals(expectedObject, response.toObject());
+
+
+    // Test getters
+    response = new proto.jspb.test.DefaultValues();
+    assertEquals(defaultString, response.getStringField());
+    assertEquals(true, response.getBoolField());
+    assertEquals(11, response.getIntField());
+    assertEquals(13, response.getEnumField());
+    assertEquals('', response.getEmptyField());
+    assertEquals('bW9v', response.getBytesField());
+
+    function makeDefault(values) {
+      return new proto.jspb.test.DefaultValues(values);
+    }
+
+    // Test with undefined values,
+    // Use push to workaround IE treating undefined array elements as holes.
+    response = makeDefault([undefined, undefined, undefined, undefined]);
+    assertEquals(defaultString, response.getStringField());
+    assertEquals(true, response.getBoolField());
+    assertEquals(11, response.getIntField());
+    assertEquals(13, response.getEnumField());
+    assertFalse(response.hasStringField());
+    assertFalse(response.hasBoolField());
+    assertFalse(response.hasIntField());
+    assertFalse(response.hasEnumField());
+
+    // Test with null values, as would be returned by a JSON serializer.
+    response = makeDefault([null, null, null, null]);
+    assertEquals(defaultString, response.getStringField());
+    assertEquals(true, response.getBoolField());
+    assertEquals(11, response.getIntField());
+    assertEquals(13, response.getEnumField());
+    assertFalse(response.hasStringField());
+    assertFalse(response.hasBoolField());
+    assertFalse(response.hasIntField());
+    assertFalse(response.hasEnumField());
+
+    // Test with false-like values.
+    response = makeDefault(['', false, 0, 0]);
+    assertEquals('', response.getStringField());
+    assertEquals(false, response.getBoolField());
+    assertEquals(true, response.getIntField() == 0);
+    assertEquals(true, response.getEnumField() == 0);
+    assertTrue(response.hasStringField());
+    assertTrue(response.hasBoolField());
+    assertTrue(response.hasIntField());
+    assertTrue(response.hasEnumField());
+
+    // 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();
+    assertEquals(defaultString, response.getStringField());
+    assertEquals(true, response.getBoolField());
+    assertEquals(11, response.getIntField());
+    assertEquals(13, response.getEnumField());
+    assertFalse(response.hasStringField());
+    assertFalse(response.hasBoolField());
+    assertFalse(response.hasIntField());
+    assertFalse(response.hasEnumField());
+
+    // 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);
+    assertEquals(defaultString, response.getStringField());
+    assertEquals(true, response.getBoolField());
+    assertEquals(11, response.getIntField());
+    assertEquals(13, response.getEnumField());
+    assertFalse(response.hasStringField());
+    assertFalse(response.hasBoolField());
+    assertFalse(response.hasIntField());
+    assertFalse(response.hasEnumField());
+  });
+
+  it('testClearFields', function() {
+    var data = ['str', true, [11], [[22], [33]], ['s1', 's2']];
+    var foo = new proto.jspb.test.OptionalFields(data);
+    foo.clearAString();
+    foo.clearABool();
+    foo.clearANestedMessage();
+    foo.clearARepeatedMessageList();
+    foo.clearARepeatedStringList();
+    assertEquals('', foo.getAString());
+    assertEquals(false, foo.getABool());
+    assertUndefined(foo.getANestedMessage());
+    assertFalse(foo.hasAString());
+    assertFalse(foo.hasABool());
+    assertObjectEquals([], foo.getARepeatedMessageList());
+    assertObjectEquals([], foo.getARepeatedStringList());
+    // NOTE: We want the missing fields in 'expected' to be undefined,
+    // 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 = [,,, [], []];
+    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 diff = /** @type {proto.jspb.test.HasExtensions} */
+        (jspb.Message.difference(p1, p2));
+    assertEquals('', diff.getStr1());
+    assertEquals('what', diff.getStr2());
+    assertEquals('', diff.getStr3());
+    assertEquals('unique', diff.extensionObject_[1000]);
+  });
+
+  it('testEqualsSimple', function() {
+    var s1 = new proto.jspb.test.Simple1(['hi']);
+    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']])));
+    // Test with messages of different types
+    var s2 = new proto.jspb.test.Simple2(['hi']);
+    assertFalse(jspb.Message.equals(s1, s2));
+  });
+
+  it('testEquals_softComparison', function() {
+    var s1 = new proto.jspb.test.Simple1(['hi', [], null]);
+    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])));
+  });
+
+  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 data4 = ['hi'];
+    var c1a = new proto.jspb.test.Complex(data1);
+    var c1b = new proto.jspb.test.Complex(data1);
+    var c2 = new proto.jspb.test.Complex(data2);
+    var c3 = new proto.jspb.test.Complex(data3);
+    var s1 = new proto.jspb.test.Simple1(data4);
+
+    assertTrue(jspb.Message.equals(c1a, c1b));
+    assertFalse(jspb.Message.equals(c1a, c2));
+    assertFalse(jspb.Message.equals(c2, c3));
+    assertFalse(jspb.Message.equals(c1a, s1));
+  });
+
+  it('testEqualsExtensionsConstructed', function() {
+    assertTrue(jspb.Message.equals(
+        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'}]}])
+    ));
+    assertFalse(jspb.Message.equals(
+        new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}]),
+        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'}]}])
+    ));
+    assertTrue(jspb.Message.equals(
+        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'}]}])
+    ));
+    assertTrue(jspb.Message.equals(
+        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'}]}])
+    ));
+  });
+
+  it('testEqualsExtensionsUnconstructed', function() {
+    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(
+        ['hi', {100: [{200: 'a'}]}], ['hi', {100: [{200: 'b'}]}]));
+    assertTrue(jspb.Message.compareFields(
+        [{100: [{200: 'a'}]}], [{100: [{200: 'a'}]}]));
+    assertTrue(jspb.Message.compareFields(
+        [{100: [{200: 'a'}]}], [,,, {100: [{200: 'a'}]}]));
+    assertTrue(jspb.Message.compareFields(
+        [,,, {100: [{200: 'a'}]}], [{100: [{200: 'a'}]}]));
+    assertTrue(jspb.Message.compareFields(
+        ['hi', {100: [{200: 'a'}]}], ['hi',,, {100: [{200: 'a'}]}]));
+    assertTrue(jspb.Message.compareFields(
+        ['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,
+        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);
+    assertEquals('k', protomap['k'].getAString());
+    assertArrayEquals(['v'], protomap['k'].getARepeatedStringList());
+  });
+
+  it('testClone', function() {
+    var supportsUint8Array =
+        !goog.userAgent.IE || goog.userAgent.isVersionOrHigher('10');
+    var original = new proto.jspb.test.TestClone();
+    original.setStr('v1');
+    var simple1 = new proto.jspb.test.Simple1(['x1', ['y1', 'z1']]);
+    var simple2 = new proto.jspb.test.Simple1(['x2', ['y2', 'z2']]);
+    var simple3 = new proto.jspb.test.Simple1(['x3', ['y3', 'z3']]);
+    original.setSimple1(simple1);
+    original.setSimple2List([simple2, simple3]);
+    var bytes1 = supportsUint8Array ? new Uint8Array([1, 2, 3]) : '123';
+    original.setBytesField(bytes1);
+    var extension = new proto.jspb.test.CloneExtension();
+    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'] }],
+        clone.toArray());
+    clone.setStr('v2');
+    var simple4 = new proto.jspb.test.Simple1(['a1', ['b1', 'c1']]);
+    var simple5 = new proto.jspb.test.Simple1(['a2', ['b2', 'c2']]);
+    var simple6 = new proto.jspb.test.Simple1(['a3', ['b3', 'c3']]);
+    clone.setSimple1(simple4);
+    clone.setSimple2List([simple5, simple6]);
+    if (supportsUint8Array) {
+      clone.getBytesField()[0] = 4;
+      assertObjectEquals(bytes1, original.getBytesField());
+    }
+    var bytes2 = supportsUint8Array ? new Uint8Array([4, 5, 6]) : '456';
+    clone.setBytesField(bytes2);
+    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'] }],
+        clone.toArray());
+    assertArrayEquals(['v1',, ['x1', ['y1', 'z1']],,
+      [['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]], bytes1,, { 100: [, 'e1'] }],
+        original.toArray());
+  });
+
+  it('testCopyInto', function() {
+    var supportsUint8Array =
+        !goog.userAgent.IE || goog.userAgent.isVersionOrHigher('10');
+    var original = new proto.jspb.test.TestClone();
+    original.setStr('v1');
+    var dest = new proto.jspb.test.TestClone();
+    dest.setStr('override');
+    var simple1 = new proto.jspb.test.Simple1(['x1', ['y1', 'z1']]);
+    var simple2 = new proto.jspb.test.Simple1(['x2', ['y2', 'z2']]);
+    var simple3 = new proto.jspb.test.Simple1(['x3', ['y3', 'z3']]);
+    var destSimple1 = new proto.jspb.test.Simple1(['ox1', ['oy1', 'oz1']]);
+    var destSimple2 = new proto.jspb.test.Simple1(['ox2', ['oy2', 'oz2']]);
+    var destSimple3 = new proto.jspb.test.Simple1(['ox3', ['oy3', 'oz3']]);
+    original.setSimple1(simple1);
+    original.setSimple2List([simple2, simple3]);
+    dest.setSimple1(destSimple1);
+    dest.setSimple2List([destSimple2, destSimple3]);
+    var bytes1 = supportsUint8Array ? new Uint8Array([1, 2, 3]) : '123';
+    var bytes2 = supportsUint8Array ? new Uint8Array([4, 5, 6]) : '456';
+    original.setBytesField(bytes1);
+    dest.setBytesField(bytes2);
+    var extension = new proto.jspb.test.CloneExtension();
+    extension.setExt('e1');
+    original.setExtension(proto.jspb.test.CloneExtension.extField, extension);
+
+    jspb.Message.copyInto(original, dest);
+    assertArrayEquals(original.toArray(), dest.toArray());
+    assertEquals('x1', dest.getSimple1().getAString());
+    assertEquals('e1',
+        dest.getExtension(proto.jspb.test.CloneExtension.extField).getExt());
+    dest.getSimple1().setAString('new value');
+    assertNotEquals(dest.getSimple1().getAString(),
+        original.getSimple1().getAString());
+    if (supportsUint8Array) {
+      dest.getBytesField()[0] = 7;
+      assertObjectEquals(bytes1, original.getBytesField());
+      assertObjectEquals(new Uint8Array([7, 2, 3]), dest.getBytesField());
+    } else {
+      dest.setBytesField('789');
+      assertObjectEquals(bytes1, original.getBytesField());
+      assertObjectEquals('789', dest.getBytesField());
+    }
+    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());
+  });
+
+  it('testCopyInto_notSameType', function() {
+    var a = new proto.jspb.test.TestClone();
+    var b = new proto.jspb.test.Simple1(['str', ['s1', 's2']]);
+
+    var e = assertThrows(function() {
+      jspb.Message.copyInto(a, b);
+    });
+    assertContains('should have the same type', e.message);
+  });
+
+  it('testExtensions', function() {
+    var extension1 = new proto.jspb.test.IsExtension(['ext1field']);
+    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.str, 'xyzzy');
+    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,
+        extendable.getExtension(proto.jspb.test.IsExtension.extField));
+    assertObjectEquals(extension2,
+        extendable.getExtension(proto.jspb.test.IndirectExtension.simple));
+    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));
+    // Not supported yet, but it should work...
+    extendable.setExtension(proto.jspb.test.IndirectExtension.simple, null);
+    assertNull(
+        extendable.getExtension(proto.jspb.test.IndirectExtension.simple));
+    extendable.setExtension(proto.jspb.test.IndirectExtension.str, null);
+    assertNull(extendable.getExtension(proto.jspb.test.IndirectExtension.str));
+
+
+    // Extension fields with jspb.ignore = true are ignored.
+    assertUndefined(proto.jspb.test.IndirectExtension['ignored']);
+    assertUndefined(proto.jspb.test.HasExtensions['ignoredFloating']);
+  });
+
+  it('testFloatingExtensions', function() {
+    // From an autogenerated container.
+    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));
+
+    // 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));
+    assertNotUndefined(proto.jspb.exttest.floatingMsgField);
+    assertNotUndefined(proto.jspb.exttest.floatingMsgFieldTwo);
+    assertNotUndefined(proto.jspb.exttest.beta.floatingStrField);
+  });
+
+  it('testToObject_extendedObject', function() {
+    var extension1 = new proto.jspb.test.IsExtension(['ext1field']);
+    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.str, 'xyzzy');
+    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());
+
+    // 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 */));
+  });
+
+  it('testInitialization_emptyArray', function() {
+    var msg = new proto.jspb.test.HasExtensions([]);
+    if (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS) {
+      assertArrayEquals([], msg.toArray());
+    } else {
+      // Extension object is created past all regular fields.
+      assertArrayEquals([,,, {}], msg.toArray());
+    }
+  });
+
+  it('testInitialization_justExtensionObject', function() {
+    var msg = new proto.jspb.test.Empty([{1: 'hi'}]);
+    // The extensionObject is not moved from its original location.
+    assertArrayEquals([{1: 'hi'}], msg.toArray());
+  });
+
+  it('testInitialization_incompleteList', function() {
+    var msg = new proto.jspb.test.Empty([1, {4: 'hi'}]);
+    // The extensionObject is not moved from its original location.
+    assertArrayEquals([1, {4: 'hi'}], msg.toArray());
+  });
+
+  it('testInitialization_forwardCompatible', function() {
+    var msg = new proto.jspb.test.Empty([1, 2, 3, {1: 'hi'}]);
+    assertArrayEquals([1, 2, 3, {1: 'hi'}], msg.toArray());
+  });
+
+  it('testExtendedMessageEnsureObject',
+     /** @suppress {visibility} */ function() {
+       var data =
+           new proto.jspb.test.HasExtensions(['str1', {'a_key': 'an_object'}]);
+       assertEquals('an_object', data.extensionObject_['a_key']);
+     });
+
+  it('testToObject_hasExtensionField', function() {
+    var data = new proto.jspb.test.HasExtensions(['str1', {100: ['ext1']}]);
+    var obj = data.toObject();
+    assertEquals('str1', obj.str1);
+    assertEquals('ext1', obj.extField.ext1);
+  });
+
+  it('testGetExtension', function() {
+    var data = new proto.jspb.test.HasExtensions(['str1', {100: ['ext1']}]);
+    assertEquals('str1', data.getStr1());
+    var extension = data.getExtension(proto.jspb.test.IsExtension.extField);
+    assertNotNull(extension);
+    assertEquals('ext1', extension.getExt1());
+  });
+
+  it('testSetExtension', function() {
+    var data = new proto.jspb.test.HasExtensions();
+    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));
+    assertEquals('is_extension', obj.extField.ext1);
+  });
+
+  /**
+   * Note that group is long deprecated, we only support it because JsPb has
+   * a goal of being able to generate JS classes for all proto descriptors.
+   */
+  it('testGroups', function() {
+    var group = new proto.jspb.test.TestGroup();
+    var someGroup = new proto.jspb.test.TestGroup.RepeatedGroup();
+    someGroup.setId('g1');
+    someGroup.setSomeBoolList([true, false]);
+    group.setRepeatedGroupList([someGroup]);
+    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());
+    var group1 = new proto.jspb.test.TestGroup1();
+    group1.setGroup(someGroup);
+    assertEquals(someGroup, group1.getGroup());
+  });
+
+  it('testNonExtensionFieldsAfterExtensionRange', function() {
+    var data = [{'1': 'a_string'}];
+    var message = new proto.jspb.test.Complex(data);
+    assertArrayEquals([], message.getARepeatedStringList());
+  });
+
+  it('testReservedGetterNames', function() {
+    var message = new proto.jspb.test.TestReservedNames();
+    message.setExtension$(11);
+    message.setExtension(proto.jspb.test.TestReservedNamesExtension.foo, 12);
+    assertEquals(11, message.getExtension$());
+    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,
+        message.getPartialOneofCase());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.
+            RECURSIVE_ONEOF_NOT_SET,
+        message.getRecursiveOneofCase());
+  });
+
+  it('testInitializeMessageWithSingleValueSetInOneof', function() {
+    var message = new proto.jspb.test.TestMessageWithOneof([,, 'x']);
+
+    assertEquals('x', message.getPone());
+    assertEquals('', message.getPthree());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PONE,
+        message.getPartialOneofCase());
+  });
+
+  it('testKeepsLastWireValueSetInUnion_multipleValues', function() {
+    var message = new proto.jspb.test.TestMessageWithOneof([,, 'x',, 'y']);
+
+    assertEquals('', message.getPone());
+    assertEquals('y', message.getPthree());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PTHREE,
+        message.getPartialOneofCase());
+  });
+
+  it('testSettingOneofFieldClearsOthers', function() {
+    var message = new proto.jspb.test.TestMessageWithOneof;
+    assertEquals('', message.getPone());
+    assertEquals('', message.getPthree());
+    assertFalse(message.hasPone());
+    assertFalse(message.hasPthree());
+
+    message.setPone('hi');
+    assertEquals('hi', message.getPone());
+    assertEquals('', message.getPthree());
+    assertTrue(message.hasPone());
+    assertFalse(message.hasPthree());
+
+    message.setPthree('bye');
+    assertEquals('', message.getPone());
+    assertEquals('bye', message.getPthree());
+    assertFalse(message.hasPone());
+    assertTrue(message.hasPthree());
+  });
+
+  it('testSettingOneofFieldDoesNotClearFieldsFromOtherUnions', function() {
+    var other = new proto.jspb.test.TestMessageWithOneof;
+    var message = new proto.jspb.test.TestMessageWithOneof;
+    assertEquals('', message.getPone());
+    assertEquals('', message.getPthree());
+    assertUndefined(message.getRone());
+    assertFalse(message.hasPone());
+    assertFalse(message.hasPthree());
+
+    message.setPone('hi');
+    message.setRone(other);
+    assertEquals('hi', message.getPone());
+    assertEquals('', message.getPthree());
+    assertEquals(other, message.getRone());
+    assertTrue(message.hasPone());
+    assertFalse(message.hasPthree());
+
+    message.setPthree('bye');
+    assertEquals('', message.getPone());
+    assertEquals('bye', message.getPthree());
+    assertEquals(other, message.getRone());
+    assertFalse(message.hasPone());
+    assertTrue(message.hasPthree());
+  });
+
+  it('testUnsetsOneofCaseWhenFieldIsCleared', function() {
+    var message = new proto.jspb.test.TestMessageWithOneof;
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.
+            PARTIAL_ONEOF_NOT_SET,
+        message.getPartialOneofCase());
+
+    message.setPone('hi');
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PONE,
+        message.getPartialOneofCase());
+
+    message.clearPone();
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.
+            PARTIAL_ONEOF_NOT_SET,
+        message.getPartialOneofCase());
+  });
+
+  it('testMessageWithDefaultOneofValues', function() {
+    var message = new proto.jspb.test.TestMessageWithOneof;
+    assertEquals(1234, message.getAone());
+    assertEquals(0, message.getAtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase
+            .DEFAULT_ONEOF_A_NOT_SET,
+        message.getDefaultOneofACase());
+
+    message.setAone(567);
+    assertEquals(567, message.getAone());
+    assertEquals(0, message.getAtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.AONE,
+        message.getDefaultOneofACase());
+
+    message.setAtwo(890);
+    assertEquals(1234, message.getAone());
+    assertEquals(890, message.getAtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.ATWO,
+        message.getDefaultOneofACase());
+
+    message.clearAtwo();
+    assertEquals(1234, message.getAone());
+    assertEquals(0, message.getAtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase
+            .DEFAULT_ONEOF_A_NOT_SET,
+        message.getDefaultOneofACase());
+  });
+
+  it('testMessageWithDefaultOneofValues_defaultNotOnFirstField', function() {
+    var message = new proto.jspb.test.TestMessageWithOneof;
+    assertEquals(0, message.getBone());
+    assertEquals(1234, message.getBtwo());
+    assertFalse(message.hasBone());
+    assertFalse(message.hasBtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase
+            .DEFAULT_ONEOF_B_NOT_SET,
+        message.getDefaultOneofBCase());
+
+    message.setBone(2);
+    assertEquals(2, message.getBone());
+    assertEquals(1234, message.getBtwo());
+    assertTrue(message.hasBone());
+    assertFalse(message.hasBtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BONE,
+        message.getDefaultOneofBCase());
+
+    message.setBtwo(3);
+    assertEquals(0, message.getBone());
+    assertFalse(message.hasBone());
+    assertTrue(message.hasBtwo());
+    assertEquals(3, message.getBtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO,
+        message.getDefaultOneofBCase());
+
+    message.clearBtwo();
+    assertEquals(0, message.getBone());
+    assertFalse(message.hasBone());
+    assertFalse(message.hasBtwo());
+    assertEquals(1234, message.getBtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase
+            .DEFAULT_ONEOF_B_NOT_SET,
+        message.getDefaultOneofBCase());
+  });
+
+  it('testInitializeMessageWithOneofDefaults', function() {
+    var message =
+        new proto.jspb.test.TestMessageWithOneof(new Array(9).concat(567));
+    assertEquals(567, message.getAone());
+    assertEquals(0, message.getAtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.AONE,
+        message.getDefaultOneofACase());
+
+    message =
+        new proto.jspb.test.TestMessageWithOneof(new Array(10).concat(890));
+    assertEquals(1234, message.getAone());
+    assertEquals(890, message.getAtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.ATWO,
+        message.getDefaultOneofACase());
+
+    message =
+        new proto.jspb.test.TestMessageWithOneof(new Array(9).concat(567, 890));
+    assertEquals(1234, message.getAone());
+    assertEquals(890, message.getAtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.ATWO,
+        message.getDefaultOneofACase());
+  });
+
+  it('testInitializeMessageWithOneofDefaults_defaultNotSetOnFirstField',
+      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(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());
+      });
+
+  it('testOneofContainingAnotherMessage', function() {
+    var message = new proto.jspb.test.TestMessageWithOneof;
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.
+            RECURSIVE_ONEOF_NOT_SET,
+        message.getRecursiveOneofCase());
+
+    var other = new proto.jspb.test.TestMessageWithOneof;
+    message.setRone(other);
+    assertEquals(other, message.getRone());
+    assertEquals('', message.getRtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.RONE,
+        message.getRecursiveOneofCase());
+
+    message.setRtwo('hi');
+    assertUndefined(message.getRone());
+    assertEquals('hi', message.getRtwo());
+    assertEquals(
+        proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.RTWO,
+        message.getRecursiveOneofCase());
+  });
+
+  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 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]);
+  });
+
+  it('testFloatingPointFieldsSupportNan', function() {
+    var assertNan = function(x) {
+      assertTrue('Expected ' + x + ' (' + goog.typeOf(x) + ') to be NaN.',
+          goog.isNumber(x) && isNaN(x));
+    };
+
+    var message = new proto.jspb.test.FloatingPointFields([
+      'NaN', 'NaN', ['NaN', 'NaN'], 'NaN',
+      'NaN', 'NaN', ['NaN', 'NaN'], 'NaN'
+    ]);
+    assertNan(message.getOptionalFloatField());
+    assertNan(message.getRequiredFloatField());
+    assertNan(message.getRepeatedFloatFieldList()[0]);
+    assertNan(message.getRepeatedFloatFieldList()[1]);
+    assertNan(message.getDefaultFloatField());
+    assertNan(message.getOptionalDoubleField());
+    assertNan(message.getRequiredDoubleField());
+    assertNan(message.getRepeatedDoubleFieldList()[0]);
+    assertNan(message.getRepeatedDoubleFieldList()[1]);
+    assertNan(message.getDefaultDoubleField());
+  });
+
+});

+ 329 - 0
js/compatibility_tests/v3.1.0/proto3_test.js

@@ -0,0 +1,329 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+goog.require('goog.crypt.base64');
+goog.require('goog.testing.asserts');
+
+// CommonJS-LoadFromFile: testbinary_pb proto.jspb.test
+goog.require('proto.jspb.test.ForeignMessage');
+
+// CommonJS-LoadFromFile: proto3_test_pb proto.jspb.test
+goog.require('proto.jspb.test.Proto3Enum');
+goog.require('proto.jspb.test.TestProto3');
+
+
+var BYTES = new Uint8Array([1, 2, 8, 9]);
+var BYTES_B64 = goog.crypt.base64.encodeByteArray(BYTES);
+
+
+/**
+ * Helper: compare a bytes field to an expected value
+ * @param {Uint8Array|string} arr
+ * @param {Uint8Array} expected
+ * @return {boolean}
+ */
+function bytesCompare(arr, expected) {
+  if (goog.isString(arr)) {
+    arr = goog.crypt.base64.decodeStringToUint8Array(arr);
+  }
+  if (arr.length != expected.length) {
+    return false;
+  }
+  for (var i = 0; i < arr.length; i++) {
+    if (arr[i] != expected[i]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+
+describe('proto3Test', function() {
+  /**
+   * Test defaults for proto3 message fields.
+   */
+  it('testProto3FieldDefaults', function() {
+    var msg = new proto.jspb.test.TestProto3();
+
+    assertEquals(msg.getOptionalInt32(), 0);
+    assertEquals(msg.getOptionalInt64(), 0);
+    assertEquals(msg.getOptionalUint32(), 0);
+    assertEquals(msg.getOptionalUint64(), 0);
+    assertEquals(msg.getOptionalSint32(), 0);
+    assertEquals(msg.getOptionalSint64(), 0);
+    assertEquals(msg.getOptionalFixed32(), 0);
+    assertEquals(msg.getOptionalFixed64(), 0);
+    assertEquals(msg.getOptionalSfixed32(), 0);
+    assertEquals(msg.getOptionalSfixed64(), 0);
+    assertEquals(msg.getOptionalFloat(), 0);
+    assertEquals(msg.getOptionalDouble(), 0);
+    assertEquals(msg.getOptionalString(), '');
+
+    // TODO(b/26173701): when we change bytes fields default getter to return
+    // Uint8Array, we'll want to switch this assertion to match the u8 case.
+    assertEquals(typeof msg.getOptionalBytes(), 'string');
+    assertEquals(msg.getOptionalBytes_asU8() instanceof Uint8Array, true);
+    assertEquals(typeof msg.getOptionalBytes_asB64(), 'string');
+    assertEquals(msg.getOptionalBytes().length, 0);
+    assertEquals(msg.getOptionalBytes_asU8().length, 0);
+    assertEquals(msg.getOptionalBytes_asB64(), '');
+
+    assertEquals(msg.getOptionalForeignEnum(),
+                 proto.jspb.test.Proto3Enum.PROTO3_FOO);
+    assertEquals(msg.getOptionalForeignMessage(), undefined);
+    assertEquals(msg.getOptionalForeignMessage(), undefined);
+
+    assertEquals(msg.getRepeatedInt32List().length, 0);
+    assertEquals(msg.getRepeatedInt64List().length, 0);
+    assertEquals(msg.getRepeatedUint32List().length, 0);
+    assertEquals(msg.getRepeatedUint64List().length, 0);
+    assertEquals(msg.getRepeatedSint32List().length, 0);
+    assertEquals(msg.getRepeatedSint64List().length, 0);
+    assertEquals(msg.getRepeatedFixed32List().length, 0);
+    assertEquals(msg.getRepeatedFixed64List().length, 0);
+    assertEquals(msg.getRepeatedSfixed32List().length, 0);
+    assertEquals(msg.getRepeatedSfixed64List().length, 0);
+    assertEquals(msg.getRepeatedFloatList().length, 0);
+    assertEquals(msg.getRepeatedDoubleList().length, 0);
+    assertEquals(msg.getRepeatedStringList().length, 0);
+    assertEquals(msg.getRepeatedBytesList().length, 0);
+    assertEquals(msg.getRepeatedForeignEnumList().length, 0);
+    assertEquals(msg.getRepeatedForeignMessageList().length, 0);
+
+  });
+
+
+  /**
+   * Test that all fields can be set and read via a serialization roundtrip.
+   */
+  it('testProto3FieldSetGet', function() {
+    var msg = new proto.jspb.test.TestProto3();
+
+    msg.setOptionalInt32(-42);
+    msg.setOptionalInt64(-0x7fffffff00000000);
+    msg.setOptionalUint32(0x80000000);
+    msg.setOptionalUint64(0xf000000000000000);
+    msg.setOptionalSint32(-100);
+    msg.setOptionalSint64(-0x8000000000000000);
+    msg.setOptionalFixed32(1234);
+    msg.setOptionalFixed64(0x1234567800000000);
+    msg.setOptionalSfixed32(-1234);
+    msg.setOptionalSfixed64(-0x1234567800000000);
+    msg.setOptionalFloat(1.5);
+    msg.setOptionalDouble(-1.5);
+    msg.setOptionalBool(true);
+    msg.setOptionalString('hello world');
+    msg.setOptionalBytes(BYTES);
+    var submsg = new proto.jspb.test.ForeignMessage();
+    submsg.setC(16);
+    msg.setOptionalForeignMessage(submsg);
+    msg.setOptionalForeignEnum(proto.jspb.test.Proto3Enum.PROTO3_BAR);
+
+    msg.setRepeatedInt32List([-42]);
+    msg.setRepeatedInt64List([-0x7fffffff00000000]);
+    msg.setRepeatedUint32List([0x80000000]);
+    msg.setRepeatedUint64List([0xf000000000000000]);
+    msg.setRepeatedSint32List([-100]);
+    msg.setRepeatedSint64List([-0x8000000000000000]);
+    msg.setRepeatedFixed32List([1234]);
+    msg.setRepeatedFixed64List([0x1234567800000000]);
+    msg.setRepeatedSfixed32List([-1234]);
+    msg.setRepeatedSfixed64List([-0x1234567800000000]);
+    msg.setRepeatedFloatList([1.5]);
+    msg.setRepeatedDoubleList([-1.5]);
+    msg.setRepeatedBoolList([true]);
+    msg.setRepeatedStringList(['hello world']);
+    msg.setRepeatedBytesList([BYTES]);
+    submsg = new proto.jspb.test.ForeignMessage();
+    submsg.setC(1000);
+    msg.setRepeatedForeignMessageList([submsg]);
+    msg.setRepeatedForeignEnumList([proto.jspb.test.Proto3Enum.PROTO3_BAR]);
+
+    msg.setOneofString('asdf');
+
+    var serialized = msg.serializeBinary();
+    msg = proto.jspb.test.TestProto3.deserializeBinary(serialized);
+
+    assertEquals(msg.getOptionalInt32(), -42);
+    assertEquals(msg.getOptionalInt64(), -0x7fffffff00000000);
+    assertEquals(msg.getOptionalUint32(), 0x80000000);
+    assertEquals(msg.getOptionalUint64(), 0xf000000000000000);
+    assertEquals(msg.getOptionalSint32(), -100);
+    assertEquals(msg.getOptionalSint64(), -0x8000000000000000);
+    assertEquals(msg.getOptionalFixed32(), 1234);
+    assertEquals(msg.getOptionalFixed64(), 0x1234567800000000);
+    assertEquals(msg.getOptionalSfixed32(), -1234);
+    assertEquals(msg.getOptionalSfixed64(), -0x1234567800000000);
+    assertEquals(msg.getOptionalFloat(), 1.5);
+    assertEquals(msg.getOptionalDouble(), -1.5);
+    assertEquals(msg.getOptionalBool(), true);
+    assertEquals(msg.getOptionalString(), 'hello world');
+    assertEquals(true, bytesCompare(msg.getOptionalBytes(), BYTES));
+    assertEquals(msg.getOptionalForeignMessage().getC(), 16);
+    assertEquals(msg.getOptionalForeignEnum(),
+        proto.jspb.test.Proto3Enum.PROTO3_BAR);
+
+    assertElementsEquals(msg.getRepeatedInt32List(), [-42]);
+    assertElementsEquals(msg.getRepeatedInt64List(), [-0x7fffffff00000000]);
+    assertElementsEquals(msg.getRepeatedUint32List(), [0x80000000]);
+    assertElementsEquals(msg.getRepeatedUint64List(), [0xf000000000000000]);
+    assertElementsEquals(msg.getRepeatedSint32List(), [-100]);
+    assertElementsEquals(msg.getRepeatedSint64List(), [-0x8000000000000000]);
+    assertElementsEquals(msg.getRepeatedFixed32List(), [1234]);
+    assertElementsEquals(msg.getRepeatedFixed64List(), [0x1234567800000000]);
+    assertElementsEquals(msg.getRepeatedSfixed32List(), [-1234]);
+    assertElementsEquals(msg.getRepeatedSfixed64List(), [-0x1234567800000000]);
+    assertElementsEquals(msg.getRepeatedFloatList(), [1.5]);
+    assertElementsEquals(msg.getRepeatedDoubleList(), [-1.5]);
+    assertElementsEquals(msg.getRepeatedBoolList(), [true]);
+    assertElementsEquals(msg.getRepeatedStringList(), ['hello world']);
+    assertEquals(msg.getRepeatedBytesList().length, 1);
+    assertEquals(true, bytesCompare(msg.getRepeatedBytesList()[0], BYTES));
+    assertEquals(msg.getRepeatedForeignMessageList().length, 1);
+    assertEquals(msg.getRepeatedForeignMessageList()[0].getC(), 1000);
+    assertElementsEquals(msg.getRepeatedForeignEnumList(),
+        [proto.jspb.test.Proto3Enum.PROTO3_BAR]);
+
+    assertEquals(msg.getOneofString(), 'asdf');
+  });
+
+
+  /**
+   * Test that oneofs continue to have a notion of field presence.
+   */
+  it('testOneofs', function() {
+    var msg = new proto.jspb.test.TestProto3();
+
+    assertEquals(msg.getOneofUint32(), 0);
+    assertEquals(msg.getOneofForeignMessage(), undefined);
+    assertEquals(msg.getOneofString(), '');
+    assertEquals(msg.getOneofBytes(), '');
+    assertFalse(msg.hasOneofUint32());
+    assertFalse(msg.hasOneofString());
+    assertFalse(msg.hasOneofBytes());
+
+    msg.setOneofUint32(42);
+    assertEquals(msg.getOneofUint32(), 42);
+    assertEquals(msg.getOneofForeignMessage(), undefined);
+    assertEquals(msg.getOneofString(), '');
+    assertEquals(msg.getOneofBytes(), '');
+    assertTrue(msg.hasOneofUint32());
+    assertFalse(msg.hasOneofString());
+    assertFalse(msg.hasOneofBytes());
+
+
+    var submsg = new proto.jspb.test.ForeignMessage();
+    msg.setOneofForeignMessage(submsg);
+    assertEquals(msg.getOneofUint32(), 0);
+    assertEquals(msg.getOneofForeignMessage(), submsg);
+    assertEquals(msg.getOneofString(), '');
+    assertEquals(msg.getOneofBytes(), '');
+    assertFalse(msg.hasOneofUint32());
+    assertFalse(msg.hasOneofString());
+    assertFalse(msg.hasOneofBytes());
+
+    msg.setOneofString('hello');
+    assertEquals(msg.getOneofUint32(), 0);
+    assertEquals(msg.getOneofForeignMessage(), undefined);
+    assertEquals(msg.getOneofString(), 'hello');
+    assertEquals(msg.getOneofBytes(), '');
+    assertFalse(msg.hasOneofUint32());
+    assertTrue(msg.hasOneofString());
+    assertFalse(msg.hasOneofBytes());
+
+    msg.setOneofBytes(goog.crypt.base64.encodeString('\u00FF\u00FF'));
+    assertEquals(msg.getOneofUint32(), 0);
+    assertEquals(msg.getOneofForeignMessage(), undefined);
+    assertEquals(msg.getOneofString(), '');
+    assertEquals(msg.getOneofBytes_asB64(),
+        goog.crypt.base64.encodeString('\u00FF\u00FF'));
+    assertFalse(msg.hasOneofUint32());
+    assertFalse(msg.hasOneofString());
+    assertTrue(msg.hasOneofBytes());
+  });
+
+
+  /**
+   * Test that "default"-valued primitive fields are not emitted on the wire.
+   */
+  it('testNoSerializeDefaults', function() {
+    var msg = new proto.jspb.test.TestProto3();
+
+    // Set each primitive to a non-default value, then back to its default, to
+    // ensure that the serialization is actually checking the value and not just
+    // whether it has ever been set.
+    msg.setOptionalInt32(42);
+    msg.setOptionalInt32(0);
+    msg.setOptionalDouble(3.14);
+    msg.setOptionalDouble(0.0);
+    msg.setOptionalBool(true);
+    msg.setOptionalBool(false);
+    msg.setOptionalString('hello world');
+    msg.setOptionalString('');
+    msg.setOptionalBytes(goog.crypt.base64.encodeString('\u00FF\u00FF'));
+    msg.setOptionalBytes('');
+    msg.setOptionalForeignMessage(new proto.jspb.test.ForeignMessage());
+    msg.setOptionalForeignMessage(null);
+    msg.setOptionalForeignEnum(proto.jspb.test.Proto3Enum.PROTO3_BAR);
+    msg.setOptionalForeignEnum(proto.jspb.test.Proto3Enum.PROTO3_FOO);
+    msg.setOneofUint32(32);
+    msg.clearOneofUint32();
+
+
+    var serialized = msg.serializeBinary();
+    assertEquals(0, serialized.length);
+  });
+
+  /**
+   * Test that base64 string and Uint8Array are interchangeable in bytes fields.
+   */
+  it('testBytesFieldsInterop', function() {
+    var msg = new proto.jspb.test.TestProto3();
+    // Set as a base64 string and check all the getters work.
+    msg.setOptionalBytes(BYTES_B64);
+    assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES));
+    assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES));
+    assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
+
+    // Test binary serialize round trip doesn't break it.
+    msg = proto.jspb.test.TestProto3.deserializeBinary(msg.serializeBinary());
+    assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES));
+    assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES));
+    assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
+
+    msg = new proto.jspb.test.TestProto3();
+    // Set as a Uint8Array and check all the getters work.
+    msg.setOptionalBytes(BYTES);
+    assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES));
+    assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES));
+    assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
+
+  });
+});

+ 89 - 0
js/compatibility_tests/v3.1.0/proto3_test.proto

@@ -0,0 +1,89 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+import "testbinary.proto";
+
+package jspb.test;
+
+message TestProto3 {
+     int32 optional_int32    =  1;
+     int64 optional_int64    =  2;
+    uint32 optional_uint32   =  3;
+    uint64 optional_uint64   =  4;
+    sint32 optional_sint32   =  5;
+    sint64 optional_sint64   =  6;
+   fixed32 optional_fixed32  =  7;
+   fixed64 optional_fixed64  =  8;
+  sfixed32 optional_sfixed32 =  9;
+  sfixed64 optional_sfixed64 = 10;
+     float optional_float    = 11;
+    double optional_double   = 12;
+      bool optional_bool     = 13;
+    string optional_string   = 14;
+     bytes optional_bytes    = 15;
+
+  ForeignMessage optional_foreign_message = 19;
+  Proto3Enum     optional_foreign_enum    = 22;
+
+  repeated    int32 repeated_int32    = 31;
+  repeated    int64 repeated_int64    = 32;
+  repeated   uint32 repeated_uint32   = 33;
+  repeated   uint64 repeated_uint64   = 34;
+  repeated   sint32 repeated_sint32   = 35;
+  repeated   sint64 repeated_sint64   = 36;
+  repeated  fixed32 repeated_fixed32  = 37;
+  repeated  fixed64 repeated_fixed64  = 38;
+  repeated sfixed32 repeated_sfixed32 = 39;
+  repeated sfixed64 repeated_sfixed64 = 40;
+  repeated    float repeated_float    = 41;
+  repeated   double repeated_double   = 42;
+  repeated     bool repeated_bool     = 43;
+  repeated   string repeated_string   = 44;
+  repeated    bytes repeated_bytes    = 45;
+
+  repeated ForeignMessage repeated_foreign_message = 49;
+  repeated Proto3Enum     repeated_foreign_enum    = 52;
+
+
+  oneof oneof_field {
+    uint32 oneof_uint32 = 111;
+    ForeignMessage oneof_foreign_message = 112;
+    string oneof_string = 113;
+    bytes oneof_bytes = 114;
+  }
+}
+
+enum Proto3Enum {
+  PROTO3_FOO = 0;
+  PROTO3_BAR = 1;
+  PROTO3_BAZ = 2;
+}

+ 262 - 0
js/compatibility_tests/v3.1.0/test.proto

@@ -0,0 +1,262 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: mwr@google.com (Mark Rawling)
+
+syntax = "proto2";
+
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+import "google/protobuf/descriptor.proto";
+
+package jspb.test;
+
+message Empty {
+}
+
+enum OuterEnum {
+  FOO = 1;
+  BAR = 2;
+}
+
+message EnumContainer {
+  optional OuterEnum outer_enum = 1;
+}
+
+message Simple1 {
+  required string a_string = 1;
+  repeated string a_repeated_string = 2;
+  optional bool a_boolean = 3;
+}
+
+// A message that differs from Simple1 only by name
+message Simple2 {
+  required string a_string = 1;
+  repeated string a_repeated_string = 2;
+}
+
+message SpecialCases {
+  required string normal = 1;
+  // Examples of Js reserved names that are converted to pb_<name>.
+  required string default = 2;
+  required string function = 3;
+  required string var = 4;
+}
+
+message OptionalFields {
+  message Nested {
+    optional int32 an_int = 1;
+  }
+  optional string a_string = 1;
+  required bool a_bool = 2;
+  optional Nested a_nested_message = 3;
+  repeated Nested a_repeated_message = 4;
+  repeated string a_repeated_string = 5;
+}
+
+message HasExtensions {
+  optional string str1 = 1;
+  optional string str2 = 2;
+  optional string str3 = 3;
+  extensions 10 to max;
+}
+
+message Complex {
+  message Nested {
+    required int32 an_int = 2;
+  }
+  required string a_string = 1;
+  required bool an_out_of_order_bool = 9;
+  optional Nested a_nested_message = 4;
+  repeated Nested a_repeated_message = 5;
+  repeated string a_repeated_string = 7;
+}
+
+message OuterMessage {
+  // Make sure this doesn't conflict with the other Complex message.
+  message Complex {
+    optional int32 inner_complex_field = 1;
+  }
+}
+
+message IsExtension {
+  extend HasExtensions {
+    optional IsExtension ext_field = 100;
+  }
+  optional string ext1 = 1;
+
+  // Extensions of proto2 Descriptor messages will be ignored.
+  extend google.protobuf.EnumOptions {
+    optional string simple_option = 42113038;
+  }
+}
+
+message IndirectExtension {
+  extend HasExtensions {
+    optional Simple1 simple = 101;
+    optional string str = 102;
+    repeated string repeated_str = 103;
+    repeated Simple1 repeated_simple = 104;
+  }
+}
+
+extend HasExtensions {
+  optional Simple1 simple1 = 105;
+}
+
+message DefaultValues {
+  enum Enum {
+    E1 = 13;
+    E2 = 77;
+  }
+  optional string string_field = 1 [default="default<>\'\"abc"];
+  optional bool bool_field = 2 [default=true];
+  optional int64 int_field = 3 [default=11];
+  optional Enum enum_field = 4 [default=E1];
+  optional string empty_field = 6 [default=""];
+  optional bytes bytes_field = 8 [default="moo"]; // Base64 encoding is "bW9v"
+}
+
+message FloatingPointFields {
+  optional float optional_float_field = 1;
+  required float required_float_field = 2;
+  repeated float repeated_float_field = 3;
+  optional float default_float_field = 4 [default = 2.0];
+  optional double optional_double_field = 5;
+  required double required_double_field = 6;
+  repeated double repeated_double_field = 7;
+  optional double default_double_field = 8 [default = 2.0];
+}
+
+message TestClone {
+  optional string str = 1;
+  optional Simple1 simple1 = 3;
+  repeated Simple1 simple2 = 5;
+  optional bytes bytes_field = 6;
+  optional string unused = 7;
+  extensions 10 to max;
+}
+
+message CloneExtension {
+  extend TestClone {
+    optional CloneExtension ext_field = 100;
+  }
+  optional string ext = 2;
+}
+
+message TestGroup {
+  repeated group RepeatedGroup = 1 {
+    required string id = 1;
+    repeated bool some_bool = 2;
+  }
+  required group RequiredGroup = 2 {
+    required string id = 1;
+  }
+  optional group OptionalGroup = 3 {
+    required string id = 1;
+  }
+  optional string id = 4;
+  required Simple2 required_simple = 5;
+  optional Simple2 optional_simple = 6;
+}
+
+message TestGroup1 {
+  optional TestGroup.RepeatedGroup group = 1;
+}
+
+message TestReservedNames {
+  optional int32 extension = 1;
+  extensions 10 to max;
+}
+
+message TestReservedNamesExtension {
+  extend TestReservedNames {
+    optional int32 foo = 10;
+  }
+}
+
+message TestMessageWithOneof {
+
+  oneof partial_oneof {
+    string pone = 3;
+    string pthree = 5;
+  }
+
+  oneof recursive_oneof {
+    TestMessageWithOneof rone = 6;
+    string rtwo = 7;
+  }
+
+  optional bool normal_field = 8;
+  repeated string repeated_field = 9;
+
+  oneof default_oneof_a {
+    int32 aone = 10 [default = 1234];
+    int32 atwo = 11;
+  }
+
+  oneof default_oneof_b {
+    int32 bone = 12;
+    int32 btwo = 13 [default = 1234];
+  }
+}
+
+message TestEndsWithBytes {
+  optional int32 value = 1;
+  optional bytes data = 2;
+}
+
+message TestMapFieldsNoBinary {
+  map<string, string> map_string_string = 1;
+  map<string, int32> map_string_int32 = 2;
+  map<string, int64> map_string_int64 = 3;
+  map<string, bool> map_string_bool = 4;
+  map<string, double> map_string_double = 5;
+  map<string, MapValueEnumNoBinary> map_string_enum = 6;
+  map<string, MapValueMessageNoBinary> map_string_msg = 7;
+
+  map<int32, string> map_int32_string = 8;
+  map<int64, string> map_int64_string = 9;
+  map<bool, string> map_bool_string = 10;
+
+  optional TestMapFieldsNoBinary test_map_fields = 11;
+  map<string, TestMapFieldsNoBinary> map_string_testmapfields = 12;
+}
+
+enum MapValueEnumNoBinary {
+  MAP_VALUE_FOO_NOBINARY = 0;
+  MAP_VALUE_BAR_NOBINARY = 1;
+  MAP_VALUE_BAZ_NOBINARY = 2;
+}
+
+message MapValueMessageNoBinary {
+  optional int32 foo = 1;
+}

+ 16 - 30
src/google/protobuf/compiler/profile.proto → js/compatibility_tests/v3.1.0/test2.proto

@@ -28,41 +28,27 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
-// Author: seongkim@google.com (Seong Beom Kim)
-//
-// protoc (Protocol Compiler) can generate more efficient code
-// if it knows how a workload accesses fields of a message;
-// e.g. some much more frequently than others.
-//
-// Protos defined here describe the access information per message
-// and per field. Note that one can use any methods to collect
-// the access patterns like CPU profiling, instrumented build, etc.
-
 syntax = "proto2";
 syntax = "proto2";
 
 
-package google.protobuf.compiler;
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+package jspb.test;
 
 
-// To convey the access pattern of a field, it classifies
-// the type of the accessor methods into getters, setters and
-// configs. Getters and setters read and write the field
-// respectively and other operations like checking if the field
-// exists are considered as configs.
-message FieldAccessInfo {
-  optional string name = 1;
-  optional uint64 getters_count = 2;
-  optional uint64 setters_count = 3;
-  optional uint64 configs_count = 4;
+message TestExtensionsMessage {
+  optional int32 intfield = 1;
+  extensions 100 to max;
 }
 }
 
 
-// "count" correlates with how many samples an access info has
-// for a message. High "count" means more confident optimization
-// based on the info.
-message MessageAccessInfo {
-  optional string name = 1;
-  optional uint64 count = 2;
-  repeated FieldAccessInfo field = 3;
+message ExtensionMessage {
+  extend TestExtensionsMessage {
+    optional ExtensionMessage ext_field = 100;
+  }
+  optional string ext1 = 1;
 }
 }
 
 
-message AccessInfo {
-  repeated MessageAccessInfo message = 1;
+// Floating extensions are only supported when generating a _lib.js library.
+extend TestExtensionsMessage {
+  optional ExtensionMessage floating_msg_field = 101;
+  optional string floating_str_field = 102;
 }
 }

+ 53 - 0
js/compatibility_tests/v3.1.0/test3.proto

@@ -0,0 +1,53 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+package jspb.exttest;
+
+message TestExtensionsMessage {
+  optional int32 intfield = 1;
+  extensions 100 to max;
+}
+
+message ExtensionMessage {
+  extend TestExtensionsMessage {
+    optional ExtensionMessage ext_field = 100;
+  }
+  optional string ext1 = 1;
+}
+
+extend TestExtensionsMessage {
+  optional ExtensionMessage floating_msg_field = 101;
+  optional string floating_str_field = 102;
+}

+ 42 - 0
js/compatibility_tests/v3.1.0/test4.proto

@@ -0,0 +1,42 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+package jspb.exttest;
+
+import "test3.proto";
+
+extend TestExtensionsMessage {
+  optional ExtensionMessage floating_msg_field_two = 103;
+}

+ 44 - 0
js/compatibility_tests/v3.1.0/test5.proto

@@ -0,0 +1,44 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+package jspb.exttest.beta;
+
+message TestBetaExtensionsMessage {
+  extensions 100 to max;
+}
+
+extend TestBetaExtensionsMessage {
+  optional string floating_str_field = 101;
+}

+ 212 - 0
js/compatibility_tests/v3.1.0/testbinary.proto

@@ -0,0 +1,212 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// LINT: ALLOW_GROUPS
+
+syntax = "proto2";
+
+
+package jspb.test;
+
+// These types are borrowed from `unittest.proto` in the protobuf tree. We want
+// to ensure that the binary-format support will handle all field types
+// properly.
+message TestAllTypes {
+  optional    int32 optional_int32    =  1;
+  optional    int64 optional_int64    =  2;
+  optional   uint32 optional_uint32   =  3;
+  optional   uint64 optional_uint64   =  4;
+  optional   sint32 optional_sint32   =  5;
+  optional   sint64 optional_sint64   =  6;
+  optional  fixed32 optional_fixed32  =  7;
+  optional  fixed64 optional_fixed64  =  8;
+  optional sfixed32 optional_sfixed32 =  9;
+  optional sfixed64 optional_sfixed64 = 10;
+  optional    float optional_float    = 11;
+  optional   double optional_double   = 12;
+  optional     bool optional_bool     = 13;
+  optional   string optional_string   = 14;
+  optional    bytes optional_bytes    = 15;
+  optional group OptionalGroup = 16 {
+    optional int32 a = 17;
+  }
+
+  optional ForeignMessage                       optional_foreign_message = 19;
+  optional ForeignEnum                          optional_foreign_enum    = 22;
+
+  // Repeated
+  repeated    int32 repeated_int32    = 31;
+  repeated    int64 repeated_int64    = 32;
+  repeated   uint32 repeated_uint32   = 33;
+  repeated   uint64 repeated_uint64   = 34;
+  repeated   sint32 repeated_sint32   = 35;
+  repeated   sint64 repeated_sint64   = 36;
+  repeated  fixed32 repeated_fixed32  = 37;
+  repeated  fixed64 repeated_fixed64  = 38;
+  repeated sfixed32 repeated_sfixed32 = 39;
+  repeated sfixed64 repeated_sfixed64 = 40;
+  repeated    float repeated_float    = 41;
+  repeated   double repeated_double   = 42;
+  repeated     bool repeated_bool     = 43;
+  repeated   string repeated_string   = 44;
+  repeated    bytes repeated_bytes    = 45;
+
+  repeated group RepeatedGroup = 46 {
+    optional int32 a = 47;
+  }
+
+  repeated ForeignMessage                       repeated_foreign_message = 49;
+  repeated ForeignEnum                          repeated_foreign_enum    = 52;
+
+  // Packed repeated
+  repeated    int32 packed_repeated_int32    = 61 [packed=true];
+  repeated    int64 packed_repeated_int64    = 62 [packed=true];
+  repeated   uint32 packed_repeated_uint32   = 63 [packed=true];
+  repeated   uint64 packed_repeated_uint64   = 64 [packed=true];
+  repeated   sint32 packed_repeated_sint32   = 65 [packed=true];
+  repeated   sint64 packed_repeated_sint64   = 66 [packed=true];
+  repeated  fixed32 packed_repeated_fixed32  = 67 [packed=true];
+  repeated  fixed64 packed_repeated_fixed64  = 68 [packed=true];
+  repeated sfixed32 packed_repeated_sfixed32 = 69 [packed=true];
+  repeated sfixed64 packed_repeated_sfixed64 = 70 [packed=true];
+  repeated    float packed_repeated_float    = 71 [packed=true];
+  repeated   double packed_repeated_double   = 72 [packed=true];
+  repeated     bool packed_repeated_bool     = 73 [packed=true];
+
+  oneof oneof_field {
+    uint32 oneof_uint32 = 111;
+    ForeignMessage oneof_foreign_message = 112;
+    string oneof_string = 113;
+    bytes oneof_bytes = 114;
+  }
+
+}
+
+message ForeignMessage {
+  optional int32 c = 1;
+}
+
+enum ForeignEnum {
+  FOREIGN_FOO = 4;
+  FOREIGN_BAR = 5;
+  FOREIGN_BAZ = 6;
+}
+
+message TestExtendable {
+  extensions 1 to max;
+}
+
+message ExtendsWithMessage {
+  extend TestExtendable {
+    optional ExtendsWithMessage optional_extension = 19;
+    repeated ExtendsWithMessage repeated_extension = 49;
+  }
+  optional int32 foo = 1;
+}
+
+extend TestExtendable {
+  optional    int32 extend_optional_int32    =  1;
+  optional    int64 extend_optional_int64    =  2;
+  optional   uint32 extend_optional_uint32   =  3;
+  optional   uint64 extend_optional_uint64   =  4;
+  optional   sint32 extend_optional_sint32   =  5;
+  optional   sint64 extend_optional_sint64   =  6;
+  optional  fixed32 extend_optional_fixed32  =  7;
+  optional  fixed64 extend_optional_fixed64  =  8;
+  optional sfixed32 extend_optional_sfixed32 =  9;
+  optional sfixed64 extend_optional_sfixed64 = 10;
+  optional    float extend_optional_float    = 11;
+  optional   double extend_optional_double   = 12;
+  optional     bool extend_optional_bool     = 13;
+  optional   string extend_optional_string   = 14;
+  optional    bytes extend_optional_bytes    = 15;
+  optional ForeignEnum extend_optional_foreign_enum    = 22;
+
+  repeated    int32 extend_repeated_int32    = 31;
+  repeated    int64 extend_repeated_int64    = 32;
+  repeated   uint32 extend_repeated_uint32   = 33;
+  repeated   uint64 extend_repeated_uint64   = 34;
+  repeated   sint32 extend_repeated_sint32   = 35;
+  repeated   sint64 extend_repeated_sint64   = 36;
+  repeated  fixed32 extend_repeated_fixed32  = 37;
+  repeated  fixed64 extend_repeated_fixed64  = 38;
+  repeated sfixed32 extend_repeated_sfixed32 = 39;
+  repeated sfixed64 extend_repeated_sfixed64 = 40;
+  repeated    float extend_repeated_float    = 41;
+  repeated   double extend_repeated_double   = 42;
+  repeated     bool extend_repeated_bool     = 43;
+  repeated   string extend_repeated_string   = 44;
+  repeated    bytes extend_repeated_bytes    = 45;
+  repeated ForeignEnum extend_repeated_foreign_enum    = 52;
+
+  repeated    int32 extend_packed_repeated_int32    = 61 [packed=true];
+  repeated    int64 extend_packed_repeated_int64    = 62 [packed=true];
+  repeated   uint32 extend_packed_repeated_uint32   = 63 [packed=true];
+  repeated   uint64 extend_packed_repeated_uint64   = 64 [packed=true];
+  repeated   sint32 extend_packed_repeated_sint32   = 65 [packed=true];
+  repeated   sint64 extend_packed_repeated_sint64   = 66 [packed=true];
+  repeated  fixed32 extend_packed_repeated_fixed32  = 67 [packed=true];
+  repeated  fixed64 extend_packed_repeated_fixed64  = 68 [packed=true];
+  repeated sfixed32 extend_packed_repeated_sfixed32 = 69 [packed=true];
+  repeated sfixed64 extend_packed_repeated_sfixed64 = 70 [packed=true];
+  repeated    float extend_packed_repeated_float    = 71 [packed=true];
+  repeated   double extend_packed_repeated_double   = 72 [packed=true];
+  repeated     bool extend_packed_repeated_bool     = 73 [packed=true];
+  repeated ForeignEnum extend_packed_repeated_foreign_enum    = 82
+      [packed=true];
+
+}
+
+message TestMapFields {
+  map<string, string> map_string_string = 1;
+  map<string, int32> map_string_int32 = 2;
+  map<string, int64> map_string_int64 = 3;
+  map<string, bool> map_string_bool = 4;
+  map<string, double> map_string_double = 5;
+  map<string, MapValueEnum> map_string_enum = 6;
+  map<string, MapValueMessage> map_string_msg = 7;
+
+  map<int32, string> map_int32_string = 8;
+  map<int64, string> map_int64_string = 9;
+  map<bool, string> map_bool_string = 10;
+
+  optional TestMapFields test_map_fields = 11;
+  map<string, TestMapFields> map_string_testmapfields = 12;
+}
+
+enum MapValueEnum {
+  MAP_VALUE_FOO = 0;
+  MAP_VALUE_BAR = 1;
+  MAP_VALUE_BAZ = 2;
+}
+
+message MapValueMessage {
+  optional int32 foo = 1;
+}

+ 34 - 0
js/compatibility_tests/v3.1.0/testempty.proto

@@ -0,0 +1,34 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+package javatests.com.google.apps.jspb;
+

+ 53 - 33
js/message.js

@@ -41,7 +41,6 @@ goog.provide('jspb.Message');
 goog.require('goog.array');
 goog.require('goog.array');
 goog.require('goog.asserts');
 goog.require('goog.asserts');
 goog.require('goog.crypt.base64');
 goog.require('goog.crypt.base64');
-goog.require('goog.json');
 goog.require('jspb.Map');
 goog.require('jspb.Map');
 
 
 // Not needed in compilation units that have no protos with xids.
 // Not needed in compilation units that have no protos with xids.
@@ -192,8 +191,8 @@ goog.define('jspb.Message.GENERATE_FROM_OBJECT', !goog.DISALLOW_TEST_ONLY_CODE);
 
 
 /**
 /**
  * @define {boolean} Whether to generate toString methods for objects. Turn
  * @define {boolean} Whether to generate toString methods for objects. Turn
- *     this off if you do use toString in your project and want to trim it from
- *     compiled JS.
+ *     this off if you do not use toString in your project and want to trim it
+ *     from the compiled JS.
  */
  */
 goog.define('jspb.Message.GENERATE_TO_STRING', true);
 goog.define('jspb.Message.GENERATE_TO_STRING', true);
 
 
@@ -351,7 +350,7 @@ jspb.Message.initialize = function(
   // which would otherwise go unused.
   // which would otherwise go unused.
   msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0;
   msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0;
   msg.array = data;
   msg.array = data;
-  jspb.Message.materializeExtensionObject_(msg, suggestedPivot);
+  jspb.Message.initPivotAndExtensionObject_(msg, suggestedPivot);
   msg.convertedFloatingPointFields_ = {};
   msg.convertedFloatingPointFields_ = {};
 
 
   if (repeatedFields) {
   if (repeatedFields) {
@@ -364,6 +363,7 @@ jspb.Message.initialize = function(
                 jspb.Message.EMPTY_LIST_SENTINEL_ :
                 jspb.Message.EMPTY_LIST_SENTINEL_ :
                 []);
                 []);
       } else {
       } else {
+        jspb.Message.maybeInitEmptyExtensionObject_(msg);
         msg.extensionObject_[fieldNumber] =
         msg.extensionObject_[fieldNumber] =
             msg.extensionObject_[fieldNumber] ||
             msg.extensionObject_[fieldNumber] ||
             (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ?
             (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ?
@@ -408,17 +408,16 @@ jspb.Message.isArray_ = function(o) {
 
 
 
 
 /**
 /**
- * Ensures that the array contains an extension object if necessary.
  * If the array contains an extension object in its last position, then the
  * If the array contains an extension object in its last position, then the
- * object is kept in place and its position is used as the pivot.  If not, then
- * create an extension object using suggestedPivot.  If suggestedPivot is -1,
- * we don't have an extension object at all, in which case all fields are stored
- * in the array.
+ * object is kept in place and its position is used as the pivot.  If not,
+ * decides the pivot of the message based on suggestedPivot without
+ * materializing the extension object.
+ *
  * @param {!jspb.Message} msg The JsPb proto to modify.
  * @param {!jspb.Message} msg The JsPb proto to modify.
  * @param {number} suggestedPivot See description for initialize().
  * @param {number} suggestedPivot See description for initialize().
  * @private
  * @private
  */
  */
-jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) {
+jspb.Message.initPivotAndExtensionObject_ = function(msg, suggestedPivot) {
   if (msg.array.length) {
   if (msg.array.length) {
     var foundIndex = msg.array.length - 1;
     var foundIndex = msg.array.length - 1;
     var obj = msg.array[foundIndex];
     var obj = msg.array[foundIndex];
@@ -434,26 +433,17 @@ jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) {
       return;
       return;
     }
     }
   }
   }
-  // This complexity exists because we keep all extension fields in the
-  // extensionObject_ regardless of proto field number. Changing this would
-  // simplify the code here, but it would require changing the serialization
-  // format from the server, which is not backwards compatible.
-  // TODO(jshneier): Should we just treat extension fields the same as
-  // non-extension fields, and select whether they appear in the object or in
-  // the array purely based on tag number? This would allow simplifying all the
-  // get/setExtension logic, but it would require the breaking change described
-  // above.
+
   if (suggestedPivot > -1) {
   if (suggestedPivot > -1) {
     msg.pivot_ = suggestedPivot;
     msg.pivot_ = suggestedPivot;
-    var pivotIndex = jspb.Message.getIndex_(msg, suggestedPivot);
-    if (!jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS) {
-      msg.extensionObject_ = msg.array[pivotIndex] = {};
-    } else {
-      // Initialize to null to avoid changing the shape of the proto when it
-      // gets eventually set.
-      msg.extensionObject_ = null;
-    }
+    // Avoid changing the shape of the proto with an empty extension object by
+    // deferring the materialization of the extension object until the first
+    // time a field set into it (may be due to getting a repeated proto field
+    // from it, in which case a new empty array is set into it at first).
+    msg.extensionObject_ = null;
   } else {
   } else {
+    // suggestedPivot is -1, which means that we don't have an extension object
+    // at all, in which case all fields are stored in the array.
     msg.pivot_ = Number.MAX_VALUE;
     msg.pivot_ = Number.MAX_VALUE;
   }
   }
 };
 };
@@ -513,7 +503,7 @@ jspb.Message.toObjectExtension = function(proto, obj, extensions,
   for (var fieldNumber in extensions) {
   for (var fieldNumber in extensions) {
     var fieldInfo = extensions[fieldNumber];
     var fieldInfo = extensions[fieldNumber];
     var value = getExtensionFn.call(proto, fieldInfo);
     var value = getExtensionFn.call(proto, fieldInfo);
-    if (goog.isDefAndNotNull(value)) {
+    if (value != null) {
       for (var name in fieldInfo.fieldName) {
       for (var name in fieldInfo.fieldName) {
         if (fieldInfo.fieldName.hasOwnProperty(name)) {
         if (fieldInfo.fieldName.hasOwnProperty(name)) {
           break; // the compiled field name
           break; // the compiled field name
@@ -557,7 +547,7 @@ jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions,
                       'without binary serialization support');
                       'without binary serialization support');
     }
     }
     var value = getExtensionFn.call(proto, fieldInfo);
     var value = getExtensionFn.call(proto, fieldInfo);
-    if (goog.isDefAndNotNull(value)) {
+    if (value != null) {
       if (fieldInfo.isMessageType()) {
       if (fieldInfo.isMessageType()) {
         // If the message type of the extension was generated without binary
         // If the message type of the extension was generated without binary
         // support, there may not be a binary message serializer function, and
         // support, there may not be a binary message serializer function, and
@@ -647,6 +637,9 @@ jspb.Message.getField = function(msg, fieldNumber) {
     }
     }
     return val;
     return val;
   } else {
   } else {
+    if (!msg.extensionObject_) {
+      return undefined;
+    }
     var val = msg.extensionObject_[fieldNumber];
     var val = msg.extensionObject_[fieldNumber];
     if (val === jspb.Message.EMPTY_LIST_SENTINEL_) {
     if (val === jspb.Message.EMPTY_LIST_SENTINEL_) {
       return msg.extensionObject_[fieldNumber] = [];
       return msg.extensionObject_[fieldNumber] = [];
@@ -656,6 +649,32 @@ jspb.Message.getField = function(msg, fieldNumber) {
 };
 };
 
 
 
 
+/**
+ * Gets the value of a non-extension repeated field.
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {number} fieldNumber The field number.
+ * @return {!Array}
+ * The field's value.
+ * @protected
+ */
+jspb.Message.getRepeatedField = function(msg, fieldNumber) {
+  if (fieldNumber < msg.pivot_) {
+    var index = jspb.Message.getIndex_(msg, fieldNumber);
+    var val = msg.array[index];
+    if (val === jspb.Message.EMPTY_LIST_SENTINEL_) {
+      return msg.array[index] = [];
+    }
+    return val;
+  }
+
+  var val = msg.extensionObject_[fieldNumber];
+  if (val === jspb.Message.EMPTY_LIST_SENTINEL_) {
+    return msg.extensionObject_[fieldNumber] = [];
+  }
+  return val;
+};
+
+
 /**
 /**
  * Gets the value of an optional float or double field.
  * Gets the value of an optional float or double field.
  * @param {!jspb.Message} msg A jspb proto.
  * @param {!jspb.Message} msg A jspb proto.
@@ -678,7 +697,7 @@ jspb.Message.getOptionalFloatingPointField = function(msg, fieldNumber) {
  * @protected
  * @protected
  */
  */
 jspb.Message.getRepeatedFloatingPointField = function(msg, fieldNumber) {
 jspb.Message.getRepeatedFloatingPointField = function(msg, fieldNumber) {
-  var values = jspb.Message.getField(msg, fieldNumber);
+  var values = jspb.Message.getRepeatedField(msg, fieldNumber);
   if (!msg.convertedFloatingPointFields_) {
   if (!msg.convertedFloatingPointFields_) {
     msg.convertedFloatingPointFields_ = {};
     msg.convertedFloatingPointFields_ = {};
   }
   }
@@ -864,6 +883,7 @@ jspb.Message.setField = function(msg, fieldNumber, value) {
   if (fieldNumber < msg.pivot_) {
   if (fieldNumber < msg.pivot_) {
     msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = value;
     msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = value;
   } else {
   } else {
+    jspb.Message.maybeInitEmptyExtensionObject_(msg);
     msg.extensionObject_[fieldNumber] = value;
     msg.extensionObject_[fieldNumber] = value;
   }
   }
 };
 };
@@ -878,7 +898,7 @@ jspb.Message.setField = function(msg, fieldNumber, value) {
  * @protected
  * @protected
  */
  */
 jspb.Message.addToRepeatedField = function(msg, fieldNumber, value, opt_index) {
 jspb.Message.addToRepeatedField = function(msg, fieldNumber, value, opt_index) {
-  var arr = jspb.Message.getField(msg, fieldNumber);
+  var arr = jspb.Message.getRepeatedField(msg, fieldNumber);
   if (opt_index != undefined) {
   if (opt_index != undefined) {
     arr.splice(opt_index, 0, value);
     arr.splice(opt_index, 0, value);
   } else {
   } else {
@@ -1006,7 +1026,7 @@ jspb.Message.wrapRepeatedField_ = function(msg, ctor, fieldNumber) {
     msg.wrappers_ = {};
     msg.wrappers_ = {};
   }
   }
   if (!msg.wrappers_[fieldNumber]) {
   if (!msg.wrappers_[fieldNumber]) {
-    var data = jspb.Message.getField(msg, fieldNumber);
+    var data = jspb.Message.getRepeatedField(msg, fieldNumber);
     for (var wrappers = [], i = 0; i < data.length; i++) {
     for (var wrappers = [], i = 0; i < data.length; i++) {
       wrappers[i] = new ctor(data[i]);
       wrappers[i] = new ctor(data[i]);
     }
     }
@@ -1101,7 +1121,7 @@ jspb.Message.addToRepeatedWrapperField = function(
     wrapperArray = msg.wrappers_[fieldNumber] = [];
     wrapperArray = msg.wrappers_[fieldNumber] = [];
   }
   }
   var insertedValue = value ? value : new ctor();
   var insertedValue = value ? value : new ctor();
-  var array = jspb.Message.getField(msg, fieldNumber);
+  var array = jspb.Message.getRepeatedField(msg, fieldNumber);
   if (index != undefined) {
   if (index != undefined) {
     wrapperArray.splice(index, 0, insertedValue);
     wrapperArray.splice(index, 0, insertedValue);
     array.splice(index, 0, insertedValue.toArray());
     array.splice(index, 0, insertedValue.toArray());

+ 2 - 14
js/message_test.js

@@ -33,6 +33,7 @@
 goog.setTestOnly();
 goog.setTestOnly();
 
 
 goog.require('goog.json');
 goog.require('goog.json');
+goog.require('goog.string');
 goog.require('goog.testing.asserts');
 goog.require('goog.testing.asserts');
 goog.require('goog.userAgent');
 goog.require('goog.userAgent');
 
 
@@ -83,8 +84,6 @@ goog.require('proto.jspb.test.ExtensionMessage');
 goog.require('proto.jspb.test.TestExtensionsMessage');
 goog.require('proto.jspb.test.TestExtensionsMessage');
 
 
 
 
-
-
 describe('Message test suite', function() {
 describe('Message test suite', function() {
   it('testEmptyProto', function() {
   it('testEmptyProto', function() {
     var empty1 = new proto.jspb.test.Empty([]);
     var empty1 = new proto.jspb.test.Empty([]);
@@ -273,12 +272,6 @@ describe('Message test suite', function() {
     assertFalse(response.hasEnumField());
     assertFalse(response.hasEnumField());
   });
   });
 
 
-  it('testMessageRegistration', /** @suppress {visibility} */ function() {
-    // goog.require(SomeResponse) will include its library, which will in
-    // turn add SomeResponse to the message registry.
-    assertEquals(jspb.Message.registry_['res'], proto.jspb.test.SomeResponse);
-  });
-
   it('testClearFields', function() {
   it('testClearFields', function() {
     var data = ['str', true, [11], [[22], [33]], ['s1', 's2']];
     var data = ['str', true, [11], [[22], [33]], ['s1', 's2']];
     var foo = new proto.jspb.test.OptionalFields(data);
     var foo = new proto.jspb.test.OptionalFields(data);
@@ -661,12 +654,7 @@ describe('Message test suite', function() {
 
 
   it('testInitialization_emptyArray', function() {
   it('testInitialization_emptyArray', function() {
     var msg = new proto.jspb.test.HasExtensions([]);
     var msg = new proto.jspb.test.HasExtensions([]);
-    if (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS) {
-      assertArrayEquals([], msg.toArray());
-    } else {
-      // Extension object is created past all regular fields.
-      assertArrayEquals([,,, {}], msg.toArray());
-    }
+    assertArrayEquals([], msg.toArray());
   });
   });
 
 
   it('testInitialization_justExtensionObject', function() {
   it('testInitialization_justExtensionObject', function() {

+ 1 - 1
js/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "google-protobuf",
   "name": "google-protobuf",
-  "version": "3.3.2",
+  "version": "3.4.0",
   "description": "Protocol Buffers for JavaScript",
   "description": "Protocol Buffers for JavaScript",
   "main": "google-protobuf.js",
   "main": "google-protobuf.js",
   "files": [
   "files": [

+ 1 - 0
js/proto3_test.proto

@@ -86,4 +86,5 @@ enum Proto3Enum {
   PROTO3_FOO = 0;
   PROTO3_FOO = 0;
   PROTO3_BAR = 1;
   PROTO3_BAR = 1;
   PROTO3_BAZ = 2;
   PROTO3_BAZ = 2;
+  MSG_PROTO3_BAH = 3;
 }
 }

+ 8 - 0
js/test.proto

@@ -235,6 +235,13 @@ message TestEndsWithBytes {
 }
 }
 
 
 
 
+message Int64Types {
+  optional int64 int64_normal = 1 [jstype=JS_NORMAL];
+  optional sint64 int64_string = 2 [jstype=JS_STRING];
+  optional uint64 int64_number = 3 [jstype=JS_NUMBER];
+
+}
+
 message TestMapFieldsNoBinary {
 message TestMapFieldsNoBinary {
 
 
   map<string, string> map_string_string = 1;
   map<string, string> map_string_string = 1;
@@ -271,3 +278,4 @@ message Deeply {
     }
     }
   }
   }
 }
 }
+

+ 19 - 0
m4/ax_cxx_compile_stdcxx.m4

@@ -180,6 +180,8 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
 
 
 #else
 #else
 
 
+#include <utility>
+
 namespace cxx11
 namespace cxx11
 {
 {
 
 
@@ -445,6 +447,23 @@ namespace cxx11
 
 
   }
   }
 
 
+  namespace test_std_move_and_forward
+  {
+    struct message {};
+    char foo(message&) { return '\0'; }
+    int foo(message&&) { return 0; }
+
+    template<typename Arg, typename RT>
+    void check(Arg&& arg, RT rt) {
+      static_assert(sizeof(rt) == sizeof(foo(std::forward<Arg>(arg))), "");
+    }
+    void test() {
+      message a;
+      check(a, char());
+      check(std::move(a), int());
+    }
+  }
+
 }  // namespace cxx11
 }  // namespace cxx11
 
 
 #endif  // __cplusplus >= 201103L
 #endif  // __cplusplus >= 201103L

+ 10 - 0
objectivec/google/protobuf/Any.pbobjc.h

@@ -87,6 +87,16 @@ typedef GPB_ENUM(GPBAny_FieldNumber) {
  *       any.Unpack(foo)
  *       any.Unpack(foo)
  *       ...
  *       ...
  *
  *
+ *  Example 4: Pack and unpack a message in Go
+ *
+ *      foo := &pb.Foo{...}
+ *      any, err := ptypes.MarshalAny(foo)
+ *      ...
+ *      foo := &pb.Foo{}
+ *      if err := ptypes.UnmarshalAny(any, foo); err != nil {
+ *        ...
+ *      }
+ *
  * The pack methods provided by protobuf library will by default use
  * The pack methods provided by protobuf library will by default use
  * 'type.googleapis.com/full.type.name' as the type URL and the unpack
  * 'type.googleapis.com/full.type.name' as the type URL and the unpack
  * methods only use the fully qualified type name after the last '/'
  * methods only use the fully qualified type name after the last '/'

+ 29 - 21
objectivec/google/protobuf/Api.pbobjc.h

@@ -63,34 +63,41 @@ typedef GPB_ENUM(GPBApi_FieldNumber) {
 };
 };
 
 
 /**
 /**
- * Api is a light-weight descriptor for a protocol buffer service.
+ * Api is a light-weight descriptor for an API Interface.
+ *
+ * Interfaces are also described as "protocol buffer services" in some contexts,
+ * such as by the "service" keyword in a .proto file, but they are different
+ * from API Services, which represent a concrete implementation of an interface
+ * as opposed to simply a description of methods and bindings. They are also
+ * sometimes simply referred to as "APIs" in other contexts, such as the name of
+ * this message itself. See https://cloud.google.com/apis/design/glossary for
+ * detailed terminology.
  **/
  **/
 @interface GPBApi : GPBMessage
 @interface GPBApi : GPBMessage
 
 
 /**
 /**
- * The fully qualified name of this api, including package name
- * followed by the api's simple name.
+ * The fully qualified name of this interface, including package name
+ * followed by the interface's simple name.
  **/
  **/
 @property(nonatomic, readwrite, copy, null_resettable) NSString *name;
 @property(nonatomic, readwrite, copy, null_resettable) NSString *name;
 
 
-/** The methods of this api, in unspecified order. */
+/** The methods of this interface, in unspecified order. */
 @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMethod*> *methodsArray;
 @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMethod*> *methodsArray;
 /** The number of items in @c methodsArray without causing the array to be created. */
 /** The number of items in @c methodsArray without causing the array to be created. */
 @property(nonatomic, readonly) NSUInteger methodsArray_Count;
 @property(nonatomic, readonly) NSUInteger methodsArray_Count;
 
 
-/** Any metadata attached to the API. */
+/** Any metadata attached to the interface. */
 @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
 @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
 /** The number of items in @c optionsArray without causing the array to be created. */
 /** The number of items in @c optionsArray without causing the array to be created. */
 @property(nonatomic, readonly) NSUInteger optionsArray_Count;
 @property(nonatomic, readonly) NSUInteger optionsArray_Count;
 
 
 /**
 /**
- * A version string for this api. If specified, must have the form
- * `major-version.minor-version`, as in `1.10`. If the minor version
- * is omitted, it defaults to zero. If the entire version field is
- * empty, the major version is derived from the package name, as
- * outlined below. If the field is not empty, the version in the
- * package name will be verified to be consistent with what is
- * provided here.
+ * A version string for this interface. If specified, must have the form
+ * `major-version.minor-version`, as in `1.10`. If the minor version is
+ * omitted, it defaults to zero. If the entire version field is empty, the
+ * major version is derived from the package name, as outlined below. If the
+ * field is not empty, the version in the package name will be verified to be
+ * consistent with what is provided here.
  *
  *
  * The versioning schema uses [semantic
  * The versioning schema uses [semantic
  * versioning](http://semver.org) where the major version number
  * versioning](http://semver.org) where the major version number
@@ -100,10 +107,10 @@ typedef GPB_ENUM(GPBApi_FieldNumber) {
  * chosen based on the product plan.
  * chosen based on the product plan.
  *
  *
  * The major version is also reflected in the package name of the
  * The major version is also reflected in the package name of the
- * API, which must end in `v<major-version>`, as in
+ * interface, which must end in `v<major-version>`, as in
  * `google.feature.v1`. For major versions 0 and 1, the suffix can
  * `google.feature.v1`. For major versions 0 and 1, the suffix can
  * be omitted. Zero major versions must only be used for
  * be omitted. Zero major versions must only be used for
- * experimental, none-GA apis.
+ * experimental, non-GA interfaces.
  **/
  **/
 @property(nonatomic, readwrite, copy, null_resettable) NSString *version;
 @property(nonatomic, readwrite, copy, null_resettable) NSString *version;
 
 
@@ -115,7 +122,7 @@ typedef GPB_ENUM(GPBApi_FieldNumber) {
 /** Test to see if @c sourceContext has been set. */
 /** Test to see if @c sourceContext has been set. */
 @property(nonatomic, readwrite) BOOL hasSourceContext;
 @property(nonatomic, readwrite) BOOL hasSourceContext;
 
 
-/** Included APIs. See [Mixin][]. */
+/** Included interfaces. See [Mixin][]. */
 @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMixin*> *mixinsArray;
 @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMixin*> *mixinsArray;
 /** The number of items in @c mixinsArray without causing the array to be created. */
 /** The number of items in @c mixinsArray without causing the array to be created. */
 @property(nonatomic, readonly) NSUInteger mixinsArray_Count;
 @property(nonatomic, readonly) NSUInteger mixinsArray_Count;
@@ -150,7 +157,7 @@ typedef GPB_ENUM(GPBMethod_FieldNumber) {
 };
 };
 
 
 /**
 /**
- * Method represents a method of an api.
+ * Method represents a method of an API interface.
  **/
  **/
 @interface GPBMethod : GPBMessage
 @interface GPBMethod : GPBMessage
 
 
@@ -199,9 +206,9 @@ typedef GPB_ENUM(GPBMixin_FieldNumber) {
 };
 };
 
 
 /**
 /**
- * Declares an API to be included in this API. The including API must
- * redeclare all the methods from the included API, but documentation
- * and options are inherited as follows:
+ * Declares an API Interface to be included in this interface. The including
+ * interface must redeclare all the methods from the included interface, but
+ * documentation and options are inherited as follows:
  *
  *
  * - If after comment and whitespace stripping, the documentation
  * - If after comment and whitespace stripping, the documentation
  *   string of the redeclared method is empty, it will be inherited
  *   string of the redeclared method is empty, it will be inherited
@@ -213,7 +220,8 @@ typedef GPB_ENUM(GPBMixin_FieldNumber) {
  *
  *
  * - If an http annotation is inherited, the path pattern will be
  * - If an http annotation is inherited, the path pattern will be
  *   modified as follows. Any version prefix will be replaced by the
  *   modified as follows. Any version prefix will be replaced by the
- *   version of the including API plus the [root][] path if specified.
+ *   version of the including interface plus the [root][] path if
+ *   specified.
  *
  *
  * Example of a simple mixin:
  * Example of a simple mixin:
  *
  *
@@ -279,7 +287,7 @@ typedef GPB_ENUM(GPBMixin_FieldNumber) {
  **/
  **/
 @interface GPBMixin : GPBMessage
 @interface GPBMixin : GPBMessage
 
 
-/** The fully qualified name of the API which is included. */
+/** The fully qualified name of the interface which is included. */
 @property(nonatomic, readwrite, copy, null_resettable) NSString *name;
 @property(nonatomic, readwrite, copy, null_resettable) NSString *name;
 
 
 /**
 /**

+ 18 - 2
php/ext/google/protobuf/package.xml

@@ -13,8 +13,8 @@
  <date>2017-01-13</date>
  <date>2017-01-13</date>
  <time>16:06:07</time>
  <time>16:06:07</time>
  <version>
  <version>
-  <release>3.3.2</release>
-  <api>3.3.0</api>
+  <release>3.4.0</release>
+  <api>3.4.0</api>
  </version>
  </version>
  <stability>
  <stability>
   <release>stable</release>
   <release>stable</release>
@@ -133,6 +133,22 @@ GA release.
    <time>15:33:07</time>
    <time>15:33:07</time>
    <license uri="https://opensource.org/licenses/BSD-3-Clause">3-Clause BSD License</license>
    <license uri="https://opensource.org/licenses/BSD-3-Clause">3-Clause BSD License</license>
    <notes>
    <notes>
+GA release.
+   </notes>
+  </release>
+  <release>
+   <version>
+    <release>3.4.0</release>
+    <api>3.4.0</api>
+   </version>
+   <stability>
+    <release>stable</release>
+    <api>stable</api>
+   </stability>
+   <date>2017-08-16</date>
+   <time>15:33:07</time>
+   <license uri="https://opensource.org/licenses/BSD-3-Clause">3-Clause BSD License</license>
+   <notes>
 GA release.
 GA release.
    </notes>
    </notes>
   </release>
   </release>

+ 1 - 1
php/ext/google/protobuf/protobuf.h

@@ -37,7 +37,7 @@
 #include "upb.h"
 #include "upb.h"
 
 
 #define PHP_PROTOBUF_EXTNAME "protobuf"
 #define PHP_PROTOBUF_EXTNAME "protobuf"
-#define PHP_PROTOBUF_VERSION "3.3.2"
+#define PHP_PROTOBUF_VERSION "3.4.0"
 
 
 #define MAX_LENGTH_OF_INT64 20
 #define MAX_LENGTH_OF_INT64 20
 #define SIZEOF_INT64 8
 #define SIZEOF_INT64 8

+ 5 - 0
php/src/GPBMetadata/Google/Protobuf/Internal/Descriptor.php

@@ -49,6 +49,7 @@ class Descriptor
         $pool->addMessage('google.protobuf.internal.DescriptorProto.ExtensionRange', \Google\Protobuf\Internal\DescriptorProto_ExtensionRange::class)
         $pool->addMessage('google.protobuf.internal.DescriptorProto.ExtensionRange', \Google\Protobuf\Internal\DescriptorProto_ExtensionRange::class)
             ->optional('start', \Google\Protobuf\Internal\GPBType::INT32, 1)
             ->optional('start', \Google\Protobuf\Internal\GPBType::INT32, 1)
             ->optional('end', \Google\Protobuf\Internal\GPBType::INT32, 2)
             ->optional('end', \Google\Protobuf\Internal\GPBType::INT32, 2)
+            ->optional('options', \Google\Protobuf\Internal\GPBType::MESSAGE, 3, 'google.protobuf.internal.ExtensionRangeOptions')
             ->finalizeToPool();
             ->finalizeToPool();
 
 
         $pool->addMessage('google.protobuf.internal.DescriptorProto.ReservedRange', \Google\Protobuf\Internal\DescriptorProto_ReservedRange::class)
         $pool->addMessage('google.protobuf.internal.DescriptorProto.ReservedRange', \Google\Protobuf\Internal\DescriptorProto_ReservedRange::class)
@@ -56,6 +57,10 @@ class Descriptor
             ->optional('end', \Google\Protobuf\Internal\GPBType::INT32, 2)
             ->optional('end', \Google\Protobuf\Internal\GPBType::INT32, 2)
             ->finalizeToPool();
             ->finalizeToPool();
 
 
+        $pool->addMessage('google.protobuf.internal.ExtensionRangeOptions', \Google\Protobuf\Internal\ExtensionRangeOptions::class)
+            ->repeated('uninterpreted_option', \Google\Protobuf\Internal\GPBType::MESSAGE, 999, 'google.protobuf.internal.UninterpretedOption')
+            ->finalizeToPool();
+
         $pool->addMessage('google.protobuf.internal.FieldDescriptorProto', \Google\Protobuf\Internal\FieldDescriptorProto::class)
         $pool->addMessage('google.protobuf.internal.FieldDescriptorProto', \Google\Protobuf\Internal\FieldDescriptorProto::class)
             ->optional('name', \Google\Protobuf\Internal\GPBType::STRING, 1)
             ->optional('name', \Google\Protobuf\Internal\GPBType::STRING, 1)
             ->optional('number', \Google\Protobuf\Internal\GPBType::INT32, 3)
             ->optional('number', \Google\Protobuf\Internal\GPBType::INT32, 3)

+ 33 - 0
php/src/Google/Protobuf/Internal/DescriptorProto_ExtensionRange.php

@@ -25,6 +25,11 @@ class DescriptorProto_ExtensionRange extends \Google\Protobuf\Internal\Message
      */
      */
     private $end = 0;
     private $end = 0;
     private $has_end = false;
     private $has_end = false;
+    /**
+     * Generated from protobuf field <code>optional .google.protobuf.ExtensionRangeOptions options = 3;</code>
+     */
+    private $options = null;
+    private $has_options = false;
 
 
     public function __construct() {
     public function __construct() {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
@@ -87,5 +92,33 @@ class DescriptorProto_ExtensionRange extends \Google\Protobuf\Internal\Message
         return $this->has_end;
         return $this->has_end;
     }
     }
 
 
+    /**
+     * Generated from protobuf field <code>optional .google.protobuf.ExtensionRangeOptions options = 3;</code>
+     * @return \Google\Protobuf\Internal\ExtensionRangeOptions
+     */
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * Generated from protobuf field <code>optional .google.protobuf.ExtensionRangeOptions options = 3;</code>
+     * @param \Google\Protobuf\Internal\ExtensionRangeOptions $var
+     * @return $this
+     */
+    public function setOptions($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Internal\ExtensionRangeOptions::class);
+        $this->options = $var;
+        $this->has_options = true;
+
+        return $this;
+    }
+
+    public function hasOptions()
+    {
+        return $this->has_options;
+    }
+
 }
 }
 
 

+ 24 - 21
php/src/Google/Protobuf/Internal/FieldOptions.php

@@ -39,13 +39,14 @@ class FieldOptions extends \Google\Protobuf\Internal\Message
     /**
     /**
      * The jstype option determines the JavaScript type used for values of the
      * The jstype option determines the JavaScript type used for values of the
      * field.  The option is permitted only for 64 bit integral and fixed types
      * field.  The option is permitted only for 64 bit integral and fixed types
-     * (int64, uint64, sint64, fixed64, sfixed64).  By default these types are
-     * represented as JavaScript strings.  This avoids loss of precision that can
-     * happen when a large value is converted to a floating point JavaScript
-     * numbers.  Specifying JS_NUMBER for the jstype causes the generated
-     * JavaScript code to use the JavaScript "number" type instead of strings.
-     * This option is an enum to permit additional types to be added,
-     * e.g. goog.math.Integer.
+     * (int64, uint64, sint64, fixed64, sfixed64).  A field with jstype JS_STRING
+     * is represented as JavaScript string, which avoids loss of precision that
+     * can happen when a large value is converted to a floating point JavaScript.
+     * Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
+     * use the JavaScript "number" type.  The behavior of the default option
+     * JS_NORMAL is implementation dependent.
+     * This option is an enum to permit additional types to be added, e.g.
+     * goog.math.Integer.
      *
      *
      * Generated from protobuf field <code>optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL];</code>
      * Generated from protobuf field <code>optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL];</code>
      */
      */
@@ -192,13 +193,14 @@ class FieldOptions extends \Google\Protobuf\Internal\Message
     /**
     /**
      * The jstype option determines the JavaScript type used for values of the
      * The jstype option determines the JavaScript type used for values of the
      * field.  The option is permitted only for 64 bit integral and fixed types
      * field.  The option is permitted only for 64 bit integral and fixed types
-     * (int64, uint64, sint64, fixed64, sfixed64).  By default these types are
-     * represented as JavaScript strings.  This avoids loss of precision that can
-     * happen when a large value is converted to a floating point JavaScript
-     * numbers.  Specifying JS_NUMBER for the jstype causes the generated
-     * JavaScript code to use the JavaScript "number" type instead of strings.
-     * This option is an enum to permit additional types to be added,
-     * e.g. goog.math.Integer.
+     * (int64, uint64, sint64, fixed64, sfixed64).  A field with jstype JS_STRING
+     * is represented as JavaScript string, which avoids loss of precision that
+     * can happen when a large value is converted to a floating point JavaScript.
+     * Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
+     * use the JavaScript "number" type.  The behavior of the default option
+     * JS_NORMAL is implementation dependent.
+     * This option is an enum to permit additional types to be added, e.g.
+     * goog.math.Integer.
      *
      *
      * Generated from protobuf field <code>optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL];</code>
      * Generated from protobuf field <code>optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL];</code>
      * @return int
      * @return int
@@ -211,13 +213,14 @@ class FieldOptions extends \Google\Protobuf\Internal\Message
     /**
     /**
      * The jstype option determines the JavaScript type used for values of the
      * The jstype option determines the JavaScript type used for values of the
      * field.  The option is permitted only for 64 bit integral and fixed types
      * field.  The option is permitted only for 64 bit integral and fixed types
-     * (int64, uint64, sint64, fixed64, sfixed64).  By default these types are
-     * represented as JavaScript strings.  This avoids loss of precision that can
-     * happen when a large value is converted to a floating point JavaScript
-     * numbers.  Specifying JS_NUMBER for the jstype causes the generated
-     * JavaScript code to use the JavaScript "number" type instead of strings.
-     * This option is an enum to permit additional types to be added,
-     * e.g. goog.math.Integer.
+     * (int64, uint64, sint64, fixed64, sfixed64).  A field with jstype JS_STRING
+     * is represented as JavaScript string, which avoids loss of precision that
+     * can happen when a large value is converted to a floating point JavaScript.
+     * Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
+     * use the JavaScript "number" type.  The behavior of the default option
+     * JS_NORMAL is implementation dependent.
+     * This option is an enum to permit additional types to be added, e.g.
+     * goog.math.Integer.
      *
      *
      * Generated from protobuf field <code>optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL];</code>
      * Generated from protobuf field <code>optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL];</code>
      * @param int $var
      * @param int $var

+ 6 - 0
protoc-artifacts/README.md

@@ -72,6 +72,7 @@ deployment for all platforms. Currently the following platforms are supported:
 - Windows (x86_32 and x86_64) with
 - Windows (x86_32 and x86_64) with
  - Cygwin64 with MinGW compilers (x86_64)
  - Cygwin64 with MinGW compilers (x86_64)
  - MSYS with MinGW32 (x86_32)
  - MSYS with MinGW32 (x86_32)
+ - Cross compile in Linux with MinGW-w64 (x86_32, x86_64)
 - MacOSX (x86_32 and x86_64)
 - MacOSX (x86_32 and x86_64)
 
 
 As for MSYS2/MinGW64 for Windows: protoc will build, but it insists on
 As for MSYS2/MinGW64 for Windows: protoc will build, but it insists on
@@ -98,6 +99,9 @@ $ mvn clean deploy -P release -Dstaging.repository=comgoogle-123
 A 32-bit artifact can be deployed from a 64-bit host with
 A 32-bit artifact can be deployed from a 64-bit host with
 ``-Dos.detected.arch=x86_32``
 ``-Dos.detected.arch=x86_32``
 
 
+A windows artifact can be deployed from a linux machine with
+``-Dos.detected.name=windows``
+
 When you have done deployment for all platforms, go to
 When you have done deployment for all platforms, go to
 https://oss.sonatype.org/#stagingRepositories, verify that the staging
 https://oss.sonatype.org/#stagingRepositories, verify that the staging
 repository has all the binaries, close and release this repository.
 repository has all the binaries, close and release this repository.
@@ -173,5 +177,7 @@ We have successfully built artifacts on the following environments:
  - Centos 6.6 (within Docker 1.6.1)
  - Centos 6.6 (within Docker 1.6.1)
  - Ubuntu 14.04.2 64-bit
  - Ubuntu 14.04.2 64-bit
 - Windows x86_32: MSYS with ``mingw32-gcc-g++ 4.8.1-4`` on Windows 7 64-bit
 - Windows x86_32: MSYS with ``mingw32-gcc-g++ 4.8.1-4`` on Windows 7 64-bit
+- Windows x86_32: Cross compile with ``i686-w64-mingw32-g++ 4.8.2`` on Ubuntu 14.04.2 64-bit
 - Windows x86_64: Cygwin64 with ``mingw64-x86_64-gcc-g++ 4.8.3-1`` on Windows 7 64-bit
 - Windows x86_64: Cygwin64 with ``mingw64-x86_64-gcc-g++ 4.8.3-1`` on Windows 7 64-bit
+- Windows x86_64: Cross compile with ``x86_64-w64-mingw32-g++ 4.8.2`` on Ubuntu 14.04.2 64-bit
 - Mac OS X x86_32 and x86_64: Mac OS X 10.9.5
 - Mac OS X x86_32 and x86_64: Mac OS X 10.9.5

+ 5 - 4
protoc-artifacts/build-protoc.sh

@@ -185,8 +185,6 @@ elif [[ "$(uname)" == Linux* ]]; then
     fi
     fi
   elif [[ "$OS" == windows ]]; then
   elif [[ "$OS" == windows ]]; then
     # Cross-compilation for Windows
     # Cross-compilation for Windows
-    # TODO(zhangkun83) MinGW 64 always adds dependency on libwinpthread-1.dll,
-    # which is undesirable for repository deployment.
     CONFIGURE_ARGS="$CONFIGURE_ARGS"
     CONFIGURE_ARGS="$CONFIGURE_ARGS"
     if [[ "$ARCH" == x86_64 ]]; then
     if [[ "$ARCH" == x86_64 ]]; then
       CONFIGURE_ARGS="$CONFIGURE_ARGS --host=x86_64-w64-mingw32"
       CONFIGURE_ARGS="$CONFIGURE_ARGS --host=x86_64-w64-mingw32"
@@ -215,8 +213,11 @@ fi
 
 
 # Statically link libgcc and libstdc++.
 # Statically link libgcc and libstdc++.
 # -s to produce stripped binary.
 # -s to produce stripped binary.
-# And they don't work under Mac.
-if [[ "$OS" != osx ]]; then
+if [[ "$OS" == windows ]]; then
+  # Also static link libpthread required by mingw64
+  LDFLAGS="$LDFLAGS -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread -s"
+elif [[ "$OS" != osx ]]; then
+  # And they don't work under Mac.
   LDFLAGS="$LDFLAGS -static-libgcc -static-libstdc++ -s"
   LDFLAGS="$LDFLAGS -static-libgcc -static-libstdc++ -s"
 fi
 fi
 
 

Some files were not shown because too many files changed in this diff