diff options
Diffstat (limited to 'java/src')
21 files changed, 509 insertions, 111 deletions
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/src/main/java/com/google/protobuf/AbstractMessage.java index e5bdefe3..b059bc98 100644 --- a/java/src/main/java/com/google/protobuf/AbstractMessage.java +++ b/java/src/main/java/com/google/protobuf/AbstractMessage.java @@ -311,6 +311,12 @@ public abstract class AbstractMessage extends AbstractMessageLite } else { field = extension.descriptor; defaultInstance = extension.defaultInstance; + if (defaultInstance == null && + field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + throw new IllegalStateException( + "Message-typed extension lacked default instance: " + + field.getFullName()); + } } } else { field = null; @@ -319,15 +325,28 @@ public abstract class AbstractMessage extends AbstractMessageLite field = type.findFieldByNumber(fieldNumber); } - if (field == null || wireType != - FieldSet.getWireFormatForFieldType( - field.getLiteType(), - field.getOptions().getPacked())) { - // Unknown field or wrong wire type. Skip. + boolean unknown = false; + boolean packed = false; + if (field == null) { + unknown = true; // Unknown field. + } else if (wireType == FieldSet.getWireFormatForFieldType( + field.getLiteType(), + false /* isPacked */)) { + packed = false; + } else if (field.isPackable() && + wireType == FieldSet.getWireFormatForFieldType( + field.getLiteType(), + true /* isPacked */)) { + packed = true; + } else { + unknown = true; // Unknown wire type. + } + + if (unknown) { // Unknown field or wrong wire type. Skip. return unknownFields.mergeFieldFrom(tag, input); } - if (field.getOptions().getPacked()) { + if (packed) { final int length = input.readRawVarint32(); final int limit = input.pushLimit(length); if (field.getLiteType() == WireFormat.FieldType.ENUM) { @@ -673,13 +692,13 @@ public abstract class AbstractMessage extends AbstractMessageLite } @Override - public BuilderType mergeDelimitedFrom(final InputStream input) + public boolean mergeDelimitedFrom(final InputStream input) throws IOException { return super.mergeDelimitedFrom(input); } @Override - public BuilderType mergeDelimitedFrom( + public boolean mergeDelimitedFrom( final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException { diff --git a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java index 86bf02d8..9210d853 100644 --- a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java +++ b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java @@ -86,7 +86,7 @@ public abstract class AbstractMessageLite implements MessageLite { CodedOutputStream.computeRawVarint32Size(serialized) + serialized); final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output, bufferSize); - codedOutput.writeRawVarint32(getSerializedSize()); + codedOutput.writeRawVarint32(serialized); writeTo(codedOutput); codedOutput.flush(); } @@ -105,13 +105,7 @@ public abstract class AbstractMessageLite implements MessageLite { public BuilderType mergeFrom(final CodedInputStream input) throws IOException { - // TODO(kenton): Don't use null here. Currently we have to because - // using ExtensionRegistry.getEmptyRegistry() would imply a dependency - // on ExtensionRegistry. However, AbstractMessage overrides this with - // a correct implementation, and lite messages don't yet support - // extensions, so it ends up not mattering for now. It will matter - // once lite messages support extensions. - return mergeFrom(input, null); + return mergeFrom(input, ExtensionRegistryLite.getEmptyRegistry()); } // Re-defined here for return type covariance. @@ -275,20 +269,24 @@ public abstract class AbstractMessageLite implements MessageLite { } } - public BuilderType mergeDelimitedFrom( + public boolean mergeDelimitedFrom( final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException { - final int size = CodedInputStream.readRawVarint32(input); + final int firstByte = input.read(); + if (firstByte == -1) { + return false; + } + final int size = CodedInputStream.readRawVarint32(firstByte, input); final InputStream limitedInput = new LimitedInputStream(input, size); - return mergeFrom(limitedInput, extensionRegistry); + mergeFrom(limitedInput, extensionRegistry); + return true; } - public BuilderType mergeDelimitedFrom(final InputStream input) + public boolean mergeDelimitedFrom(final InputStream input) throws IOException { - final int size = CodedInputStream.readRawVarint32(input); - final InputStream limitedInput = new LimitedInputStream(input, size); - return mergeFrom(limitedInput); + return mergeDelimitedFrom(input, + ExtensionRegistryLite.getEmptyRegistry()); } /** diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/src/main/java/com/google/protobuf/ByteString.java index c83c335c..c043df8b 100644 --- a/java/src/main/java/com/google/protobuf/ByteString.java +++ b/java/src/main/java/com/google/protobuf/ByteString.java @@ -99,6 +99,24 @@ public final class ByteString { } /** + * Copies {@code size} bytes from a {@code java.nio.ByteBuffer} into + * a {@code ByteString}. + */ + public static ByteString copyFrom(final ByteBuffer bytes, final int size) { + final byte[] copy = new byte[size]; + bytes.get(copy); + return new ByteString(copy); + } + + /** + * Copies the remaining bytes from a {@code java.nio.ByteBuffer} into + * a {@code ByteString}. + */ + public static ByteString copyFrom(final ByteBuffer bytes) { + return copyFrom(bytes, bytes.remaining()); + } + + /** * Encodes {@code text} into a sequence of bytes using the named charset * and returns the result as a {@code ByteString}. */ diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/src/main/java/com/google/protobuf/CodedInputStream.java index f339c00e..f0c1051d 100644 --- a/java/src/main/java/com/google/protobuf/CodedInputStream.java +++ b/java/src/main/java/com/google/protobuf/CodedInputStream.java @@ -84,8 +84,9 @@ public final class CodedInputStream { } lastTag = readRawVarint32(); - if (lastTag == 0) { - // If we actually read zero, that's not a valid tag. + if (WireFormat.getTagFieldNumber(lastTag) == 0) { + // If we actually read zero (or any tag number corresponding to field + // number zero), that's not a valid tag. throw InvalidProtocolBufferException.invalidTag(); } return lastTag; @@ -355,8 +356,26 @@ public final class CodedInputStream { * CodedInputStream buffers its input. */ static int readRawVarint32(final InputStream input) throws IOException { - int result = 0; - int offset = 0; + final int firstByte = input.read(); + if (firstByte == -1) { + throw InvalidProtocolBufferException.truncatedMessage(); + } + return readRawVarint32(firstByte, input); + } + + /** + * Like {@link #readRawVarint32(InputStream)}, but expects that the caller + * has already read one byte. This allows the caller to determine if EOF + * has been reached before attempting to read. + */ + static int readRawVarint32(final int firstByte, + final InputStream input) throws IOException { + if ((firstByte & 0x80) == 0) { + return firstByte; + } + + int result = firstByte & 0x7f; + int offset = 7; for (; offset < 32; offset += 7) { final int b = input.read(); if (b == -1) { diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java index 0c162d5d..c5e9a04b 100644 --- a/java/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -48,7 +48,7 @@ import java.io.UnsupportedEncodingException; * (given a message object of the type) {@code message.getDescriptorForType()}. * * Descriptors are built from DescriptorProtos, as defined in - * {@code net/proto2/proto/descriptor.proto}. + * {@code google/protobuf/descriptor.proto}. * * @author kenton@google.com Kenton Varda */ @@ -699,6 +699,11 @@ public final class Descriptors { return getOptions().getPacked(); } + /** Can this field be packed? i.e. is it a repeated primitive field? */ + public boolean isPackable() { + return isRepeated() && getLiteType().isPackable(); + } + /** Returns true if the field had an explicitly-defined default value. */ public boolean hasDefaultValue() { return proto.hasDefaultValue(); } @@ -810,39 +815,34 @@ public final class Descriptors { private Object defaultValue; public enum Type { - DOUBLE (FieldDescriptorProto.Type.TYPE_DOUBLE , JavaType.DOUBLE ), - FLOAT (FieldDescriptorProto.Type.TYPE_FLOAT , JavaType.FLOAT ), - INT64 (FieldDescriptorProto.Type.TYPE_INT64 , JavaType.LONG ), - UINT64 (FieldDescriptorProto.Type.TYPE_UINT64 , JavaType.LONG ), - INT32 (FieldDescriptorProto.Type.TYPE_INT32 , JavaType.INT ), - FIXED64 (FieldDescriptorProto.Type.TYPE_FIXED64 , JavaType.LONG ), - FIXED32 (FieldDescriptorProto.Type.TYPE_FIXED32 , JavaType.INT ), - BOOL (FieldDescriptorProto.Type.TYPE_BOOL , JavaType.BOOLEAN ), - STRING (FieldDescriptorProto.Type.TYPE_STRING , JavaType.STRING ), - GROUP (FieldDescriptorProto.Type.TYPE_GROUP , JavaType.MESSAGE ), - MESSAGE (FieldDescriptorProto.Type.TYPE_MESSAGE , JavaType.MESSAGE ), - BYTES (FieldDescriptorProto.Type.TYPE_BYTES , JavaType.BYTE_STRING), - UINT32 (FieldDescriptorProto.Type.TYPE_UINT32 , JavaType.INT ), - ENUM (FieldDescriptorProto.Type.TYPE_ENUM , JavaType.ENUM ), - SFIXED32(FieldDescriptorProto.Type.TYPE_SFIXED32, JavaType.INT ), - SFIXED64(FieldDescriptorProto.Type.TYPE_SFIXED64, JavaType.LONG ), - SINT32 (FieldDescriptorProto.Type.TYPE_SINT32 , JavaType.INT ), - SINT64 (FieldDescriptorProto.Type.TYPE_SINT64 , JavaType.LONG ); - - Type(final FieldDescriptorProto.Type proto, final JavaType javaType) { - this.proto = proto; + DOUBLE (JavaType.DOUBLE ), + FLOAT (JavaType.FLOAT ), + INT64 (JavaType.LONG ), + UINT64 (JavaType.LONG ), + INT32 (JavaType.INT ), + FIXED64 (JavaType.LONG ), + FIXED32 (JavaType.INT ), + BOOL (JavaType.BOOLEAN ), + STRING (JavaType.STRING ), + GROUP (JavaType.MESSAGE ), + MESSAGE (JavaType.MESSAGE ), + BYTES (JavaType.BYTE_STRING), + UINT32 (JavaType.INT ), + ENUM (JavaType.ENUM ), + SFIXED32(JavaType.INT ), + SFIXED64(JavaType.LONG ), + SINT32 (JavaType.INT ), + SINT64 (JavaType.LONG ); + + Type(final JavaType javaType) { this.javaType = javaType; - - if (ordinal() != proto.getNumber() - 1) { - throw new RuntimeException( - "descriptor.proto changed but Desrciptors.java wasn't updated."); - } } - private FieldDescriptorProto.Type proto; private JavaType javaType; - public FieldDescriptorProto.Type toProto() { return proto; } + public FieldDescriptorProto.Type toProto() { + return FieldDescriptorProto.Type.valueOf(ordinal() + 1); + } public JavaType getJavaType() { return javaType; } public static Type valueOf(final FieldDescriptorProto.Type type) { @@ -902,16 +902,10 @@ public final class Descriptors { } // Only repeated primitive fields may be packed. - if (proto.getOptions().getPacked()) { - if (proto.getLabel() != FieldDescriptorProto.Label.LABEL_REPEATED || - proto.getType() == FieldDescriptorProto.Type.TYPE_STRING || - proto.getType() == FieldDescriptorProto.Type.TYPE_GROUP || - proto.getType() == FieldDescriptorProto.Type.TYPE_MESSAGE || - proto.getType() == FieldDescriptorProto.Type.TYPE_BYTES) { - throw new DescriptorValidationException(this, - "[packed = true] can only be specified for repeated primitive " + - "fields."); - } + if (proto.getOptions().getPacked() && !isPackable()) { + throw new DescriptorValidationException(this, + "[packed = true] can only be specified for repeated primitive " + + "fields."); } if (isExtension) { @@ -1030,10 +1024,26 @@ public final class Descriptors { defaultValue = TextFormat.parseUInt64(proto.getDefaultValue()); break; case FLOAT: - defaultValue = Float.valueOf(proto.getDefaultValue()); + if (proto.getDefaultValue().equals("inf")) { + defaultValue = Float.POSITIVE_INFINITY; + } else if (proto.getDefaultValue().equals("-inf")) { + defaultValue = Float.NEGATIVE_INFINITY; + } else if (proto.getDefaultValue().equals("nan")) { + defaultValue = Float.NaN; + } else { + defaultValue = Float.valueOf(proto.getDefaultValue()); + } break; case DOUBLE: - defaultValue = Double.valueOf(proto.getDefaultValue()); + if (proto.getDefaultValue().equals("inf")) { + defaultValue = Double.POSITIVE_INFINITY; + } else if (proto.getDefaultValue().equals("-inf")) { + defaultValue = Double.NEGATIVE_INFINITY; + } else if (proto.getDefaultValue().equals("nan")) { + defaultValue = Double.NaN; + } else { + defaultValue = Double.valueOf(proto.getDefaultValue()); + } break; case BOOL: defaultValue = Boolean.valueOf(proto.getDefaultValue()); @@ -1064,12 +1074,9 @@ public final class Descriptors { "Message type had default value."); } } catch (NumberFormatException e) { - final DescriptorValidationException validationException = - new DescriptorValidationException(this, - "Could not parse default value: \"" + - proto.getDefaultValue() + '\"'); - validationException.initCause(e); - throw validationException; + throw new DescriptorValidationException(this, + "Could not parse default value: \"" + + proto.getDefaultValue() + '\"', e); } } else { // Determine the default default for this field. @@ -1536,14 +1543,7 @@ public final class Descriptors { private DescriptorValidationException( final GenericDescriptor problemDescriptor, final String description) { - this(problemDescriptor, description, null); - } - - private DescriptorValidationException( - final GenericDescriptor problemDescriptor, - final String description, - final Throwable cause) { - super(problemDescriptor.getFullName() + ": " + description, cause); + super(problemDescriptor.getFullName() + ": " + description); // Note that problemDescriptor may be partially uninitialized, so we // don't want to expose it directly to the user. So, we only provide @@ -1554,6 +1554,14 @@ public final class Descriptors { } private DescriptorValidationException( + final GenericDescriptor problemDescriptor, + final String description, + final Throwable cause) { + this(problemDescriptor, description); + initCause(cause); + } + + private DescriptorValidationException( final FileDescriptor problemDescriptor, final String description) { super(problemDescriptor.getName() + ": " + description); diff --git a/java/src/main/java/com/google/protobuf/ExtensionRegistry.java b/java/src/main/java/com/google/protobuf/ExtensionRegistry.java index 87bbd6eb..d4f6ba9e 100644 --- a/java/src/main/java/com/google/protobuf/ExtensionRegistry.java +++ b/java/src/main/java/com/google/protobuf/ExtensionRegistry.java @@ -157,6 +157,11 @@ public final class ExtensionRegistry extends ExtensionRegistryLite { public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) { if (extension.getDescriptor().getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (extension.getMessageDefaultInstance() == null) { + throw new IllegalStateException( + "Registered message-type extension had null default instance: " + + extension.getDescriptor().getFullName()); + } add(new ExtensionInfo(extension.getDescriptor(), extension.getMessageDefaultInstance())); } else { diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java index 4994faad..b5583ba3 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessage.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -789,6 +789,10 @@ public abstract class GeneratedMessage extends AbstractMessage { messageDefaultInstance = (Message) invokeOrDie(getMethodOrDie(type, "getDefaultInstance"), null); + if (messageDefaultInstance == null) { + throw new IllegalStateException( + type.getName() + ".getDefaultInstance() returned null."); + } break; case ENUM: enumValueOf = getMethodOrDie(type, "valueOf", diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java index c68414bd..e327f745 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java @@ -303,7 +303,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { final ExtensionRegistryLite extensionRegistry, final int tag) throws IOException { final FieldSet<ExtensionDescriptor> extensions = - internalGetResult().extensions; + ((ExtendableMessage) internalGetResult()).extensions; final int wireType = WireFormat.getTagWireType(tag); final int fieldNumber = WireFormat.getTagFieldNumber(tag); @@ -312,15 +312,29 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { extensionRegistry.findLiteExtensionByNumber( getDefaultInstanceForType(), fieldNumber); - if (extension == null || wireType != - FieldSet.getWireFormatForFieldType( - extension.descriptor.getLiteType(), - extension.descriptor.isPacked())) { - // Unknown field or wrong wire type. Skip. + boolean unknown = false; + boolean packed = false; + if (extension == null) { + unknown = true; // Unknown field. + } else if (wireType == FieldSet.getWireFormatForFieldType( + extension.descriptor.getLiteType(), + false /* isPacked */)) { + packed = false; // Normal, unpacked value. + } else if (extension.descriptor.isRepeated && + extension.descriptor.type.isPackable() && + wireType == FieldSet.getWireFormatForFieldType( + extension.descriptor.getLiteType(), + true /* isPacked */)) { + packed = true; // Packed value. + } else { + unknown = true; // Wrong wire type. + } + + if (unknown) { // Unknown field or wrong wire type. Skip. return input.skipField(tag); } - if (extension.descriptor.isPacked()) { + if (packed) { final int length = input.readRawVarint32(); final int limit = input.pushLimit(length); if (extension.descriptor.getLiteType() == WireFormat.FieldType.ENUM) { @@ -396,7 +410,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { } protected final void mergeExtensionFields(final MessageType other) { - internalGetResult().extensions.mergeFrom(other.extensions); + ((ExtendableMessage) internalGetResult()).extensions.mergeFrom( + ((ExtendableMessage) other).extensions); } } diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java index c11abdc5..8c29e212 100644 --- a/java/src/main/java/com/google/protobuf/Message.java +++ b/java/src/main/java/com/google/protobuf/Message.java @@ -296,9 +296,9 @@ public interface Message extends MessageLite { Builder mergeFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException; - Builder mergeDelimitedFrom(InputStream input) + boolean mergeDelimitedFrom(InputStream input) throws IOException; - Builder mergeDelimitedFrom(InputStream input, + boolean mergeDelimitedFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException; } diff --git a/java/src/main/java/com/google/protobuf/MessageLite.java b/java/src/main/java/com/google/protobuf/MessageLite.java index 3ebe9bba..cf7f39e2 100644 --- a/java/src/main/java/com/google/protobuf/MessageLite.java +++ b/java/src/main/java/com/google/protobuf/MessageLite.java @@ -317,14 +317,18 @@ public interface MessageLite { * then the message data. Use * {@link MessageLite#writeDelimitedTo(OutputStream)} to write messages in * this format. + * + * @returns True if successful, or false if the stream is at EOF when the + * method starts. Any other error (including reaching EOF during + * parsing) will cause an exception to be thrown. */ - Builder mergeDelimitedFrom(InputStream input) + boolean mergeDelimitedFrom(InputStream input) throws IOException; /** * Like {@link #mergeDelimitedFrom(InputStream)} but supporting extensions. */ - Builder mergeDelimitedFrom(InputStream input, + boolean mergeDelimitedFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException; } diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java index a855720b..698992f0 100644 --- a/java/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -426,7 +426,7 @@ public final class TextFormat { Pattern.compile("(\\s|(#.*$))++", Pattern.MULTILINE); private static final Pattern TOKEN = Pattern.compile( "[a-zA-Z_][0-9a-zA-Z_+-]*+|" + // an identifier - "[0-9+-][0-9a-zA-Z_.+-]*+|" + // a number + "[.]?[0-9+-][0-9a-zA-Z_.+-]*+|" + // a number "\"([^\"\n\\\\]|\\\\.)*+(\"|\\\\?$)|" + // a double-quoted string "\'([^\"\n\\\\]|\\\\.)*+(\'|\\\\?$)", // a single-quoted string Pattern.MULTILINE); diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java index 7f7e4939..26a15d00 100644 --- a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java +++ b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java @@ -30,6 +30,8 @@ package com.google.protobuf; +import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -551,19 +553,23 @@ public final class UnknownFieldSet implements MessageLite { return this; } - public Builder mergeDelimitedFrom(InputStream input) + public boolean mergeDelimitedFrom(InputStream input) throws IOException { - final int size = CodedInputStream.readRawVarint32(input); - final InputStream limitedInput = - new AbstractMessage.Builder.LimitedInputStream(input, size); - return mergeFrom(limitedInput, null); + final int firstByte = input.read(); + if (firstByte == -1) { + return false; + } + final int size = CodedInputStream.readRawVarint32(firstByte, input); + final InputStream limitedInput = new LimitedInputStream(input, size); + mergeFrom(limitedInput); + return true; } - public Builder mergeDelimitedFrom( + public boolean mergeDelimitedFrom( InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException { // UnknownFieldSet has no extensions. - return mergeFrom(input); + return mergeDelimitedFrom(input); } public Builder mergeFrom( diff --git a/java/src/main/java/com/google/protobuf/WireFormat.java b/java/src/main/java/com/google/protobuf/WireFormat.java index 3b0bdcd0..c46f7b0a 100644 --- a/java/src/main/java/com/google/protobuf/WireFormat.java +++ b/java/src/main/java/com/google/protobuf/WireFormat.java @@ -113,10 +113,18 @@ public final class WireFormat { FIXED64 (JavaType.LONG , WIRETYPE_FIXED64 ), FIXED32 (JavaType.INT , WIRETYPE_FIXED32 ), BOOL (JavaType.BOOLEAN , WIRETYPE_VARINT ), - STRING (JavaType.STRING , WIRETYPE_LENGTH_DELIMITED), - GROUP (JavaType.MESSAGE , WIRETYPE_START_GROUP ), - MESSAGE (JavaType.MESSAGE , WIRETYPE_LENGTH_DELIMITED), - BYTES (JavaType.BYTE_STRING, WIRETYPE_LENGTH_DELIMITED), + STRING (JavaType.STRING , WIRETYPE_LENGTH_DELIMITED) { + public boolean isPackable() { return false; } + }, + GROUP (JavaType.MESSAGE , WIRETYPE_START_GROUP ) { + public boolean isPackable() { return false; } + }, + MESSAGE (JavaType.MESSAGE , WIRETYPE_LENGTH_DELIMITED) { + public boolean isPackable() { return false; } + }, + BYTES (JavaType.BYTE_STRING, WIRETYPE_LENGTH_DELIMITED) { + public boolean isPackable() { return false; } + }, UINT32 (JavaType.INT , WIRETYPE_VARINT ), ENUM (JavaType.ENUM , WIRETYPE_VARINT ), SFIXED32(JavaType.INT , WIRETYPE_FIXED32 ), @@ -134,6 +142,8 @@ public final class WireFormat { public JavaType getJavaType() { return javaType; } public int getWireType() { return wireType; } + + public boolean isPackable() { return true; } } // Field numbers for feilds in MessageSet wire format. diff --git a/java/src/test/java/com/google/protobuf/AbstractMessageTest.java b/java/src/test/java/com/google/protobuf/AbstractMessageTest.java index 2c59fd0d..c44d6605 100644 --- a/java/src/test/java/com/google/protobuf/AbstractMessageTest.java +++ b/java/src/test/java/com/google/protobuf/AbstractMessageTest.java @@ -38,6 +38,7 @@ import protobuf_unittest.UnittestProto.TestAllTypes; import protobuf_unittest.UnittestProto.TestPackedTypes; import protobuf_unittest.UnittestProto.TestRequired; import protobuf_unittest.UnittestProto.TestRequiredForeign; +import protobuf_unittest.UnittestProto.TestUnpackedTypes; import junit.framework.TestCase; @@ -238,6 +239,43 @@ public class AbstractMessageTest extends TestCase { TestUtil.assertPackedFieldsSet((TestPackedTypes) message.wrappedMessage); } + public void testUnpackedSerialization() throws Exception { + Message abstractMessage = + new AbstractMessageWrapper(TestUtil.getUnpackedSet()); + + TestUtil.assertUnpackedFieldsSet( + TestUnpackedTypes.parseFrom(abstractMessage.toByteString())); + + assertEquals(TestUtil.getUnpackedSet().toByteString(), + abstractMessage.toByteString()); + } + + public void testParsePackedToUnpacked() throws Exception { + AbstractMessageWrapper.Builder builder = + new AbstractMessageWrapper.Builder(TestUnpackedTypes.newBuilder()); + AbstractMessageWrapper message = + builder.mergeFrom(TestUtil.getPackedSet().toByteString()).build(); + TestUtil.assertUnpackedFieldsSet( + (TestUnpackedTypes) message.wrappedMessage); + } + + public void testParseUnpackedToPacked() throws Exception { + AbstractMessageWrapper.Builder builder = + new AbstractMessageWrapper.Builder(TestPackedTypes.newBuilder()); + AbstractMessageWrapper message = + builder.mergeFrom(TestUtil.getUnpackedSet().toByteString()).build(); + TestUtil.assertPackedFieldsSet((TestPackedTypes) message.wrappedMessage); + } + + public void testUnpackedParsing() throws Exception { + AbstractMessageWrapper.Builder builder = + new AbstractMessageWrapper.Builder(TestUnpackedTypes.newBuilder()); + AbstractMessageWrapper message = + builder.mergeFrom(TestUtil.getUnpackedSet().toByteString()).build(); + TestUtil.assertUnpackedFieldsSet( + (TestUnpackedTypes) message.wrappedMessage); + } + public void testOptimizedForSize() throws Exception { // We're mostly only checking that this class was compiled successfully. TestOptimizedForSize message = diff --git a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java index a75a400b..6acd3223 100644 --- a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java +++ b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java @@ -490,4 +490,18 @@ public class CodedInputStreamTest extends TestCase { assertEquals(0, in.readTag()); assertEquals(5, in.getTotalBytesRead()); } + + public void testInvalidTag() throws Exception { + // Any tag number which corresponds to field number zero is invalid and + // should throw InvalidProtocolBufferException. + for (int i = 0; i < 8; i++) { + try { + CodedInputStream.newInstance(bytes(i)).readTag(); + fail("Should have thrown an exception."); + } catch (InvalidProtocolBufferException e) { + assertEquals(InvalidProtocolBufferException.invalidTag().getMessage(), + e.getMessage()); + } + } + } } diff --git a/java/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/src/test/java/com/google/protobuf/DescriptorsTest.java index c5c38b27..f41d070e 100644 --- a/java/src/test/java/com/google/protobuf/DescriptorsTest.java +++ b/java/src/test/java/com/google/protobuf/DescriptorsTest.java @@ -30,6 +30,10 @@ package com.google.protobuf; +import com.google.protobuf.DescriptorProtos.DescriptorProto; +import com.google.protobuf.DescriptorProtos.FieldDescriptorProto; +import com.google.protobuf.DescriptorProtos.FileDescriptorProto; +import com.google.protobuf.Descriptors.DescriptorValidationException; import com.google.protobuf.Descriptors.FileDescriptor; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; @@ -63,6 +67,22 @@ import java.util.Collections; * @author kenton@google.com Kenton Varda */ public class DescriptorsTest extends TestCase { + + // Regression test for bug where referencing a FieldDescriptor.Type value + // before a FieldDescriptorProto.Type value would yield a + // ExceptionInInitializerError. + private static final Object STATIC_INIT_TEST = FieldDescriptor.Type.BOOL; + + public void testFieldTypeEnumMapping() throws Exception { + assertEquals(FieldDescriptor.Type.values().length, + FieldDescriptorProto.Type.values().length); + for (FieldDescriptor.Type type : FieldDescriptor.Type.values()) { + FieldDescriptorProto.Type protoType = type.toProto(); + assertEquals("TYPE_" + type.name(), protoType.name()); + assertEquals(type, FieldDescriptor.Type.valueOf(protoType)); + } + } + public void testFileDescriptor() throws Exception { FileDescriptor file = UnittestProto.getDescriptor(); @@ -405,4 +425,35 @@ public class DescriptorsTest extends TestCase { UnittestEnormousDescriptor.getDescriptor() .toProto().getSerializedSize() > 65536); } + + /** + * Tests that the DescriptorValidationException works as intended. + */ + public void testDescriptorValidatorException() throws Exception { + FileDescriptorProto fileDescriptorProto = FileDescriptorProto.newBuilder() + .setName("foo.proto") + .addMessageType(DescriptorProto.newBuilder() + .setName("Foo") + .addField(FieldDescriptorProto.newBuilder() + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setName("foo") + .setNumber(1) + .setDefaultValue("invalid") + .build()) + .build()) + .build(); + try { + Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, + new FileDescriptor[0]); + fail("DescriptorValidationException expected"); + } catch (DescriptorValidationException e) { + // Expected; check that the error message contains some useful hints + assertTrue(e.getMessage().indexOf("foo") != -1); + assertTrue(e.getMessage().indexOf("Foo") != -1); + assertTrue(e.getMessage().indexOf("invalid") != -1); + assertTrue(e.getCause() instanceof NumberFormatException); + assertTrue(e.getCause().getMessage().indexOf("invalid") != -1); + } + } } diff --git a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java index cdf60c5e..73c71f31 100644 --- a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java +++ b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java @@ -39,6 +39,8 @@ import protobuf_unittest.UnittestProto.ForeignEnum; import protobuf_unittest.UnittestProto.TestAllTypes; import protobuf_unittest.UnittestProto.TestAllExtensions; import protobuf_unittest.UnittestProto.TestExtremeDefaultValues; +import protobuf_unittest.UnittestProto.TestPackedTypes; +import protobuf_unittest.UnittestProto.TestUnpackedTypes; import protobuf_unittest.MultipleFilesTestProto; import protobuf_unittest.MessageWithNoOuter; import protobuf_unittest.EnumWithNoOuter; @@ -303,8 +305,15 @@ public class GeneratedMessageTest extends TestCase { TestUtil.assertClear(TestAllTypes.getDefaultInstance()); TestUtil.assertClear(TestAllTypes.newBuilder().build()); - assertEquals("\u1234", - TestExtremeDefaultValues.getDefaultInstance().getUtf8String()); + TestExtremeDefaultValues message = + TestExtremeDefaultValues.getDefaultInstance(); + assertEquals("\u1234", message.getUtf8String()); + assertEquals(Double.POSITIVE_INFINITY, message.getInfDouble()); + assertEquals(Double.NEGATIVE_INFINITY, message.getNegInfDouble()); + assertTrue(Double.isNaN(message.getNanDouble())); + assertEquals(Float.POSITIVE_INFINITY, message.getInfFloat()); + assertEquals(Float.NEGATIVE_INFINITY, message.getNegInfFloat()); + assertTrue(Float.isNaN(message.getNanFloat())); } public void testReflectionGetters() throws Exception { @@ -361,6 +370,20 @@ public class GeneratedMessageTest extends TestCase { assertTrue(map.findValueByNumber(12345) == null); } + public void testParsePackedToUnpacked() throws Exception { + TestUnpackedTypes.Builder builder = TestUnpackedTypes.newBuilder(); + TestUnpackedTypes message = + builder.mergeFrom(TestUtil.getPackedSet().toByteString()).build(); + TestUtil.assertUnpackedFieldsSet(message); + } + + public void testParseUnpackedToPacked() throws Exception { + TestPackedTypes.Builder builder = TestPackedTypes.newBuilder(); + TestPackedTypes message = + builder.mergeFrom(TestUtil.getUnpackedSet().toByteString()).build(); + TestUtil.assertPackedFieldsSet(message); + } + // ================================================================= // Extensions. @@ -615,4 +638,12 @@ public class GeneratedMessageTest extends TestCase { UnittestProto.REPEATED_NESTED_MESSAGE_EXTENSION_FIELD_NUMBER, 48); assertEquals(UnittestProto.REPEATED_NESTED_ENUM_EXTENSION_FIELD_NUMBER, 51); } + + public void testRecursiveMessageDefaultInstance() throws Exception { + UnittestProto.TestRecursiveMessage message = + UnittestProto.TestRecursiveMessage.getDefaultInstance(); + assertTrue(message != null); + assertTrue(message.getA() != null); + assertTrue(message.getA() == message); + } } diff --git a/java/src/test/java/com/google/protobuf/ServiceTest.java b/java/src/test/java/com/google/protobuf/ServiceTest.java index d8523ea3..e10322dc 100644 --- a/java/src/test/java/com/google/protobuf/ServiceTest.java +++ b/java/src/test/java/com/google/protobuf/ServiceTest.java @@ -30,7 +30,9 @@ package com.google.protobuf; +import com.google.protobuf.Descriptors.FileDescriptor; import com.google.protobuf.Descriptors.MethodDescriptor; +import google.protobuf.no_generic_services_test.UnittestNoGenericServices; import protobuf_unittest.MessageWithNoOuter; import protobuf_unittest.ServiceWithNoOuter; import protobuf_unittest.UnittestProto.TestAllTypes; @@ -44,6 +46,9 @@ import org.easymock.classextension.EasyMock; import org.easymock.classextension.IMocksControl; import org.easymock.IArgumentMatcher; +import java.util.HashSet; +import java.util.Set; + import junit.framework.TestCase; /** @@ -220,6 +225,48 @@ public class ServiceTest extends TestCase { control.verify(); } + public void testNoGenericServices() throws Exception { + // Non-services should be usable. + UnittestNoGenericServices.TestMessage message = + UnittestNoGenericServices.TestMessage.newBuilder() + .setA(123) + .setExtension(UnittestNoGenericServices.testExtension, 456) + .build(); + assertEquals(123, message.getA()); + assertEquals(1, UnittestNoGenericServices.TestEnum.FOO.getNumber()); + + // Build a list of the class names nested in UnittestNoGenericServices. + String outerName = "google.protobuf.no_generic_services_test." + + "UnittestNoGenericServices"; + Class<?> outerClass = Class.forName(outerName); + + Set<String> innerClassNames = new HashSet<String>(); + for (Class<?> innerClass : outerClass.getClasses()) { + String fullName = innerClass.getName(); + // Figure out the unqualified name of the inner class. + // Note: Surprisingly, the full name of an inner class will be separated + // from the outer class name by a '$' rather than a '.'. This is not + // mentioned in the documentation for java.lang.Class. I don't want to + // make assumptions, so I'm just going to accept any character as the + // separator. + assertTrue(fullName.startsWith(outerName)); + innerClassNames.add(fullName.substring(outerName.length() + 1)); + } + + // No service class should have been generated. + assertTrue(innerClassNames.contains("TestMessage")); + assertTrue(innerClassNames.contains("TestEnum")); + assertFalse(innerClassNames.contains("TestService")); + + // But descriptors are there. + FileDescriptor file = UnittestNoGenericServices.getDescriptor(); + assertEquals(1, file.getServices().size()); + assertEquals("TestService", file.getServices().get(0).getName()); + assertEquals(1, file.getServices().get(0).getMethods().size()); + assertEquals("Foo", + file.getServices().get(0).getMethods().get(0).getName()); + } + // ================================================================= /** diff --git a/java/src/test/java/com/google/protobuf/TestUtil.java b/java/src/test/java/com/google/protobuf/TestUtil.java index 805c42ae..db6acb07 100644 --- a/java/src/test/java/com/google/protobuf/TestUtil.java +++ b/java/src/test/java/com/google/protobuf/TestUtil.java @@ -217,6 +217,7 @@ import protobuf_unittest.UnittestProto.TestAllExtensions; import protobuf_unittest.UnittestProto.TestAllTypes; import protobuf_unittest.UnittestProto.TestPackedExtensions; import protobuf_unittest.UnittestProto.TestPackedTypes; +import protobuf_unittest.UnittestProto.TestUnpackedTypes; import protobuf_unittest.UnittestProto.ForeignMessage; import protobuf_unittest.UnittestProto.ForeignEnum; import com.google.protobuf.test.UnittestImport.ImportMessage; @@ -289,6 +290,12 @@ class TestUtil { return builder.build(); } + public static TestUnpackedTypes getUnpackedSet() { + TestUnpackedTypes.Builder builder = TestUnpackedTypes.newBuilder(); + setUnpackedFields(builder); + return builder.build(); + } + public static TestPackedExtensions getPackedExtensionsSet() { TestPackedExtensions.Builder builder = TestPackedExtensions.newBuilder(); setPackedExtensions(builder); @@ -956,6 +963,42 @@ class TestUtil { } /** + * Set every field of {@code message} to a unique value. Must correspond with + * the values applied by {@code setPackedFields}. + */ + public static void setUnpackedFields(TestUnpackedTypes.Builder message) { + message.addUnpackedInt32 (601); + message.addUnpackedInt64 (602); + message.addUnpackedUint32 (603); + message.addUnpackedUint64 (604); + message.addUnpackedSint32 (605); + message.addUnpackedSint64 (606); + message.addUnpackedFixed32 (607); + message.addUnpackedFixed64 (608); + message.addUnpackedSfixed32(609); + message.addUnpackedSfixed64(610); + message.addUnpackedFloat (611); + message.addUnpackedDouble (612); + message.addUnpackedBool (true); + message.addUnpackedEnum (ForeignEnum.FOREIGN_BAR); + // Add a second one of each field. + message.addUnpackedInt32 (701); + message.addUnpackedInt64 (702); + message.addUnpackedUint32 (703); + message.addUnpackedUint64 (704); + message.addUnpackedSint32 (705); + message.addUnpackedSint64 (706); + message.addUnpackedFixed32 (707); + message.addUnpackedFixed64 (708); + message.addUnpackedSfixed32(709); + message.addUnpackedSfixed64(710); + message.addUnpackedFloat (711); + message.addUnpackedDouble (712); + message.addUnpackedBool (false); + message.addUnpackedEnum (ForeignEnum.FOREIGN_BAZ); + } + + /** * Assert (using {@code junit.framework.Assert}} that all fields of * {@code message} are set to the values assigned by {@code setPackedFields}. */ @@ -1004,6 +1047,55 @@ class TestUtil { Assert.assertEquals(ForeignEnum.FOREIGN_BAZ, message.getPackedEnum(1)); } + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are set to the values assigned by {@code setUnpackedFields}. + */ + public static void assertUnpackedFieldsSet(TestUnpackedTypes message) { + Assert.assertEquals(2, message.getUnpackedInt32Count ()); + Assert.assertEquals(2, message.getUnpackedInt64Count ()); + Assert.assertEquals(2, message.getUnpackedUint32Count ()); + Assert.assertEquals(2, message.getUnpackedUint64Count ()); + Assert.assertEquals(2, message.getUnpackedSint32Count ()); + Assert.assertEquals(2, message.getUnpackedSint64Count ()); + Assert.assertEquals(2, message.getUnpackedFixed32Count ()); + Assert.assertEquals(2, message.getUnpackedFixed64Count ()); + Assert.assertEquals(2, message.getUnpackedSfixed32Count()); + Assert.assertEquals(2, message.getUnpackedSfixed64Count()); + Assert.assertEquals(2, message.getUnpackedFloatCount ()); + Assert.assertEquals(2, message.getUnpackedDoubleCount ()); + Assert.assertEquals(2, message.getUnpackedBoolCount ()); + Assert.assertEquals(2, message.getUnpackedEnumCount ()); + Assert.assertEquals(601 , message.getUnpackedInt32 (0)); + Assert.assertEquals(602 , message.getUnpackedInt64 (0)); + Assert.assertEquals(603 , message.getUnpackedUint32 (0)); + Assert.assertEquals(604 , message.getUnpackedUint64 (0)); + Assert.assertEquals(605 , message.getUnpackedSint32 (0)); + Assert.assertEquals(606 , message.getUnpackedSint64 (0)); + Assert.assertEquals(607 , message.getUnpackedFixed32 (0)); + Assert.assertEquals(608 , message.getUnpackedFixed64 (0)); + Assert.assertEquals(609 , message.getUnpackedSfixed32(0)); + Assert.assertEquals(610 , message.getUnpackedSfixed64(0)); + Assert.assertEquals(611 , message.getUnpackedFloat (0), 0.0); + Assert.assertEquals(612 , message.getUnpackedDouble (0), 0.0); + Assert.assertEquals(true , message.getUnpackedBool (0)); + Assert.assertEquals(ForeignEnum.FOREIGN_BAR, message.getUnpackedEnum(0)); + Assert.assertEquals(701 , message.getUnpackedInt32 (1)); + Assert.assertEquals(702 , message.getUnpackedInt64 (1)); + Assert.assertEquals(703 , message.getUnpackedUint32 (1)); + Assert.assertEquals(704 , message.getUnpackedUint64 (1)); + Assert.assertEquals(705 , message.getUnpackedSint32 (1)); + Assert.assertEquals(706 , message.getUnpackedSint64 (1)); + Assert.assertEquals(707 , message.getUnpackedFixed32 (1)); + Assert.assertEquals(708 , message.getUnpackedFixed64 (1)); + Assert.assertEquals(709 , message.getUnpackedSfixed32(1)); + Assert.assertEquals(710 , message.getUnpackedSfixed64(1)); + Assert.assertEquals(711 , message.getUnpackedFloat (1), 0.0); + Assert.assertEquals(712 , message.getUnpackedDouble (1), 0.0); + Assert.assertEquals(false, message.getUnpackedBool (1)); + Assert.assertEquals(ForeignEnum.FOREIGN_BAZ, message.getUnpackedEnum(1)); + } + // =================================================================== // Like above, but for extensions diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/src/test/java/com/google/protobuf/TextFormatTest.java index 1d73165e..3ea7b2cf 100644 --- a/java/src/test/java/com/google/protobuf/TextFormatTest.java +++ b/java/src/test/java/com/google/protobuf/TextFormatTest.java @@ -68,7 +68,7 @@ public class TextFormatTest extends TestCase { private static String allExtensionsSetText = TestUtil.readTextFromFile( "text_format_unittest_extensions_data.txt"); - private String exoticText = + private static String exoticText = "repeated_int32: -1\n" + "repeated_int32: -2147483648\n" + "repeated_int64: -1\n" + @@ -80,7 +80,13 @@ public class TextFormatTest extends TestCase { "repeated_double: 123.0\n" + "repeated_double: 123.5\n" + "repeated_double: 0.125\n" + + "repeated_double: .125\n" + + "repeated_double: -.125\n" + "repeated_double: 1.23E17\n" + + "repeated_double: 1.23E+17\n" + + "repeated_double: -1.23e-17\n" + + "repeated_double: .23e+17\n" + + "repeated_double: -.23E17\n" + "repeated_double: 1.235E22\n" + "repeated_double: 1.235E-18\n" + "repeated_double: 123.456789\n" + @@ -91,6 +97,10 @@ public class TextFormatTest extends TestCase { "\\341\\210\\264\"\n" + "repeated_bytes: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\376\"\n"; + private static String canonicalExoticText = + exoticText.replace(": .", ": 0.").replace(": -.", ": -0.") // short-form double + .replace("23e", "23E").replace("E+", "E").replace("0.23E17", "2.3E16"); + private String messageSetText = "[protobuf_unittest.TestMessageSetExtension1] {\n" + " i: 123\n" + @@ -231,7 +241,13 @@ public class TextFormatTest extends TestCase { .addRepeatedDouble(123) .addRepeatedDouble(123.5) .addRepeatedDouble(0.125) + .addRepeatedDouble(.125) + .addRepeatedDouble(-.125) + .addRepeatedDouble(123e15) .addRepeatedDouble(123e15) + .addRepeatedDouble(-1.23e-17) + .addRepeatedDouble(.23e17) + .addRepeatedDouble(-23e15) .addRepeatedDouble(123.5e20) .addRepeatedDouble(123.5e-20) .addRepeatedDouble(123.456789) @@ -244,7 +260,7 @@ public class TextFormatTest extends TestCase { .addRepeatedBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"\u00fe")) .build(); - assertEquals(exoticText, message.toString()); + assertEquals(canonicalExoticText, message.toString()); } public void testPrintMessageSet() throws Exception { @@ -319,7 +335,7 @@ public class TextFormatTest extends TestCase { // Too lazy to check things individually. Don't try to debug this // if testPrintExotic() is failing. - assertEquals(exoticText, builder.build().toString()); + assertEquals(canonicalExoticText, builder.build().toString()); } public void testParseMessageSet() throws Exception { diff --git a/java/src/test/java/com/google/protobuf/WireFormatTest.java b/java/src/test/java/com/google/protobuf/WireFormatTest.java index 6a5bd5de..5ea1dd6a 100644 --- a/java/src/test/java/com/google/protobuf/WireFormatTest.java +++ b/java/src/test/java/com/google/protobuf/WireFormatTest.java @@ -235,6 +235,9 @@ public class WireFormatTest extends TestCase { TestUtil.assertPackedFieldsSet(TestPackedTypes.parseDelimitedFrom(input)); assertEquals(34, input.read()); assertEquals(-1, input.read()); + + // We're at EOF, so parsing again should return null. + assertTrue(TestAllTypes.parseDelimitedFrom(input) == null); } private void assertFieldsInOrder(ByteString data) throws Exception { |