From 92a7e778e7394386f413cec28d67a07630f784b1 Mon Sep 17 00:00:00 2001 From: Adam Cozzette Date: Fri, 1 Dec 2017 10:05:10 -0800 Subject: Integrated internal changes from Google --- .../src/main/java/com/google/protobuf/Android.java | 57 ++++++++++++++ .../main/java/com/google/protobuf/ByteString.java | 10 +-- .../main/java/com/google/protobuf/FieldSet.java | 5 ++ .../com/google/protobuf/GeneratedMessageLite.java | 42 ++++++----- .../main/java/com/google/protobuf/TextFormat.java | 35 +++++++++ .../main/java/com/google/protobuf/UnsafeUtil.java | 9 --- .../com/google/protobuf/AbstractMessageTest.java | 12 ++- .../test/java/com/google/protobuf/TestUtil.java | 6 ++ .../java/com/google/protobuf/TextFormatTest.java | 88 +++++++++++++++++++--- 9 files changed, 212 insertions(+), 52 deletions(-) create mode 100644 java/core/src/main/java/com/google/protobuf/Android.java (limited to 'java/core') diff --git a/java/core/src/main/java/com/google/protobuf/Android.java b/java/core/src/main/java/com/google/protobuf/Android.java new file mode 100644 index 00000000..cad54783 --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/Android.java @@ -0,0 +1,57 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +final class Android { + + private static final Class MEMORY_CLASS = getClassForName("libcore.io.Memory"); + private static final boolean IS_ROBOLECTRIC = + getClassForName("org.robolectric.Robolectric") != null; + + /** Returns {@code true} if running on an Android device. */ + static boolean isOnAndroidDevice() { + return MEMORY_CLASS != null && !IS_ROBOLECTRIC; + } + + /** Returns the memory class or {@code null} if not on Android device. */ + static Class getMemoryClass() { + return MEMORY_CLASS; + } + + @SuppressWarnings("unchecked") + private static Class getClassForName(String name) { + try { + return (Class) Class.forName(name); + } catch (Throwable e) { + return null; + } + } +} 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 99a31209..d67bb54a 100644 --- a/java/core/src/main/java/com/google/protobuf/ByteString.java +++ b/java/core/src/main/java/com/google/protobuf/ByteString.java @@ -124,14 +124,8 @@ public abstract class ByteString implements Iterable, Serializable { private static final ByteArrayCopier byteArrayCopier; static { - boolean isAndroid = true; - try { - Class.forName("android.content.Context"); - } catch (ClassNotFoundException e) { - isAndroid = false; - } - - byteArrayCopier = isAndroid ? new SystemByteArrayCopier() : new ArraysByteArrayCopier(); + byteArrayCopier = + Android.isOnAndroidDevice() ? new SystemByteArrayCopier() : new ArraysByteArrayCopier(); } /** diff --git a/java/core/src/main/java/com/google/protobuf/FieldSet.java b/java/core/src/main/java/com/google/protobuf/FieldSet.java index 8a9239ed..c09daa32 100644 --- a/java/core/src/main/java/com/google/protobuf/FieldSet.java +++ b/java/core/src/main/java/com/google/protobuf/FieldSet.java @@ -102,6 +102,11 @@ final class FieldSet implements ExtendableMessageOrBuilder { - /** - * Represents the set of extensions on this message. For use by generated - * code only. - */ - protected FieldSet extensions = FieldSet.newFieldSet(); + /** Represents the set of extensions on this message. For use by generated code only. */ + protected FieldSet extensions = FieldSet.emptySet(); @SuppressWarnings("unchecked") protected final void mergeExtensionFields(final MessageType other) { @@ -578,7 +575,11 @@ public abstract class GeneratedMessageLite< if (unknown) { // Unknown field or wrong wire type. Skip. return parseUnknownField(tag, input); } - + + if (extensions.isImmutable()) { + extensions = extensions.clone(); + } + if (packed) { int length = input.readRawVarint32(); int limit = input.pushLimit(length); @@ -942,12 +943,6 @@ public abstract class GeneratedMessageLite< implements ExtendableMessageOrBuilder { protected ExtendableBuilder(MessageType defaultInstance) { super(defaultInstance); - - // TODO(dweis): This is kind of an unnecessary clone since we construct a - // new instance in the parent constructor which makes the extensions - // immutable. This extra allocation shouldn't matter in practice - // though. - instance.extensions = instance.extensions.clone(); } // For immutable message conversion. @@ -966,6 +961,15 @@ public abstract class GeneratedMessageLite< instance.extensions = instance.extensions.clone(); } + private FieldSet ensureExtensionsAreMutable() { + FieldSet extensions = instance.extensions; + if (extensions.isImmutable()) { + extensions = extensions.clone(); + instance.extensions = extensions; + } + return extensions; + } + @Override public final MessageType buildPartial() { if (isBuilt) { @@ -1024,7 +1028,8 @@ public abstract class GeneratedMessageLite< verifyExtensionContainingType(extensionLite); copyOnWrite(); - instance.extensions.setField(extensionLite.descriptor, extensionLite.toFieldSetType(value)); + ensureExtensionsAreMutable() + .setField(extensionLite.descriptor, extensionLite.toFieldSetType(value)); return (BuilderType) this; } @@ -1037,8 +1042,9 @@ public abstract class GeneratedMessageLite< verifyExtensionContainingType(extensionLite); copyOnWrite(); - instance.extensions.setRepeatedField( - extensionLite.descriptor, index, extensionLite.singularToFieldSetType(value)); + ensureExtensionsAreMutable() + .setRepeatedField( + extensionLite.descriptor, index, extensionLite.singularToFieldSetType(value)); return (BuilderType) this; } @@ -1051,8 +1057,8 @@ public abstract class GeneratedMessageLite< verifyExtensionContainingType(extensionLite); copyOnWrite(); - instance.extensions.addRepeatedField( - extensionLite.descriptor, extensionLite.singularToFieldSetType(value)); + ensureExtensionsAreMutable() + .addRepeatedField(extensionLite.descriptor, extensionLite.singularToFieldSetType(value)); return (BuilderType) this; } @@ -1063,7 +1069,7 @@ public abstract class GeneratedMessageLite< verifyExtensionContainingType(extensionLite); copyOnWrite(); - instance.extensions.clearField(extensionLite.descriptor); + ensureExtensionsAreMutable().clearField(extensionLite.descriptor); return (BuilderType) this; } } diff --git a/java/core/src/main/java/com/google/protobuf/TextFormat.java b/java/core/src/main/java/com/google/protobuf/TextFormat.java index ab9acf2f..0c8b898c 100644 --- a/java/core/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java @@ -1223,6 +1223,22 @@ public final class TextFormat { PARSER.merge(input, builder); } + /** + * Parse a text-format message from {@code input}. + * + * @return the parsed message, guaranteed initialized + */ + public static T parse(final CharSequence input, + final Class protoClass) + throws ParseException { + Message.Builder builder = + Internal.getDefaultInstance(protoClass).newBuilderForType(); + merge(input, builder); + @SuppressWarnings("unchecked") + T output = (T) builder.build(); + return output; + } + /** * Parse a text-format message from {@code input} and merge the contents * into {@code builder}. Extensions will be recognized if they are @@ -1248,6 +1264,25 @@ public final class TextFormat { PARSER.merge(input, extensionRegistry, builder); } + /** + * Parse a text-format message from {@code input}. Extensions will be + * recognized if they are registered in {@code extensionRegistry}. + * + * @return the parsed message, guaranteed initialized + */ + public static T parse( + final CharSequence input, + final ExtensionRegistry extensionRegistry, + final Class protoClass) + throws ParseException { + Message.Builder builder = + Internal.getDefaultInstance(protoClass).newBuilderForType(); + merge(input, extensionRegistry, builder); + @SuppressWarnings("unchecked") + T output = (T) builder.build(); + return output; + } + /** * Parser for text-format proto2 instances. This class is thread-safe. diff --git a/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java b/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java index 88315cb6..98c93b90 100644 --- a/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java +++ b/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java @@ -364,15 +364,6 @@ final class UnsafeUtil { } - @SuppressWarnings("unchecked") - private static Class getClassForName(String name) { - try { - return (Class) Class.forName(name); - } catch (Throwable e) { - return null; - } - } - /** Finds the address field within a direct {@link Buffer}. */ private static Field bufferAddressField() { return field(Buffer.class, "address"); diff --git a/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java b/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java index 622e36a4..cb2d34eb 100644 --- a/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java +++ b/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java @@ -30,6 +30,9 @@ package com.google.protobuf; +import static com.google.protobuf.TestUtil.TEST_REQUIRED_INITIALIZED; +import static com.google.protobuf.TestUtil.TEST_REQUIRED_UNINITIALIZED; + import com.google.protobuf.Descriptors.FieldDescriptor; import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize; import protobuf_unittest.UnittestProto; @@ -346,11 +349,6 @@ public class AbstractMessageTest extends TestCase { // ----------------------------------------------------------------- // Tests for isInitialized(). - private static final TestRequired TEST_REQUIRED_UNINITIALIZED = - TestRequired.getDefaultInstance(); - private static final TestRequired TEST_REQUIRED_INITIALIZED = - TestRequired.newBuilder().setA(1).setB(2).setC(3).build(); - public void testIsInitialized() throws Exception { TestRequired.Builder builder = TestRequired.newBuilder(); AbstractMessageWrapper.Builder abstractBuilder = @@ -380,7 +378,7 @@ public class AbstractMessageTest extends TestCase { builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED); assertFalse(abstractBuilder.isInitialized()); assertEquals( - "optional_message.a, optional_message.b, optional_message.c", + "optional_message.b, optional_message.c", abstractBuilder.getInitializationErrorString()); builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED); @@ -390,7 +388,7 @@ public class AbstractMessageTest extends TestCase { builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED); assertFalse(abstractBuilder.isInitialized()); assertEquals( - "repeated_message[0].a, repeated_message[0].b, repeated_message[0].c", + "repeated_message[0].b, repeated_message[0].c", abstractBuilder.getInitializationErrorString()); builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED); diff --git a/java/core/src/test/java/com/google/protobuf/TestUtil.java b/java/core/src/test/java/com/google/protobuf/TestUtil.java index e96adf07..b4bc3a3d 100644 --- a/java/core/src/test/java/com/google/protobuf/TestUtil.java +++ b/java/core/src/test/java/com/google/protobuf/TestUtil.java @@ -231,6 +231,7 @@ import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder; import protobuf_unittest.UnittestProto.TestOneof2; import protobuf_unittest.UnittestProto.TestPackedExtensions; import protobuf_unittest.UnittestProto.TestPackedTypes; +import protobuf_unittest.UnittestProto.TestRequired; import protobuf_unittest.UnittestProto.TestUnpackedTypes; import java.io.File; import java.io.IOException; @@ -252,6 +253,11 @@ import junit.framework.Assert; public final class TestUtil { private TestUtil() {} + public static final TestRequired TEST_REQUIRED_UNINITIALIZED = + TestRequired.newBuilder().setA(1).buildPartial(); + public static final TestRequired TEST_REQUIRED_INITIALIZED = + TestRequired.newBuilder().setA(1).setB(2).setC(3).build(); + /** Helper to convert a String to ByteString. */ static ByteString toBytes(String str) { return ByteString.copyFrom(str.getBytes(Internal.UTF_8)); diff --git a/java/core/src/test/java/com/google/protobuf/TextFormatTest.java b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java index 28c4fdea..5d38ca51 100644 --- a/java/core/src/test/java/com/google/protobuf/TextFormatTest.java +++ b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java @@ -30,6 +30,9 @@ package com.google.protobuf; +import static com.google.protobuf.TestUtil.TEST_REQUIRED_INITIALIZED; +import static com.google.protobuf.TestUtil.TEST_REQUIRED_UNINITIALIZED; + import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.TextFormat.Parser.SingularOverwritePolicy; @@ -42,6 +45,7 @@ import protobuf_unittest.UnittestProto.TestAllTypes; import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage; import protobuf_unittest.UnittestProto.TestEmptyMessage; import protobuf_unittest.UnittestProto.TestOneof2; +import protobuf_unittest.UnittestProto.TestRequired; import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet; import java.io.StringReader; import java.util.List; @@ -326,19 +330,58 @@ public class TextFormatTest extends TestCase { // ================================================================= - public void testParse() throws Exception { + public void testMerge() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TextFormat.merge(allFieldsSetText, builder); TestUtil.assertAllFieldsSet(builder.build()); } - public void testParseReader() throws Exception { + public void testParse() throws Exception { + TestUtil.assertAllFieldsSet( + TextFormat.parse(allFieldsSetText, TestAllTypes.class)); + } + + public void testMergeInitialized() throws Exception { + TestRequired.Builder builder = TestRequired.newBuilder(); + TextFormat.merge(TEST_REQUIRED_INITIALIZED.toString(), builder); + assertEquals(TEST_REQUIRED_INITIALIZED.toString(), + builder.buildPartial().toString()); + assertTrue(builder.isInitialized()); + } + + public void testParseInitialized() throws Exception { + TestRequired parsed = + TextFormat.parse(TEST_REQUIRED_INITIALIZED.toString(), + TestRequired.class); + assertEquals(TEST_REQUIRED_INITIALIZED.toString(), parsed.toString()); + assertTrue(parsed.isInitialized()); + } + + public void testMergeUninitialized() throws Exception { + TestRequired.Builder builder = TestRequired.newBuilder(); + TextFormat.merge(TEST_REQUIRED_UNINITIALIZED.toString(), builder); + assertEquals(TEST_REQUIRED_UNINITIALIZED.toString(), + builder.buildPartial().toString()); + assertFalse(builder.isInitialized()); + } + + public void testParseUninitialized() throws Exception { + try { + TextFormat.parse(TEST_REQUIRED_UNINITIALIZED.toString(), + TestRequired.class); + fail("Expected UninitializedMessageException."); + } catch (UninitializedMessageException e) { + assertEquals("Message missing required fields: b, c", e.getMessage()); + } + } + + public void testMergeReader() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TextFormat.merge(new StringReader(allFieldsSetText), builder); TestUtil.assertAllFieldsSet(builder.build()); } - public void testParseExtensions() throws Exception { + public void testMergeExtensions() throws Exception { TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); TextFormat.merge(allExtensionsSetText, TestUtil.getExtensionRegistry(), @@ -346,7 +389,14 @@ public class TextFormatTest extends TestCase { TestUtil.assertAllExtensionsSet(builder.build()); } - public void testParseCompatibility() throws Exception { + public void testParseExtensions() throws Exception { + TestUtil.assertAllExtensionsSet( + TextFormat.parse(allExtensionsSetText, + TestUtil.getExtensionRegistry(), + TestAllExtensions.class)); + } + + public void testMergeAndParseCompatibility() throws Exception { String original = "repeated_float: inf\n" + "repeated_float: -inf\n" + "repeated_float: nan\n" + @@ -371,21 +421,29 @@ public class TextFormatTest extends TestCase { "repeated_double: Infinity\n" + "repeated_double: -Infinity\n" + "repeated_double: NaN\n"; + + // Test merge(). TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TextFormat.merge(original, builder); assertEquals(canonical, builder.build().toString()); + + // Test parse(). + assertEquals(canonical, + TextFormat.parse(original, TestAllTypes.class).toString()); } - public void testParseExotic() throws Exception { + public void testMergeAndParseExotic() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TextFormat.merge(exoticText, builder); // Too lazy to check things individually. Don't try to debug this // if testPrintExotic() is failing. assertEquals(canonicalExoticText, builder.build().toString()); + assertEquals(canonicalExoticText, + TextFormat.parse(exoticText, TestAllTypes.class).toString()); } - public void testParseMessageSet() throws Exception { + public void testMergeMessageSet() throws Exception { ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); extensionRegistry.add(TestMessageSetExtension1.messageSetExtension); extensionRegistry.add(TestMessageSetExtension2.messageSetExtension); @@ -411,7 +469,7 @@ public class TextFormatTest extends TestCase { TestMessageSetExtension1.messageSetExtension).getI()); } - public void testParseMessageSetWithOverwriteForbidden() throws Exception { + public void testMergeMessageSetWithOverwriteForbidden() throws Exception { ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); extensionRegistry.add(TestMessageSetExtension1.messageSetExtension); extensionRegistry.add(TestMessageSetExtension2.messageSetExtension); @@ -438,20 +496,20 @@ public class TextFormatTest extends TestCase { } } - public void testParseNumericEnum() throws Exception { + public void testMergeNumericEnum() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TextFormat.merge("optional_nested_enum: 2", builder); assertEquals(TestAllTypes.NestedEnum.BAR, builder.getOptionalNestedEnum()); } - public void testParseAngleBrackets() throws Exception { + public void testMergeAngleBrackets() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TextFormat.merge("OptionalGroup: < a: 1 >", builder); assertTrue(builder.hasOptionalGroup()); assertEquals(1, builder.getOptionalGroup().getA()); } - public void testParseComment() throws Exception { + public void testMergeComment() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TextFormat.merge( "# this is a comment\n" + @@ -463,6 +521,7 @@ public class TextFormatTest extends TestCase { } private void assertParseError(String error, String text) { + // Test merge(). TestAllTypes.Builder builder = TestAllTypes.newBuilder(); try { TextFormat.merge(text, TestUtil.getExtensionRegistry(), builder); @@ -470,6 +529,15 @@ public class TextFormatTest extends TestCase { } catch (TextFormat.ParseException e) { assertEquals(error, e.getMessage()); } + + // Test parse(). + try { + TextFormat.parse( + text, TestUtil.getExtensionRegistry(), TestAllTypes.class); + fail("Expected parse exception."); + } catch (TextFormat.ParseException e) { + assertEquals(error, e.getMessage()); + } } -- cgit v1.2.3