diff --git a/Makefile.am b/Makefile.am
index 4763d9495aadc..0d8fe10354ddf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -605,6 +605,8 @@ php_EXTRA_DIST= \
php/tests/test.proto \
php/tests/test.pb.php \
php/tests/memory_leak_test.php \
+ php/tests/google/protobuf/empty.pb.php \
+ php/tests/well_known_test.php \
php/README.md \
php/ext/google/protobuf/utf8.h \
php/ext/google/protobuf/message.c \
@@ -712,6 +714,7 @@ python_EXTRA_DIST= \
python/google/protobuf/pyext/repeated_composite_container.h \
python/google/protobuf/pyext/repeated_scalar_container.cc \
python/google/protobuf/pyext/repeated_scalar_container.h \
+ python/google/protobuf/pyext/safe_numerics.h \
python/google/protobuf/pyext/scoped_pyobject_ptr.h \
python/google/protobuf/reflection.py \
python/google/protobuf/service.py \
diff --git a/cmake/libprotoc.cmake b/cmake/libprotoc.cmake
index 71c3494ccb7c2..2d238449c2251 100644
--- a/cmake/libprotoc.cmake
+++ b/cmake/libprotoc.cmake
@@ -72,6 +72,7 @@ set(libprotoc_files
${protobuf_source_dir}/src/google/protobuf/compiler/javanano/javanano_message_field.cc
${protobuf_source_dir}/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
${protobuf_source_dir}/src/google/protobuf/compiler/js/js_generator.cc
+ ${protobuf_source_dir}/src/google/protobuf/compiler/js/well_known_types_embed.cc
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_enum.cc
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_extension.cc
diff --git a/conformance/ConformanceJava.java b/conformance/ConformanceJava.java
index 43787ffcb2740..24d206cbbaf09 100644
--- a/conformance/ConformanceJava.java
+++ b/conformance/ConformanceJava.java
@@ -1,8 +1,11 @@
-
+import com.google.protobuf.ByteString;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.conformance.Conformance;
-import com.google.protobuf.util.JsonFormat;
import com.google.protobuf.util.JsonFormat.TypeRegistry;
-import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.util.JsonFormat;
+import java.io.IOException;
+import java.nio.ByteBuffer;
class ConformanceJava {
private int testCount = 0;
@@ -47,13 +50,182 @@ private void writeLittleEndianIntToStdout(int val) throws Exception {
writeToStdout(buf);
}
+ private enum BinaryDecoder {
+ BYTE_STRING_DECODER() {
+ @Override
+ public Conformance.TestAllTypes parse(ByteString bytes)
+ throws InvalidProtocolBufferException {
+ return Conformance.TestAllTypes.parseFrom(bytes);
+ }
+ },
+ BYTE_ARRAY_DECODER() {
+ @Override
+ public Conformance.TestAllTypes parse(ByteString bytes)
+ throws InvalidProtocolBufferException {
+ return Conformance.TestAllTypes.parseFrom(bytes.toByteArray());
+ }
+ },
+ ARRAY_BYTE_BUFFER_DECODER() {
+ @Override
+ public Conformance.TestAllTypes parse(ByteString bytes)
+ throws InvalidProtocolBufferException {
+ ByteBuffer buffer = ByteBuffer.allocate(bytes.size());
+ bytes.copyTo(buffer);
+ buffer.flip();
+ try {
+ return Conformance.TestAllTypes.parseFrom(CodedInputStream.newInstance(buffer));
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new RuntimeException(
+ "ByteString based ByteBuffer should not throw IOException.", e);
+ }
+ }
+ },
+ READONLY_ARRAY_BYTE_BUFFER_DECODER() {
+ @Override
+ public Conformance.TestAllTypes parse(ByteString bytes)
+ throws InvalidProtocolBufferException {
+ try {
+ return Conformance.TestAllTypes.parseFrom(
+ CodedInputStream.newInstance(bytes.asReadOnlyByteBuffer()));
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new RuntimeException(
+ "ByteString based ByteBuffer should not throw IOException.", e);
+ }
+ }
+ },
+ DIRECT_BYTE_BUFFER_DECODER() {
+ @Override
+ public Conformance.TestAllTypes parse(ByteString bytes)
+ throws InvalidProtocolBufferException {
+ ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.size());
+ bytes.copyTo(buffer);
+ buffer.flip();
+ try {
+ return Conformance.TestAllTypes.parseFrom(CodedInputStream.newInstance(buffer));
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new RuntimeException(
+ "ByteString based ByteBuffer should not throw IOException.", e);
+ }
+ }
+ },
+ READONLY_DIRECT_BYTE_BUFFER_DECODER() {
+ @Override
+ public Conformance.TestAllTypes parse(ByteString bytes)
+ throws InvalidProtocolBufferException {
+ ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.size());
+ bytes.copyTo(buffer);
+ buffer.flip();
+ try {
+ return Conformance.TestAllTypes.parseFrom(
+ CodedInputStream.newInstance(buffer.asReadOnlyBuffer()));
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new RuntimeException(
+ "ByteString based ByteBuffer should not throw IOException.", e);
+ }
+ }
+ },
+ INPUT_STREAM_DECODER() {
+ @Override
+ public Conformance.TestAllTypes parse(ByteString bytes)
+ throws InvalidProtocolBufferException {
+ try {
+ return Conformance.TestAllTypes.parseFrom(bytes.newInput());
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new RuntimeException(
+ "ByteString based InputStream should not throw IOException.", e);
+ }
+ }
+ };
+
+ public abstract Conformance.TestAllTypes parse(ByteString bytes)
+ throws InvalidProtocolBufferException;
+ }
+
+ private Conformance.TestAllTypes parseBinary(ByteString bytes)
+ throws InvalidProtocolBufferException {
+ Conformance.TestAllTypes[] messages =
+ new Conformance.TestAllTypes[BinaryDecoder.values().length];
+ InvalidProtocolBufferException[] exceptions =
+ new InvalidProtocolBufferException[BinaryDecoder.values().length];
+
+ boolean hasMessage = false;
+ boolean hasException = false;
+ for (int i = 0; i < BinaryDecoder.values().length; ++i) {
+ try {
+ messages[i] = BinaryDecoder.values()[i].parse(bytes);
+ hasMessage = true;
+ } catch (InvalidProtocolBufferException e) {
+ exceptions[i] = e;
+ hasException = true;
+ }
+ }
+
+ if (hasMessage && hasException) {
+ StringBuilder sb =
+ new StringBuilder("Binary decoders disagreed on whether the payload was valid.\n");
+ for (int i = 0; i < BinaryDecoder.values().length; ++i) {
+ sb.append(BinaryDecoder.values()[i].name());
+ if (messages[i] != null) {
+ sb.append(" accepted the payload.\n");
+ } else {
+ sb.append(" rejected the payload.\n");
+ }
+ }
+ throw new RuntimeException(sb.toString());
+ }
+
+ if (hasException) {
+ // We do not check if exceptions are equal. Different implementations may return different
+ // exception messages. Throw an arbitrary one out instead.
+ throw exceptions[0];
+ }
+
+ // Fast path comparing all the messages with the first message, assuming equality being
+ // symmetric and transitive.
+ boolean allEqual = true;
+ for (int i = 1; i < messages.length; ++i) {
+ if (!messages[0].equals(messages[i])) {
+ allEqual = false;
+ break;
+ }
+ }
+
+ // Slow path: compare and find out all unequal pairs.
+ if (!allEqual) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < messages.length - 1; ++i) {
+ for (int j = i + 1; j < messages.length; ++j) {
+ if (!messages[i].equals(messages[j])) {
+ sb.append(BinaryDecoder.values()[i].name())
+ .append(" and ")
+ .append(BinaryDecoder.values()[j].name())
+ .append(" parsed the payload differently.\n");
+ }
+ }
+ }
+ throw new RuntimeException(sb.toString());
+ }
+
+ return messages[0];
+ }
+
private Conformance.ConformanceResponse doTest(Conformance.ConformanceRequest request) {
Conformance.TestAllTypes testMessage;
switch (request.getPayloadCase()) {
case PROTOBUF_PAYLOAD: {
try {
- testMessage = Conformance.TestAllTypes.parseFrom(request.getProtobufPayload());
+ testMessage = parseBinary(request.getProtobufPayload());
} catch (InvalidProtocolBufferException e) {
return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build();
}
diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc
index b74122c6ed3ac..e709ac8d47de3 100644
--- a/conformance/conformance_test.cc
+++ b/conformance/conformance_test.cc
@@ -685,7 +685,7 @@ bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner,
R"({
"fieldname1": 1,
"fieldName2": 2,
- "fieldName3": 3,
+ "FieldName3": 3,
"fieldName4": 4
})",
R"(
@@ -725,12 +725,12 @@ bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner,
RunValidJsonTest(
"FieldNameWithDoubleUnderscores", RECOMMENDED,
R"({
- "fieldName13": 13,
- "fieldName14": 14,
+ "FieldName13": 13,
+ "FieldName14": 14,
"fieldName15": 15,
"fieldName16": 16,
"fieldName17": 17,
- "fieldName18": 18
+ "FieldName18": 18
})",
R"(
__field_name13: 13
@@ -873,21 +873,19 @@ bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner,
"optionalNestedMessage": {a: 1},
"optional_nested_message": {}
})");
- // NOTE: The spec for JSON support is still being sorted out, these may not
- // all be correct.
// Serializers should use lowerCamelCase by default.
RunValidJsonTestWithValidator(
"FieldNameInLowerCamelCase", REQUIRED,
R"({
"fieldname1": 1,
"fieldName2": 2,
- "fieldName3": 3,
+ "FieldName3": 3,
"fieldName4": 4
})",
[](const Json::Value& value) {
return value.isMember("fieldname1") &&
value.isMember("fieldName2") &&
- value.isMember("fieldName3") &&
+ value.isMember("FieldName3") &&
value.isMember("fieldName4");
});
RunValidJsonTestWithValidator(
@@ -921,20 +919,20 @@ bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner,
RunValidJsonTestWithValidator(
"FieldNameWithDoubleUnderscores", RECOMMENDED,
R"({
- "fieldName13": 13,
- "fieldName14": 14,
+ "FieldName13": 13,
+ "FieldName14": 14,
"fieldName15": 15,
"fieldName16": 16,
"fieldName17": 17,
- "fieldName18": 18
+ "FieldName18": 18
})",
[](const Json::Value& value) {
- return value.isMember("fieldName13") &&
- value.isMember("fieldName14") &&
+ return value.isMember("FieldName13") &&
+ value.isMember("FieldName14") &&
value.isMember("fieldName15") &&
value.isMember("fieldName16") &&
value.isMember("fieldName17") &&
- value.isMember("fieldName18");
+ value.isMember("FieldName18");
});
// Integer fields.
diff --git a/conformance/failure_list_cpp.txt b/conformance/failure_list_cpp.txt
index 508be5062b43c..8cfd74da6e0ea 100644
--- a/conformance/failure_list_cpp.txt
+++ b/conformance/failure_list_cpp.txt
@@ -17,9 +17,6 @@ Recommended.JsonInput.FieldNameDuplicate
Recommended.JsonInput.FieldNameDuplicateDifferentCasing1
Recommended.JsonInput.FieldNameDuplicateDifferentCasing2
Recommended.JsonInput.FieldNameNotQuoted
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.JsonOutput
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.ProtobufOutput
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.Validator
Recommended.JsonInput.MapFieldValueIsNull
Recommended.JsonInput.RepeatedFieldMessageElementIsNull
Recommended.JsonInput.RepeatedFieldPrimitiveElementIsNull
@@ -35,10 +32,6 @@ Recommended.JsonInput.TrailingCommaInAnObject
Recommended.JsonInput.TrailingCommaInAnObjectWithNewlines
Recommended.JsonInput.TrailingCommaInAnObjectWithSpace
Recommended.JsonInput.TrailingCommaInAnObjectWithSpaceCommaSpace
-Required.JsonInput.DoubleFieldTooSmall
-Required.JsonInput.FieldNameInLowerCamelCase.Validator
-Required.JsonInput.FieldNameInSnakeCase.JsonOutput
-Required.JsonInput.FieldNameInSnakeCase.ProtobufOutput
Required.ProtobufInput.PrematureEofBeforeKnownRepeatedValue.MESSAGE
Required.ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE
Required.ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE
diff --git a/conformance/failure_list_csharp.txt b/conformance/failure_list_csharp.txt
index a43519cde9085..e69de29bb2d1d 100644
--- a/conformance/failure_list_csharp.txt
+++ b/conformance/failure_list_csharp.txt
@@ -1,6 +0,0 @@
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.JsonOutput
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.ProtobufOutput
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.Validator
-Required.JsonInput.FieldNameInLowerCamelCase.Validator
-Required.JsonInput.FieldNameInSnakeCase.JsonOutput
-Required.JsonInput.FieldNameInSnakeCase.ProtobufOutput
diff --git a/conformance/failure_list_java.txt b/conformance/failure_list_java.txt
index c9007d68d8f49..632940efc8e42 100644
--- a/conformance/failure_list_java.txt
+++ b/conformance/failure_list_java.txt
@@ -20,9 +20,6 @@ Recommended.JsonInput.DoubleFieldNegativeInfinityNotQuoted
Recommended.JsonInput.FieldMaskInvalidCharacter
Recommended.JsonInput.FieldNameDuplicate
Recommended.JsonInput.FieldNameNotQuoted
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.JsonOutput
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.ProtobufOutput
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.Validator
Recommended.JsonInput.FloatFieldInfinityNotQuoted
Recommended.JsonInput.FloatFieldNanNotQuoted
Recommended.JsonInput.FloatFieldNegativeInfinityNotQuoted
@@ -38,12 +35,11 @@ Recommended.JsonInput.StringFieldUnpairedLowSurrogate
Recommended.JsonInput.Uint32MapFieldKeyNotQuoted
Recommended.JsonInput.Uint64MapFieldKeyNotQuoted
Required.JsonInput.EnumFieldNotQuoted
-Required.JsonInput.FieldNameInLowerCamelCase.Validator
-Required.JsonInput.FieldNameInSnakeCase.JsonOutput
-Required.JsonInput.FieldNameInSnakeCase.ProtobufOutput
Required.JsonInput.Int32FieldLeadingZero
Required.JsonInput.Int32FieldNegativeWithLeadingZero
Required.JsonInput.Int32FieldPlusSign
Required.JsonInput.RepeatedFieldWrongElementTypeExpectingStringsGotBool
Required.JsonInput.RepeatedFieldWrongElementTypeExpectingStringsGotInt
Required.JsonInput.StringFieldNotAString
+Required.ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE
+Required.ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE
diff --git a/conformance/failure_list_python.txt b/conformance/failure_list_python.txt
index 0498519927439..9d556a036b391 100644
--- a/conformance/failure_list_python.txt
+++ b/conformance/failure_list_python.txt
@@ -1,18 +1,12 @@
Recommended.JsonInput.DoubleFieldInfinityNotQuoted
Recommended.JsonInput.DoubleFieldNanNotQuoted
Recommended.JsonInput.DoubleFieldNegativeInfinityNotQuoted
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.JsonOutput
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.ProtobufOutput
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.Validator
Recommended.JsonInput.FloatFieldInfinityNotQuoted
Recommended.JsonInput.FloatFieldNanNotQuoted
Recommended.JsonInput.FloatFieldNegativeInfinityNotQuoted
Required.JsonInput.BytesFieldInvalidBase64Characters
Required.JsonInput.DoubleFieldTooSmall
Required.JsonInput.EnumFieldUnknownValue.Validator
-Required.JsonInput.FieldNameInLowerCamelCase.Validator
-Required.JsonInput.FieldNameInSnakeCase.JsonOutput
-Required.JsonInput.FieldNameInSnakeCase.ProtobufOutput
Required.JsonInput.FloatFieldTooLarge
Required.JsonInput.FloatFieldTooSmall
Required.JsonInput.RepeatedFieldWrongElementTypeExpectingIntegersGotBool
diff --git a/conformance/failure_list_python_cpp.txt b/conformance/failure_list_python_cpp.txt
index f3958f5ff7c90..92404d2f6a7e1 100644
--- a/conformance/failure_list_python_cpp.txt
+++ b/conformance/failure_list_python_cpp.txt
@@ -10,18 +10,12 @@
Recommended.JsonInput.DoubleFieldInfinityNotQuoted
Recommended.JsonInput.DoubleFieldNanNotQuoted
Recommended.JsonInput.DoubleFieldNegativeInfinityNotQuoted
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.JsonOutput
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.ProtobufOutput
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.Validator
Recommended.JsonInput.FloatFieldInfinityNotQuoted
Recommended.JsonInput.FloatFieldNanNotQuoted
Recommended.JsonInput.FloatFieldNegativeInfinityNotQuoted
Required.JsonInput.BytesFieldInvalidBase64Characters
Required.JsonInput.DoubleFieldTooSmall
Required.JsonInput.EnumFieldUnknownValue.Validator
-Required.JsonInput.FieldNameInLowerCamelCase.Validator
-Required.JsonInput.FieldNameInSnakeCase.JsonOutput
-Required.JsonInput.FieldNameInSnakeCase.ProtobufOutput
Required.JsonInput.FloatFieldTooLarge
Required.JsonInput.FloatFieldTooSmall
Required.JsonInput.RepeatedFieldWrongElementTypeExpectingIntegersGotBool
diff --git a/conformance/failure_list_ruby.txt b/conformance/failure_list_ruby.txt
index dbd556977cf02..1de6c4390c39d 100644
--- a/conformance/failure_list_ruby.txt
+++ b/conformance/failure_list_ruby.txt
@@ -7,9 +7,6 @@ Recommended.JsonInput.DurationHas3FractionalDigits.Validator
Recommended.JsonInput.DurationHas6FractionalDigits.Validator
Recommended.JsonInput.DurationHas9FractionalDigits.Validator
Recommended.JsonInput.DurationHasZeroFractionalDigit.Validator
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.JsonOutput
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.ProtobufOutput
-Recommended.JsonInput.FieldNameWithDoubleUnderscores.Validator
Recommended.JsonInput.Int64FieldBeString.Validator
Recommended.JsonInput.OneofZeroDouble.JsonOutput
Recommended.JsonInput.OneofZeroDouble.ProtobufOutput
@@ -80,9 +77,6 @@ Required.JsonInput.EnumFieldNumericValueZero.ProtobufOutput
Required.JsonInput.EnumFieldUnknownValue.Validator
Required.JsonInput.FieldMask.JsonOutput
Required.JsonInput.FieldMask.ProtobufOutput
-Required.JsonInput.FieldNameInLowerCamelCase.Validator
-Required.JsonInput.FieldNameInSnakeCase.JsonOutput
-Required.JsonInput.FieldNameInSnakeCase.ProtobufOutput
Required.JsonInput.FloatFieldInfinity.JsonOutput
Required.JsonInput.FloatFieldInfinity.ProtobufOutput
Required.JsonInput.FloatFieldNan.JsonOutput
diff --git a/csharp/src/Google.Protobuf/Reflection/Descriptor.cs b/csharp/src/Google.Protobuf/Reflection/Descriptor.cs
index 191554694592c..75ceeac4026ce 100644
--- a/csharp/src/Google.Protobuf/Reflection/Descriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/Descriptor.cs
@@ -120,9 +120,13 @@ static DescriptorReflection() {
"COgHEICAgIACInsKDlNlcnZpY2VPcHRpb25zEhkKCmRlcHJlY2F0ZWQYISAB",
"KAg6BWZhbHNlEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdv",
"b2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIi",
- "egoNTWV0aG9kT3B0aW9ucxIZCgpkZXByZWNhdGVkGCEgASgIOgVmYWxzZRJD",
- "ChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9i",
- "dWYuVW5pbnRlcnByZXRlZE9wdGlvbioJCOgHEICAgIACIp4CChNVbmludGVy",
+ "rQIKDU1ldGhvZE9wdGlvbnMSGQoKZGVwcmVjYXRlZBghIAEoCDoFZmFsc2US",
+ "XwoRaWRlbXBvdGVuY3lfbGV2ZWwYIiABKA4yLy5nb29nbGUucHJvdG9idWYu",
+ "TWV0aG9kT3B0aW9ucy5JZGVtcG90ZW5jeUxldmVsOhNJREVNUE9URU5DWV9V",
+ "TktOT1dOEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2ds",
+ "ZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uIlAKEElkZW1wb3RlbmN5",
+ "TGV2ZWwSFwoTSURFTVBPVEVOQ1lfVU5LTk9XThAAEhMKD05PX1NJREVfRUZG",
+ "RUNUUxABEg4KCklERU1QT1RFTlQQAioJCOgHEICAgIACIp4CChNVbmludGVy",
"cHJldGVkT3B0aW9uEjsKBG5hbWUYAiADKAsyLS5nb29nbGUucHJvdG9idWYu",
"VW5pbnRlcnByZXRlZE9wdGlvbi5OYW1lUGFydBIYChBpZGVudGlmaWVyX3Zh",
"bHVlGAMgASgJEhoKEnBvc2l0aXZlX2ludF92YWx1ZRgEIAEoBBIaChJuZWdh",
@@ -160,7 +164,7 @@ static DescriptorReflection() {
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumOptions), global::Google.Protobuf.Reflection.EnumOptions.Parser, new[]{ "AllowAlias", "Deprecated", "UninterpretedOption" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumValueOptions), global::Google.Protobuf.Reflection.EnumValueOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.ServiceOptions), global::Google.Protobuf.Reflection.ServiceOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null),
- new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MethodOptions), global::Google.Protobuf.Reflection.MethodOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MethodOptions), global::Google.Protobuf.Reflection.MethodOptions.Parser, new[]{ "Deprecated", "IdempotencyLevel", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.MethodOptions.Types.IdempotencyLevel) }, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.UninterpretedOption), global::Google.Protobuf.Reflection.UninterpretedOption.Parser, new[]{ "Name", "IdentifierValue", "PositiveIntValue", "NegativeIntValue", "DoubleValue", "StringValue", "AggregateValue" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.UninterpretedOption.Types.NamePart), global::Google.Protobuf.Reflection.UninterpretedOption.Types.NamePart.Parser, new[]{ "NamePart_", "IsExtension" }, null, null, null)}),
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.SourceCodeInfo), global::Google.Protobuf.Reflection.SourceCodeInfo.Parser, new[]{ "Location" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.SourceCodeInfo.Types.Location), global::Google.Protobuf.Reflection.SourceCodeInfo.Types.Location.Parser, new[]{ "Path", "Span", "LeadingComments", "TrailingComments", "LeadingDetachedComments" }, null, null, null)}),
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.GeneratedCodeInfo), global::Google.Protobuf.Reflection.GeneratedCodeInfo.Parser, new[]{ "Annotation" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation), global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation.Parser, new[]{ "Path", "SourceFile", "Begin", "End" }, null, null, null)})
@@ -1790,9 +1794,6 @@ internal enum Label {
///
[pbr::OriginalName("LABEL_OPTIONAL")] Optional = 1,
[pbr::OriginalName("LABEL_REQUIRED")] Required = 2,
- ///
- /// TODO(sanjay): Should we add LABEL_MAP?
- ///
[pbr::OriginalName("LABEL_REPEATED")] Repeated = 3,
}
@@ -4617,6 +4618,7 @@ public MethodOptions() {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public MethodOptions(MethodOptions other) : this() {
deprecated_ = other.deprecated_;
+ idempotencyLevel_ = other.idempotencyLevel_;
uninterpretedOption_ = other.uninterpretedOption_.Clone();
}
@@ -4642,6 +4644,17 @@ public bool Deprecated {
}
}
+ /// Field number for the "idempotency_level" field.
+ public const int IdempotencyLevelFieldNumber = 34;
+ private global::Google.Protobuf.Reflection.MethodOptions.Types.IdempotencyLevel idempotencyLevel_ = 0;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public global::Google.Protobuf.Reflection.MethodOptions.Types.IdempotencyLevel IdempotencyLevel {
+ get { return idempotencyLevel_; }
+ set {
+ idempotencyLevel_ = value;
+ }
+ }
+
/// Field number for the "uninterpreted_option" field.
public const int UninterpretedOptionFieldNumber = 999;
private static readonly pb::FieldCodec _repeated_uninterpretedOption_codec
@@ -4669,6 +4682,7 @@ public bool Equals(MethodOptions other) {
return true;
}
if (Deprecated != other.Deprecated) return false;
+ if (IdempotencyLevel != other.IdempotencyLevel) return false;
if(!uninterpretedOption_.Equals(other.uninterpretedOption_)) return false;
return true;
}
@@ -4677,6 +4691,7 @@ public bool Equals(MethodOptions other) {
public override int GetHashCode() {
int hash = 1;
if (Deprecated != false) hash ^= Deprecated.GetHashCode();
+ if (IdempotencyLevel != 0) hash ^= IdempotencyLevel.GetHashCode();
hash ^= uninterpretedOption_.GetHashCode();
return hash;
}
@@ -4692,6 +4707,10 @@ public void WriteTo(pb::CodedOutputStream output) {
output.WriteRawTag(136, 2);
output.WriteBool(Deprecated);
}
+ if (IdempotencyLevel != 0) {
+ output.WriteRawTag(144, 2);
+ output.WriteEnum((int) IdempotencyLevel);
+ }
uninterpretedOption_.WriteTo(output, _repeated_uninterpretedOption_codec);
}
@@ -4701,6 +4720,9 @@ public int CalculateSize() {
if (Deprecated != false) {
size += 2 + 1;
}
+ if (IdempotencyLevel != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) IdempotencyLevel);
+ }
size += uninterpretedOption_.CalculateSize(_repeated_uninterpretedOption_codec);
return size;
}
@@ -4713,6 +4735,9 @@ public void MergeFrom(MethodOptions other) {
if (other.Deprecated != false) {
Deprecated = other.Deprecated;
}
+ if (other.IdempotencyLevel != 0) {
+ IdempotencyLevel = other.IdempotencyLevel;
+ }
uninterpretedOption_.Add(other.uninterpretedOption_);
}
@@ -4728,6 +4753,10 @@ public void MergeFrom(pb::CodedInputStream input) {
Deprecated = input.ReadBool();
break;
}
+ case 272: {
+ idempotencyLevel_ = (global::Google.Protobuf.Reflection.MethodOptions.Types.IdempotencyLevel) input.ReadEnum();
+ break;
+ }
case 7994: {
uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
break;
@@ -4736,6 +4765,30 @@ public void MergeFrom(pb::CodedInputStream input) {
}
}
+ #region Nested types
+ /// Container for nested types declared in the MethodOptions message type.
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public static partial class Types {
+ ///
+ /// Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
+ /// or neither? HTTP based RPC implementation may choose GET verb for safe
+ /// methods, and PUT verb for idempotent methods instead of the default POST.
+ ///
+ internal enum IdempotencyLevel {
+ [pbr::OriginalName("IDEMPOTENCY_UNKNOWN")] IdempotencyUnknown = 0,
+ ///
+ /// implies idempotent
+ ///
+ [pbr::OriginalName("NO_SIDE_EFFECTS")] NoSideEffects = 1,
+ ///
+ /// idempotent, but may have side effects
+ ///
+ [pbr::OriginalName("IDEMPOTENT")] Idempotent = 2,
+ }
+
+ }
+ #endregion
+
}
///
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
index 234095e996904..f72571d7f376e 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
@@ -35,8 +35,9 @@ static ApiReflection() {
"ChFyZXNwb25zZV90eXBlX3VybBgEIAEoCRIaChJyZXNwb25zZV9zdHJlYW1p",
"bmcYBSABKAgSKAoHb3B0aW9ucxgGIAMoCzIXLmdvb2dsZS5wcm90b2J1Zi5P",
"cHRpb24SJwoGc3ludGF4GAcgASgOMhcuZ29vZ2xlLnByb3RvYnVmLlN5bnRh",
- "eCIjCgVNaXhpbhIMCgRuYW1lGAEgASgJEgwKBHJvb3QYAiABKAlCSAoTY29t",
- "Lmdvb2dsZS5wcm90b2J1ZkIIQXBpUHJvdG9QAaICA0dQQqoCHkdvb2dsZS5Q",
+ "eCIjCgVNaXhpbhIMCgRuYW1lGAEgASgJEgwKBHJvb3QYAiABKAlCdQoTY29t",
+ "Lmdvb2dsZS5wcm90b2J1ZkIIQXBpUHJvdG9QAVorZ29vZ2xlLmdvbGFuZy5v",
+ "cmcvZ2VucHJvdG8vcHJvdG9idWYvYXBpO2FwaaICA0dQQqoCHkdvb2dsZS5Q",
"cm90b2J1Zi5XZWxsS25vd25UeXBlc2IGcHJvdG8z"));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.SourceContextReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TypeReflection.Descriptor, },
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
index c73d6bd822e14..99a8011ef81bf 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
@@ -23,9 +23,11 @@ static FieldMaskReflection() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"CiBnb29nbGUvcHJvdG9idWYvZmllbGRfbWFzay5wcm90bxIPZ29vZ2xlLnBy",
- "b3RvYnVmIhoKCUZpZWxkTWFzaxINCgVwYXRocxgBIAMoCUJOChNjb20uZ29v",
- "Z2xlLnByb3RvYnVmQg5GaWVsZE1hc2tQcm90b1ABogIDR1BCqgIeR29vZ2xl",
- "LlByb3RvYnVmLldlbGxLbm93blR5cGVzYgZwcm90bzM="));
+ "b3RvYnVmIhoKCUZpZWxkTWFzaxINCgVwYXRocxgBIAMoCUKJAQoTY29tLmdv",
+ "b2dsZS5wcm90b2J1ZkIORmllbGRNYXNrUHJvdG9QAVo5Z29vZ2xlLmdvbGFu",
+ "Zy5vcmcvZ2VucHJvdG8vcHJvdG9idWYvZmllbGRfbWFzaztmaWVsZF9tYXNr",
+ "ogIDR1BCqgIeR29vZ2xlLlByb3RvYnVmLldlbGxLbm93blR5cGVzYgZwcm90",
+ "bzM="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs b/csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs
index c3400327556cb..c5713466e4afc 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs
@@ -24,9 +24,10 @@ static SourceContextReflection() {
string.Concat(
"CiRnb29nbGUvcHJvdG9idWYvc291cmNlX2NvbnRleHQucHJvdG8SD2dvb2ds",
"ZS5wcm90b2J1ZiIiCg1Tb3VyY2VDb250ZXh0EhEKCWZpbGVfbmFtZRgBIAEo",
- "CUJSChNjb20uZ29vZ2xlLnByb3RvYnVmQhJTb3VyY2VDb250ZXh0UHJvdG9Q",
- "AaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBlc2IGcHJv",
- "dG8z"));
+ "CUKVAQoTY29tLmdvb2dsZS5wcm90b2J1ZkISU291cmNlQ29udGV4dFByb3Rv",
+ "UAFaQWdvb2dsZS5nb2xhbmcub3JnL2dlbnByb3RvL3Byb3RvYnVmL3NvdXJj",
+ "ZV9jb250ZXh0O3NvdXJjZV9jb250ZXh0ogIDR1BCqgIeR29vZ2xlLlByb3Rv",
+ "YnVmLldlbGxLbm93blR5cGVzYgZwcm90bzM="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
index e6363480efa94..d80735f16d237 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
@@ -54,9 +54,10 @@ static TypeReflection() {
"ASgFEigKB29wdGlvbnMYAyADKAsyFy5nb29nbGUucHJvdG9idWYuT3B0aW9u",
"IjsKBk9wdGlvbhIMCgRuYW1lGAEgASgJEiMKBXZhbHVlGAIgASgLMhQuZ29v",
"Z2xlLnByb3RvYnVmLkFueSouCgZTeW50YXgSEQoNU1lOVEFYX1BST1RPMhAA",
- "EhEKDVNZTlRBWF9QUk9UTzMQAUJMChNjb20uZ29vZ2xlLnByb3RvYnVmQglU",
- "eXBlUHJvdG9QAfgBAaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25v",
- "d25UeXBlc2IGcHJvdG8z"));
+ "EhEKDVNZTlRBWF9QUk9UTzMQAUJ9ChNjb20uZ29vZ2xlLnByb3RvYnVmQglU",
+ "eXBlUHJvdG9QAVovZ29vZ2xlLmdvbGFuZy5vcmcvZ2VucHJvdG8vcHJvdG9i",
+ "dWYvcHR5cGU7cHR5cGX4AQGiAgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2Vs",
+ "bEtub3duVHlwZXNiBnByb3RvMw=="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.AnyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.SourceContextReflection.Descriptor, },
new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.WellKnownTypes.Syntax), }, new pbr::GeneratedClrTypeInfo[] {
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractParser.java b/java/core/src/main/java/com/google/protobuf/AbstractParser.java
index 66b0ee3bd5494..7ff73ba4d1b46 100644
--- a/java/core/src/main/java/com/google/protobuf/AbstractParser.java
+++ b/java/core/src/main/java/com/google/protobuf/AbstractParser.java
@@ -232,7 +232,7 @@ public MessageType parsePartialDelimitedFrom(
}
size = CodedInputStream.readRawVarint32(firstByte, input);
} catch (IOException e) {
- throw new InvalidProtocolBufferException(e.getMessage());
+ throw new InvalidProtocolBufferException(e);
}
InputStream limitedInput = new LimitedInputStream(input, size);
return parsePartialFrom(limitedInput, extensionRegistry);
diff --git a/java/core/src/main/java/com/google/protobuf/ByteString.java b/java/core/src/main/java/com/google/protobuf/ByteString.java
index 5b24976d6d440..99a312096d877 100644
--- a/java/core/src/main/java/com/google/protobuf/ByteString.java
+++ b/java/core/src/main/java/com/google/protobuf/ByteString.java
@@ -51,14 +51,12 @@
import java.util.NoSuchElementException;
/**
- * Immutable sequence of bytes. Substring is supported by sharing the reference
- * to the immutable underlying bytes. Concatenation is likewise supported
- * without copying (long strings) by building a tree of pieces in
- * {@link RopeByteString}.
- *
- * Like {@link String}, the contents of a {@link ByteString} can never be
- * observed to change, not even in the presence of a data race or incorrect
- * API usage in the client code.
+ * Immutable sequence of bytes. Substring is supported by sharing the reference to the immutable
+ * underlying bytes. Concatenation is likewise supported without copying (long strings) by building
+ * a tree of pieces in {@link RopeByteString}.
+ *
+ *
Like {@link String}, the contents of a {@link ByteString} can never be observed to change, not
+ * even in the presence of a data race or incorrect API usage in the client code.
*
* @author crazybob@google.com Bob Lee
* @author kenton@google.com Kenton Varda
@@ -565,7 +563,9 @@ public static ByteString copyFrom(Iterable byteStrings) {
// Create a balanced concatenation of the next "length" elements from the
// iterable.
private static ByteString balancedConcat(Iterator iterator, int length) {
- assert length >= 1;
+ if (length < 1) {
+ throw new IllegalArgumentException(String.format("length (%s) must be >= 1", length));
+ }
ByteString result;
if (length == 1) {
result = iterator.next();
diff --git a/java/core/src/main/java/com/google/protobuf/Descriptors.java b/java/core/src/main/java/com/google/protobuf/Descriptors.java
index cab7118a4a39c..38346f1559fb2 100644
--- a/java/core/src/main/java/com/google/protobuf/Descriptors.java
+++ b/java/core/src/main/java/com/google/protobuf/Descriptors.java
@@ -2123,7 +2123,7 @@ enum SearchFilter {
// Can't happen, because addPackage() only fails when the name
// conflicts with a non-package, but we have not yet added any
// non-packages at this point.
- assert false;
+ throw new AssertionError(e);
}
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/DynamicMessage.java b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java
index c54da67fbb5ac..e6358c3bae341 100644
--- a/java/core/src/main/java/com/google/protobuf/DynamicMessage.java
+++ b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java
@@ -297,7 +297,7 @@ public DynamicMessage parsePartialFrom(
} catch (InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(builder.buildPartial());
} catch (IOException e) {
- throw new InvalidProtocolBufferException(e.getMessage())
+ throw new InvalidProtocolBufferException(e)
.setUnfinishedMessage(builder.buildPartial());
}
return builder.buildPartial();
diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java b/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java
index 1c2e7e6f89e3c..a22a74a03caee 100644
--- a/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java
+++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java
@@ -32,7 +32,6 @@
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
-
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
index 5e4d7739adf25..f3d48d3af34e7 100644
--- a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
+++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
@@ -105,9 +105,9 @@ public static void setEagerlyParseMessageSets(boolean isEagerlyParse) {
/**
* Construct a new, empty instance.
- *
- *
- * This may be an {@code ExtensionRegistry} if the full (non-Lite) proto libraries are available.
+ *
+ *
This may be an {@code ExtensionRegistry} if the full (non-Lite) proto libraries are
+ * available.
*/
public static ExtensionRegistryLite newInstance() {
return ExtensionRegistryFactory.create();
@@ -121,6 +121,7 @@ public static ExtensionRegistryLite getEmptyRegistry() {
return ExtensionRegistryFactory.createEmpty();
}
+
/** Returns an unmodifiable view of the registry. */
public ExtensionRegistryLite getUnmodifiable() {
return new ExtensionRegistryLite(this);
diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
index 214971b101a38..f885b01eba2f0 100644
--- a/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
@@ -39,7 +39,6 @@
import com.google.protobuf.Internal.LongList;
import com.google.protobuf.Internal.ProtobufList;
import com.google.protobuf.WireFormat.FieldType;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectStreamException;
@@ -479,7 +478,6 @@ protected boolean parseUnknownField(
CodedInputStream input,
ExtensionRegistryLite extensionRegistry,
int tag) throws IOException {
- int wireType = WireFormat.getTagWireType(tag);
int fieldNumber = WireFormat.getTagFieldNumber(tag);
// TODO(dweis): How much bytecode would be saved by not requiring the generated code to
@@ -487,6 +485,17 @@ protected boolean parseUnknownField(
GeneratedExtension extension = extensionRegistry.findLiteExtensionByNumber(
defaultInstance, fieldNumber);
+ return parseExtension(input, extensionRegistry, extension, tag, fieldNumber);
+ }
+
+ private boolean parseExtension(
+ CodedInputStream input,
+ ExtensionRegistryLite extensionRegistry,
+ GeneratedExtension, ?> extension,
+ int tag,
+ int fieldNumber)
+ throws IOException {
+ int wireType = WireFormat.getTagWireType(tag);
boolean unknown = false;
boolean packed = false;
if (extension == null) {
@@ -508,7 +517,7 @@ protected boolean parseUnknownField(
if (unknown) { // Unknown field or wrong wire type. Skip.
return parseUnknownField(tag, input);
}
-
+
if (packed) {
int length = input.readRawVarint32();
int limit = input.pushLimit(length);
@@ -587,9 +596,147 @@ protected boolean parseUnknownField(
extension.singularToFieldSetType(value));
}
}
-
return true;
}
+
+ /**
+ * Parse an unknown field or an extension. For use by generated code only.
+ *
+ *
For use by generated code only.
+ *
+ * @return {@code true} unless the tag is an end-group tag.
+ */
+ protected boolean parseUnknownFieldAsMessageSet(
+ MessageType defaultInstance,
+ CodedInputStream input,
+ ExtensionRegistryLite extensionRegistry,
+ int tag)
+ throws IOException {
+
+ if (tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
+ mergeMessageSetExtensionFromCodedStream(defaultInstance, input, extensionRegistry);
+ return true;
+ }
+
+ // TODO(dweis): Do we really want to support non message set wire format in message sets?
+ // Full runtime does... So we do for now.
+ int wireType = WireFormat.getTagWireType(tag);
+ if (wireType == WireFormat.WIRETYPE_LENGTH_DELIMITED) {
+ return parseUnknownField(defaultInstance, input, extensionRegistry, tag);
+ } else {
+ // TODO(dweis): Should we throw on invalid input? Full runtime does not...
+ return input.skipField(tag);
+ }
+ }
+
+ /**
+ * Merges the message set from the input stream; requires message set wire format.
+ *
+ * @param defaultInstance the default instance of the containing message we are parsing in
+ * @param input the stream to parse from
+ * @param extensionRegistry the registry to use when parsing
+ */
+ private void mergeMessageSetExtensionFromCodedStream(
+ MessageType defaultInstance,
+ CodedInputStream input,
+ ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ // The wire format for MessageSet is:
+ // message MessageSet {
+ // repeated group Item = 1 {
+ // required int32 typeId = 2;
+ // required bytes message = 3;
+ // }
+ // }
+ // "typeId" is the extension's field number. The extension can only be
+ // a message type, where "message" contains the encoded bytes of that
+ // message.
+ //
+ // In practice, we will probably never see a MessageSet item in which
+ // the message appears before the type ID, or where either field does not
+ // appear exactly once. However, in theory such cases are valid, so we
+ // should be prepared to accept them.
+
+ int typeId = 0;
+ ByteString rawBytes = null; // If we encounter "message" before "typeId"
+ GeneratedExtension, ?> extension = null;
+
+ // Read bytes from input, if we get it's type first then parse it eagerly,
+ // otherwise we store the raw bytes in a local variable.
+ while (true) {
+ final int tag = input.readTag();
+ if (tag == 0) {
+ break;
+ }
+
+ if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) {
+ typeId = input.readUInt32();
+ if (typeId != 0) {
+ extension = extensionRegistry.findLiteExtensionByNumber(defaultInstance, typeId);
+ }
+
+ } else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) {
+ if (typeId != 0) {
+ if (extension != null) {
+ // We already know the type, so we can parse directly from the
+ // input with no copying. Hooray!
+ eagerlyMergeMessageSetExtension(input, extension, extensionRegistry, typeId);
+ rawBytes = null;
+ continue;
+ }
+ }
+ // We haven't seen a type ID yet or we want parse message lazily.
+ rawBytes = input.readBytes();
+
+ } else { // Unknown tag. Skip it.
+ if (!input.skipField(tag)) {
+ break; // End of group
+ }
+ }
+ }
+ input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG);
+
+ // Process the raw bytes.
+ if (rawBytes != null && typeId != 0) { // Zero is not a valid type ID.
+ if (extension != null) { // We known the type
+ mergeMessageSetExtensionFromBytes(rawBytes, extensionRegistry, extension);
+ } else { // We don't know how to parse this. Ignore it.
+ if (rawBytes != null) {
+ mergeLengthDelimitedField(typeId, rawBytes);
+ }
+ }
+ }
+ }
+
+ private void eagerlyMergeMessageSetExtension(
+ CodedInputStream input,
+ GeneratedExtension, ?> extension,
+ ExtensionRegistryLite extensionRegistry,
+ int typeId)
+ throws IOException {
+ int fieldNumber = typeId;
+ int tag = WireFormat.makeTag(typeId, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ parseExtension(input, extensionRegistry, extension, tag, fieldNumber);
+ }
+
+ private void mergeMessageSetExtensionFromBytes(
+ ByteString rawBytes,
+ ExtensionRegistryLite extensionRegistry,
+ GeneratedExtension, ?> extension)
+ throws IOException {
+ MessageLite.Builder subBuilder = null;
+ MessageLite existingValue = (MessageLite) extensions.getField(extension.descriptor);
+ if (existingValue != null) {
+ subBuilder = existingValue.toBuilder();
+ }
+ if (subBuilder == null) {
+ subBuilder = extension.getMessageDefaultInstance().newBuilderForType();
+ }
+ rawBytes.newCodedInput().readMessage(subBuilder, extensionRegistry);
+ MessageLite value = subBuilder.build();
+
+ extensions.setField(extension.descriptor, extension.singularToFieldSetType(value));
+ }
private void verifyExtensionContainingType(
final GeneratedExtension extension) {
@@ -807,14 +954,6 @@ public final Type getExtension(
return instance.getExtension(extension, index);
}
- // This is implemented here only to work around an apparent bug in the
- // Java compiler and/or build system. See bug #1898463. The mere presence
- // of this dummy clone() implementation makes it go away.
- @Override
- public BuilderType clone() {
- return super.clone();
- }
-
/** Set the value of an extension. */
public final BuilderType setExtension(
final ExtensionLite extension,
diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
index 4cbbd2961100d..2a5d8b50b7f0d 100644
--- a/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
@@ -36,6 +36,16 @@
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
+// In opensource protobuf, we have versioned this GeneratedMessageV3 class to GeneratedMessageV3V3 and
+// in the future may have GeneratedMessageV3V4 etc. This allows us to change some aspects of this
+// class without breaking binary compatibility with old generated code that still subclasses
+// the old GeneratedMessageV3 class. To allow these different GeneratedMessageV3V? classes to
+// interoperate (e.g., a GeneratedMessageV3V3 object has a message extension field whose class
+// type is GeneratedMessageV3V4), these classes still share a common parent class AbstarctMessage
+// and are using the same GeneratedMessage.GeneratedExtension class for extension definitions.
+// Since this class becomes GeneratedMessageV3V? in opensource, we have to add an import here
+// to be able to use GeneratedMessage.GeneratedExtension. The GeneratedExtension definition in
+// this file is also excluded from opensource to avoid conflict.
import com.google.protobuf.GeneratedMessage.GeneratedExtension;
import java.io.IOException;
@@ -1207,14 +1217,6 @@ public BuilderType clear() {
return super.clear();
}
- // This is implemented here only to work around an apparent bug in the
- // Java compiler and/or build system. See bug #1898463. The mere presence
- // of this clone() implementation makes it go away.
- @Override
- public BuilderType clone() {
- return super.clone();
- }
-
private void ensureExtensionsIsMutable() {
if (extensions.isImmutable()) {
extensions = extensions.clone();
@@ -1610,6 +1612,7 @@ static interface ExtensionDescriptorRetriever {
FieldDescriptor getDescriptor();
}
+
// =================================================================
/** Calls Class.getMethod and throws a RuntimeException if it fails. */
@@ -1705,11 +1708,6 @@ public FieldAccessorTable(
initialized = false;
}
- private boolean isMapFieldEnabled(FieldDescriptor field) {
- boolean result = true;
- return result;
- }
-
/**
* Ensures the field accessors are initialized. This method is thread-safe.
*
@@ -1733,7 +1731,7 @@ public FieldAccessorTable ensureFieldAccessorsInitialized(
}
if (field.isRepeated()) {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
- if (field.isMapField() && isMapFieldEnabled(field)) {
+ if (field.isMapField()) {
fields[i] = new MapFieldAccessor(
field, camelCaseNames[i], messageClass, builderClass);
} else {
diff --git a/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java b/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java
index 2febaacecbae0..4b0ba0fd6b7c5 100644
--- a/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java
+++ b/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java
@@ -284,29 +284,8 @@ public void merge(LazyFieldLite other) {
return;
}
- // At this point we have two fully parsed messages. We can't merge directly from one to the
- // other because only generated builder code contains methods to mergeFrom another parsed
- // message. We have to serialize one instance and then merge the bytes into the other. This may
- // drop extensions from one of the messages if one of the values had an extension set on it
- // directly.
- //
- // To mitigate this we prefer serializing a message that has an extension registry, and
- // therefore a chance that all extensions set on it are in that registry.
- //
- // NOTE: The check for other.extensionRegistry not being null must come first because at this
- // point in time if other.extensionRegistry is not null then this.extensionRegistry will not be
- // null either.
- if (other.extensionRegistry != null) {
- setValue(mergeValueAndBytes(this.value, other.toByteString(), other.extensionRegistry));
- return;
- } else if (this.extensionRegistry != null) {
- setValue(mergeValueAndBytes(other.value, this.toByteString(), this.extensionRegistry));
- return;
- } else {
- // All extensions from the other message will be dropped because we have no registry.
- setValue(mergeValueAndBytes(this.value, other.toByteString(), EMPTY_REGISTRY));
- return;
- }
+ // At this point we have two fully parsed messages.
+ setValue(this.value.toBuilder().mergeFrom(other.value).build());
}
/**
diff --git a/java/core/src/main/java/com/google/protobuf/MapEntry.java b/java/core/src/main/java/com/google/protobuf/MapEntry.java
index 4f0351f4813ac..179c3348a1e09 100644
--- a/java/core/src/main/java/com/google/protobuf/MapEntry.java
+++ b/java/core/src/main/java/com/google/protobuf/MapEntry.java
@@ -109,7 +109,7 @@ private MapEntry(
} catch (InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(this);
} catch (IOException e) {
- throw new InvalidProtocolBufferException(e.getMessage()).setUnfinishedMessage(this);
+ throw new InvalidProtocolBufferException(e).setUnfinishedMessage(this);
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java b/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
index 43847651fe7ff..23373ef4cdc77 100644
--- a/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
@@ -95,7 +95,7 @@ private static void reflectivePrintWithIndent(
// Try to reflectively get the value and toString() the field as if it were repeated. This
// only works if the method names have not be proguarded out or renamed.
Method listMethod = nameToNoArgMethod.get("get" + suffix);
- if (listMethod != null) {
+ if (listMethod != null && listMethod.getReturnType().equals(List.class)) {
printField(
buffer,
indent,
@@ -115,7 +115,7 @@ private static void reflectivePrintWithIndent(
// Heuristic to skip bytes based accessors for string fields.
continue;
}
-
+
String camelCase = suffix.substring(0, 1).toLowerCase() + suffix.substring(1);
// Try to reflectively get the value and toString() the field as if it were optional. This
diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java
index 6d33d3a862f4b..49b3504fe75c7 100644
--- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java
+++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java
@@ -1022,7 +1022,7 @@ public UnknownFieldSet parsePartialFrom(
} catch (InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(builder.buildPartial());
} catch (IOException e) {
- throw new InvalidProtocolBufferException(e.getMessage())
+ throw new InvalidProtocolBufferException(e)
.setUnfinishedMessage(builder.buildPartial());
}
return builder.buildPartial();
diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
index 9500f905fcaf5..104f8007b878c 100644
--- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
+++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
@@ -175,6 +175,41 @@ public void writeTo(CodedOutputStream output) throws IOException {
}
}
+ /**
+ * Serializes the set and writes it to {@code output} using {@code MessageSet} wire format.
+ *
+ *
For use by generated code only.
+ */
+ public void writeAsMessageSetTo(CodedOutputStream output) throws IOException {
+ for (int i = 0; i < count; i++) {
+ int fieldNumber = WireFormat.getTagFieldNumber(tags[i]);
+ output.writeRawMessageSetExtension(fieldNumber, (ByteString) objects[i]);
+ }
+ }
+
+ /**
+ * Get the number of bytes required to encode this field, including field
+ * number, using {@code MessageSet} wire format.
+ */
+ public int getSerializedSizeAsMessageSet() {
+ int size = memoizedSerializedSize;
+ if (size != -1) {
+ return size;
+ }
+
+ size = 0;
+ for (int i = 0; i < count; i++) {
+ int tag = tags[i];
+ int fieldNumber = WireFormat.getTagFieldNumber(tag);
+ size += CodedOutputStream.computeRawMessageSetExtensionSize(
+ fieldNumber, (ByteString) objects[i]);
+ }
+
+ memoizedSerializedSize = size;
+
+ return size;
+ }
+
/**
* Get the number of bytes required to encode this set.
*
@@ -268,7 +303,8 @@ final void printWithIndent(StringBuilder buffer, int indent) {
}
}
- private void storeField(int tag, Object value) {
+ // Package private for unsafe experimental runtime.
+ void storeField(int tag, Object value) {
ensureCapacity();
tags[count] = tag;
diff --git a/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java b/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
index efdfdfbe307a1..813fe6bc69688 100644
--- a/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
@@ -31,7 +31,6 @@
package com.google.protobuf;
import static protobuf_unittest.UnittestProto.optionalInt32Extension;
-import static protobuf_unittest.UnittestProto.optionalInt64Extension;
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
@@ -219,29 +218,6 @@ public void testMergeKeepsExtensionsWhenPossible() throws Exception {
assertEquals(messageWithExtensions, field.getValue(TestAllExtensions.getDefaultInstance()));
}
- public void testMergeMightLoseExtensions() throws Exception {
- // Test that we don't know about the extensions when parsing.
- TestAllExtensions message1 =
- TestAllExtensions.newBuilder().setExtension(optionalInt32Extension, 1).build();
- TestAllExtensions message2 =
- TestAllExtensions.newBuilder().setExtension(optionalInt64Extension, 2L).build();
-
- LazyFieldLite field = LazyFieldLite.fromValue(message1);
- field.merge(LazyFieldLite.fromValue(message2));
-
- // We lose the extensions from message 2 because we have to serialize it and then parse it
- // again, using the empty registry this time.
- TestAllExtensions value =
- (TestAllExtensions) field.getValue(TestAllExtensions.getDefaultInstance());
- assertTrue(value.hasExtension(optionalInt32Extension));
- assertEquals(Integer.valueOf(1), value.getExtension(optionalInt32Extension));
- assertFalse(value.hasExtension(optionalInt64Extension));
-
- // The field is still there, it is just unknown.
- assertTrue(value.getUnknownFields()
- .hasField(optionalInt64Extension.getDescriptor().getNumber()));
- }
-
// Help methods.
diff --git a/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java b/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
index c8ca9b6abba43..968ca2065cdab 100644
--- a/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
@@ -101,6 +101,19 @@ public void testSetRepeatedValues() {
assertEquals(119, outer.getRepeatedInner(0).getNum());
assertEquals(122, outer.getRepeatedInner(1).getNum());
}
+
+ public void testRepeatedMutability() throws Exception {
+ LazyMessageLite outer = LazyMessageLite.newBuilder()
+ .addRepeatedInner(LazyInnerMessageLite.newBuilder().setNum(119))
+ .addRepeatedInner(LazyInnerMessageLite.newBuilder().setNum(122))
+ .build();
+
+ outer = LazyMessageLite.parseFrom(outer.toByteArray());
+ try {
+ outer.getRepeatedInnerList().set(1, null);
+ fail();
+ } catch (UnsupportedOperationException expected) {}
+ }
public void testAddAll() {
ArrayList inners = new ArrayList();
diff --git a/java/core/src/test/java/com/google/protobuf/LiteTest.java b/java/core/src/test/java/com/google/protobuf/LiteTest.java
index 254beba1bd7d1..538432f7c1b1e 100644
--- a/java/core/src/test/java/com/google/protobuf/LiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LiteTest.java
@@ -45,6 +45,7 @@
import com.google.protobuf.UnittestLite.TestAllTypesLite.OptionalGroup;
import com.google.protobuf.UnittestLite.TestAllTypesLite.RepeatedGroup;
import com.google.protobuf.UnittestLite.TestAllTypesLiteOrBuilder;
+import com.google.protobuf.UnittestLite.TestHugeFieldNumbersLite;
import com.google.protobuf.UnittestLite.TestNestedExtensionLite;
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Bar;
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.BarPrime;
@@ -1424,6 +1425,12 @@ public void testToStringDefaultInstance() throws Exception {
assertToStringEquals("", TestAllTypesLite.getDefaultInstance());
}
+ public void testToStringScalarFieldsSuffixedWithList() throws Exception {
+ assertToStringEquals("deceptively_named_list: 7", TestAllTypesLite.newBuilder()
+ .setDeceptivelyNamedList(7)
+ .build());
+ }
+
public void testToStringPrimitives() throws Exception {
TestAllTypesLite proto = TestAllTypesLite.newBuilder()
.setOptionalInt32(1)
@@ -2235,6 +2242,25 @@ public void testEquals_oneOfMessages() {
assertFalse(other.equals(mine));
}
+ public void testHugeFieldNumbers() throws InvalidProtocolBufferException {
+ TestHugeFieldNumbersLite message =
+ TestHugeFieldNumbersLite.newBuilder()
+ .setOptionalInt32(1)
+ .addRepeatedInt32(2)
+ .setOptionalEnum(ForeignEnumLite.FOREIGN_LITE_FOO)
+ .setOptionalString("xyz")
+ .setOptionalMessage(ForeignMessageLite.newBuilder().setC(3).build())
+ .build();
+
+ TestHugeFieldNumbersLite parsedMessage =
+ TestHugeFieldNumbersLite.parseFrom(message.toByteArray());
+ assertEquals(1, parsedMessage.getOptionalInt32());
+ assertEquals(2, parsedMessage.getRepeatedInt32(0));
+ assertEquals(ForeignEnumLite.FOREIGN_LITE_FOO, parsedMessage.getOptionalEnum());
+ assertEquals("xyz", parsedMessage.getOptionalString());
+ assertEquals(3, parsedMessage.getOptionalMessage().getC());
+ }
+
private void assertEqualsAndHashCodeAreFalse(Object o1, Object o2) {
assertFalse(o1.equals(o2));
assertFalse(o1.hashCode() == o2.hashCode());
diff --git a/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java b/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
index 8b64fa271a41f..0a14f584177fd 100644
--- a/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
@@ -501,7 +501,6 @@ public void testUnknownEnumValues() throws Exception {
assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue());
}
-
public void testIterationOrder() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
diff --git a/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java b/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java
index 76bd7bd9daf3c..cfe4c4536ea95 100644
--- a/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java
+++ b/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java
@@ -58,26 +58,26 @@ private void setMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 11);
builder.getMutableInt32ToInt32Field().put(2, 22);
builder.getMutableInt32ToInt32Field().put(3, 33);
-
+ //
builder.getMutableInt32ToStringField().put(1, "11");
builder.getMutableInt32ToStringField().put(2, "22");
builder.getMutableInt32ToStringField().put(3, "33");
-
+ //
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
-
+ //
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
-
+ //
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(11).build());
builder.getMutableInt32ToMessageField().put(
2, MessageValue.newBuilder().setValue(22).build());
builder.getMutableInt32ToMessageField().put(
3, MessageValue.newBuilder().setValue(33).build());
-
+ //
builder.getMutableStringToInt32Field().put("1", 11);
builder.getMutableStringToInt32Field().put("2", 22);
builder.getMutableStringToInt32Field().put("3", 33);
@@ -120,6 +120,7 @@ public void testSetMapValues() {
setMapValuesUsingAccessors(usingAccessorsBuilder);
TestMap usingAccessors = usingAccessorsBuilder.build();
assertMapValuesSet(usingAccessors);
+
assertEquals(usingAccessors, usingMutableMap);
}
@@ -169,25 +170,25 @@ private void updateMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 111);
builder.getMutableInt32ToInt32Field().remove(2);
builder.getMutableInt32ToInt32Field().put(4, 44);
-
+ //
builder.getMutableInt32ToStringField().put(1, "111");
builder.getMutableInt32ToStringField().remove(2);
builder.getMutableInt32ToStringField().put(4, "44");
-
+ //
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
builder.getMutableInt32ToBytesField().remove(2);
builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
-
+ //
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().remove(2);
builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
-
+ //
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(111).build());
builder.getMutableInt32ToMessageField().remove(2);
builder.getMutableInt32ToMessageField().put(
4, MessageValue.newBuilder().setValue(44).build());
-
+ //
builder.getMutableStringToInt32Field().put("1", 111);
builder.getMutableStringToInt32Field().remove("2");
builder.getMutableStringToInt32Field().put("4", 44);
@@ -230,8 +231,9 @@ public void testUpdateMapValues() {
setMapValuesUsingAccessors(usingAccessorsBuilder);
TestMap usingAccessors = usingAccessorsBuilder.build();
assertMapValuesSet(usingAccessors);
- assertEquals(usingAccessors, usingMutableMap);
+ assertEquals(usingAccessors, usingMutableMap);
+ //
usingMutableMapBuilder = usingMutableMap.toBuilder();
updateMapValuesUsingMutableMap(usingMutableMapBuilder);
usingMutableMap = usingMutableMapBuilder.build();
@@ -335,7 +337,7 @@ public void testMutableMapLifecycle() {
assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
builder.getMutableInt32ToInt32Field().put(2, 3);
assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
-
+ //
Map enumMap = builder.getMutableInt32ToEnumField();
enumMap.put(1, TestMap.EnumValue.BAR);
assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField());
@@ -350,7 +352,7 @@ public void testMutableMapLifecycle() {
assertEquals(
newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
builder.getInt32ToEnumField());
-
+ //
Map stringMap = builder.getMutableInt32ToStringField();
stringMap.put(1, "1");
assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
@@ -365,7 +367,7 @@ public void testMutableMapLifecycle() {
assertEquals(
newMap(1, "1", 2, "2"),
builder.getInt32ToStringField());
-
+ //
Map messageMap = builder.getMutableInt32ToMessageField();
messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
@@ -384,7 +386,7 @@ public void testMutableMapLifecycle() {
2, TestMap.MessageValue.getDefaultInstance()),
builder.getInt32ToMessageField());
}
-
+ //
public void testMutableMapLifecycle_collections() {
TestMap.Builder builder = TestMap.newBuilder();
Map intMap = builder.getMutableInt32ToInt32Field();
@@ -431,18 +433,19 @@ public void testMutableMapLifecycle_collections() {
assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
}
+
public void testGettersAndSetters() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build();
assertMapValuesCleared(message);
builder = message.toBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
message = builder.build();
assertMapValuesSet(message);
builder = message.toBuilder();
- updateMapValuesUsingMutableMap(builder);
+ updateMapValuesUsingAccessors(builder);
message = builder.build();
assertMapValuesUpdated(message);
@@ -455,7 +458,7 @@ public void testGettersAndSetters() throws Exception {
public void testPutAll() throws Exception {
TestMap.Builder sourceBuilder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(sourceBuilder);
+ setMapValuesUsingAccessors(sourceBuilder);
TestMap source = sourceBuilder.build();
assertMapValuesSet(source);
@@ -507,14 +510,14 @@ public void testPutChecksNullKeysAndValues() throws Exception {
public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message);
builder = message.toBuilder();
- updateMapValuesUsingMutableMap(builder);
+ updateMapValuesUsingAccessors(builder);
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
@@ -579,7 +582,7 @@ public void testParseError() throws Exception {
public void testMergeFrom() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
TestMap.Builder other = TestMap.newBuilder();
@@ -594,22 +597,22 @@ public void testEqualsAndHashCode() throws Exception {
// We can't control the order of elements in a HashMap. The best we can do
// here is to add elements in different order.
TestMap.Builder b1 = TestMap.newBuilder();
- b1.getMutableInt32ToInt32Field().put(1, 2);
- b1.getMutableInt32ToInt32Field().put(3, 4);
- b1.getMutableInt32ToInt32Field().put(5, 6);
+ b1.putInt32ToInt32Field(1, 2);
+ b1.putInt32ToInt32Field(3, 4);
+ b1.putInt32ToInt32Field(5, 6);
TestMap m1 = b1.build();
TestMap.Builder b2 = TestMap.newBuilder();
- b2.getMutableInt32ToInt32Field().put(5, 6);
- b2.getMutableInt32ToInt32Field().put(1, 2);
- b2.getMutableInt32ToInt32Field().put(3, 4);
+ b2.putInt32ToInt32Field(5, 6);
+ b2.putInt32ToInt32Field(1, 2);
+ b2.putInt32ToInt32Field(3, 4);
TestMap m2 = b2.build();
assertEquals(m1, m2);
assertEquals(m1.hashCode(), m2.hashCode());
// Make sure we did compare map fields.
- b2.getMutableInt32ToInt32Field().put(1, 0);
+ b2.putInt32ToInt32Field(1, 0);
m2 = b2.build();
assertFalse(m1.equals(m2));
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
@@ -684,13 +687,11 @@ private static Map mapForValues(
public void testReflectionApi() throws Exception {
// In reflection API, map fields are just repeated message fields.
- TestMap.Builder builder = TestMap.newBuilder();
- builder.getMutableInt32ToInt32Field().put(1, 2);
- builder.getMutableInt32ToInt32Field().put(3, 4);
- builder.getMutableInt32ToMessageField().put(
- 11, MessageValue.newBuilder().setValue(22).build());
- builder.getMutableInt32ToMessageField().put(
- 33, MessageValue.newBuilder().setValue(44).build());
+ TestMap.Builder builder = TestMap.newBuilder()
+ .putInt32ToInt32Field(1, 2)
+ .putInt32ToInt32Field(3, 4)
+ .putInt32ToMessageField(11, MessageValue.newBuilder().setValue(22).build())
+ .putInt32ToMessageField(33, MessageValue.newBuilder().setValue(44).build());
TestMap message = builder.build();
// Test getField(), getRepeatedFieldCount(), getRepeatedField().
@@ -760,7 +761,7 @@ public void testReflectionApi() throws Exception {
public void testTextFormat() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
String textData = TextFormat.printToString(message);
@@ -774,7 +775,7 @@ public void testTextFormat() throws Exception {
public void testDynamicMessage() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
Message dynamicDefaultInstance =
@@ -819,10 +820,9 @@ public void testReflectionEqualsAndHashCode() throws Exception {
}
public void testUnknownEnumValues() throws Exception {
- TestUnknownEnumValue.Builder builder =
- TestUnknownEnumValue.newBuilder();
- builder.getMutableInt32ToInt32Field().put(1, 1);
- builder.getMutableInt32ToInt32Field().put(2, 54321);
+ TestUnknownEnumValue.Builder builder = TestUnknownEnumValue.newBuilder()
+ .putInt32ToInt32Field(1, 1)
+ .putInt32ToInt32Field(2, 54321);
ByteString data = builder.build().toByteString();
TestMap message = TestMap.parseFrom(data);
@@ -841,26 +841,21 @@ public void testUnknownEnumValues() throws Exception {
assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue());
}
-
public void testRequiredMessage() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- builder.getMutableRequiredMessageMap().put(0,
- MessageWithRequiredFields.newBuilder().buildPartial());
+ builder.putRequiredMessageMap(0, MessageWithRequiredFields.newBuilder().buildPartial());
TestMap message = builder.buildPartial();
assertFalse(message.isInitialized());
- builder.getMutableRequiredMessageMap().put(0,
- MessageWithRequiredFields.newBuilder().setValue(1).build());
+ builder.putRequiredMessageMap(0, MessageWithRequiredFields.newBuilder().setValue(1).build());
message = builder.build();
assertTrue(message.isInitialized());
}
public void testRecursiveMap() throws Exception {
TestRecursiveMap.Builder builder = TestRecursiveMap.newBuilder();
- builder.getMutableRecursiveMapField().put(
- 1, TestRecursiveMap.newBuilder().setValue(2).build());
- builder.getMutableRecursiveMapField().put(
- 3, TestRecursiveMap.newBuilder().setValue(4).build());
+ builder.putRecursiveMapField(1, TestRecursiveMap.newBuilder().setValue(2).build());
+ builder.putRecursiveMapField(3, TestRecursiveMap.newBuilder().setValue(4).build());
ByteString data = builder.build().toByteString();
TestRecursiveMap message = TestRecursiveMap.parseFrom(data);
@@ -870,7 +865,7 @@ public void testRecursiveMap() throws Exception {
public void testIterationOrder() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
assertEquals(Arrays.asList("1", "2", "3"),
@@ -879,7 +874,7 @@ public void testIterationOrder() throws Exception {
public void testContains() {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
assertMapContainsSetValues(builder);
assertMapContainsSetValues(builder.build());
}
@@ -920,7 +915,7 @@ public void testCount() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapCounts(0, builder);
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
assertMapCounts(3, builder);
TestMap message = builder.build();
@@ -1091,7 +1086,7 @@ public void testPut() {
public void testRemove() {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToInt32Field(1);
@@ -1137,7 +1132,7 @@ public void testMapInitializationOrder() throws Exception {
map_test.Message1.Builder builder =
map_test.Message1.newBuilder();
- builder.getMutableMapField().put("key", true);
+ builder.putMapField("key", true);
map_test.Message1 message = builder.build();
Message mapEntry = (Message) message.getRepeatedField(
message.getDescriptorForType().findFieldByName("map_field"), 0);
diff --git a/java/core/src/test/java/com/google/protobuf/MapTest.java b/java/core/src/test/java/com/google/protobuf/MapTest.java
index 035a8335f0814..81e951ccf7e1b 100644
--- a/java/core/src/test/java/com/google/protobuf/MapTest.java
+++ b/java/core/src/test/java/com/google/protobuf/MapTest.java
@@ -60,26 +60,26 @@ private void setMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 11);
builder.getMutableInt32ToInt32Field().put(2, 22);
builder.getMutableInt32ToInt32Field().put(3, 33);
-
+ //
builder.getMutableInt32ToStringField().put(1, "11");
builder.getMutableInt32ToStringField().put(2, "22");
builder.getMutableInt32ToStringField().put(3, "33");
-
+ //
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
-
+ //
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
-
+ //
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(11).build());
builder.getMutableInt32ToMessageField().put(
2, MessageValue.newBuilder().setValue(22).build());
builder.getMutableInt32ToMessageField().put(
3, MessageValue.newBuilder().setValue(33).build());
-
+ //
builder.getMutableStringToInt32Field().put("1", 11);
builder.getMutableStringToInt32Field().put("2", 22);
builder.getMutableStringToInt32Field().put("3", 33);
@@ -122,6 +122,7 @@ public void testSetMapValues() {
setMapValuesUsingAccessors(usingAccessorsBuilder);
TestMap usingAccessors = usingAccessorsBuilder.build();
assertMapValuesSet(usingAccessors);
+
assertEquals(usingAccessors, usingMutableMap);
}
@@ -171,25 +172,25 @@ private void updateMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 111);
builder.getMutableInt32ToInt32Field().remove(2);
builder.getMutableInt32ToInt32Field().put(4, 44);
-
+ //
builder.getMutableInt32ToStringField().put(1, "111");
builder.getMutableInt32ToStringField().remove(2);
builder.getMutableInt32ToStringField().put(4, "44");
-
+ //
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
builder.getMutableInt32ToBytesField().remove(2);
builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
-
+ //
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().remove(2);
builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
-
+ //
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(111).build());
builder.getMutableInt32ToMessageField().remove(2);
builder.getMutableInt32ToMessageField().put(
4, MessageValue.newBuilder().setValue(44).build());
-
+ //
builder.getMutableStringToInt32Field().put("1", 111);
builder.getMutableStringToInt32Field().remove("2");
builder.getMutableStringToInt32Field().put("4", 44);
@@ -232,8 +233,9 @@ public void testUpdateMapValues() {
setMapValuesUsingAccessors(usingAccessorsBuilder);
TestMap usingAccessors = usingAccessorsBuilder.build();
assertMapValuesSet(usingAccessors);
- assertEquals(usingAccessors, usingMutableMap);
+ assertEquals(usingAccessors, usingMutableMap);
+ //
usingMutableMapBuilder = usingMutableMap.toBuilder();
updateMapValuesUsingMutableMap(usingMutableMapBuilder);
usingMutableMap = usingMutableMapBuilder.build();
@@ -337,7 +339,7 @@ public void testMutableMapLifecycle() {
assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
builder.getMutableInt32ToInt32Field().put(2, 3);
assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
-
+ //
Map enumMap = builder.getMutableInt32ToEnumField();
enumMap.put(1, TestMap.EnumValue.BAR);
assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField());
@@ -352,7 +354,7 @@ public void testMutableMapLifecycle() {
assertEquals(
newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
builder.getInt32ToEnumField());
-
+ //
Map stringMap = builder.getMutableInt32ToStringField();
stringMap.put(1, "1");
assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
@@ -363,11 +365,11 @@ public void testMutableMapLifecycle() {
// expected
}
assertEquals(newMap(1, "1"), builder.getInt32ToStringField());
- builder.getMutableInt32ToStringField().put(2, "2");
+ builder.putInt32ToStringField(2, "2");
assertEquals(
newMap(1, "1", 2, "2"),
builder.getInt32ToStringField());
-
+ //
Map messageMap = builder.getMutableInt32ToMessageField();
messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
@@ -380,13 +382,13 @@ public void testMutableMapLifecycle() {
}
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
builder.getInt32ToMessageField());
- builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance());
+ builder.putInt32ToMessageField(2, TestMap.MessageValue.getDefaultInstance());
assertEquals(
newMap(1, TestMap.MessageValue.getDefaultInstance(),
2, TestMap.MessageValue.getDefaultInstance()),
builder.getInt32ToMessageField());
}
-
+ //
public void testMutableMapLifecycle_collections() {
TestMap.Builder builder = TestMap.newBuilder();
Map intMap = builder.getMutableInt32ToInt32Field();
@@ -433,18 +435,19 @@ public void testMutableMapLifecycle_collections() {
assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
}
+
public void testGettersAndSetters() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build();
assertMapValuesCleared(message);
builder = message.toBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
message = builder.build();
assertMapValuesSet(message);
builder = message.toBuilder();
- updateMapValuesUsingMutableMap(builder);
+ updateMapValuesUsingAccessors(builder);
message = builder.build();
assertMapValuesUpdated(message);
@@ -457,7 +460,7 @@ public void testGettersAndSetters() throws Exception {
public void testPutAll() throws Exception {
TestMap.Builder sourceBuilder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(sourceBuilder);
+ setMapValuesUsingAccessors(sourceBuilder);
TestMap source = sourceBuilder.build();
assertMapValuesSet(source);
@@ -467,15 +470,16 @@ public void testPutAll() throws Exception {
}
public void testPutAllForUnknownEnumValues() throws Exception {
- TestMap.Builder sourceBuilder = TestMap.newBuilder();
- sourceBuilder.getMutableInt32ToEnumFieldValue().put(0, 0);
- sourceBuilder.getMutableInt32ToEnumFieldValue().put(1, 1);
- sourceBuilder.getMutableInt32ToEnumFieldValue().put(2, 1000); // unknown value.
- TestMap source = sourceBuilder.build();
+ TestMap source = TestMap.newBuilder()
+ .putAllInt32ToEnumFieldValue(newMap(
+ 0, 0,
+ 1, 1,
+ 2, 1000)) // unknown value.
+ .build();
- TestMap.Builder destinationBuilder = TestMap.newBuilder();
- destinationBuilder.putAllInt32ToEnumFieldValue(source.getInt32ToEnumFieldValue());
- TestMap destination = destinationBuilder.build();
+ TestMap destination = TestMap.newBuilder()
+ .putAllInt32ToEnumFieldValue(source.getInt32ToEnumFieldValue())
+ .build();
assertEquals(0, destination.getInt32ToEnumFieldValue().get(0).intValue());
assertEquals(1, destination.getInt32ToEnumFieldValue().get(1).intValue());
@@ -542,14 +546,14 @@ public void testPutChecksNullKeysAndValues() throws Exception {
public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message);
builder = message.toBuilder();
- updateMapValuesUsingMutableMap(builder);
+ updateMapValuesUsingAccessors(builder);
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
@@ -614,7 +618,7 @@ public void testParseError() throws Exception {
public void testMergeFrom() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
TestMap.Builder other = TestMap.newBuilder();
@@ -628,23 +632,23 @@ public void testEqualsAndHashCode() throws Exception {
// We can't control the order of elements in a HashMap. The best we can do
// here is to add elements in different order.
- TestMap.Builder b1 = TestMap.newBuilder();
- b1.getMutableInt32ToInt32Field().put(1, 2);
- b1.getMutableInt32ToInt32Field().put(3, 4);
- b1.getMutableInt32ToInt32Field().put(5, 6);
+ TestMap.Builder b1 = TestMap.newBuilder()
+ .putInt32ToInt32Field(1, 2)
+ .putInt32ToInt32Field(3, 4)
+ .putInt32ToInt32Field(5, 6);
TestMap m1 = b1.build();
- TestMap.Builder b2 = TestMap.newBuilder();
- b2.getMutableInt32ToInt32Field().put(5, 6);
- b2.getMutableInt32ToInt32Field().put(1, 2);
- b2.getMutableInt32ToInt32Field().put(3, 4);
+ TestMap.Builder b2 = TestMap.newBuilder()
+ .putInt32ToInt32Field(5, 6)
+ .putInt32ToInt32Field(1, 2)
+ .putInt32ToInt32Field(3, 4);
TestMap m2 = b2.build();
assertEquals(m1, m2);
assertEquals(m1.hashCode(), m2.hashCode());
// Make sure we did compare map fields.
- b2.getMutableInt32ToInt32Field().put(1, 0);
+ b2.putInt32ToInt32Field(1, 0);
m2 = b2.build();
assertFalse(m1.equals(m2));
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
@@ -652,7 +656,7 @@ public void testEqualsAndHashCode() throws Exception {
// Regression test for b/18549190: if a map is a subset of the other map,
// equals() should return false.
- b2.getMutableInt32ToInt32Field().remove(1);
+ b2.removeInt32ToInt32Field(1);
m2 = b2.build();
assertFalse(m1.equals(m2));
assertFalse(m2.equals(m1));
@@ -661,20 +665,19 @@ public void testEqualsAndHashCode() throws Exception {
public void testNestedBuilderOnChangeEventPropagation() {
TestOnChangeEventPropagation.Builder parent =
TestOnChangeEventPropagation.newBuilder();
- parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 2);
+ parent.getOptionalMessageBuilder().putInt32ToInt32Field(1, 2);
TestOnChangeEventPropagation message = parent.build();
assertEquals(2, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
// Make a change using nested builder.
- parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 3);
+ parent.getOptionalMessageBuilder().putInt32ToInt32Field(1, 3);
// Should be able to observe the change.
message = parent.build();
assertEquals(3, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
// Make another change using mergeFrom()
- TestMap.Builder other = TestMap.newBuilder();
- other.getMutableInt32ToInt32Field().put(1, 4);
+ TestMap.Builder other = TestMap.newBuilder().putInt32ToInt32Field(1, 4);
parent.getOptionalMessageBuilder().mergeFrom(other.build());
// Should be able to observe the change.
@@ -697,8 +700,7 @@ public void testNestedBuilderOnChangeEventPropagationReflection() {
TestMap.Builder testMapBuilder = parentBuilder.getOptionalMessageBuilder();
// Create a map entry message.
- TestMap.Builder entryBuilder = TestMap.newBuilder();
- entryBuilder.getMutableInt32ToInt32Field().put(1, 1);
+ TestMap.Builder entryBuilder = TestMap.newBuilder().putInt32ToInt32Field(1, 1);
// Put the entry into the nested builder.
testMapBuilder.addRepeatedField(
@@ -709,7 +711,7 @@ public void testNestedBuilderOnChangeEventPropagationReflection() {
assertEquals(1, message.getOptionalMessage().getInt32ToInt32Field().size());
// Change the entry value.
- entryBuilder.getMutableInt32ToInt32Field().put(1, 4);
+ entryBuilder.putInt32ToInt32Field(1, 4);
testMapBuilder = parentBuilder.getOptionalMessageBuilder();
testMapBuilder.setRepeatedField(
intMapField, 0, entryBuilder.getRepeatedField(intMapField, 0));
@@ -796,13 +798,11 @@ Map mapForValues(
public void testReflectionApi() throws Exception {
// In reflection API, map fields are just repeated message fields.
- TestMap.Builder builder = TestMap.newBuilder();
- builder.getMutableInt32ToInt32Field().put(1, 2);
- builder.getMutableInt32ToInt32Field().put(3, 4);
- builder.getMutableInt32ToMessageField().put(
- 11, MessageValue.newBuilder().setValue(22).build());
- builder.getMutableInt32ToMessageField().put(
- 33, MessageValue.newBuilder().setValue(44).build());
+ TestMap.Builder builder = TestMap.newBuilder()
+ .putInt32ToInt32Field(1, 2)
+ .putInt32ToInt32Field(3, 4)
+ .putInt32ToMessageField(11, MessageValue.newBuilder().setValue(22).build())
+ .putInt32ToMessageField(33, MessageValue.newBuilder().setValue(44).build());
TestMap message = builder.build();
// Test getField(), getRepeatedFieldCount(), getRepeatedField().
@@ -872,7 +872,7 @@ public void testReflectionApi() throws Exception {
public void testTextFormat() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
String textData = TextFormat.printToString(message);
@@ -886,7 +886,7 @@ public void testTextFormat() throws Exception {
public void testDynamicMessage() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
Message dynamicDefaultInstance =
@@ -931,10 +931,11 @@ public void testReflectionEqualsAndHashCode() throws Exception {
}
public void testUnknownEnumValues() throws Exception {
- TestMap.Builder builder = TestMap.newBuilder();
- builder.getMutableInt32ToEnumFieldValue().put(0, 0);
- builder.getMutableInt32ToEnumFieldValue().put(1, 1);
- builder.getMutableInt32ToEnumFieldValue().put(2, 1000); // unknown value.
+ TestMap.Builder builder = TestMap.newBuilder()
+ .putAllInt32ToEnumFieldValue(newMap(
+ 0, 0,
+ 1, 1,
+ 2, 1000)); // unknown value.
TestMap message = builder.build();
assertEquals(TestMap.EnumValue.FOO,
@@ -957,7 +958,7 @@ public void testUnknownEnumValues() throws Exception {
assertEquals(1000, builder.getInt32ToEnumFieldValue().get(2).intValue());
// hashCode()/equals() should take unknown enum values into account.
- builder.getMutableInt32ToEnumFieldValue().put(2, 1001);
+ builder.putAllInt32ToEnumFieldValue(newMap(2, 1001));
TestMap message2 = builder.build();
assertFalse(message.hashCode() == message2.hashCode());
assertFalse(message.equals(message2));
@@ -971,15 +972,13 @@ public void testUnknownEnumValuesInReflectionApi() throws Exception {
EnumDescriptor enumDescriptor = TestMap.EnumValue.getDescriptor();
FieldDescriptor field = descriptor.findFieldByName("int32_to_enum_field");
- Map data = new HashMap();
- data.put(0, 0);
- data.put(1, 1);
- data.put(2, 1000); // unknown value.
+ Map data = newMap(
+ 0, 0,
+ 1, 1,
+ 2, 1000); // unknown value
- TestMap.Builder builder = TestMap.newBuilder();
- for (Map.Entry entry : data.entrySet()) {
- builder.getMutableInt32ToEnumFieldValue().put(entry.getKey(), entry.getValue());
- }
+ TestMap.Builder builder = TestMap.newBuilder()
+ .putAllInt32ToEnumFieldValue(data);
// Try to read unknown enum values using reflection API.
for (int i = 0; i < builder.getRepeatedFieldCount(field); i++) {
@@ -1003,7 +1002,7 @@ public void testUnknownEnumValuesInReflectionApi() throws Exception {
public void testIterationOrder() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
assertEquals(Arrays.asList("1", "2", "3"),
@@ -1012,7 +1011,7 @@ public void testIterationOrder() throws Exception {
public void testGetMap() {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
assertEquals(
message.getStringToInt32Field(),
@@ -1033,7 +1032,7 @@ public void testGetMap() {
public void testContains() {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
assertMapContainsSetValues(builder);
assertMapContainsSetValues(builder.build());
}
@@ -1074,7 +1073,7 @@ public void testCount() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapCounts(0, builder);
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
assertMapCounts(3, builder);
TestMap message = builder.build();
@@ -1269,7 +1268,7 @@ public void testPut() {
public void testRemove() {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValuesUsingMutableMap(builder);
+ setMapValuesUsingAccessors(builder);
assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToInt32Field(1);
@@ -1485,4 +1484,12 @@ private static Map newMap(K key1, V value1, K key2, V value2) {
map.put(key2, value2);
return map;
}
+
+ private static Map newMap(K key1, V value1, K key2, V value2, K key3, V value3) {
+ Map map = new HashMap();
+ map.put(key1, value1);
+ map.put(key2, value2);
+ map.put(key3, value3);
+ return map;
+ }
}
diff --git a/java/core/src/test/java/com/google/protobuf/ParserTest.java b/java/core/src/test/java/com/google/protobuf/ParserTest.java
index 7086912b95e68..8c2e4c261c0e1 100644
--- a/java/core/src/test/java/com/google/protobuf/ParserTest.java
+++ b/java/core/src/test/java/com/google/protobuf/ParserTest.java
@@ -46,6 +46,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InterruptedIOException;
import junit.framework.TestCase;
/**
@@ -372,4 +373,43 @@ public void testParsingMergeLite() throws Exception {
assertEquals(3, parsingMerge.getExtensionCount(
TestParsingMergeLite.repeatedExt));
}
+
+ public void testParseDelimitedFrom_firstByteInterrupted_preservesCause() {
+ try {
+ TestUtil.getAllSet().parseDelimitedFrom(
+ new InputStream() {
+ @Override
+ public int read() throws IOException {
+ throw new InterruptedIOException();
+ }
+ });
+ fail("Expected InterruptedIOException");
+ } catch (Exception e) {
+ assertEquals(InterruptedIOException.class, e.getClass());
+ }
+ }
+
+ public void testParseDelimitedFrom_secondByteInterrupted_preservesCause() {
+ try {
+ TestUtil.getAllSet().parseDelimitedFrom(
+ new InputStream() {
+ private int i;
+
+ @Override
+ public int read() throws IOException {
+ switch (i++) {
+ case 0:
+ return 1;
+ case 1:
+ throw new InterruptedIOException();
+ default:
+ throw new AssertionError();
+ }
+ }
+ });
+ fail("Expected InterruptedIOException");
+ } catch (Exception e) {
+ assertEquals(InterruptedIOException.class, e.getClass());
+ }
+ }
}
diff --git a/java/util/src/main/java/com/google/protobuf/util/Durations.java b/java/util/src/main/java/com/google/protobuf/util/Durations.java
index 9333168d36e3b..46b2182865d2d 100644
--- a/java/util/src/main/java/com/google/protobuf/util/Durations.java
+++ b/java/util/src/main/java/com/google/protobuf/util/Durations.java
@@ -42,7 +42,6 @@
import static com.google.protobuf.util.Timestamps.NANOS_PER_MILLISECOND;
import static com.google.protobuf.util.Timestamps.NANOS_PER_SECOND;
-import com.google.common.collect.ComparisonChain;
import com.google.protobuf.Duration;
import java.text.ParseException;
import java.util.Comparator;
@@ -71,11 +70,8 @@ private Durations() {}
public int compare(Duration d1, Duration d2) {
checkValid(d1);
checkValid(d2);
-
- return ComparisonChain.start()
- .compare(d1.getSeconds(), d2.getSeconds())
- .compare(d1.getNanos(), d2.getNanos())
- .result();
+ int secDiff = Long.compare(d1.getSeconds(), d2.getSeconds());
+ return (secDiff != 0) ? secDiff : Integer.compare(d1.getNanos(), d2.getNanos());
}
};
diff --git a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
index b577495d8de46..b192b53edf5ab 100644
--- a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
+++ b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
@@ -217,7 +217,12 @@ private void merge(
Message source,
Message.Builder destination,
FieldMaskUtil.MergeOptions options) {
- assert source.getDescriptorForType() == destination.getDescriptorForType();
+ if (source.getDescriptorForType() != destination.getDescriptorForType()) {
+ throw new IllegalArgumentException(
+ String.format(
+ "source (%s) and destination (%s) descriptor must be equal",
+ source.getDescriptorForType(), destination.getDescriptorForType()));
+ }
Descriptor descriptor = source.getDescriptorForType();
for (Entry entry : node.children.entrySet()) {
diff --git a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
index 6361b4ac5915e..7f6c8aea901b1 100644
--- a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
+++ b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
@@ -67,7 +67,6 @@
import com.google.protobuf.UInt32Value;
import com.google.protobuf.UInt64Value;
import com.google.protobuf.Value;
-
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
@@ -224,7 +223,7 @@ public String print(MessageOrBuilder message) throws InvalidProtocolBufferExcept
* Creates a {@link Parser} with default configuration.
*/
public static Parser parser() {
- return new Parser(TypeRegistry.getEmptyTypeRegistry(), false);
+ return new Parser(TypeRegistry.getEmptyTypeRegistry(), false, Parser.DEFAULT_RECURSION_LIMIT);
}
/**
@@ -233,10 +232,15 @@ public static Parser parser() {
public static class Parser {
private final TypeRegistry registry;
private final boolean ignoringUnknownFields;
+ private final int recursionLimit;
+
+ // The default parsing recursion limit is aligned with the proto binary parser.
+ private static final int DEFAULT_RECURSION_LIMIT = 100;
- private Parser(TypeRegistry registry, boolean ignoreUnknownFields) {
+ private Parser(TypeRegistry registry, boolean ignoreUnknownFields, int recursionLimit) {
this.registry = registry;
this.ignoringUnknownFields = ignoreUnknownFields;
+ this.recursionLimit = recursionLimit;
}
/**
@@ -249,16 +253,15 @@ public Parser usingTypeRegistry(TypeRegistry registry) {
if (this.registry != TypeRegistry.getEmptyTypeRegistry()) {
throw new IllegalArgumentException("Only one registry is allowed.");
}
- return new Parser(registry, this.ignoringUnknownFields);
+ return new Parser(registry, ignoringUnknownFields, recursionLimit);
}
/**
- * Creates a new {@link Parser} configured to not throw an exception
- * when an unknown field is encountered. The new Parser clones all other
- * configurations from this Parser.
+ * Creates a new {@link Parser} configured to not throw an exception when an unknown field is
+ * encountered. The new Parser clones all other configurations from this Parser.
*/
public Parser ignoringUnknownFields() {
- return new Parser(this.registry, true);
+ return new Parser(this.registry, true, recursionLimit);
}
/**
@@ -270,7 +273,7 @@ public Parser ignoringUnknownFields() {
public void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
// TODO(xiaofeng): Investigate the allocation overhead and optimize for
// mobile.
- new ParserImpl(registry, ignoringUnknownFields).merge(json, builder);
+ new ParserImpl(registry, ignoringUnknownFields, recursionLimit).merge(json, builder);
}
/**
@@ -283,7 +286,12 @@ public void merge(String json, Message.Builder builder) throws InvalidProtocolBu
public void merge(Reader json, Message.Builder builder) throws IOException {
// TODO(xiaofeng): Investigate the allocation overhead and optimize for
// mobile.
- new ParserImpl(registry, ignoringUnknownFields).merge(json, builder);
+ new ParserImpl(registry, ignoringUnknownFields, recursionLimit).merge(json, builder);
+ }
+
+ // For testing only.
+ Parser usingRecursionLimit(int recursionLimit) {
+ return new Parser(registry, ignoringUnknownFields, recursionLimit);
}
}
@@ -1040,11 +1048,15 @@ private static class ParserImpl {
private final TypeRegistry registry;
private final JsonParser jsonParser;
private final boolean ignoringUnknownFields;
+ private final int recursionLimit;
+ private int currentDepth;
- ParserImpl(TypeRegistry registry, boolean ignoreUnknownFields) {
+ ParserImpl(TypeRegistry registry, boolean ignoreUnknownFields, int recursionLimit) {
this.registry = registry;
this.ignoringUnknownFields = ignoreUnknownFields;
this.jsonParser = new JsonParser();
+ this.recursionLimit = recursionLimit;
+ this.currentDepth = 0;
}
void merge(Reader json, Message.Builder builder) throws IOException {
@@ -1715,8 +1727,13 @@ private Object parseFieldValue(FieldDescriptor field, JsonElement json, Message.
case MESSAGE:
case GROUP:
+ if (currentDepth >= recursionLimit) {
+ throw new InvalidProtocolBufferException("Hit recursion limit.");
+ }
+ ++currentDepth;
Message.Builder subBuilder = builder.newBuilderForField(field);
merge(json, subBuilder);
+ --currentDepth;
return subBuilder.build();
default:
diff --git a/java/util/src/main/java/com/google/protobuf/util/Timestamps.java b/java/util/src/main/java/com/google/protobuf/util/Timestamps.java
index 52b1ab9887ac1..2160e4d57ddda 100644
--- a/java/util/src/main/java/com/google/protobuf/util/Timestamps.java
+++ b/java/util/src/main/java/com/google/protobuf/util/Timestamps.java
@@ -37,7 +37,6 @@
import static com.google.common.math.LongMath.checkedMultiply;
import static com.google.common.math.LongMath.checkedSubtract;
-import com.google.common.collect.ComparisonChain;
import com.google.protobuf.Duration;
import com.google.protobuf.Timestamp;
import java.text.ParseException;
@@ -101,11 +100,8 @@ private Timestamps() {}
public int compare(Timestamp t1, Timestamp t2) {
checkValid(t1);
checkValid(t2);
-
- return ComparisonChain.start()
- .compare(t1.getSeconds(), t2.getSeconds())
- .compare(t1.getNanos(), t2.getNanos())
- .result();
+ int secDiff = Long.compare(t1.getSeconds(), t2.getSeconds());
+ return (secDiff != 0) ? secDiff : Integer.compare(t1.getNanos(), t2.getNanos());
}
};
diff --git a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
index 32739d441ad4e..164ee54b2ba2b 100644
--- a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
@@ -57,12 +57,15 @@
import com.google.protobuf.util.JsonTestProto.TestFieldMask;
import com.google.protobuf.util.JsonTestProto.TestMap;
import com.google.protobuf.util.JsonTestProto.TestOneof;
+import com.google.protobuf.util.JsonTestProto.TestRecursive;
import com.google.protobuf.util.JsonTestProto.TestStruct;
import com.google.protobuf.util.JsonTestProto.TestTimestamp;
import com.google.protobuf.util.JsonTestProto.TestWrappers;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.Map;
import junit.framework.TestCase;
public class JsonFormatTest extends TestCase {
@@ -216,7 +219,9 @@ public void testUnknownEnumValues() throws Exception {
TestMap.Builder mapBuilder = TestMap.newBuilder();
mapBuilder.putInt32ToEnumMapValue(1, 0);
- mapBuilder.getMutableInt32ToEnumMapValue().put(2, 12345);
+ Map mapWithInvalidValues = new HashMap();
+ mapWithInvalidValues.put(2, 12345);
+ mapBuilder.putAllInt32ToEnumMapValue(mapWithInvalidValues);
TestMap mapMessage = mapBuilder.build();
assertEquals(
"{\n"
@@ -1140,6 +1145,7 @@ public void testParserUnknownFields() throws Exception {
// Expected.
}
}
+
public void testParserIgnoringUnknownFields() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
String json = "{\n" + " \"unknownField\": \"XXX\"\n" + "}";
@@ -1358,4 +1364,34 @@ public void testEmptyWrapperTypesInAny() throws Exception {
Any any = builder.build();
assertEquals(0, any.getValue().size());
}
+
+ public void testRecursionLimit() throws Exception {
+ String input =
+ "{\n"
+ + " \"nested\": {\n"
+ + " \"nested\": {\n"
+ + " \"nested\": {\n"
+ + " \"nested\": {\n"
+ + " \"value\": 1234\n"
+ + " }\n"
+ + " }\n"
+ + " }\n"
+ + " }\n"
+ + "}\n";
+
+ JsonFormat.Parser parser = JsonFormat.parser();
+ TestRecursive.Builder builder = TestRecursive.newBuilder();
+ parser.merge(input, builder);
+ TestRecursive message = builder.build();
+ assertEquals(1234, message.getNested().getNested().getNested().getNested().getValue());
+
+ parser = JsonFormat.parser().usingRecursionLimit(3);
+ builder = TestRecursive.newBuilder();
+ try {
+ parser.merge(input, builder);
+ fail("Exception is expected.");
+ } catch (InvalidProtocolBufferException e) {
+ // Expected.
+ }
+ }
}
diff --git a/java/util/src/test/proto/com/google/protobuf/util/json_test.proto b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
index bd22f65a74e84..a75338ef167ff 100644
--- a/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
+++ b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
@@ -201,3 +201,8 @@ message TestAny {
message TestCustomJsonName {
int32 value = 1 [json_name = "@value"];
}
+
+message TestRecursive {
+ int32 value = 1;
+ TestRecursive nested = 2;
+}
diff --git a/js/binary/decoder.js b/js/binary/decoder.js
index 040cf7153fcda..36828a7305d89 100644
--- a/js/binary/decoder.js
+++ b/js/binary/decoder.js
@@ -732,6 +732,24 @@ jspb.BinaryDecoder.prototype.readZigzagVarint64 = function() {
};
+/**
+ * Reads a signed, zigzag-encoded 64-bit varint from the binary stream and
+ * returns its valud as a string.
+ *
+ * Zigzag encoding is a modification of varint encoding that reduces the
+ * storage overhead for small negative integers - for more details on the
+ * format, see https://developers.google.com/protocol-buffers/docs/encoding
+ *
+ * @return {string} The decoded signed, zigzag-encoded 64-bit varint as a
+ * string.
+ */
+jspb.BinaryDecoder.prototype.readZigzagVarint64String = function() {
+ // TODO(haberman): write lossless 64-bit zig-zag math.
+ var value = this.readZigzagVarint64();
+ return value.toString();
+};
+
+
/**
* Reads a raw unsigned 8-bit integer from the binary stream.
*
@@ -790,6 +808,20 @@ jspb.BinaryDecoder.prototype.readUint64 = function() {
};
+/**
+ * Reads a raw unsigned 64-bit integer from the binary stream. Note that since
+ * Javascript represents all numbers as double-precision floats, there will be
+ * precision lost if the absolute value of the integer is larger than 2^53.
+ *
+ * @return {string} The unsigned 64-bit integer read from the binary stream.
+ */
+jspb.BinaryDecoder.prototype.readUint64String = function() {
+ var bitsLow = this.readUint32();
+ var bitsHigh = this.readUint32();
+ return jspb.utils.joinUnsignedDecimalString(bitsLow, bitsHigh);
+};
+
+
/**
* Reads a raw signed 8-bit integer from the binary stream.
*
@@ -848,6 +880,20 @@ jspb.BinaryDecoder.prototype.readInt64 = function() {
};
+/**
+ * Reads a raw signed 64-bit integer from the binary stream and returns it as a
+ * string.
+ *
+ * @return {string} The signed 64-bit integer read from the binary stream.
+ * Precision will be lost if the integer exceeds 2^53.
+ */
+jspb.BinaryDecoder.prototype.readInt64String = function() {
+ var bitsLow = this.readUint32();
+ var bitsHigh = this.readUint32();
+ return jspb.utils.joinSignedDecimalString(bitsLow, bitsHigh);
+};
+
+
/**
* Reads a 32-bit floating-point number from the binary stream, using the
* temporary buffer to realign the data.
diff --git a/js/binary/encoder.js b/js/binary/encoder.js
index d952d714d7965..1663bbd43b2c9 100644
--- a/js/binary/encoder.js
+++ b/js/binary/encoder.js
@@ -99,6 +99,24 @@ jspb.BinaryEncoder.prototype.writeSplitVarint64 = function(lowBits, highBits) {
};
+/**
+ * Encodes a 64-bit integer in 32:32 split representation into its wire-format
+ * fixed representation and stores it in the buffer.
+ * @param {number} lowBits The low 32 bits of the int.
+ * @param {number} highBits The high 32 bits of the int.
+ */
+jspb.BinaryEncoder.prototype.writeSplitFixed64 = function(lowBits, highBits) {
+ goog.asserts.assert(lowBits == Math.floor(lowBits));
+ goog.asserts.assert(highBits == Math.floor(highBits));
+ goog.asserts.assert((lowBits >= 0) &&
+ (lowBits < jspb.BinaryConstants.TWO_TO_32));
+ goog.asserts.assert((highBits >= 0) &&
+ (highBits < jspb.BinaryConstants.TWO_TO_32));
+ this.writeUint32(lowBits);
+ this.writeUint32(highBits);
+};
+
+
/**
* Encodes a 32-bit unsigned integer into its wire-format varint representation
* and stores it in the buffer.
@@ -207,6 +225,18 @@ jspb.BinaryEncoder.prototype.writeZigzagVarint64 = function(value) {
};
+/**
+ * Encodes a JavaScript decimal string into its wire-format, zigzag-encoded
+ * varint representation and stores it in the buffer. Integers not representable
+ * in 64 bits will be truncated.
+ * @param {string} value The integer to convert.
+ */
+jspb.BinaryEncoder.prototype.writeZigzagVarint64String = function(value) {
+ // TODO(haberman): write lossless 64-bit zig-zag math.
+ this.writeZigzagVarint64(parseInt(value, 10));
+};
+
+
/**
* Writes a 8-bit unsigned integer to the buffer. Numbers outside the range
* [0,2^8) will be truncated.
@@ -314,8 +344,21 @@ jspb.BinaryEncoder.prototype.writeInt64 = function(value) {
goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) &&
(value < jspb.BinaryConstants.TWO_TO_63));
jspb.utils.splitInt64(value);
- this.writeUint32(jspb.utils.split64Low);
- this.writeUint32(jspb.utils.split64High);
+ this.writeSplitFixed64(jspb.utils.split64Low, jspb.utils.split64High);
+};
+
+
+/**
+ * Writes a 64-bit integer decimal strings to the buffer. Numbers outside the
+ * range [-2^63,2^63) will be truncated.
+ * @param {string} value The value to write.
+ */
+jspb.BinaryEncoder.prototype.writeInt64String = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) &&
+ (value < jspb.BinaryConstants.TWO_TO_63));
+ jspb.utils.splitHash64(jspb.utils.decimalStringToHash64(value));
+ this.writeSplitFixed64(jspb.utils.split64Low, jspb.utils.split64High);
};
diff --git a/js/binary/proto_test.js b/js/binary/proto_test.js
index 26e1d30f1c21d..f86dc6455bb48 100644
--- a/js/binary/proto_test.js
+++ b/js/binary/proto_test.js
@@ -32,6 +32,7 @@
goog.require('goog.crypt.base64');
goog.require('goog.testing.asserts');
+goog.require('jspb.BinaryWriter');
goog.require('jspb.Message');
// CommonJS-LoadFromFile: ../testbinary_pb proto.jspb.test
@@ -87,6 +88,9 @@ goog.require('proto.jspb.test.extendRepeatedStringList');
goog.require('proto.jspb.test.extendRepeatedUint32List');
goog.require('proto.jspb.test.extendRepeatedUint64List');
+// CommonJS-LoadFromFile: ../node_modules/google-protobuf/google/protobuf/any_pb proto.google.protobuf
+goog.require('proto.google.protobuf.Any');
+
var suite = {};
@@ -194,8 +198,6 @@ function bytesCompare(arr, expected) {
* @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);
@@ -270,6 +272,9 @@ function checkAllFields(original, copy) {
assertElementsEquals(copy.getPackedRepeatedFloatList(), [1.5]);
assertElementsEquals(copy.getPackedRepeatedDoubleList(), [-1.5]);
+
+ // Check last so we get more granular errors first.
+ assertTrue(jspb.Message.equals(original, copy));
}
@@ -625,4 +630,36 @@ describe('protoBinaryTest', function() {
var decoded = proto.jspb.test.TestExtendable.deserializeBinary(encoded);
checkExtensions(decoded);
});
+
+ /**
+ * Tests that unknown extensions don't cause deserialization failure.
+ */
+ it('testUnknownExtension', function() {
+ var msg = new proto.jspb.test.TestExtendable();
+ fillExtensions(msg);
+ var writer = new jspb.BinaryWriter();
+ writer.writeBool((1 << 29) - 1, true);
+ proto.jspb.test.TestExtendable.serializeBinaryToWriter(msg, writer);
+ var encoded = writer.getResultBuffer();
+ var decoded = proto.jspb.test.TestExtendable.deserializeBinary(encoded);
+ checkExtensions(decoded);
+ });
+
+ it('testAnyWellKnownType', function() {
+ var any = new proto.google.protobuf.Any();
+ var msg = new proto.jspb.test.TestAllTypes();
+
+ fillAllFields(msg);
+
+ any.pack(msg.serializeBinary(), 'jspb.test.TestAllTypes');
+
+ assertEquals('type.googleapis.com/jspb.test.TestAllTypes',
+ any.getTypeUrl());
+
+ var msg2 = any.unpack(
+ proto.jspb.test.TestAllTypes.deserializeBinary,
+ 'jspb.test.TestAllTypes');
+
+ checkAllFields(msg, msg2);
+ });
});
diff --git a/js/binary/reader.js b/js/binary/reader.js
index 15f90432d89ee..8c5a4e88dbb9f 100644
--- a/js/binary/reader.js
+++ b/js/binary/reader.js
@@ -743,6 +743,20 @@ jspb.BinaryReader.prototype.readSint64 = function() {
};
+/**
+ * Reads a signed zigzag-encoded 64-bit integer field from the binary stream,
+ * or throws an error if the next field in the stream is not of the correct
+ * wire type.
+ *
+ * @return {string} The value of the signed 64-bit integer field as a decimal string.
+ */
+jspb.BinaryReader.prototype.readSint64String = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readZigzagVarint64String();
+};
+
+
/**
* Reads an unsigned 32-bit fixed-length integer fiield from the binary stream,
* or throws an error if the next field in the stream is not of the correct
@@ -771,12 +785,29 @@ jspb.BinaryReader.prototype.readFixed64 = function() {
};
+/**
+ * Reads a signed 64-bit integer field from the binary stream as a string, or
+ * throws an error if the next field in the stream is not of the correct wire
+ * type.
+ *
+ * Returns the value as a string.
+ *
+ * @return {string} The value of the unsigned 64-bit integer field as a decimal
+ * string.
+ */
+jspb.BinaryReader.prototype.readFixed64String = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED64);
+ return this.decoder_.readUint64String();
+};
+
+
/**
* Reads a signed 32-bit fixed-length integer fiield from the binary stream, or
* throws an error if the next field in the stream is not of the correct wire
* type.
*
- * @return {number} The value of the double field.
+ * @return {number} The value of the signed 32-bit integer field.
*/
jspb.BinaryReader.prototype.readSfixed32 = function() {
goog.asserts.assert(
@@ -785,12 +816,27 @@ jspb.BinaryReader.prototype.readSfixed32 = function() {
};
+/**
+ * Reads a signed 32-bit fixed-length integer fiield from the binary stream, or
+ * throws an error if the next field in the stream is not of the correct wire
+ * type.
+ *
+ * @return {string} The value of the signed 32-bit integer field as a decimal
+ * string.
+ */
+jspb.BinaryReader.prototype.readSfixed32String = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED32);
+ return this.decoder_.readInt32().toString();
+};
+
+
/**
* Reads a signed 64-bit fixed-length integer fiield from the binary stream, or
* throws an error if the next field in the stream is not of the correct wire
* type.
*
- * @return {number} The value of the float field.
+ * @return {number} The value of the sfixed64 field.
*/
jspb.BinaryReader.prototype.readSfixed64 = function() {
goog.asserts.assert(
@@ -799,6 +845,22 @@ jspb.BinaryReader.prototype.readSfixed64 = function() {
};
+/**
+ * Reads a signed 64-bit fixed-length integer fiield from the binary stream, or
+ * throws an error if the next field in the stream is not of the correct wire
+ * type.
+ *
+ * Returns the value as a string.
+ *
+ * @return {string} The value of the sfixed64 field as a decimal string.
+ */
+jspb.BinaryReader.prototype.readSfixed64String = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED64);
+ return this.decoder_.readInt64String();
+};
+
+
/**
* Reads a 32-bit floating-point field from the binary stream, or throws an
* error if the next field in the stream is not of the correct wire type.
@@ -1027,6 +1089,16 @@ jspb.BinaryReader.prototype.readPackedSint64 = function() {
};
+/**
+ * Reads a packed sint64 field, which consists of a length header and a list of
+ * zigzag varints. Returns a list of strings.
+ * @return {!Array.}
+ */
+jspb.BinaryReader.prototype.readPackedSint64String = function() {
+ return this.readPackedField_(this.decoder_.readZigzagVarint64String);
+};
+
+
/**
* Reads a packed fixed32 field, which consists of a length header and a list
* of unsigned 32-bit ints.
@@ -1047,6 +1119,16 @@ jspb.BinaryReader.prototype.readPackedFixed64 = function() {
};
+/**
+ * Reads a packed fixed64 field, which consists of a length header and a list
+ * of unsigned 64-bit ints. Returns a list of strings.
+ * @return {!Array.}
+ */
+jspb.BinaryReader.prototype.readPackedFixed64String = function() {
+ return this.readPackedField_(this.decoder_.readUint64String);
+};
+
+
/**
* Reads a packed sfixed32 field, which consists of a length header and a list
* of 32-bit ints.
@@ -1067,6 +1149,16 @@ jspb.BinaryReader.prototype.readPackedSfixed64 = function() {
};
+/**
+ * Reads a packed sfixed64 field, which consists of a length header and a list
+ * of 64-bit ints. Returns a list of strings.
+ * @return {!Array.}
+ */
+jspb.BinaryReader.prototype.readPackedSfixed64String = function() {
+ return this.readPackedField_(this.decoder_.readInt64String);
+};
+
+
/**
* Reads a packed float field, which consists of a length header and a list of
* floats.
diff --git a/js/binary/utils.js b/js/binary/utils.js
index 514055531c637..bbf99cdf5c8f7 100644
--- a/js/binary/utils.js
+++ b/js/binary/utils.js
@@ -617,6 +617,16 @@ jspb.utils.decimalStringToHash64 = function(dec) {
};
+/**
+ * Converts a signed or unsigned decimal string into two 32-bit halves, and
+ * stores them in the temp variables listed above.
+ * @param {string} value The decimal string to convert.
+ */
+jspb.utils.splitDecimalString = function(value) {
+ jspb.utils.splitHash64(jspb.utils.decimalStringToHash64(value));
+};
+
+
/**
* Converts an 8-character hash string into its hexadecimal representation.
* @param {string} hash
diff --git a/js/binary/writer.js b/js/binary/writer.js
index 3eb2f1bdfe95e..c3009dbb546f0 100644
--- a/js/binary/writer.js
+++ b/js/binary/writer.js
@@ -434,6 +434,20 @@ jspb.BinaryWriter.prototype.writeZigzagVarint64_ = function(field, value) {
};
+/**
+ * Writes a zigzag varint field to the buffer without range checking.
+ * @param {number} field The field number.
+ * @param {string?} value The value to write.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writeZigzagVarint64String_ = function(
+ field, value) {
+ if (value == null) return;
+ this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
+ this.encoder_.writeZigzagVarint64String(value);
+};
+
+
/**
* Writes an int32 field to the buffer. Numbers outside the range [-2^31,2^31)
* will be truncated.
@@ -574,6 +588,20 @@ jspb.BinaryWriter.prototype.writeSint64 = function(field, value) {
};
+/**
+ * Writes a sint64 field to the buffer. Numbers outside the range [-2^63,2^63)
+ * will be truncated.
+ * @param {number} field The field number.
+ * @param {string?} value The decimal string to write.
+ */
+jspb.BinaryWriter.prototype.writeSint64String = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) &&
+ (value < jspb.BinaryConstants.TWO_TO_63));
+ this.writeZigzagVarint64String_(field, value);
+};
+
+
/**
* Writes a fixed32 field to the buffer. Numbers outside the range [0,2^32)
* will be truncated.
@@ -604,6 +632,19 @@ jspb.BinaryWriter.prototype.writeFixed64 = function(field, value) {
};
+/**
+ * Writes a fixed64 field (with value as a string) to the buffer.
+ * @param {number} field The field number.
+ * @param {string?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeFixed64String = function(field, value) {
+ if (value == null) return;
+ var num = jspb.arith.UInt64.fromString(value);
+ this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64);
+ this.encoder_.writeSplitFixed64(num.lo, num.hi);
+};
+
+
/**
* Writes a sfixed32 field to the buffer. Numbers outside the range
* [-2^31,2^31) will be truncated.
@@ -634,6 +675,20 @@ jspb.BinaryWriter.prototype.writeSfixed64 = function(field, value) {
};
+/**
+ * Writes a sfixed64 string field to the buffer. Numbers outside the range
+ * [-2^63,2^63) will be truncated.
+ * @param {number} field The field number.
+ * @param {string?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeSfixed64String = function(field, value) {
+ if (value == null) return;
+ var num = jspb.arith.Int64.fromString(value);
+ this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64);
+ this.encoder_.writeSplitFixed64(num.lo, num.hi);
+};
+
+
/**
* Writes a single-precision floating point field to the buffer. Numbers
* requiring more than 32 bits of precision will be truncated.
@@ -796,28 +851,11 @@ jspb.BinaryWriter.prototype.writeVarintHash64 = function(field, value) {
/**
- * Writes an array of numbers to the buffer as a repeated varint field.
+ * Writes an array of numbers to the buffer as a repeated 32-bit int field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
- * @private
*/
-jspb.BinaryWriter.prototype.writeRepeatedUnsignedVarint32_ =
- function(field, value) {
- if (value == null) return;
- for (var i = 0; i < value.length; i++) {
- this.writeUnsignedVarint32_(field, value[i]);
- }
-};
-
-
-/**
- * Writes an array of numbers to the buffer as a repeated varint field.
- * @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
- * @private
- */
-jspb.BinaryWriter.prototype.writeRepeatedSignedVarint32_ =
- function(field, value) {
+jspb.BinaryWriter.prototype.writeRepeatedInt32 = function(field, value) {
if (value == null) return;
for (var i = 0; i < value.length; i++) {
this.writeSignedVarint32_(field, value[i]);
@@ -826,28 +864,25 @@ jspb.BinaryWriter.prototype.writeRepeatedSignedVarint32_ =
/**
- * Writes an array of numbers to the buffer as a repeated varint field.
+ * Writes an array of numbers formatted as strings to the buffer as a repeated
+ * 32-bit int field.
* @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
- * @private
+ * @param {?Array.} value The array of ints to write.
*/
-jspb.BinaryWriter.prototype.writeRepeatedUnsignedVarint64_ =
- function(field, value) {
+jspb.BinaryWriter.prototype.writeRepeatedInt32String = function(field, value) {
if (value == null) return;
for (var i = 0; i < value.length; i++) {
- this.writeUnsignedVarint64_(field, value[i]);
+ this.writeInt32String(field, value[i]);
}
};
/**
- * Writes an array of numbers to the buffer as a repeated varint field.
+ * Writes an array of numbers to the buffer as a repeated 64-bit int field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
- * @private
*/
-jspb.BinaryWriter.prototype.writeRepeatedSignedVarint64_ =
- function(field, value) {
+jspb.BinaryWriter.prototype.writeRepeatedInt64 = function(field, value) {
if (value == null) return;
for (var i = 0; i < value.length; i++) {
this.writeSignedVarint64_(field, value[i]);
@@ -856,147 +891,112 @@ jspb.BinaryWriter.prototype.writeRepeatedSignedVarint64_ =
/**
- * Writes an array of numbers to the buffer as a repeated zigzag field.
+ * Writes an array of numbers formatted as strings to the buffer as a repeated
+ * 64-bit int field.
* @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
- * @private
+ * @param {?Array.} value The array of ints to write.
*/
-jspb.BinaryWriter.prototype.writeRepeatedZigzag32_ = function(field, value) {
+jspb.BinaryWriter.prototype.writeRepeatedInt64String = function(field, value) {
if (value == null) return;
for (var i = 0; i < value.length; i++) {
- this.writeZigzagVarint32_(field, value[i]);
+ this.writeInt64String(field, value[i]);
}
};
/**
- * Writes an array of numbers to the buffer as a repeated zigzag field.
+ * Writes an array numbers to the buffer as a repeated unsigned 32-bit int
+ * field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
- * @private
*/
-jspb.BinaryWriter.prototype.writeRepeatedZigzag_ = function(field, value) {
+jspb.BinaryWriter.prototype.writeRepeatedUint32 = function(field, value) {
if (value == null) return;
for (var i = 0; i < value.length; i++) {
- this.writeZigzagVarint64_(field, value[i]);
+ this.writeUnsignedVarint32_(field, value[i]);
}
};
-/**
- * Writes an array of numbers to the buffer as a repeated 32-bit int field.
- * @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
- */
-jspb.BinaryWriter.prototype.writeRepeatedInt32 =
- jspb.BinaryWriter.prototype.writeRepeatedSignedVarint32_;
-
-
/**
* Writes an array of numbers formatted as strings to the buffer as a repeated
- * 32-bit int field.
+ * unsigned 32-bit int field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
*/
-jspb.BinaryWriter.prototype.writeRepeatedInt32String =
- function(field, value) {
+jspb.BinaryWriter.prototype.writeRepeatedUint32String = function(field, value) {
if (value == null) return;
for (var i = 0; i < value.length; i++) {
- this.writeInt32String(field, value[i]);
+ this.writeUint32String(field, value[i]);
}
};
/**
- * Writes an array of numbers to the buffer as a repeated 64-bit int field.
+ * Writes an array numbers to the buffer as a repeated unsigned 64-bit int
+ * field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
*/
-jspb.BinaryWriter.prototype.writeRepeatedInt64 =
- jspb.BinaryWriter.prototype.writeRepeatedSignedVarint64_;
-
-
-/**
- * Writes an array of numbers formatted as strings to the buffer as a repeated
- * 64-bit int field.
- * @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
- */
-jspb.BinaryWriter.prototype.writeRepeatedInt64String =
- function(field, value) {
+jspb.BinaryWriter.prototype.writeRepeatedUint64 = function(field, value) {
if (value == null) return;
for (var i = 0; i < value.length; i++) {
- this.writeInt64String(field, value[i]);
+ this.writeUnsignedVarint64_(field, value[i]);
}
};
-/**
- * Writes an array numbers to the buffer as a repeated unsigned 32-bit int
- * field.
- * @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
- */
-jspb.BinaryWriter.prototype.writeRepeatedUint32 =
- jspb.BinaryWriter.prototype.writeRepeatedUnsignedVarint32_;
-
-
/**
* Writes an array of numbers formatted as strings to the buffer as a repeated
- * unsigned 32-bit int field.
+ * unsigned 64-bit int field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
*/
-jspb.BinaryWriter.prototype.writeRepeatedUint32String =
- function(field, value) {
+jspb.BinaryWriter.prototype.writeRepeatedUint64String = function(field, value) {
if (value == null) return;
for (var i = 0; i < value.length; i++) {
- this.writeUint32String(field, value[i]);
+ this.writeUint64String(field, value[i]);
}
};
/**
- * Writes an array numbers to the buffer as a repeated unsigned 64-bit int
- * field.
+ * Writes an array numbers to the buffer as a repeated signed 32-bit int field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
*/
-jspb.BinaryWriter.prototype.writeRepeatedUint64 =
- jspb.BinaryWriter.prototype.writeRepeatedUnsignedVarint64_;
-
-
-/**
- * Writes an array of numbers formatted as strings to the buffer as a repeated
- * unsigned 64-bit int field.
- * @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
- */
-jspb.BinaryWriter.prototype.writeRepeatedUint64String =
- function(field, value) {
+jspb.BinaryWriter.prototype.writeRepeatedSint32 = function(field, value) {
if (value == null) return;
for (var i = 0; i < value.length; i++) {
- this.writeUint64String(field, value[i]);
+ this.writeZigzagVarint32_(field, value[i]);
}
};
/**
- * Writes an array numbers to the buffer as a repeated signed 32-bit int field.
+ * Writes an array numbers to the buffer as a repeated signed 64-bit int field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
*/
-jspb.BinaryWriter.prototype.writeRepeatedSint32 =
- jspb.BinaryWriter.prototype.writeRepeatedZigzag32_;
+jspb.BinaryWriter.prototype.writeRepeatedSint64 = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeZigzagVarint64_(field, value[i]);
+ }
+};
/**
* Writes an array numbers to the buffer as a repeated signed 64-bit int field.
* @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
+ * @param {?Array.} value The array of ints to write.
*/
-jspb.BinaryWriter.prototype.writeRepeatedSint64 =
- jspb.BinaryWriter.prototype.writeRepeatedZigzag_;
+jspb.BinaryWriter.prototype.writeRepeatedSint64String = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeZigzagVarint64String_(field, value[i]);
+ }
+};
/**
@@ -1027,6 +1027,21 @@ jspb.BinaryWriter.prototype.writeRepeatedFixed64 = function(field, value) {
};
+/**
+ * Writes an array of numbers to the buffer as a repeated fixed64 field. This
+ * works for both signed and unsigned fixed64s.
+ * @param {number} field The field number.
+ * @param {?Array.} value The array of decimal strings to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedFixed64String = function(
+ field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeFixed64String(field, value[i]);
+ }
+};
+
+
/**
* Writes an array of numbers to the buffer as a repeated sfixed32 field.
* @param {number} field The field number.
@@ -1053,6 +1068,20 @@ jspb.BinaryWriter.prototype.writeRepeatedSfixed64 = function(field, value) {
};
+/**
+ * Writes an array of decimal strings to the buffer as a repeated sfixed64
+ * field.
+ * @param {number} field The field number.
+ * @param {?Array.} value The array of decimal strings to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedSfixed64String = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeSfixed64String(field, value[i]);
+ }
+};
+
+
/**
* Writes an array of numbers to the buffer as a repeated float field.
* @param {number} field The field number.
@@ -1203,151 +1232,127 @@ jspb.BinaryWriter.prototype.writeRepeatedVarintHash64 =
/**
- * Writes an array of numbers to the buffer as a packed varint field.
+ * Writes an array of numbers to the buffer as a packed 32-bit int field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
- * @private
*/
-jspb.BinaryWriter.prototype.writePackedUnsignedVarint32_ = function(
- field, value) {
+jspb.BinaryWriter.prototype.writePackedInt32 = function(field, value) {
if (value == null || !value.length) return;
var bookmark = this.beginDelimited_(field);
for (var i = 0; i < value.length; i++) {
- this.encoder_.writeUnsignedVarint32(value[i]);
+ this.encoder_.writeSignedVarint32(value[i]);
}
this.endDelimited_(bookmark);
};
/**
- * Writes an array of numbers to the buffer as a packed varint field.
- * @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
- * @private
+ * Writes an array of numbers represented as strings to the buffer as a packed
+ * 32-bit int field.
+ * @param {number} field
+ * @param {?Array.} value
*/
-jspb.BinaryWriter.prototype.writePackedSignedVarint32_ = function(
- field, value) {
+jspb.BinaryWriter.prototype.writePackedInt32String = function(field, value) {
if (value == null || !value.length) return;
var bookmark = this.beginDelimited_(field);
for (var i = 0; i < value.length; i++) {
- this.encoder_.writeSignedVarint32(value[i]);
+ this.encoder_.writeSignedVarint32(parseInt(value[i], 10));
}
this.endDelimited_(bookmark);
};
/**
- * Writes an array of numbers to the buffer as a packed varint field.
+ * Writes an array of numbers to the buffer as a packed 64-bit int field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
- * @private
*/
-jspb.BinaryWriter.prototype.writePackedUnsignedVarint64_ = function(
- field, value) {
+jspb.BinaryWriter.prototype.writePackedInt64 = function(field, value) {
if (value == null || !value.length) return;
var bookmark = this.beginDelimited_(field);
for (var i = 0; i < value.length; i++) {
- this.encoder_.writeUnsignedVarint64(value[i]);
+ this.encoder_.writeSignedVarint64(value[i]);
}
this.endDelimited_(bookmark);
};
/**
- * Writes an array of numbers to the buffer as a packed varint field.
- * @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
- * @private
+ * Writes an array of numbers represented as strings to the buffer as a packed
+ * 64-bit int field.
+ * @param {number} field
+ * @param {?Array.} value
*/
-jspb.BinaryWriter.prototype.writePackedSignedVarint64_ = function(
- field, value) {
+jspb.BinaryWriter.prototype.writePackedInt64String = function(field, value) {
if (value == null || !value.length) return;
var bookmark = this.beginDelimited_(field);
for (var i = 0; i < value.length; i++) {
- this.encoder_.writeSignedVarint64(value[i]);
+ var num = jspb.arith.Int64.fromString(value[i]);
+ this.encoder_.writeSplitVarint64(num.lo, num.hi);
}
this.endDelimited_(bookmark);
};
/**
- * Writes an array of numbers to the buffer as a packed zigzag field.
+ * Writes an array numbers to the buffer as a packed unsigned 32-bit int field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
- * @private
*/
-jspb.BinaryWriter.prototype.writePackedZigzag32_ = function(field, value) {
+jspb.BinaryWriter.prototype.writePackedUint32 = function(field, value) {
if (value == null || !value.length) return;
var bookmark = this.beginDelimited_(field);
for (var i = 0; i < value.length; i++) {
- this.encoder_.writeZigzagVarint32(value[i]);
+ this.encoder_.writeUnsignedVarint32(value[i]);
}
this.endDelimited_(bookmark);
};
/**
- * Writes an array of numbers to the buffer as a packed zigzag field.
- * @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
- * @private
+ * Writes an array of numbers represented as strings to the buffer as a packed
+ * unsigned 32-bit int field.
+ * @param {number} field
+ * @param {?Array.} value
*/
-jspb.BinaryWriter.prototype.writePackedZigzag64_ = function(field, value) {
+jspb.BinaryWriter.prototype.writePackedUint32String =
+ function(field, value) {
if (value == null || !value.length) return;
var bookmark = this.beginDelimited_(field);
for (var i = 0; i < value.length; i++) {
- this.encoder_.writeZigzagVarint64(value[i]);
+ this.encoder_.writeUnsignedVarint32(parseInt(value[i], 10));
}
this.endDelimited_(bookmark);
};
/**
- * Writes an array of numbers to the buffer as a packed 32-bit int field.
+ * Writes an array numbers to the buffer as a packed unsigned 64-bit int field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
*/
-jspb.BinaryWriter.prototype.writePackedInt32 =
- jspb.BinaryWriter.prototype.writePackedSignedVarint32_;
-
-
-/**
- * Writes an array of numbers represented as strings to the buffer as a packed
- * 32-bit int field.
- * @param {number} field
- * @param {?Array.} value
- */
-jspb.BinaryWriter.prototype.writePackedInt32String = function(field, value) {
+jspb.BinaryWriter.prototype.writePackedUint64 = function(field, value) {
if (value == null || !value.length) return;
var bookmark = this.beginDelimited_(field);
for (var i = 0; i < value.length; i++) {
- this.encoder_.writeSignedVarint32(parseInt(value[i], 10));
+ this.encoder_.writeUnsignedVarint64(value[i]);
}
this.endDelimited_(bookmark);
};
-/**
- * Writes an array of numbers to the buffer as a packed 64-bit int field.
- * @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
- */
-jspb.BinaryWriter.prototype.writePackedInt64 =
- jspb.BinaryWriter.prototype.writePackedSignedVarint64_;
-
-
/**
* Writes an array of numbers represented as strings to the buffer as a packed
- * 64-bit int field.
+ * unsigned 64-bit int field.
* @param {number} field
* @param {?Array.} value
*/
-jspb.BinaryWriter.prototype.writePackedInt64String =
+jspb.BinaryWriter.prototype.writePackedUint64String =
function(field, value) {
if (value == null || !value.length) return;
var bookmark = this.beginDelimited_(field);
for (var i = 0; i < value.length; i++) {
- var num = jspb.arith.Int64.fromString(value[i]);
+ var num = jspb.arith.UInt64.fromString(value[i]);
this.encoder_.writeSplitVarint64(num.lo, num.hi);
}
this.endDelimited_(bookmark);
@@ -1355,74 +1360,50 @@ jspb.BinaryWriter.prototype.writePackedInt64String =
/**
- * Writes an array numbers to the buffer as a packed unsigned 32-bit int field.
+ * Writes an array numbers to the buffer as a packed signed 32-bit int field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
*/
-jspb.BinaryWriter.prototype.writePackedUint32 =
- jspb.BinaryWriter.prototype.writePackedUnsignedVarint32_;
-
-
-/**
- * Writes an array of numbers represented as strings to the buffer as a packed
- * unsigned 32-bit int field.
- * @param {number} field
- * @param {?Array.} value
- */
-jspb.BinaryWriter.prototype.writePackedUint32String =
- function(field, value) {
+jspb.BinaryWriter.prototype.writePackedSint32 = function(field, value) {
if (value == null || !value.length) return;
var bookmark = this.beginDelimited_(field);
for (var i = 0; i < value.length; i++) {
- this.encoder_.writeUnsignedVarint32(parseInt(value[i], 10));
+ this.encoder_.writeZigzagVarint32(value[i]);
}
this.endDelimited_(bookmark);
};
/**
- * Writes an array numbers to the buffer as a packed unsigned 64-bit int field.
+ * Writes an array of numbers to the buffer as a packed signed 64-bit int field.
* @param {number} field The field number.
* @param {?Array.} value The array of ints to write.
*/
-jspb.BinaryWriter.prototype.writePackedUint64 =
- jspb.BinaryWriter.prototype.writePackedUnsignedVarint64_;
-
-
-/**
- * Writes an array of numbers represented as strings to the buffer as a packed
- * unsigned 64-bit int field.
- * @param {number} field
- * @param {?Array.} value
- */
-jspb.BinaryWriter.prototype.writePackedUint64String =
- function(field, value) {
+jspb.BinaryWriter.prototype.writePackedSint64 = function(field, value) {
if (value == null || !value.length) return;
var bookmark = this.beginDelimited_(field);
for (var i = 0; i < value.length; i++) {
- var num = jspb.arith.UInt64.fromString(value[i]);
- this.encoder_.writeSplitVarint64(num.lo, num.hi);
+ this.encoder_.writeZigzagVarint64(value[i]);
}
this.endDelimited_(bookmark);
};
/**
- * Writes an array numbers to the buffer as a packed signed 32-bit int field.
+ * Writes an array of decimal strings to the buffer as a packed signed 64-bit
+ * int field.
* @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
+ * @param {?Array.} value The array of decimal strings to write.
*/
-jspb.BinaryWriter.prototype.writePackedSint32 =
- jspb.BinaryWriter.prototype.writePackedZigzag32_;
-
-
-/**
- * Writes an array numbers to the buffer as a packed signed 64-bit int field.
- * @param {number} field The field number.
- * @param {?Array.} value The array of ints to write.
- */
-jspb.BinaryWriter.prototype.writePackedSint64 =
- jspb.BinaryWriter.prototype.writePackedZigzag64_;
+jspb.BinaryWriter.prototype.writePackedSint64String = function(field, value) {
+ if (value == null || !value.length) return;
+ var bookmark = this.beginDelimited_(field);
+ for (var i = 0; i < value.length; i++) {
+ // TODO(haberman): make lossless
+ this.encoder_.writeZigzagVarint64(parseInt(value[i], 10));
+ }
+ this.endDelimited_(bookmark);
+};
/**
@@ -1455,6 +1436,23 @@ jspb.BinaryWriter.prototype.writePackedFixed64 = function(field, value) {
};
+/**
+ * Writes an array of numbers represented as strings to the buffer as a packed
+ * fixed64 field.
+ * @param {number} field The field number.
+ * @param {?Array.} value The array of strings to write.
+ */
+jspb.BinaryWriter.prototype.writePackedFixed64String = function(field, value) {
+ if (value == null || !value.length) return;
+ this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED);
+ this.encoder_.writeUnsignedVarint32(value.length * 8);
+ for (var i = 0; i < value.length; i++) {
+ var num = jspb.arith.UInt64.fromString(value[i]);
+ this.encoder_.writeSplitFixed64(num.lo, num.hi);
+ }
+};
+
+
/**
* Writes an array of numbers to the buffer as a packed sfixed32 field.
* @param {number} field The field number.
@@ -1485,6 +1483,21 @@ jspb.BinaryWriter.prototype.writePackedSfixed64 = function(field, value) {
};
+/**
+ * Writes an array of numbers to the buffer as a packed sfixed64 field.
+ * @param {number} field The field number.
+ * @param {?Array.} value The array of decimal strings to write.
+ */
+jspb.BinaryWriter.prototype.writePackedSfixed64String = function(field, value) {
+ if (value == null || !value.length) return;
+ this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED);
+ this.encoder_.writeUnsignedVarint32(value.length * 8);
+ for (var i = 0; i < value.length; i++) {
+ this.encoder_.writeInt64String(value[i]);
+ }
+};
+
+
/**
* Writes an array of numbers to the buffer as a packed float field.
* @param {number} field The field number.
diff --git a/js/commonjs/export.js b/js/commonjs/export.js
index 2403b1a490ccc..1eaab5d51cbe4 100644
--- a/js/commonjs/export.js
+++ b/js/commonjs/export.js
@@ -24,3 +24,4 @@ exports.ExtensionFieldBinaryInfo = jspb.ExtensionFieldBinaryInfo;
exports.exportSymbol = goog.exportSymbol;
exports.inherits = goog.inherits;
exports.object = {extend: goog.object.extend};
+exports.typeOf = goog.typeOf;
diff --git a/js/commonjs/export_testdeps.js b/js/commonjs/export_testdeps.js
index 59c77ca2cd4f3..174c61e6840c4 100644
--- a/js/commonjs/export_testdeps.js
+++ b/js/commonjs/export_testdeps.js
@@ -12,6 +12,7 @@ goog.require('jspb.arith.Int64');
goog.require('jspb.arith.UInt64');
goog.require('jspb.BinaryEncoder');
goog.require('jspb.BinaryDecoder');
+goog.require('jspb.BinaryWriter');
goog.require('jspb.utils');
exports.goog = goog;
diff --git a/js/gulpfile.js b/js/gulpfile.js
index cca99131a77b5..5f1b1d93f6081 100644
--- a/js/gulpfile.js
+++ b/js/gulpfile.js
@@ -24,7 +24,7 @@ var wellKnownTypes = [
];
gulp.task('genproto_closure', function (cb) {
- exec(protoc + ' --js_out=library=testproto_libs,binary:. -I ../src -I . *.proto ../src/google/protobuf/descriptor.proto',
+ exec(protoc + ' --js_out=library=testproto_libs,binary:. -I ../src -I . *.proto && ' + protoc + ' --js_out=one_output_file_per_input_file,binary:. -I ../src -I . ' + wellKnownTypes.join(' '),
function (err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
@@ -33,7 +33,7 @@ gulp.task('genproto_closure', function (cb) {
});
gulp.task('genproto_commonjs', function (cb) {
- exec('mkdir -p commonjs_out && ' + protoc + ' --js_out=import_style=commonjs,binary:commonjs_out -I ../src -I commonjs -I . *.proto commonjs/test*/*.proto ../src/google/protobuf/descriptor.proto',
+ exec('mkdir -p commonjs_out && ' + protoc + ' --js_out=import_style=commonjs,binary:commonjs_out -I ../src -I commonjs -I . *.proto commonjs/test*/*.proto ' + wellKnownTypes.join(' '),
function (err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
@@ -42,7 +42,7 @@ gulp.task('genproto_commonjs', function (cb) {
});
gulp.task('genproto_commonjs_wellknowntypes', function (cb) {
- exec('mkdir -p commonjs_out/node_modules/google-protobuf && ' + protoc + ' --js_out=import_style=commonjs,binary:commonjs_out/node_modules/google-protobuf -I ../src ../src/google/protobuf/descriptor.proto',
+ exec('mkdir -p commonjs_out/node_modules/google-protobuf && ' + protoc + ' --js_out=import_style=commonjs,binary:commonjs_out/node_modules/google-protobuf -I ../src ' + wellKnownTypes.join(' '),
function (err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
diff --git a/js/jasmine.json b/js/jasmine.json
index f83c54c09d35d..093f15794f71b 100644
--- a/js/jasmine.json
+++ b/js/jasmine.json
@@ -7,6 +7,9 @@
"helpers": [
"node_modules/google-closure-library/closure/goog/bootstrap/nodejs.js",
"node_loader.js",
- "deps.js"
+ "deps.js",
+ "google/protobuf/any.js",
+ "google/protobuf/struct.js",
+ "google/protobuf/timestamp.js"
]
}
diff --git a/js/map.js b/js/map.js
index 0f8401c47906c..93f08e0376812 100644
--- a/js/map.js
+++ b/js/map.js
@@ -130,6 +130,58 @@ jspb.Map.prototype.toArray = function() {
};
+/**
+ * Returns the map formatted as an array of key-value pairs, suitable for the
+ * toObject() form of a message.
+ *
+ * @param {boolean=} includeInstance Whether to include the JSPB instance for
+ * transitional soy proto support: http://goto/soy-param-migration
+ * @param {!function((boolean|undefined),!V):!Object=} valueToObject
+ * The static toObject() method, if V is a message type.
+ * @return {!Array>}
+ */
+jspb.Map.prototype.toObject = function(includeInstance, valueToObject) {
+ var rawArray = this.toArray();
+ var entries = [];
+ for (var i = 0; i < rawArray.length; i++) {
+ var entry = this.map_[rawArray[i][0].toString()];
+ this.wrapEntry_(entry);
+ var valueWrapper = /** @type {!V|undefined} */ (entry.valueWrapper);
+ if (valueWrapper) {
+ goog.asserts.assert(valueToObject);
+ entries.push([entry.key, valueToObject(includeInstance, valueWrapper)]);
+ } else {
+ entries.push([entry.key, entry.value]);
+ }
+ }
+ return entries;
+};
+
+
+/**
+ * Returns a Map from the given array of key-value pairs when the values are of
+ * message type. The values in the array must match the format returned by their
+ * message type's toObject() method.
+ *
+ * @template K, V
+ * @param {!Array>} entries
+ * @param {!function(new:V)|function(new:V,?)} valueCtor
+ * The constructor for type V.
+ * @param {!function(!Object):V} valueFromObject
+ * The fromObject function for type V.
+ * @return {!jspb.Map}
+ */
+jspb.Map.fromObject = function(entries, valueCtor, valueFromObject) {
+ var result = new jspb.Map([], valueCtor);
+ for (var i = 0; i < entries.length; i++) {
+ var key = entries[i][0];
+ var value = valueFromObject(entries[i][1]);
+ result.set(key, value);
+ }
+ return result;
+};
+
+
/**
* Helper: return an iterator over an array.
* @template T
@@ -193,7 +245,7 @@ jspb.Map.prototype.del = function(key) {
* to help out Angular 1.x users. Still evaluating whether this is the best
* option.
*
- * @return {!Array}
+ * @return {!Array>}
*/
jspb.Map.prototype.getEntryList = function() {
var entries = [];
diff --git a/js/maps_test.js b/js/maps_test.js
index 0d442f4fd1011..6e6ddc295c8f7 100755
--- a/js/maps_test.js
+++ b/js/maps_test.js
@@ -262,6 +262,7 @@ function makeTests(msgInfo, submessageCtor, suffix) {
});
}
+
/**
* Exercises the lazy map<->underlying array sync.
*/
@@ -290,12 +291,16 @@ function makeTests(msgInfo, submessageCtor, suffix) {
}
describe('mapsTest', function() {
- makeTests({
- constructor: proto.jspb.test.TestMapFields,
- deserializeBinary: proto.jspb.test.TestMapFields.deserializeBinary
- }, proto.jspb.test.MapValueMessage, "_Binary");
- makeTests({
- constructor: proto.jspb.test.TestMapFieldsNoBinary,
- deserializeBinary: null
- }, proto.jspb.test.MapValueMessageNoBinary, "_NoBinary");
+ makeTests(
+ {
+ constructor: proto.jspb.test.TestMapFields,
+ deserializeBinary: proto.jspb.test.TestMapFields.deserializeBinary
+ },
+ proto.jspb.test.MapValueMessage, '_Binary');
+ makeTests(
+ {
+ constructor: proto.jspb.test.TestMapFieldsNoBinary,
+ deserializeBinary: null
+ },
+ proto.jspb.test.MapValueMessageNoBinary, '_NoBinary');
});
diff --git a/js/message.js b/js/message.js
index 8def763e9cd13..1eb88aef0300e 100644
--- a/js/message.js
+++ b/js/message.js
@@ -550,11 +550,11 @@ jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions,
jspb.Message.readBinaryExtension = function(msg, reader, extensions,
getExtensionFn, setExtensionFn) {
var binaryFieldInfo = extensions[reader.getFieldNumber()];
- var fieldInfo = binaryFieldInfo.fieldInfo;
if (!binaryFieldInfo) {
reader.skipField();
return;
}
+ var fieldInfo = binaryFieldInfo.fieldInfo;
if (!binaryFieldInfo.binaryReaderFn) {
throw new Error('Deserializing extension whose generated code does not ' +
'support binary format');
@@ -972,7 +972,8 @@ jspb.Message.wrapRepeatedField_ = function(msg, ctor, fieldNumber) {
* Sets a proto field and syncs it to the backing array.
* @param {!jspb.Message} msg A jspb proto.
* @param {number} fieldNumber The field number.
- * @param {jspb.Message|undefined} value A new value for this proto field.
+ * @param {?jspb.Message|?jspb.Map|undefined} value A new value for this proto
+ * field.
* @protected
*/
jspb.Message.setWrapperField = function(msg, fieldNumber, value) {
diff --git a/js/message_test.js b/js/message_test.js
index b0a0a72e649b5..082da944b3b1f 100644
--- a/js/message_test.js
+++ b/js/message_test.js
@@ -73,10 +73,12 @@ goog.require('proto.jspb.test.TestGroup1');
goog.require('proto.jspb.test.TestMessageWithOneof');
goog.require('proto.jspb.test.TestReservedNames');
goog.require('proto.jspb.test.TestReservedNamesExtension');
+goog.require('proto.jspb.test.Deeply.Nested.Message');
// CommonJS-LoadFromFile: test2_pb proto.jspb.test
goog.require('proto.jspb.test.ExtensionMessage');
goog.require('proto.jspb.test.TestExtensionsMessage');
+goog.require('proto.jspb.test.ForeignNestedFieldMessage');
@@ -1047,11 +1049,14 @@ describe('Message test suite', function() {
var nested = new proto.jspb.test.Deeply.Nested.Message();
nested.setCount(5);
msg.setDeeplyNestedMessage(nested);
+ assertEquals(5, msg.getDeeplyNestedMessage().getCount());
// After a serialization-deserialization round trip we should get back the
// same data we started with.
var serialized = msg.serializeBinary();
- var deserialized = proto.jspb.test.ForeignNestedFieldMessage.deserializeBinary(serialized);
+ var deserialized =
+ proto.jspb.test.ForeignNestedFieldMessage.deserializeBinary(serialized);
assertEquals(5, deserialized.getDeeplyNestedMessage().getCount());
});
+
});
diff --git a/js/proto3_test.js b/js/proto3_test.js
index 3c929effd58c8..81d6de2f5eeec 100644
--- a/js/proto3_test.js
+++ b/js/proto3_test.js
@@ -38,6 +38,12 @@ goog.require('proto.jspb.test.ForeignMessage');
goog.require('proto.jspb.test.Proto3Enum');
goog.require('proto.jspb.test.TestProto3');
+// CommonJS-LoadFromFile: google/protobuf/timestamp_pb proto.google.protobuf
+goog.require('proto.google.protobuf.Timestamp');
+
+// CommonJS-LoadFromFile: google/protobuf/struct_pb proto.google.protobuf
+goog.require('proto.google.protobuf.Struct');
+
var BYTES = new Uint8Array([1, 2, 8, 9]);
var BYTES_B64 = goog.crypt.base64.encodeByteArray(BYTES);
@@ -326,4 +332,36 @@ describe('proto3Test', function() {
assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
});
+
+ it('testTimestampWellKnownType', function() {
+ var msg = new proto.google.protobuf.Timestamp();
+ msg.fromDate(new Date(123456789));
+ assertEquals(123456, msg.getSeconds());
+ assertEquals(789000000, msg.getNanos());
+ var date = msg.toDate();
+ assertEquals(123456789, date.getTime());
+ });
+
+ it('testStructWellKnownType', function() {
+ var jsObj = {
+ abc: "def",
+ number: 12345.678,
+ nullKey: null,
+ boolKey: true,
+ listKey: [1, null, true, false, "abc"],
+ structKey: {foo: "bar", somenum: 123},
+ complicatedKey: [{xyz: {abc: [3, 4, null, false]}}, "zzz"]
+ };
+
+ var struct = proto.google.protobuf.Struct.fromJavaScript(jsObj);
+ var jsObj2 = struct.toJavaScript();
+
+ assertEquals("def", jsObj2.abc);
+ assertEquals(12345.678, jsObj2.number);
+ assertEquals(null, jsObj2.nullKey);
+ assertEquals(true, jsObj2.boolKey);
+ assertEquals("abc", jsObj2.listKey[4]);
+ assertEquals("bar", jsObj2.structKey.foo);
+ assertEquals(4, jsObj2.complicatedKey[0].xyz.abc[1]);
+ });
});
diff --git a/js/test.proto b/js/test.proto
index db238e1a6b7ea..2be5b8c184140 100644
--- a/js/test.proto
+++ b/js/test.proto
@@ -234,7 +234,9 @@ message TestEndsWithBytes {
optional bytes data = 2;
}
+
message TestMapFieldsNoBinary {
+
map map_string_string = 1;
map map_string_int32 = 2;
map map_string_int64 = 3;
@@ -258,6 +260,7 @@ enum MapValueEnumNoBinary {
}
message MapValueMessageNoBinary {
+
optional int32 foo = 1;
}
diff --git a/protobuf.bzl b/protobuf.bzl
index 74cb9ad54c142..3998e77b03916 100644
--- a/protobuf.bzl
+++ b/protobuf.bzl
@@ -1,5 +1,3 @@
-# -*- mode: python; -*- PYTHON-PREPROCESSING-REQUIRED
-
def _GetPath(ctx, path):
if ctx.label.workspace_root:
return ctx.label.workspace_root + '/' + path
@@ -240,7 +238,6 @@ def cc_proto_library(
includes=includes,
**kargs)
-
def internal_gen_well_known_protos_java(srcs):
"""Bazel rule to generate the gen_well_known_protos_java genrule
@@ -264,7 +261,6 @@ def internal_gen_well_known_protos_java(srcs):
tools = [":protoc"],
)
-
def internal_copied_filegroup(name, srcs, strip_prefix, dest, **kwargs):
"""Macro to copy files to a different directory and then create a filegroup.
@@ -294,7 +290,6 @@ def internal_copied_filegroup(name, srcs, strip_prefix, dest, **kwargs):
srcs = outs,
**kwargs)
-
def py_proto_library(
name,
srcs=[],
diff --git a/python/google/protobuf/descriptor_pool.py b/python/google/protobuf/descriptor_pool.py
index 5f43ee5f210e6..fc3a7f4404442 100644
--- a/python/google/protobuf/descriptor_pool.py
+++ b/python/google/protobuf/descriptor_pool.py
@@ -57,6 +57,8 @@
__author__ = 'matthewtoia@google.com (Matt Toia)'
+import collections
+
from google.protobuf import descriptor
from google.protobuf import descriptor_database
from google.protobuf import text_encoding
@@ -88,6 +90,14 @@ def _OptionsOrNone(descriptor_proto):
return None
+def _IsMessageSetExtension(field):
+ return (field.is_extension and
+ field.containing_type.has_options and
+ field.containing_type.GetOptions().message_set_wire_format and
+ field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and
+ field.label == descriptor.FieldDescriptor.LABEL_OPTIONAL)
+
+
class DescriptorPool(object):
"""A collection of protobufs dynamically constructed by descriptor protos."""
@@ -115,6 +125,12 @@ def __init__(self, descriptor_db=None):
self._descriptors = {}
self._enum_descriptors = {}
self._file_descriptors = {}
+ self._toplevel_extensions = {}
+ # We store extensions in two two-level mappings: The first key is the
+ # descriptor of the message being extended, the second key is the extension
+ # full name or its tag number.
+ self._extensions_by_name = collections.defaultdict(dict)
+ self._extensions_by_number = collections.defaultdict(dict)
def Add(self, file_desc_proto):
"""Adds the FileDescriptorProto and its types to this pool.
@@ -170,6 +186,48 @@ def AddEnumDescriptor(self, enum_desc):
self._enum_descriptors[enum_desc.full_name] = enum_desc
self.AddFileDescriptor(enum_desc.file)
+ def AddExtensionDescriptor(self, extension):
+ """Adds a FieldDescriptor describing an extension to the pool.
+
+ Args:
+ extension: A FieldDescriptor.
+
+ Raises:
+ AssertionError: when another extension with the same number extends the
+ same message.
+ TypeError: when the specified extension is not a
+ descriptor.FieldDescriptor.
+ """
+ if not (isinstance(extension, descriptor.FieldDescriptor) and
+ extension.is_extension):
+ raise TypeError('Expected an extension descriptor.')
+
+ if extension.extension_scope is None:
+ self._toplevel_extensions[extension.full_name] = extension
+
+ try:
+ existing_desc = self._extensions_by_number[
+ extension.containing_type][extension.number]
+ except KeyError:
+ pass
+ else:
+ if extension is not existing_desc:
+ raise AssertionError(
+ 'Extensions "%s" and "%s" both try to extend message type "%s" '
+ 'with field number %d.' %
+ (extension.full_name, existing_desc.full_name,
+ extension.containing_type.full_name, extension.number))
+
+ self._extensions_by_number[extension.containing_type][
+ extension.number] = extension
+ self._extensions_by_name[extension.containing_type][
+ extension.full_name] = extension
+
+ # Also register MessageSet extensions with the type name.
+ if _IsMessageSetExtension(extension):
+ self._extensions_by_name[extension.containing_type][
+ extension.message_type.full_name] = extension
+
def AddFileDescriptor(self, file_desc):
"""Adds a FileDescriptor to the pool, non-recursively.
@@ -302,6 +360,14 @@ def FindExtensionByName(self, full_name):
A FieldDescriptor, describing the named extension.
"""
full_name = _NormalizeFullyQualifiedName(full_name)
+ try:
+ # The proto compiler does not give any link between the FileDescriptor
+ # and top-level extensions unless the FileDescriptorProto is added to
+ # the DescriptorDatabase, but this can impact memory usage.
+ # So we registered these extensions by name explicitly.
+ return self._toplevel_extensions[full_name]
+ except KeyError:
+ pass
message_name, _, extension_name = full_name.rpartition('.')
try:
# Most extensions are nested inside a message.
@@ -311,6 +377,39 @@ def FindExtensionByName(self, full_name):
scope = self.FindFileContainingSymbol(full_name)
return scope.extensions_by_name[extension_name]
+ def FindExtensionByNumber(self, message_descriptor, number):
+ """Gets the extension of the specified message with the specified number.
+
+ Extensions have to be registered to this pool by calling
+ AddExtensionDescriptor.
+
+ Args:
+ message_descriptor: descriptor of the extended message.
+ number: integer, number of the extension field.
+
+ Returns:
+ A FieldDescriptor describing the extension.
+
+ Raise:
+ KeyError: when no extension with the given number is known for the
+ specified message.
+ """
+ return self._extensions_by_number[message_descriptor][number]
+
+ def FindAllExtensions(self, message_descriptor):
+ """Gets all the known extension of a given message.
+
+ Extensions have to be registered to this pool by calling
+ AddExtensionDescriptor.
+
+ Args:
+ message_descriptor: descriptor of the extended message.
+
+ Returns:
+ A list of FieldDescriptor describing the extensions.
+ """
+ return list(self._extensions_by_number[message_descriptor].values())
+
def _ConvertFileProtoToFileDescriptor(self, file_proto):
"""Creates a FileDescriptor from a proto or returns a cached copy.
diff --git a/python/google/protobuf/internal/decoder.py b/python/google/protobuf/internal/decoder.py
index 31869e4575ca9..c5f73dc1b566c 100755
--- a/python/google/protobuf/internal/decoder.py
+++ b/python/google/protobuf/internal/decoder.py
@@ -642,10 +642,10 @@ def DecodeField(buffer, pos, end, message, field_dict):
MESSAGE_SET_ITEM_TAG = encoder.TagBytes(1, wire_format.WIRETYPE_START_GROUP)
-def MessageSetItemDecoder(extensions_by_number):
+def MessageSetItemDecoder(descriptor):
"""Returns a decoder for a MessageSet item.
- The parameter is the _extensions_by_number map for the message class.
+ The parameter is the message Descriptor.
The message set message looks like this:
message MessageSet {
@@ -694,7 +694,7 @@ def DecodeItem(buffer, pos, end, message, field_dict):
if message_start == -1:
raise _DecodeError('MessageSet item missing message.')
- extension = extensions_by_number.get(type_id)
+ extension = message.Extensions._FindExtensionByNumber(type_id)
if extension is not None:
value = field_dict.get(extension)
if value is None:
diff --git a/python/google/protobuf/internal/descriptor_pool_test.py b/python/google/protobuf/internal/descriptor_pool_test.py
index d4de2d812ade0..1e710dcf82b1a 100644
--- a/python/google/protobuf/internal/descriptor_pool_test.py
+++ b/python/google/protobuf/internal/descriptor_pool_test.py
@@ -254,6 +254,53 @@ def testFindExtensionByName(self):
with self.assertRaises(KeyError):
self.pool.FindFieldByName('Does not exist')
+ def testFindAllExtensions(self):
+ factory1_message = self.pool.FindMessageTypeByName(
+ 'google.protobuf.python.internal.Factory1Message')
+ factory2_message = self.pool.FindMessageTypeByName(
+ 'google.protobuf.python.internal.Factory2Message')
+ # An extension defined in a message.
+ one_more_field = factory2_message.extensions_by_name['one_more_field']
+ self.pool.AddExtensionDescriptor(one_more_field)
+ # An extension defined at file scope.
+ factory_test2 = self.pool.FindFileByName(
+ 'google/protobuf/internal/factory_test2.proto')
+ another_field = factory_test2.extensions_by_name['another_field']
+ self.pool.AddExtensionDescriptor(another_field)
+
+ extensions = self.pool.FindAllExtensions(factory1_message)
+ expected_extension_numbers = set([one_more_field, another_field])
+ self.assertEqual(expected_extension_numbers, set(extensions))
+ # Verify that mutating the returned list does not affect the pool.
+ extensions.append('unexpected_element')
+ # Get the extensions again, the returned value does not contain the
+ # 'unexpected_element'.
+ extensions = self.pool.FindAllExtensions(factory1_message)
+ self.assertEqual(expected_extension_numbers, set(extensions))
+
+ def testFindExtensionByNumber(self):
+ factory1_message = self.pool.FindMessageTypeByName(
+ 'google.protobuf.python.internal.Factory1Message')
+ factory2_message = self.pool.FindMessageTypeByName(
+ 'google.protobuf.python.internal.Factory2Message')
+ # An extension defined in a message.
+ one_more_field = factory2_message.extensions_by_name['one_more_field']
+ self.pool.AddExtensionDescriptor(one_more_field)
+ # An extension defined at file scope.
+ factory_test2 = self.pool.FindFileByName(
+ 'google/protobuf/internal/factory_test2.proto')
+ another_field = factory_test2.extensions_by_name['another_field']
+ self.pool.AddExtensionDescriptor(another_field)
+
+ # An extension defined in a message.
+ extension = self.pool.FindExtensionByNumber(factory1_message, 1001)
+ self.assertEqual(extension.name, 'one_more_field')
+ # An extension defined at file scope.
+ extension = self.pool.FindExtensionByNumber(factory1_message, 1002)
+ self.assertEqual(extension.name, 'another_field')
+ with self.assertRaises(KeyError):
+ extension = self.pool.FindExtensionByNumber(factory1_message, 1234567)
+
def testExtensionsAreNotFields(self):
with self.assertRaises(KeyError):
self.pool.FindFieldByName('google.protobuf.python.internal.another_field')
diff --git a/python/google/protobuf/internal/message_factory_test.py b/python/google/protobuf/internal/message_factory_test.py
index 7bb7d1acecd62..4caa244344a59 100644
--- a/python/google/protobuf/internal/message_factory_test.py
+++ b/python/google/protobuf/internal/message_factory_test.py
@@ -114,18 +114,18 @@ def testGetMessages(self):
).issubset(set(messages.keys())))
self._ExerciseDynamicClass(
messages['google.protobuf.python.internal.Factory2Message'])
- self.assertTrue(
- set(['google.protobuf.python.internal.Factory2Message.one_more_field',
- 'google.protobuf.python.internal.another_field'],
- ).issubset(
- set(messages['google.protobuf.python.internal.Factory1Message']
- ._extensions_by_name.keys())))
factory_msg1 = messages['google.protobuf.python.internal.Factory1Message']
+ self.assertTrue(set(
+ ['google.protobuf.python.internal.Factory2Message.one_more_field',
+ 'google.protobuf.python.internal.another_field'],).issubset(set(
+ ext.full_name
+ for ext in factory_msg1.DESCRIPTOR.file.pool.FindAllExtensions(
+ factory_msg1.DESCRIPTOR))))
msg1 = messages['google.protobuf.python.internal.Factory1Message']()
- ext1 = factory_msg1._extensions_by_name[
- 'google.protobuf.python.internal.Factory2Message.one_more_field']
- ext2 = factory_msg1._extensions_by_name[
- 'google.protobuf.python.internal.another_field']
+ ext1 = msg1.Extensions._FindExtensionByName(
+ 'google.protobuf.python.internal.Factory2Message.one_more_field')
+ ext2 = msg1.Extensions._FindExtensionByName(
+ 'google.protobuf.python.internal.another_field')
msg1.Extensions[ext1] = 'test1'
msg1.Extensions[ext2] = 'test2'
self.assertEqual('test1', msg1.Extensions[ext1])
diff --git a/python/google/protobuf/internal/python_message.py b/python/google/protobuf/internal/python_message.py
index dc6565d42b8ce..c1bd1f9c3bde8 100755
--- a/python/google/protobuf/internal/python_message.py
+++ b/python/google/protobuf/internal/python_message.py
@@ -51,8 +51,8 @@
__author__ = 'robinson@google.com (Will Robinson)'
from io import BytesIO
-import sys
import struct
+import sys
import weakref
import six
@@ -162,12 +162,10 @@ def __init__(cls, name, bases, dictionary):
"""
descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
cls._decoders_by_tag = {}
- cls._extensions_by_name = {}
- cls._extensions_by_number = {}
if (descriptor.has_options and
descriptor.GetOptions().message_set_wire_format):
cls._decoders_by_tag[decoder.MESSAGE_SET_ITEM_TAG] = (
- decoder.MessageSetItemDecoder(cls._extensions_by_number), None)
+ decoder.MessageSetItemDecoder(descriptor), None)
# Attach stuff to each FieldDescriptor for quick lookup later on.
for field in descriptor.fields:
@@ -747,32 +745,21 @@ def _AddPropertiesForExtensions(descriptor, cls):
constant_name = extension_name.upper() + "_FIELD_NUMBER"
setattr(cls, constant_name, extension_field.number)
+ # TODO(amauryfa): Migrate all users of these attributes to functions like
+ # pool.FindExtensionByNumber(descriptor).
+ if descriptor.file is not None:
+ # TODO(amauryfa): Use cls.MESSAGE_FACTORY.pool when available.
+ pool = descriptor.file.pool
+ cls._extensions_by_number = pool._extensions_by_number[descriptor]
+ cls._extensions_by_name = pool._extensions_by_name[descriptor]
def _AddStaticMethods(cls):
# TODO(robinson): This probably needs to be thread-safe(?)
def RegisterExtension(extension_handle):
extension_handle.containing_type = cls.DESCRIPTOR
+ # TODO(amauryfa): Use cls.MESSAGE_FACTORY.pool when available.
+ cls.DESCRIPTOR.file.pool.AddExtensionDescriptor(extension_handle)
_AttachFieldHelpers(cls, extension_handle)
-
- # Try to insert our extension, failing if an extension with the same number
- # already exists.
- actual_handle = cls._extensions_by_number.setdefault(
- extension_handle.number, extension_handle)
- if actual_handle is not extension_handle:
- raise AssertionError(
- 'Extensions "%s" and "%s" both try to extend message type "%s" with '
- 'field number %d.' %
- (extension_handle.full_name, actual_handle.full_name,
- cls.DESCRIPTOR.full_name, extension_handle.number))
-
- cls._extensions_by_name[extension_handle.full_name] = extension_handle
-
- handle = extension_handle # avoid line wrapping
- if _IsMessageSetExtension(handle):
- # MessageSet extension. Also register under type name.
- cls._extensions_by_name[
- extension_handle.message_type.full_name] = extension_handle
-
cls.RegisterExtension = staticmethod(RegisterExtension)
def FromString(s):
@@ -1230,7 +1217,7 @@ def MergeFrom(self, msg):
if not isinstance(msg, cls):
raise TypeError(
"Parameter to MergeFrom() must be instance of same class: "
- "expected %s got %s." % (cls.__name__, type(msg).__name__))
+ 'expected %s got %s.' % (cls.__name__, msg.__class__.__name__))
assert msg is not self
self._Modified()
diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py
index dad79c37e2e57..0e8810157825a 100755
--- a/python/google/protobuf/internal/reflection_test.py
+++ b/python/google/protobuf/internal/reflection_test.py
@@ -99,12 +99,12 @@ def ReadFieldNumberAndWireType(self):
return wire_format.UnpackTag(self.ReadVarint())
def ReadFloat(self):
- result = struct.unpack("pool->FindExtensionByNumber(descriptor, number);
+ if (extension_descriptor == NULL) {
+ PyErr_Format(PyExc_KeyError, "Couldn't find extension %d", number);
+ return NULL;
+ }
+
+ return PyFieldDescriptor_FromDescriptor(extension_descriptor);
+}
+
+PyObject* FindAllExtensions(PyDescriptorPool* self, PyObject* arg) {
+ const Descriptor* descriptor = PyMessageDescriptor_AsDescriptor(arg);
+ if (descriptor == NULL) {
+ return NULL;
+ }
+
+ std::vector extensions;
+ self->pool->FindAllExtensions(descriptor, &extensions);
+
+ ScopedPyObjectPtr result(PyList_New(extensions.size()));
+ if (result == NULL) {
+ return NULL;
+ }
+ for (int i = 0; i < extensions.size(); i++) {
+ PyObject* extension = PyFieldDescriptor_FromDescriptor(extensions[i]);
+ if (extension == NULL) {
+ return NULL;
+ }
+ PyList_SET_ITEM(result.get(), i, extension); // Steals the reference.
+ }
+ return result.release();
+}
+
// These functions should not exist -- the only valid way to create
// descriptors is to call Add() or AddSerializedFile().
// But these AddDescriptor() functions were created in Python and some people
@@ -376,6 +421,22 @@ PyObject* AddEnumDescriptor(PyDescriptorPool* self, PyObject* descriptor) {
Py_RETURN_NONE;
}
+PyObject* AddExtensionDescriptor(PyDescriptorPool* self, PyObject* descriptor) {
+ const FieldDescriptor* extension_descriptor =
+ PyFieldDescriptor_AsDescriptor(descriptor);
+ if (!extension_descriptor) {
+ return NULL;
+ }
+ if (extension_descriptor !=
+ self->pool->FindExtensionByName(extension_descriptor->full_name())) {
+ PyErr_Format(PyExc_ValueError,
+ "The extension descriptor %s does not belong to this pool",
+ extension_descriptor->full_name().c_str());
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
// The code below loads new Descriptors from a serialized FileDescriptorProto.
@@ -475,6 +536,8 @@ static PyMethodDef Methods[] = {
"No-op. Add() must have been called before." },
{ "AddEnumDescriptor", (PyCFunction)AddEnumDescriptor, METH_O,
"No-op. Add() must have been called before." },
+ { "AddExtensionDescriptor", (PyCFunction)AddExtensionDescriptor, METH_O,
+ "No-op. Add() must have been called before." },
{ "FindFileByName", (PyCFunction)FindFileByName, METH_O,
"Searches for a file descriptor by its .proto name." },
@@ -495,6 +558,10 @@ static PyMethodDef Methods[] = {
{ "FindFileContainingSymbol", (PyCFunction)FindFileContainingSymbol, METH_O,
"Gets the FileDescriptor containing the specified symbol." },
+ { "FindExtensionByNumber", (PyCFunction)FindExtensionByNumber, METH_VARARGS,
+ "Gets the extension descriptor for the given number." },
+ { "FindAllExtensions", (PyCFunction)FindAllExtensions, METH_O,
+ "Gets all known extensions of the given message descriptor." },
{NULL}
};
diff --git a/python/google/protobuf/pyext/extension_dict.cc b/python/google/protobuf/pyext/extension_dict.cc
index dbb7bca06d35b..9423c1d890b10 100644
--- a/python/google/protobuf/pyext/extension_dict.cc
+++ b/python/google/protobuf/pyext/extension_dict.cc
@@ -38,6 +38,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -46,6 +47,16 @@
#include
#include
+#if PY_MAJOR_VERSION >= 3
+ #if PY_VERSION_HEX < 0x03030000
+ #error "Python 3.0 - 3.2 are not supported."
+ #endif
+ #define PyString_AsStringAndSize(ob, charpp, sizep) \
+ (PyUnicode_Check(ob)? \
+ ((*(charpp) = PyUnicode_AsUTF8AndSize(ob, (sizep))) == NULL? -1: 0): \
+ PyBytes_AsStringAndSize(ob, (charpp), (sizep)))
+#endif
+
namespace google {
namespace protobuf {
namespace python {
@@ -90,6 +101,7 @@ PyObject* subscript(ExtensionDict* self, PyObject* key) {
if (descriptor->label() != FieldDescriptor::LABEL_REPEATED &&
descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ // TODO(plabatut): consider building the class on the fly!
PyObject* sub_message = cmessage::InternalGetSubMessage(
self->parent, descriptor);
if (sub_message == NULL) {
@@ -101,7 +113,17 @@ PyObject* subscript(ExtensionDict* self, PyObject* key) {
if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- CMessageClass* message_class = message_factory::GetMessageClass(
+ // On the fly message class creation is needed to support the following
+ // situation:
+ // 1- add FileDescriptor to the pool that contains extensions of a message
+ // defined by another proto file. Do not create any message classes.
+ // 2- instantiate an extended message, and access the extension using
+ // the field descriptor.
+ // 3- the extension submessage fails to be returned, because no class has
+ // been created.
+ // It happens when deserializing text proto format, or when enumerating
+ // fields of a deserialized message.
+ CMessageClass* message_class = message_factory::GetOrCreateMessageClass(
cmessage::GetFactoryForMessage(self->parent),
descriptor->message_type());
if (message_class == NULL) {
@@ -154,34 +176,51 @@ int ass_subscript(ExtensionDict* self, PyObject* key, PyObject* value) {
return 0;
}
-PyObject* _FindExtensionByName(ExtensionDict* self, PyObject* name) {
- ScopedPyObjectPtr extensions_by_name(PyObject_GetAttrString(
- reinterpret_cast(self->parent), "_extensions_by_name"));
- if (extensions_by_name == NULL) {
+PyObject* _FindExtensionByName(ExtensionDict* self, PyObject* arg) {
+ char* name;
+ Py_ssize_t name_size;
+ if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
return NULL;
}
- PyObject* result = PyDict_GetItem(extensions_by_name.get(), name);
- if (result == NULL) {
+
+ PyDescriptorPool* pool = cmessage::GetFactoryForMessage(self->parent)->pool;
+ const FieldDescriptor* message_extension =
+ pool->pool->FindExtensionByName(string(name, name_size));
+ if (message_extension == NULL) {
+ // Is is the name of a message set extension?
+ const Descriptor* message_descriptor = pool->pool->FindMessageTypeByName(
+ string(name, name_size));
+ if (message_descriptor && message_descriptor->extension_count() > 0) {
+ const FieldDescriptor* extension = message_descriptor->extension(0);
+ if (extension->is_extension() &&
+ extension->containing_type()->options().message_set_wire_format() &&
+ extension->type() == FieldDescriptor::TYPE_MESSAGE &&
+ extension->label() == FieldDescriptor::LABEL_OPTIONAL) {
+ message_extension = extension;
+ }
+ }
+ }
+ if (message_extension == NULL) {
Py_RETURN_NONE;
- } else {
- Py_INCREF(result);
- return result;
}
+
+ return PyFieldDescriptor_FromDescriptor(message_extension);
}
-PyObject* _FindExtensionByNumber(ExtensionDict* self, PyObject* number) {
- ScopedPyObjectPtr extensions_by_number(PyObject_GetAttrString(
- reinterpret_cast(self->parent), "_extensions_by_number"));
- if (extensions_by_number == NULL) {
+PyObject* _FindExtensionByNumber(ExtensionDict* self, PyObject* arg) {
+ int64 number = PyLong_AsLong(arg);
+ if (number == -1 && PyErr_Occurred()) {
return NULL;
}
- PyObject* result = PyDict_GetItem(extensions_by_number.get(), number);
- if (result == NULL) {
+
+ PyDescriptorPool* pool = cmessage::GetFactoryForMessage(self->parent)->pool;
+ const FieldDescriptor* message_extension = pool->pool->FindExtensionByNumber(
+ self->parent->message->GetDescriptor(), number);
+ if (message_extension == NULL) {
Py_RETURN_NONE;
- } else {
- Py_INCREF(result);
- return result;
}
+
+ return PyFieldDescriptor_FromDescriptor(message_extension);
}
ExtensionDict* NewExtensionDict(CMessage *parent) {
diff --git a/python/google/protobuf/pyext/map_container.cc b/python/google/protobuf/pyext/map_container.cc
index 318c2e7c7440e..088ddf93536ac 100644
--- a/python/google/protobuf/pyext/map_container.cc
+++ b/python/google/protobuf/pyext/map_container.cc
@@ -374,7 +374,7 @@ static int InitializeAndCopyToParentContainer(MapContainer* from,
// A somewhat roundabout way of copying just one field from old_message to
// new_message. This is the best we can do with what Reflection gives us.
Message* mutable_old = from->GetMutableMessage();
- vector fields;
+ std::vector fields;
fields.push_back(from->parent_field_descriptor);
// Move the map field into the new message.
diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc
index 6626ddeffb84a..4f3abc84bedce 100644
--- a/python/google/protobuf/pyext/message.cc
+++ b/python/google/protobuf/pyext/message.cc
@@ -64,11 +64,11 @@
#include
#include
#include
+#include
#include
#include
#if PY_MAJOR_VERSION >= 3
- #define PyInt_Check PyLong_Check
#define PyInt_AsLong PyLong_AsLong
#define PyInt_FromLong PyLong_FromLong
#define PyInt_FromSize_t PyLong_FromSize_t
@@ -92,8 +92,6 @@ namespace protobuf {
namespace python {
static PyObject* kDESCRIPTOR;
-static PyObject* k_extensions_by_name;
-static PyObject* k_extensions_by_number;
PyObject* EnumTypeWrapper_class;
static PyObject* PythonMessage_class;
static PyObject* kEmptyWeakref;
@@ -128,19 +126,6 @@ static bool AddFieldNumberToClass(
// Finalize the creation of the Message class.
static int AddDescriptors(PyObject* cls, const Descriptor* descriptor) {
- // If there are extension_ranges, the message is "extendable", and extension
- // classes will register themselves in this class.
- if (descriptor->extension_range_count() > 0) {
- ScopedPyObjectPtr by_name(PyDict_New());
- if (PyObject_SetAttr(cls, k_extensions_by_name, by_name.get()) < 0) {
- return -1;
- }
- ScopedPyObjectPtr by_number(PyDict_New());
- if (PyObject_SetAttr(cls, k_extensions_by_number, by_number.get()) < 0) {
- return -1;
- }
- }
-
// For each field set: cls._FIELD_NUMBER =
for (int i = 0; i < descriptor->field_count(); ++i) {
if (!AddFieldNumberToClass(cls, descriptor->field(i))) {
@@ -357,6 +342,61 @@ static int InsertEmptyWeakref(PyTypeObject *base_type) {
#endif // PY_MAJOR_VERSION >= 3
}
+// The _extensions_by_name dictionary is built on every access.
+// TODO(amauryfa): Migrate all users to pool.FindAllExtensions()
+static PyObject* GetExtensionsByName(CMessageClass *self, void *closure) {
+ const PyDescriptorPool* pool = self->py_message_factory->pool;
+
+ std::vector extensions;
+ pool->pool->FindAllExtensions(self->message_descriptor, &extensions);
+
+ ScopedPyObjectPtr result(PyDict_New());
+ for (int i = 0; i < extensions.size(); i++) {
+ ScopedPyObjectPtr extension(
+ PyFieldDescriptor_FromDescriptor(extensions[i]));
+ if (extension == NULL) {
+ return NULL;
+ }
+ if (PyDict_SetItemString(result.get(), extensions[i]->full_name().c_str(),
+ extension.get()) < 0) {
+ return NULL;
+ }
+ }
+ return result.release();
+}
+
+// The _extensions_by_number dictionary is built on every access.
+// TODO(amauryfa): Migrate all users to pool.FindExtensionByNumber()
+static PyObject* GetExtensionsByNumber(CMessageClass *self, void *closure) {
+ const PyDescriptorPool* pool = self->py_message_factory->pool;
+
+ std::vector extensions;
+ pool->pool->FindAllExtensions(self->message_descriptor, &extensions);
+
+ ScopedPyObjectPtr result(PyDict_New());
+ for (int i = 0; i < extensions.size(); i++) {
+ ScopedPyObjectPtr extension(
+ PyFieldDescriptor_FromDescriptor(extensions[i]));
+ if (extension == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr number(PyInt_FromLong(extensions[i]->number()));
+ if (number == NULL) {
+ return NULL;
+ }
+ if (PyDict_SetItem(result.get(), number.get(), extension.get()) < 0) {
+ return NULL;
+ }
+ }
+ return result.release();
+}
+
+static PyGetSetDef Getters[] = {
+ {"_extensions_by_name", (getter)GetExtensionsByName, NULL},
+ {"_extensions_by_number", (getter)GetExtensionsByNumber, NULL},
+ {NULL}
+};
+
} // namespace message_meta
PyTypeObject CMessageClass_Type = {
@@ -389,7 +429,7 @@ PyTypeObject CMessageClass_Type = {
0, // tp_iternext
0, // tp_methods
0, // tp_members
- 0, // tp_getset
+ message_meta::Getters, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
@@ -525,23 +565,10 @@ int ForEachCompositeField(CMessage* self, Visitor visitor) {
// ---------------------------------------------------------------------
-// Constants used for integer type range checking.
-PyObject* kPythonZero;
-PyObject* kint32min_py;
-PyObject* kint32max_py;
-PyObject* kuint32max_py;
-PyObject* kint64min_py;
-PyObject* kint64max_py;
-PyObject* kuint64max_py;
-
PyObject* EncodeError_class;
PyObject* DecodeError_class;
PyObject* PickleError_class;
-// Constant PyString values used for GetAttr/GetItem.
-static PyObject* k_cdescriptor;
-static PyObject* kfull_name;
-
/* Is 64bit */
void FormatTypeError(PyObject* arg, char* expected_types) {
PyObject* repr = PyObject_Repr(arg);
@@ -555,68 +582,126 @@ void FormatTypeError(PyObject* arg, char* expected_types) {
}
}
-template
-bool CheckAndGetInteger(
- PyObject* arg, T* value, PyObject* min, PyObject* max) {
- bool is_long = PyLong_Check(arg);
-#if PY_MAJOR_VERSION < 3
- if (!PyInt_Check(arg) && !is_long) {
- FormatTypeError(arg, "int, long");
- return false;
+void OutOfRangeError(PyObject* arg) {
+ PyObject *s = PyObject_Str(arg);
+ if (s) {
+ PyErr_Format(PyExc_ValueError,
+ "Value out of range: %s",
+ PyString_AsString(s));
+ Py_DECREF(s);
}
- if (PyObject_Compare(min, arg) > 0 || PyObject_Compare(max, arg) < 0) {
-#else
- if (!is_long) {
- FormatTypeError(arg, "int");
+}
+
+template
+bool VerifyIntegerCastAndRange(PyObject* arg, ValueType value) {
+ if GOOGLE_PREDICT_FALSE(value == -1 && PyErr_Occurred()) {
+ if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+ // Replace it with the same ValueError as pure python protos instead of
+ // the default one.
+ PyErr_Clear();
+ OutOfRangeError(arg);
+ } // Otherwise propagate existing error.
return false;
}
- if (PyObject_RichCompareBool(min, arg, Py_LE) != 1 ||
- PyObject_RichCompareBool(max, arg, Py_GE) != 1) {
-#endif
- if (!PyErr_Occurred()) {
- PyObject *s = PyObject_Str(arg);
- if (s) {
- PyErr_Format(PyExc_ValueError,
- "Value out of range: %s",
- PyString_AsString(s));
- Py_DECREF(s);
- }
- }
+ if GOOGLE_PREDICT_FALSE(!IsValidNumericCast(value)) {
+ OutOfRangeError(arg);
return false;
}
+ return true;
+}
+
+template
+bool CheckAndGetInteger(PyObject* arg, T* value) {
+ // The fast path.
#if PY_MAJOR_VERSION < 3
- if (!is_long) {
- *value = static_cast(PyInt_AsLong(arg));
- } else // NOLINT
+ // For the typical case, offer a fast path.
+ if GOOGLE_PREDICT_TRUE(PyInt_Check(arg)) {
+ long int_result = PyInt_AsLong(arg);
+ if GOOGLE_PREDICT_TRUE(IsValidNumericCast(int_result)) {
+ *value = static_cast(int_result);
+ return true;
+ } else {
+ OutOfRangeError(arg);
+ return false;
+ }
+ }
#endif
- {
- if (min == kPythonZero) {
- *value = static_cast(PyLong_AsUnsignedLongLong(arg));
+ // This effectively defines an integer as "an object that can be cast as
+ // an integer and can be used as an ordinal number".
+ // This definition includes everything that implements numbers.Integral
+ // and shouldn't cast the net too wide.
+ if GOOGLE_PREDICT_FALSE(!PyIndex_Check(arg)) {
+ FormatTypeError(arg, "int, long");
+ return false;
+ }
+
+ // Now we have an integral number so we can safely use PyLong_ functions.
+ // We need to treat the signed and unsigned cases differently in case arg is
+ // holding a value above the maximum for signed longs.
+ if (std::numeric_limits::min() == 0) {
+ // Unsigned case.
+ unsigned PY_LONG_LONG ulong_result;
+ if (PyLong_Check(arg)) {
+ ulong_result = PyLong_AsUnsignedLongLong(arg);
+ } else {
+ // Unlike PyLong_AsLongLong, PyLong_AsUnsignedLongLong is very
+ // picky about the exact type.
+ PyObject* casted = PyNumber_Long(arg);
+ if GOOGLE_PREDICT_FALSE(casted == NULL) {
+ // Propagate existing error.
+ return false;
+ }
+ ulong_result = PyLong_AsUnsignedLongLong(casted);
+ Py_DECREF(casted);
+ }
+ if (VerifyIntegerCastAndRange(arg,
+ ulong_result)) {
+ *value = static_cast(ulong_result);
+ } else {
+ return false;
+ }
+ } else {
+ // Signed case.
+ PY_LONG_LONG long_result;
+ PyNumberMethods *nb;
+ if ((nb = arg->ob_type->tp_as_number) != NULL && nb->nb_int != NULL) {
+ // PyLong_AsLongLong requires it to be a long or to have an __int__()
+ // method.
+ long_result = PyLong_AsLongLong(arg);
} else {
- *value = static_cast(PyLong_AsLongLong(arg));
+ // Valid subclasses of numbers.Integral should have a __long__() method
+ // so fall back to that.
+ PyObject* casted = PyNumber_Long(arg);
+ if GOOGLE_PREDICT_FALSE(casted == NULL) {
+ // Propagate existing error.
+ return false;
+ }
+ long_result = PyLong_AsLongLong(casted);
+ Py_DECREF(casted);
+ }
+ if (VerifyIntegerCastAndRange(arg, long_result)) {
+ *value = static_cast(long_result);
+ } else {
+ return false;
}
}
+
return true;
}
// These are referenced by repeated_scalar_container, and must
// be explicitly instantiated.
-template bool CheckAndGetInteger(
- PyObject*, int32*, PyObject*, PyObject*);
-template bool CheckAndGetInteger(
- PyObject*, int64*, PyObject*, PyObject*);
-template bool CheckAndGetInteger(
- PyObject*, uint32*, PyObject*, PyObject*);
-template bool CheckAndGetInteger(
- PyObject*, uint64*, PyObject*, PyObject*);
+template bool CheckAndGetInteger(PyObject*, int32*);
+template bool CheckAndGetInteger(PyObject*, int64*);
+template bool CheckAndGetInteger(PyObject*, uint32*);
+template bool CheckAndGetInteger(PyObject*, uint64*);
bool CheckAndGetDouble(PyObject* arg, double* value) {
- if (!PyInt_Check(arg) && !PyLong_Check(arg) &&
- !PyFloat_Check(arg)) {
+ *value = PyFloat_AsDouble(arg);
+ if GOOGLE_PREDICT_FALSE(*value == -1 && PyErr_Occurred()) {
FormatTypeError(arg, "int, long, float");
return false;
}
- *value = PyFloat_AsDouble(arg);
return true;
}
@@ -630,11 +715,13 @@ bool CheckAndGetFloat(PyObject* arg, float* value) {
}
bool CheckAndGetBool(PyObject* arg, bool* value) {
- if (!PyInt_Check(arg) && !PyBool_Check(arg) && !PyLong_Check(arg)) {
+ long long_value = PyInt_AsLong(arg);
+ if (long_value == -1 && PyErr_Occurred()) {
FormatTypeError(arg, "int, long, bool");
return false;
}
- *value = static_cast(PyInt_AsLong(arg));
+ *value = static_cast(long_value);
+
return true;
}
@@ -966,20 +1053,7 @@ int InternalDeleteRepeatedField(
int min, max;
length = reflection->FieldSize(*message, field_descriptor);
- if (PyInt_Check(slice) || PyLong_Check(slice)) {
- from = to = PyLong_AsLong(slice);
- if (from < 0) {
- from = to = length + from;
- }
- step = 1;
- min = max = from;
-
- // Range check.
- if (from < 0 || from >= length) {
- PyErr_Format(PyExc_IndexError, "list assignment index out of range");
- return -1;
- }
- } else if (PySlice_Check(slice)) {
+ if (PySlice_Check(slice)) {
from = to = step = slice_length = 0;
PySlice_GetIndicesEx(
#if PY_MAJOR_VERSION < 3
@@ -996,8 +1070,23 @@ int InternalDeleteRepeatedField(
max = from;
}
} else {
- PyErr_SetString(PyExc_TypeError, "list indices must be integers");
- return -1;
+ from = to = PyLong_AsLong(slice);
+ if (from == -1 && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError, "list indices must be integers");
+ return -1;
+ }
+
+ if (from < 0) {
+ from = to = length + from;
+ }
+ step = 1;
+ min = max = from;
+
+ // Range check.
+ if (from < 0 || from >= length) {
+ PyErr_Format(PyExc_IndexError, "list assignment index out of range");
+ return -1;
+ }
}
Py_ssize_t i = from;
@@ -1962,99 +2051,29 @@ static PyObject* ByteSize(CMessage* self, PyObject* args) {
return PyLong_FromLong(self->message->ByteSize());
}
-static PyObject* RegisterExtension(PyObject* cls,
- PyObject* extension_handle) {
+PyObject* RegisterExtension(PyObject* cls, PyObject* extension_handle) {
const FieldDescriptor* descriptor =
GetExtensionDescriptor(extension_handle);
if (descriptor == NULL) {
return NULL;
}
-
- ScopedPyObjectPtr extensions_by_name(
- PyObject_GetAttr(cls, k_extensions_by_name));
- if (extensions_by_name == NULL) {
- PyErr_SetString(PyExc_TypeError, "no extensions_by_name on class");
+ if (!PyObject_TypeCheck(cls, &CMessageClass_Type)) {
+ PyErr_Format(PyExc_TypeError, "Expected a message class, got %s",
+ cls->ob_type->tp_name);
return NULL;
}
- ScopedPyObjectPtr full_name(PyObject_GetAttr(extension_handle, kfull_name));
- if (full_name == NULL) {
+ CMessageClass *message_class = reinterpret_cast(cls);
+ if (message_class == NULL) {
return NULL;
}
-
// If the extension was already registered, check that it is the same.
- PyObject* existing_extension =
- PyDict_GetItem(extensions_by_name.get(), full_name.get());
- if (existing_extension != NULL) {
- const FieldDescriptor* existing_extension_descriptor =
- GetExtensionDescriptor(existing_extension);
- if (existing_extension_descriptor != descriptor) {
- PyErr_SetString(PyExc_ValueError, "Double registration of Extensions");
- return NULL;
- }
- // Nothing else to do.
- Py_RETURN_NONE;
- }
-
- if (PyDict_SetItem(extensions_by_name.get(), full_name.get(),
- extension_handle) < 0) {
- return NULL;
- }
-
- // Also store a mapping from extension number to implementing class.
- ScopedPyObjectPtr extensions_by_number(
- PyObject_GetAttr(cls, k_extensions_by_number));
- if (extensions_by_number == NULL) {
- PyErr_SetString(PyExc_TypeError, "no extensions_by_number on class");
- return NULL;
- }
-
- ScopedPyObjectPtr number(PyObject_GetAttrString(extension_handle, "number"));
- if (number == NULL) {
- return NULL;
- }
-
- // If the extension was already registered by number, check that it is the
- // same.
- existing_extension = PyDict_GetItem(extensions_by_number.get(), number.get());
- if (existing_extension != NULL) {
- const FieldDescriptor* existing_extension_descriptor =
- GetExtensionDescriptor(existing_extension);
- if (existing_extension_descriptor != descriptor) {
- const Descriptor* msg_desc = GetMessageDescriptor(
- reinterpret_cast(cls));
- PyErr_Format(
- PyExc_ValueError,
- "Extensions \"%s\" and \"%s\" both try to extend message type "
- "\"%s\" with field number %ld.",
- existing_extension_descriptor->full_name().c_str(),
- descriptor->full_name().c_str(),
- msg_desc->full_name().c_str(),
- PyInt_AsLong(number.get()));
- return NULL;
- }
- // Nothing else to do.
- Py_RETURN_NONE;
- }
- if (PyDict_SetItem(extensions_by_number.get(), number.get(),
- extension_handle) < 0) {
+ const FieldDescriptor* existing_extension =
+ message_class->py_message_factory->pool->pool->FindExtensionByNumber(
+ descriptor->containing_type(), descriptor->number());
+ if (existing_extension != NULL && existing_extension != descriptor) {
+ PyErr_SetString(PyExc_ValueError, "Double registration of Extensions");
return NULL;
}
-
- // Check if it's a message set
- if (descriptor->is_extension() &&
- descriptor->containing_type()->options().message_set_wire_format() &&
- descriptor->type() == FieldDescriptor::TYPE_MESSAGE &&
- descriptor->label() == FieldDescriptor::LABEL_OPTIONAL) {
- ScopedPyObjectPtr message_name(PyString_FromStringAndSize(
- descriptor->message_type()->full_name().c_str(),
- descriptor->message_type()->full_name().size()));
- if (message_name == NULL) {
- return NULL;
- }
- PyDict_SetItem(extensions_by_name.get(), message_name.get(),
- extension_handle);
- }
-
Py_RETURN_NONE;
}
@@ -2091,7 +2110,7 @@ static PyObject* WhichOneof(CMessage* self, PyObject* arg) {
static PyObject* GetExtensionDict(CMessage* self, void *closure);
static PyObject* ListFields(CMessage* self) {
- vector fields;
+ std::vector fields;
self->message->GetReflection()->ListFields(*self->message, &fields);
// Normally, the list will be exactly the size of the fields.
@@ -2182,7 +2201,7 @@ static PyObject* DiscardUnknownFields(CMessage* self) {
PyObject* FindInitializationErrors(CMessage* self) {
Message* message = self->message;
- vector errors;
+ std::vector errors;
message->FindInitializationErrors(&errors);
PyObject* error_list = PyList_New(errors.size());
@@ -2574,11 +2593,24 @@ static PyObject* GetExtensionDict(CMessage* self, void *closure) {
return NULL;
}
+static PyObject* GetExtensionsByName(CMessage *self, void *closure) {
+ return message_meta::GetExtensionsByName(
+ reinterpret_cast(Py_TYPE(self)), closure);
+}
+
+static PyObject* GetExtensionsByNumber(CMessage *self, void *closure) {
+ return message_meta::GetExtensionsByNumber(
+ reinterpret_cast(Py_TYPE(self)), closure);
+}
+
static PyGetSetDef Getters[] = {
{"Extensions", (getter)GetExtensionDict, NULL, "Extension dict"},
+ {"_extensions_by_name", (getter)GetExtensionsByName, NULL},
+ {"_extensions_by_number", (getter)GetExtensionsByNumber, NULL},
{NULL}
};
+
static PyMethodDef Methods[] = {
{ "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
"Makes a deep copy of the class." },
@@ -2839,19 +2871,7 @@ void InitGlobals() {
// TODO(gps): Check all return values in this function for NULL and propagate
// the error (MemoryError) on up to result in an import failure. These should
// also be freed and reset to NULL during finalization.
- kPythonZero = PyInt_FromLong(0);
- kint32min_py = PyInt_FromLong(kint32min);
- kint32max_py = PyInt_FromLong(kint32max);
- kuint32max_py = PyLong_FromLongLong(kuint32max);
- kint64min_py = PyLong_FromLongLong(kint64min);
- kint64max_py = PyLong_FromLongLong(kint64max);
- kuint64max_py = PyLong_FromUnsignedLongLong(kuint64max);
-
kDESCRIPTOR = PyString_FromString("DESCRIPTOR");
- k_cdescriptor = PyString_FromString("_cdescriptor");
- kfull_name = PyString_FromString("full_name");
- k_extensions_by_name = PyString_FromString("_extensions_by_name");
- k_extensions_by_number = PyString_FromString("_extensions_by_number");
PyObject *dummy_obj = PySet_New(NULL);
kEmptyWeakref = PyWeakref_NewRef(dummy_obj, NULL);
@@ -2891,25 +2911,6 @@ bool InitProto2MessageModule(PyObject *m) {
// DESCRIPTOR is set on each protocol buffer message class elsewhere, but set
// it here as well to document that subclasses need to set it.
PyDict_SetItem(CMessage_Type.tp_dict, kDESCRIPTOR, Py_None);
- // Subclasses with message extensions will override _extensions_by_name and
- // _extensions_by_number with fresh mutable dictionaries in AddDescriptors.
- // All other classes can share this same immutable mapping.
- ScopedPyObjectPtr empty_dict(PyDict_New());
- if (empty_dict == NULL) {
- return false;
- }
- ScopedPyObjectPtr immutable_dict(PyDictProxy_New(empty_dict.get()));
- if (immutable_dict == NULL) {
- return false;
- }
- if (PyDict_SetItem(CMessage_Type.tp_dict,
- k_extensions_by_name, immutable_dict.get()) < 0) {
- return false;
- }
- if (PyDict_SetItem(CMessage_Type.tp_dict,
- k_extensions_by_number, immutable_dict.get()) < 0) {
- return false;
- }
PyModule_AddObject(m, "Message", reinterpret_cast(&CMessage_Type));
diff --git a/python/google/protobuf/pyext/message.h b/python/google/protobuf/pyext/message.h
index 1550724c8f53e..ce80497e6b4c7 100644
--- a/python/google/protobuf/pyext/message.h
+++ b/python/google/protobuf/pyext/message.h
@@ -117,6 +117,7 @@ typedef struct CMessage {
PyObject* weakreflist;
} CMessage;
+extern PyTypeObject CMessageClass_Type;
extern PyTypeObject CMessage_Type;
@@ -235,6 +236,10 @@ int InitAttributes(CMessage* self, PyObject* args, PyObject* kwargs);
PyObject* MergeFrom(CMessage* self, PyObject* arg);
+// This method does not do anything beyond checking that no other extension
+// has been registered with the same field number on this class.
+PyObject* RegisterExtension(PyObject* cls, PyObject* extension_handle);
+
// Retrieves an attribute named 'name' from CMessage 'self'. Returns
// the attribute value on success, or NULL on failure.
//
@@ -275,25 +280,25 @@ PyObject* SetAllowOversizeProtos(PyObject* m, PyObject* arg);
#define GOOGLE_CHECK_GET_INT32(arg, value, err) \
int32 value; \
- if (!CheckAndGetInteger(arg, &value, kint32min_py, kint32max_py)) { \
+ if (!CheckAndGetInteger(arg, &value)) { \
return err; \
}
#define GOOGLE_CHECK_GET_INT64(arg, value, err) \
int64 value; \
- if (!CheckAndGetInteger(arg, &value, kint64min_py, kint64max_py)) { \
+ if (!CheckAndGetInteger(arg, &value)) { \
return err; \
}
#define GOOGLE_CHECK_GET_UINT32(arg, value, err) \
uint32 value; \
- if (!CheckAndGetInteger(arg, &value, kPythonZero, kuint32max_py)) { \
+ if (!CheckAndGetInteger(arg, &value)) { \
return err; \
}
#define GOOGLE_CHECK_GET_UINT64(arg, value, err) \
uint64 value; \
- if (!CheckAndGetInteger(arg, &value, kPythonZero, kuint64max_py)) { \
+ if (!CheckAndGetInteger(arg, &value)) { \
return err; \
}
@@ -316,20 +321,11 @@ PyObject* SetAllowOversizeProtos(PyObject* m, PyObject* arg);
}
-extern PyObject* kPythonZero;
-extern PyObject* kint32min_py;
-extern PyObject* kint32max_py;
-extern PyObject* kuint32max_py;
-extern PyObject* kint64min_py;
-extern PyObject* kint64max_py;
-extern PyObject* kuint64max_py;
-
#define FULL_MODULE_NAME "google.protobuf.pyext._message"
void FormatTypeError(PyObject* arg, char* expected_types);
template
-bool CheckAndGetInteger(
- PyObject* arg, T* value, PyObject* min, PyObject* max);
+bool CheckAndGetInteger(PyObject* arg, T* value);
bool CheckAndGetDouble(PyObject* arg, double* value);
bool CheckAndGetFloat(PyObject* arg, float* value);
bool CheckAndGetBool(PyObject* arg, bool* value);
diff --git a/python/google/protobuf/pyext/message_factory.cc b/python/google/protobuf/pyext/message_factory.cc
index 2ad8902284a27..e0b45bf2d95bc 100644
--- a/python/google/protobuf/pyext/message_factory.cc
+++ b/python/google/protobuf/pyext/message_factory.cc
@@ -130,6 +130,72 @@ int RegisterMessageClass(PyMessageFactory* self,
return 0;
}
+CMessageClass* GetOrCreateMessageClass(PyMessageFactory* self,
+ const Descriptor* descriptor) {
+ // This is the same implementation as MessageFactory.GetPrototype().
+ ScopedPyObjectPtr py_descriptor(
+ PyMessageDescriptor_FromDescriptor(descriptor));
+ if (py_descriptor == NULL) {
+ return NULL;
+ }
+ // Do not create a MessageClass that already exists.
+ hash_map::iterator it =
+ self->classes_by_descriptor->find(descriptor);
+ if (it != self->classes_by_descriptor->end()) {
+ Py_INCREF(it->second);
+ return it->second;
+ }
+ // Create a new message class.
+ ScopedPyObjectPtr args(Py_BuildValue(
+ "s(){sOsOsO}", descriptor->name().c_str(),
+ "DESCRIPTOR", py_descriptor.get(),
+ "__module__", Py_None,
+ "message_factory", self));
+ if (args == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr message_class(PyObject_CallObject(
+ reinterpret_cast(&CMessageClass_Type), args.get()));
+ if (message_class == NULL) {
+ return NULL;
+ }
+ // Create messages class for the messages used by the fields, and registers
+ // all extensions for these messages during the recursion.
+ for (int field_idx = 0; field_idx < descriptor->field_count(); field_idx++) {
+ const Descriptor* sub_descriptor =
+ descriptor->field(field_idx)->message_type();
+ // It is NULL if the field type is not a message.
+ if (sub_descriptor != NULL) {
+ CMessageClass* result = GetOrCreateMessageClass(self, sub_descriptor);
+ if (result == NULL) {
+ return NULL;
+ }
+ Py_DECREF(result);
+ }
+ }
+
+ // Register extensions defined in this message.
+ for (int ext_idx = 0 ; ext_idx < descriptor->extension_count() ; ext_idx++) {
+ const FieldDescriptor* extension = descriptor->extension(ext_idx);
+ ScopedPyObjectPtr py_extended_class(
+ GetOrCreateMessageClass(self, extension->containing_type())
+ ->AsPyObject());
+ if (py_extended_class == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr py_extension(PyFieldDescriptor_FromDescriptor(extension));
+ if (py_extension == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr result(cmessage::RegisterExtension(
+ py_extended_class.get(), py_extension.get()));
+ if (result == NULL) {
+ return NULL;
+ }
+ }
+ return reinterpret_cast(message_class.release());
+}
+
// Retrieve the message class added to our database.
CMessageClass* GetMessageClass(PyMessageFactory* self,
const Descriptor* message_descriptor) {
diff --git a/python/google/protobuf/pyext/message_factory.h b/python/google/protobuf/pyext/message_factory.h
index 07cccbfbb9edd..36092f7e40a60 100644
--- a/python/google/protobuf/pyext/message_factory.h
+++ b/python/google/protobuf/pyext/message_factory.h
@@ -82,14 +82,14 @@ PyMessageFactory* NewMessageFactory(PyTypeObject* type, PyDescriptorPool* pool);
int RegisterMessageClass(PyMessageFactory* self,
const Descriptor* message_descriptor,
CMessageClass* message_class);
-
-// Retrieves the Python class registered with the given message descriptor.
-//
-// Returns a *borrowed* reference if found, otherwise returns NULL with an
-// exception set.
+// Retrieves the Python class registered with the given message descriptor, or
+// fail with a TypeError. Returns a *borrowed* reference.
CMessageClass* GetMessageClass(PyMessageFactory* self,
const Descriptor* message_descriptor);
-
+// Retrieves the Python class registered with the given message descriptor.
+// The class is created if not done yet. Returns a *new* reference.
+CMessageClass* GetOrCreateMessageClass(PyMessageFactory* self,
+ const Descriptor* message_descriptor);
} // namespace message_factory
// Initialize objects used by this module.
diff --git a/python/google/protobuf/pyext/safe_numerics.h b/python/google/protobuf/pyext/safe_numerics.h
new file mode 100644
index 0000000000000..639ba2c8e3e91
--- /dev/null
+++ b/python/google/protobuf/pyext/safe_numerics.h
@@ -0,0 +1,164 @@
+// 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.
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_SAFE_NUMERICS_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_SAFE_NUMERICS_H__
+// Copied from chromium with only changes to the namespace.
+
+#include
+
+#include
+#include
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+template
+struct IsValidNumericCastImpl;
+
+#define BASE_NUMERIC_CAST_CASE_SPECIALIZATION(A, B, C, D, Code) \
+template <> struct IsValidNumericCastImpl { \
+ template static inline bool Test( \
+ Source source, DestBounds min, DestBounds max) { \
+ return Code; \
+ } \
+}
+
+#define BASE_NUMERIC_CAST_CASE_SAME_SIZE(DestSigned, SourceSigned, Code) \
+ BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
+ true, true, DestSigned, SourceSigned, Code); \
+ BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
+ true, false, DestSigned, SourceSigned, Code)
+
+#define BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(DestSigned, SourceSigned, Code) \
+ BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
+ false, false, DestSigned, SourceSigned, Code); \
+
+#define BASE_NUMERIC_CAST_CASE_DEST_LARGER(DestSigned, SourceSigned, Code) \
+ BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
+ false, true, DestSigned, SourceSigned, Code); \
+
+// The three top level cases are:
+// - Same size
+// - Source larger
+// - Dest larger
+// And for each of those three cases, we handle the 4 different possibilities
+// of signed and unsigned. This gives 12 cases to handle, which we enumerate
+// below.
+//
+// The last argument in each of the macros is the actual comparison code. It
+// has three arguments available, source (the value), and min/max which are
+// the ranges of the destination.
+
+
+// These are the cases where both types have the same size.
+
+// Both signed.
+BASE_NUMERIC_CAST_CASE_SAME_SIZE(true, true, true);
+// Both unsigned.
+BASE_NUMERIC_CAST_CASE_SAME_SIZE(false, false, true);
+// Dest unsigned, Source signed.
+BASE_NUMERIC_CAST_CASE_SAME_SIZE(false, true, source >= 0);
+// Dest signed, Source unsigned.
+// This cast is OK because Dest's max must be less than Source's.
+BASE_NUMERIC_CAST_CASE_SAME_SIZE(true, false,
+ source <= static_cast