From 5a76e633ea9b5adb215e93fdc11e1c0c08b3fc74 Mon Sep 17 00:00:00 2001 From: Adam Cozzette Date: Thu, 17 Nov 2016 16:48:38 -0800 Subject: Integrated internal changes from Google --- .../java/com/google/protobuf/AbstractParser.java | 2 +- .../main/java/com/google/protobuf/ByteString.java | 18 +-- .../main/java/com/google/protobuf/Descriptors.java | 2 +- .../java/com/google/protobuf/DynamicMessage.java | 2 +- .../com/google/protobuf/ExtensionRegistry.java | 1 - .../com/google/protobuf/ExtensionRegistryLite.java | 7 +- .../com/google/protobuf/GeneratedMessageLite.java | 163 +++++++++++++++++++-- .../com/google/protobuf/GeneratedMessageV3.java | 26 ++-- .../java/com/google/protobuf/LazyFieldLite.java | 25 +--- .../main/java/com/google/protobuf/MapEntry.java | 2 +- .../com/google/protobuf/MessageLiteToString.java | 4 +- .../java/com/google/protobuf/UnknownFieldSet.java | 2 +- .../com/google/protobuf/UnknownFieldSetLite.java | 38 ++++- .../com/google/protobuf/LazyFieldLiteTest.java | 24 --- .../com/google/protobuf/LazyMessageLiteTest.java | 13 ++ .../test/java/com/google/protobuf/LiteTest.java | 26 ++++ .../com/google/protobuf/MapForProto2LiteTest.java | 1 - .../java/com/google/protobuf/MapForProto2Test.java | 105 +++++++------ .../src/test/java/com/google/protobuf/MapTest.java | 157 ++++++++++---------- .../test/java/com/google/protobuf/ParserTest.java | 40 +++++ .../java/com/google/protobuf/util/Durations.java | 8 +- .../com/google/protobuf/util/FieldMaskTree.java | 7 +- .../java/com/google/protobuf/util/JsonFormat.java | 39 +++-- .../java/com/google/protobuf/util/Timestamps.java | 8 +- .../com/google/protobuf/util/JsonFormatTest.java | 38 ++++- .../proto/com/google/protobuf/util/json_test.proto | 5 + 26 files changed, 513 insertions(+), 250 deletions(-) (limited to 'java') 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 66b0ee3b..7ff73ba4 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 abstract class AbstractParser } 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 5b24976d..99a31209 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.List; 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 abstract class ByteString implements Iterable, Serializable { // 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 cab7118a..38346f15 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 @@ public final class Descriptors { // 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 c54da67f..e6358c3b 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 final class DynamicMessage extends AbstractMessage { } 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 1c2e7e6f..a22a74a0 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 @@ package com.google.protobuf; 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 5e4d7739..f3d48d3a 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 class ExtensionRegistryLite { /** * 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 class ExtensionRegistryLite { 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 214971b1..f885b01e 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.IntList; 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 @@ public abstract class GeneratedMessageLite< 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 @@ public abstract class GeneratedMessageLite< 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 @@ public abstract class GeneratedMessageLite< 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 @@ public abstract class GeneratedMessageLite< 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 abstract class GeneratedMessageLite< 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 4cbbd296..2a5d8b50 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.EnumValueDescriptor; 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 abstract class GeneratedMessageV3 extends AbstractMessage 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 @@ public abstract class GeneratedMessageV3 extends AbstractMessage FieldDescriptor getDescriptor(); } + // ================================================================= /** Calls Class.getMethod and throws a RuntimeException if it fails. */ @@ -1705,11 +1708,6 @@ public abstract class GeneratedMessageV3 extends AbstractMessage 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 abstract class GeneratedMessageV3 extends AbstractMessage } 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 2febaace..4b0ba0fd 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 class LazyFieldLite { 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 4f0351f4..179c3348 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 @@ public final class MapEntry extends AbstractMessage { } 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 43847651..23373ef4 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 @@ final class MessageLiteToString { // 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 @@ final class MessageLiteToString { // 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 6d33d3a8..49b3504f 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 final class UnknownFieldSet implements MessageLite { } 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 9500f905..104f8007 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 final class UnknownFieldSetLite { } } + /** + * 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 @@ public final class UnknownFieldSetLite { } } - 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 efdfdfbe..813fe6bc 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 class LazyFieldLiteTest extends TestCase { 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 c8ca9b6a..968ca206 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 class LazyMessageLiteTest extends TestCase { 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 254beba1..538432f7 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.OneofFieldCase; 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 class LiteTest extends TestCase { 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 class LiteTest extends TestCase { 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 8b64fa27..0a14f584 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 final class MapForProto2LiteTest extends TestCase { 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 76bd7bd9..cfe4c453 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 @@ public class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { setMapValuesUsingAccessors(usingAccessorsBuilder); TestMap usingAccessors = usingAccessorsBuilder.build(); assertMapValuesSet(usingAccessors); + assertEquals(usingAccessors, usingMutableMap); } @@ -169,25 +170,25 @@ public class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { 2, TestMap.MessageValue.getDefaultInstance()), builder.getInt32ToMessageField()); } - + // public void testMutableMapLifecycle_collections() { TestMap.Builder builder = TestMap.newBuilder(); Map intMap = builder.getMutableInt32ToInt32Field(); @@ -431,18 +433,19 @@ public class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { // 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 @@ public class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { } 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 class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { public void testContains() { TestMap.Builder builder = TestMap.newBuilder(); - setMapValuesUsingMutableMap(builder); + setMapValuesUsingAccessors(builder); assertMapContainsSetValues(builder); assertMapContainsSetValues(builder.build()); } @@ -920,7 +915,7 @@ public class MapForProto2Test extends TestCase { TestMap.Builder builder = TestMap.newBuilder(); assertMapCounts(0, builder); - setMapValuesUsingMutableMap(builder); + setMapValuesUsingAccessors(builder); assertMapCounts(3, builder); TestMap message = builder.build(); @@ -1091,7 +1086,7 @@ public class MapForProto2Test extends TestCase { 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 class MapForProto2Test extends TestCase { 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 035a8335..81e951cc 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 @@ public class MapTest extends TestCase { 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 class MapTest extends TestCase { setMapValuesUsingAccessors(usingAccessorsBuilder); TestMap usingAccessors = usingAccessorsBuilder.build(); assertMapValuesSet(usingAccessors); + assertEquals(usingAccessors, usingMutableMap); } @@ -171,25 +172,25 @@ public class MapTest extends TestCase { 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { // 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 class MapTest extends TestCase { } 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { } 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { // 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 class MapTest extends TestCase { // 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { 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 @@ public class MapTest extends TestCase { 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { } 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { 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 class MapTest extends TestCase { public void testGetMap() { TestMap.Builder builder = TestMap.newBuilder(); - setMapValuesUsingMutableMap(builder); + setMapValuesUsingAccessors(builder); TestMap message = builder.build(); assertEquals( message.getStringToInt32Field(), @@ -1033,7 +1032,7 @@ public class MapTest extends TestCase { public void testContains() { TestMap.Builder builder = TestMap.newBuilder(); - setMapValuesUsingMutableMap(builder); + setMapValuesUsingAccessors(builder); assertMapContainsSetValues(builder); assertMapContainsSetValues(builder.build()); } @@ -1074,7 +1073,7 @@ public class MapTest extends TestCase { TestMap.Builder builder = TestMap.newBuilder(); assertMapCounts(0, builder); - setMapValuesUsingMutableMap(builder); + setMapValuesUsingAccessors(builder); assertMapCounts(3, builder); TestMap message = builder.build(); @@ -1269,7 +1268,7 @@ public class MapTest extends TestCase { 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 @@ public class MapTest extends TestCase { 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 7086912b..8c2e4c26 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.ByteArrayInputStream; 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 class ParserTest extends TestCase { 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 9333168d..46b21828 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_MICROSECOND; 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 @@ public final class 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 b577495d..b192b53e 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 @@ final class FieldMaskTree { 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 6361b4ac..7f6c8aea 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.Timestamp; 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 class JsonFormat { * 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 class JsonFormat { 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 class JsonFormat { 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 class JsonFormat { public void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException { // TODO(xiaofeng): Investigate the allocation overhead and optimize for // mobile. - new ParserImpl(registry, ignoringUnknownFields).merge(json, builder); + new ParserImpl(registry, ignoringUnknownFields, recursionLimit).merge(json, builder); } /** @@ -283,7 +286,12 @@ public class JsonFormat { public void merge(Reader json, Message.Builder builder) throws IOException { // TODO(xiaofeng): Investigate the allocation overhead and optimize for // mobile. - new ParserImpl(registry, ignoringUnknownFields).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 @@ public class JsonFormat { 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 @@ public class JsonFormat { 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 52b1ab98..2160e4d5 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.checkedAdd; 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 @@ public final class 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 32739d44..164ee54b 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.TestDuration; 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 class JsonFormatTest extends TestCase { 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 class JsonFormatTest extends TestCase { // Expected. } } + public void testParserIgnoringUnknownFields() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); String json = "{\n" + " \"unknownField\": \"XXX\"\n" + "}"; @@ -1358,4 +1364,34 @@ public class JsonFormatTest extends TestCase { 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 bd22f65a..a75338ef 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; +} -- cgit v1.2.3