diff options
author | liujisi@google.com <liujisi@google.com@630680e5-0e50-0410-840e-4b1c322b438d> | 2010-11-02 13:14:58 +0000 |
---|---|---|
committer | liujisi@google.com <liujisi@google.com@630680e5-0e50-0410-840e-4b1c322b438d> | 2010-11-02 13:14:58 +0000 |
commit | 33165fe0d5c265c92f2a67fc2b437b567c24e294 (patch) | |
tree | 52def0850ddd2e976da238d1a437fbda79c96e44 | |
parent | 80aa23df6c63750e8cdfdcf3996fbc37d63cac61 (diff) |
Submit recent changes from internal branch. See CHANGES.txt for more details.
160 files changed, 19944 insertions, 4115 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 11b332ed..6d1932be 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,66 @@ +2010-11-01 version 2.4.0: + + General + * The RPC (cc|java|py)_generic_services default value is now false instead of + true. + * Custom options can have aggregate types. For example, + message MyOption { + optional string comment = 1; + optional string author = 2; + } + extend google.protobuf.FieldOptions { + optional MyOption myoption = 12345; + } + This option can now be set as follows: + message SomeType { + optional int32 field = 1 [(myoption) = { comment:'x' author:'y' }]; + } + + C++ + * Various speed and code size optimizations. + * Added a release_foo() method on string and message fields. + * Fixed gzip_output_stream sub-stream handling. + + Java + * Builders now maintain sub-builders for sub-messages. Use getFooBuilder() to + get the builder for the sub-message "foo". This allows you to repeatedly + modify deeply-nested sub-messages without rebuilding them. + * Builder.build() no longer invalidates the Builder for generated messages + (You may continue to modify it and then build another message). + * Code generator will generate efficient equals() and hashCode() + implementations if new option java_generate_equals_and_hash is enabled. + (Otherwise, reflection-based implementations are used.) + * Generated messages now implement Serializable. + * Fields with [deprecated=true] will be marked with @Deprecated in Java. + * Added lazy conversion of UTF-8 encoded strings to String objects to improve + performance. + * Various optimizations. + * Enum value can be accessed directly, instead of calling getNumber() on the + enum member. + * For each enum value, an integer constant is also generated with the suffix + _VALUE. + + Python + * Added an experimental C++ implementation for Python messages via a Python + extension. Implementation type is controlled by an environment variable + PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION (valid values: "cpp" and "python") + The default value is currently "python" but will be changed to "cpp" in + future release. + * Improved performance on message instantiation significantly. + Most of the work on message instantiation is done just once per message + class, instead of once per message instance. + * Improved performance on text message parsing. + * Allow add() to forward keyword arguments to the concrete class. + E.g. instead of + item = repeated_field.add() + item.foo = bar + item.baz = quux + You can do: + repeated_field.add(foo=bar, baz=quux) + * Added a sort() interface to the BaseContainer. + * Added an extend() method to repeated composite fields. + * Added UTF8 debug string support. + 2010-01-08 version 2.3.0: General diff --git a/Makefile.am b/Makefile.am index b65daee0..9b960fc5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -83,33 +83,57 @@ EXTRA_DIST = \ java/src/main/java/com/google/protobuf/GeneratedMessageLite.java \ java/src/main/java/com/google/protobuf/Internal.java \ java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java \ + java/src/main/java/com/google/protobuf/LazyStringArrayList.java \ + java/src/main/java/com/google/protobuf/LazyStringList.java \ java/src/main/java/com/google/protobuf/Message.java \ java/src/main/java/com/google/protobuf/MessageLite.java \ + java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java \ + java/src/main/java/com/google/protobuf/MessageOrBuilder.java \ java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java \ + java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java \ java/src/main/java/com/google/protobuf/RpcCallback.java \ java/src/main/java/com/google/protobuf/RpcChannel.java \ java/src/main/java/com/google/protobuf/RpcController.java \ java/src/main/java/com/google/protobuf/RpcUtil.java \ java/src/main/java/com/google/protobuf/Service.java \ java/src/main/java/com/google/protobuf/ServiceException.java \ + java/src/main/java/com/google/protobuf/SingleFieldBuilder.java \ + java/src/main/java/com/google/protobuf/SmallSortedMap.java \ java/src/main/java/com/google/protobuf/TextFormat.java \ java/src/main/java/com/google/protobuf/UninitializedMessageException.java \ java/src/main/java/com/google/protobuf/UnknownFieldSet.java \ + java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java \ java/src/main/java/com/google/protobuf/WireFormat.java \ java/src/test/java/com/google/protobuf/AbstractMessageTest.java \ java/src/test/java/com/google/protobuf/CodedInputStreamTest.java \ java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java \ + java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java \ java/src/test/java/com/google/protobuf/DescriptorsTest.java \ java/src/test/java/com/google/protobuf/DynamicMessageTest.java \ + java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java \ java/src/test/java/com/google/protobuf/GeneratedMessageTest.java \ + java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java \ + java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java \ java/src/test/java/com/google/protobuf/LiteTest.java \ java/src/test/java/com/google/protobuf/MessageTest.java \ + java/src/test/java/com/google/protobuf/NestedBuildersTest.java \ + java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java \ java/src/test/java/com/google/protobuf/ServiceTest.java \ + java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java \ + java/src/test/java/com/google/protobuf/SmallSortedMapTest.java \ + java/src/test/java/com/google/protobuf/TestBadIdentifiers.java \ java/src/test/java/com/google/protobuf/TestUtil.java \ java/src/test/java/com/google/protobuf/TextFormatTest.java \ java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java \ + java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java \ java/src/test/java/com/google/protobuf/WireFormatTest.java \ java/src/test/java/com/google/protobuf/multiple_files_test.proto \ + java/src/test/java/com/google/protobuf/nested_builders_test.proto \ + java/src/test/java/com/google/protobuf/nested_extension.proto \ + java/src/test/java/com/google/protobuf/nested_extension_lite.proto \ + java/src/test/java/com/google/protobuf/non_nested_extension.proto \ + java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto \ + java/src/test/java/com/google/protobuf/test_bad_identifiers.proto \ java/pom.xml \ java/README.txt \ python/google/protobuf/internal/generator_test.py \ @@ -121,6 +145,9 @@ EXTRA_DIST = \ python/google/protobuf/internal/message_test.py \ python/google/protobuf/internal/more_extensions.proto \ python/google/protobuf/internal/more_messages.proto \ + python/google/protobuf/internal/python_message.py \ + python/google/protobuf/internal/cpp_message.py \ + python/google/protobuf/internal/api_implementation.py \ python/google/protobuf/internal/reflection_test.py \ python/google/protobuf/internal/service_reflection_test.py \ python/google/protobuf/internal/test_util.py \ @@ -129,6 +156,11 @@ EXTRA_DIST = \ python/google/protobuf/internal/wire_format.py \ python/google/protobuf/internal/wire_format_test.py \ python/google/protobuf/internal/__init__.py \ + python/google/protobuf/pyext/python-proto2.cc \ + python/google/protobuf/pyext/python_descriptor.cc \ + python/google/protobuf/pyext/python_descriptor.h \ + python/google/protobuf/pyext/python_protobuf.cc \ + python/google/protobuf/pyext/python_protobuf.h \ python/google/protobuf/descriptor.py \ python/google/protobuf/message.py \ python/google/protobuf/reflection.py \ diff --git a/java/pom.xml b/java/pom.xml index 94f1d7f6..cdf85de4 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -105,6 +105,12 @@ <arg value="../src/google/protobuf/unittest_mset.proto" /> <arg value="src/test/java/com/google/protobuf/multiple_files_test.proto" /> + <arg value="src/test/java/com/google/protobuf/nested_builders_test.proto" /> + <arg value="src/test/java/com/google/protobuf/nested_extension.proto" /> + <arg value="src/test/java/com/google/protobuf/nested_extension_lite.proto" /> + <arg value="src/test/java/com/google/protobuf/non_nested_extension.proto" /> + <arg value="src/test/java/com/google/protobuf/non_nested_extension_lite.proto" /> + <arg value="src/test/java/com/google/protobuf/test_bad_identifiers.proto" /> <arg value="../src/google/protobuf/unittest_optimize_for.proto" /> <arg diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/src/main/java/com/google/protobuf/AbstractMessage.java index fb416bdc..b9d83016 100644 --- a/java/src/main/java/com/google/protobuf/AbstractMessage.java +++ b/java/src/main/java/com/google/protobuf/AbstractMessage.java @@ -32,9 +32,10 @@ package com.google.protobuf; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Internal.EnumLite; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -167,11 +168,66 @@ public abstract class AbstractMessage extends AbstractMessageLite public int hashCode() { int hash = 41; hash = (19 * hash) + getDescriptorForType().hashCode(); - hash = (53 * hash) + getAllFields().hashCode(); + hash = hashFields(hash, getAllFields()); hash = (29 * hash) + getUnknownFields().hashCode(); return hash; } + /** Get a hash code for given fields and values, using the given seed. */ + @SuppressWarnings("unchecked") + protected int hashFields(int hash, Map<FieldDescriptor, Object> map) { + for (Map.Entry<FieldDescriptor, Object> entry : map.entrySet()) { + FieldDescriptor field = entry.getKey(); + Object value = entry.getValue(); + hash = (37 * hash) + field.getNumber(); + if (field.getType() != FieldDescriptor.Type.ENUM){ + hash = (53 * hash) + value.hashCode(); + } else if (field.isRepeated()) { + List<? extends EnumLite> list = (List<? extends EnumLite>) value; + hash = (53 * hash) + hashEnumList(list); + } else { + hash = (53 * hash) + hashEnum((EnumLite) value); + } + } + return hash; + } + + /** + * Helper method for implementing {@link Message#hashCode()}. + * @see Boolean#hashCode() + */ + protected static int hashLong(long n) { + return (int) (n ^ (n >>> 32)); + } + + /** + * Helper method for implementing {@link Message#hashCode()}. + * @see Boolean#hashCode() + */ + protected static int hashBoolean(boolean b) { + return b ? 1231 : 1237; + } + + /** + * Helper method for implementing {@link Message#hashCode()}. + * <p> + * This is needed because {@link java.lang.Enum#hashCode()} is final, but we + * need to use the field number as the hash code to ensure compatibility + * between statically and dynamically generated enum objects. + */ + protected static int hashEnum(EnumLite e) { + return e.getNumber(); + } + + /** Helper method for implementing {@link Message#hashCode()}. */ + protected static int hashEnumList(List<? extends EnumLite> list) { + int hash = 1; + for (EnumLite e : list) { + hash = 31 * hash + hashEnum(e); + } + return hash; + } + // ================================================================= /** diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/src/main/java/com/google/protobuf/ByteString.java index 5fade03a..91356357 100644 --- a/java/src/main/java/com/google/protobuf/ByteString.java +++ b/java/src/main/java/com/google/protobuf/ByteString.java @@ -194,6 +194,18 @@ public final class ByteString { } /** + * Copies bytes into a ByteBuffer. + * + * @param target ByteBuffer to copy into. + * @throws ReadOnlyBufferException if the {@code target} is read-only + * @throws BufferOverflowException if the {@code target}'s remaining() + * space is not large enough to hold the data. + */ + public void copyTo(ByteBuffer target) { + target.put(bytes, 0, bytes.length); + } + + /** * Copies bytes to a {@code byte[]}. */ public byte[] toByteArray() { diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/src/main/java/com/google/protobuf/CodedInputStream.java index ad43f96d..b3e08555 100644 --- a/java/src/main/java/com/google/protobuf/CodedInputStream.java +++ b/java/src/main/java/com/google/protobuf/CodedInputStream.java @@ -67,7 +67,25 @@ public final class CodedInputStream { */ public static CodedInputStream newInstance(final byte[] buf, final int off, final int len) { - return new CodedInputStream(buf, off, len); + CodedInputStream result = new CodedInputStream(buf, off, len); + try { + // Some uses of CodedInputStream can be more efficient if they know + // exactly how many bytes are available. By pushing the end point of the + // buffer as a limit, we allow them to get this information via + // getBytesUntilLimit(). Pushing a limit that we know is at the end of + // the stream can never hurt, since we can never past that point anyway. + result.pushLimit(len); + } catch (InvalidProtocolBufferException ex) { + // The only reason pushLimit() might throw an exception here is if len + // is negative. Normally pushLimit()'s parameter comes directly off the + // wire, so it's important to catch exceptions in case of corrupt or + // malicious data. However, in this case, we expect that len is not a + // user-supplied value, so we can assume that it being negative indicates + // a programming error. Therefore, throwing an unchecked exception is + // appropriate. + throw new IllegalArgumentException(ex); + } + return result; } // ----------------------------------------------------------------- @@ -263,7 +281,9 @@ public final class CodedInputStream { /** Read a {@code bytes} field value from the stream. */ public ByteString readBytes() throws IOException { final int size = readRawVarint32(); - if (size <= (bufferSize - bufferPos) && size > 0) { + if (size == 0) { + return ByteString.EMPTY; + } else if (size <= (bufferSize - bufferPos) && size > 0) { // Fast path: We already have the bytes in a contiguous buffer, so // just copy directly from it. final ByteString result = ByteString.copyFrom(buffer, bufferPos, size); @@ -368,8 +388,8 @@ public final class CodedInputStream { * 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 { + public static int readRawVarint32( + final int firstByte, final InputStream input) throws IOException { if ((firstByte & 0x80) == 0) { return firstByte; } @@ -847,19 +867,19 @@ public final class CodedInputStream { } else { // Skipping more bytes than are in the buffer. First skip what we have. int pos = bufferSize - bufferPos; - totalBytesRetired += pos; - bufferPos = 0; - bufferSize = 0; + bufferPos = bufferSize; - // Then skip directly from the InputStream for the rest. - while (pos < size) { - final int n = (input == null) ? -1 : (int) input.skip(size - pos); - if (n <= 0) { - throw InvalidProtocolBufferException.truncatedMessage(); - } - pos += n; - totalBytesRetired += n; + // Keep refilling the buffer until we get to the point we wanted to skip + // to. This has the side effect of ensuring the limits are updated + // correctly. + refillBuffer(true); + while (size - pos > bufferSize) { + pos += bufferSize; + bufferPos = bufferSize; + refillBuffer(true); } + + bufferPos = size - pos; } } } diff --git a/java/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/src/main/java/com/google/protobuf/CodedOutputStream.java index 58dd1506..51a932a3 100644 --- a/java/src/main/java/com/google/protobuf/CodedOutputStream.java +++ b/java/src/main/java/com/google/protobuf/CodedOutputStream.java @@ -33,6 +33,7 @@ package com.google.protobuf; import java.io.OutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.io.InputStream; /** * Encodes and writes protocol message fields. @@ -381,9 +382,8 @@ public final class CodedOutputStream { /** Write a {@code bytes} field to the stream. */ public void writeBytesNoTag(final ByteString value) throws IOException { - final byte[] bytes = value.toByteArray(); - writeRawVarint32(bytes.length); - writeRawBytes(bytes); + writeRawVarint32(value.size()); + writeRawBytes(value); } /** Write a {@code uint32} field to the stream. */ @@ -870,6 +870,11 @@ public final class CodedOutputStream { writeRawByte((byte) value); } + /** Write a byte string. */ + public void writeRawBytes(final ByteString value) throws IOException { + writeRawBytes(value, 0, value.size()); + } + /** Write an array of bytes. */ public void writeRawBytes(final byte[] value) throws IOException { writeRawBytes(value, 0, value.length); @@ -906,6 +911,53 @@ public final class CodedOutputStream { } } + /** Write part of a byte string. */ + public void writeRawBytes(final ByteString value, int offset, int length) + throws IOException { + if (limit - position >= length) { + // We have room in the current buffer. + value.copyTo(buffer, offset, position, length); + position += length; + } else { + // Write extends past current buffer. Fill the rest of this buffer and + // flush. + final int bytesWritten = limit - position; + value.copyTo(buffer, offset, position, bytesWritten); + offset += bytesWritten; + length -= bytesWritten; + position = limit; + refreshBuffer(); + + // Now deal with the rest. + // Since we have an output stream, this is our buffer + // and buffer offset == 0 + if (length <= limit) { + // Fits in new buffer. + value.copyTo(buffer, offset, 0, length); + position = length; + } else { + // Write is very big, but we can't do it all at once without allocating + // an a copy of the byte array since ByteString does not give us access + // to the underlying bytes. Use the InputStream interface on the + // ByteString and our buffer to copy between the two. + InputStream inputStreamFrom = value.newInput(); + if (offset != inputStreamFrom.skip(offset)) { + throw new IllegalStateException("Skip failed? Should never happen."); + } + // Use the buffer as the temporary buffer to avoid allocating memory. + while (length > 0) { + int bytesToRead = Math.min(length, limit); + int bytesRead = inputStreamFrom.read(buffer, 0, bytesToRead); + if (bytesRead != bytesToRead) { + throw new IllegalStateException("Read failed? Should never happen"); + } + output.write(buffer, 0, bytesRead); + length -= bytesRead; + } + } + } + } + /** Encode and write a tag. */ public void writeTag(final int fieldNumber, final int wireType) throws IOException { diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java index c5e9a04b..2ee84594 100644 --- a/java/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -46,6 +46,11 @@ import java.io.UnsupportedEncodingException; * its fields and other information about a type. You can get a message * type's descriptor by calling {@code MessageType.getDescriptor()}, or * (given a message object of the type) {@code message.getDescriptorForType()}. + * Furthermore, each message is associated with a {@link FileDescriptor} for + * a relevant {@code .proto} file. You can obtain it by calling + * {@code Descriptor.getFile()}. A {@link FileDescriptor} contains descriptors + * for all the messages defined in that file, and file descriptors for all the + * imported {@code .proto} files. * * Descriptors are built from DescriptorProtos, as defined in * {@code google/protobuf/descriptor.proto}. @@ -55,6 +60,9 @@ import java.io.UnsupportedEncodingException; public final class Descriptors { /** * Describes a {@code .proto} file, including everything defined within. + * That includes, in particular, descriptors for all the messages and + * file descriptors for all other imported {@code .proto} files + * (dependencies). */ public static final class FileDescriptor { /** Convert the descriptor to its protocol message representation. */ diff --git a/java/src/main/java/com/google/protobuf/FieldSet.java b/java/src/main/java/com/google/protobuf/FieldSet.java index bc1bb797..a85dbaa6 100644 --- a/java/src/main/java/com/google/protobuf/FieldSet.java +++ b/java/src/main/java/com/google/protobuf/FieldSet.java @@ -33,7 +33,6 @@ package com.google.protobuf; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; -import java.util.TreeMap; import java.util.List; import java.util.Map; import java.io.IOException; @@ -67,16 +66,12 @@ final class FieldSet<FieldDescriptorType extends MessageLite.Builder to, MessageLite from); } - private Map<FieldDescriptorType, Object> fields; + private final SmallSortedMap<FieldDescriptorType, Object> fields; + private boolean isImmutable; /** Construct a new FieldSet. */ private FieldSet() { - // Use a TreeMap because fields need to be in canonical order when - // serializing. - // TODO(kenton): Maybe use some sort of sparse array instead? It would - // even make sense to store the first 16 or so tags in a flat array - // to make DynamicMessage faster. - fields = new TreeMap<FieldDescriptorType, Object>(); + this.fields = SmallSortedMap.newFieldMap(16); } /** @@ -84,7 +79,8 @@ final class FieldSet<FieldDescriptorType extends * DEFAULT_INSTANCE. */ private FieldSet(final boolean dummy) { - this.fields = Collections.emptyMap(); + this.fields = SmallSortedMap.newFieldMap(0); + makeImmutable(); } /** Construct a new FieldSet. */ @@ -105,14 +101,45 @@ final class FieldSet<FieldDescriptorType extends /** Make this FieldSet immutable from this point forward. */ @SuppressWarnings("unchecked") public void makeImmutable() { - for (final Map.Entry<FieldDescriptorType, Object> entry: - fields.entrySet()) { - if (entry.getKey().isRepeated()) { - final List value = (List)entry.getValue(); - fields.put(entry.getKey(), Collections.unmodifiableList(value)); - } + if (isImmutable) { + return; } - fields = Collections.unmodifiableMap(fields); + fields.makeImmutable(); + isImmutable = true; + } + + /** + * Retuns whether the FieldSet is immutable. This is true if it is the + * {@link #emptySet} or if {@link #makeImmutable} were called. + * + * @return whether the FieldSet is immutable. + */ + public boolean isImmutable() { + return isImmutable; + } + + /** + * Clones the FieldSet. The returned FieldSet will be mutable even if the + * original FieldSet was immutable. + * + * @return the newly cloned FieldSet + */ + @Override + public FieldSet<FieldDescriptorType> clone() { + // We can't just call fields.clone because List objects in the map + // should not be shared. + FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet(); + for (int i = 0; i < fields.getNumArrayEntries(); i++) { + Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i); + FieldDescriptorType descriptor = entry.getKey(); + clone.setField(descriptor, entry.getValue()); + } + for (Map.Entry<FieldDescriptorType, Object> entry : + fields.getOverflowEntries()) { + FieldDescriptorType descriptor = entry.getKey(); + clone.setField(descriptor, entry.getValue()); + } + return clone; } // ================================================================= @@ -126,12 +153,13 @@ final class FieldSet<FieldDescriptorType extends * Get a simple map containing all the fields. */ public Map<FieldDescriptorType, Object> getAllFields() { - return Collections.unmodifiableMap(fields); + return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields); } /** - * Get an iterator to the field map. This iterator should not be leaked - * out of the protobuf library as it is not protected from mutation. + * Get an iterator to the field map. This iterator should not be leaked out + * of the protobuf library as it is not protected from mutation when + * fields is not immutable. */ public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() { return fields.entrySet().iterator(); @@ -336,27 +364,39 @@ final class FieldSet<FieldDescriptorType extends * aren't actually present in the set, it is up to the caller to check * that all required fields are present. */ - @SuppressWarnings("unchecked") public boolean isInitialized() { - for (final Map.Entry<FieldDescriptorType, Object> entry: - fields.entrySet()) { - final FieldDescriptorType descriptor = entry.getKey(); - if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { - if (descriptor.isRepeated()) { - for (final MessageLite element: - (List<MessageLite>) entry.getValue()) { - if (!element.isInitialized()) { - return false; - } - } - } else { - if (!((MessageLite) entry.getValue()).isInitialized()) { + for (int i = 0; i < fields.getNumArrayEntries(); i++) { + if (!isInitialized(fields.getArrayEntryAt(i))) { + return false; + } + } + for (final Map.Entry<FieldDescriptorType, Object> entry : + fields.getOverflowEntries()) { + if (!isInitialized(entry)) { + return false; + } + } + return true; + } + + @SuppressWarnings("unchecked") + private boolean isInitialized( + final Map.Entry<FieldDescriptorType, Object> entry) { + final FieldDescriptorType descriptor = entry.getKey(); + if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { + if (descriptor.isRepeated()) { + for (final MessageLite element: + (List<MessageLite>) entry.getValue()) { + if (!element.isInitialized()) { return false; } } + } else { + if (!((MessageLite) entry.getValue()).isInitialized()) { + return false; + } } } - return true; } @@ -378,39 +418,48 @@ final class FieldSet<FieldDescriptorType extends /** * Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}. */ - @SuppressWarnings("unchecked") public void mergeFrom(final FieldSet<FieldDescriptorType> other) { - for (final Map.Entry<FieldDescriptorType, Object> entry: - other.fields.entrySet()) { - final FieldDescriptorType descriptor = entry.getKey(); - final Object otherValue = entry.getValue(); + for (int i = 0; i < other.fields.getNumArrayEntries(); i++) { + mergeFromField(other.fields.getArrayEntryAt(i)); + } + for (final Map.Entry<FieldDescriptorType, Object> entry : + other.fields.getOverflowEntries()) { + mergeFromField(entry); + } + } - if (descriptor.isRepeated()) { - Object value = fields.get(descriptor); - if (value == null) { - // Our list is empty, but we still need to make a defensive copy of - // the other list since we don't know if the other FieldSet is still - // mutable. - fields.put(descriptor, new ArrayList((List) otherValue)); - } else { - // Concatenate the lists. - ((List) value).addAll((List) otherValue); - } - } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { - Object value = fields.get(descriptor); - if (value == null) { - fields.put(descriptor, otherValue); - } else { - // Merge the messages. - fields.put(descriptor, - descriptor.internalMergeFrom( - ((MessageLite) value).toBuilder(), (MessageLite) otherValue) - .build()); - } + @SuppressWarnings("unchecked") + private void mergeFromField( + final Map.Entry<FieldDescriptorType, Object> entry) { + final FieldDescriptorType descriptor = entry.getKey(); + final Object otherValue = entry.getValue(); + if (descriptor.isRepeated()) { + Object value = fields.get(descriptor); + if (value == null) { + // Our list is empty, but we still need to make a defensive copy of + // the other list since we don't know if the other FieldSet is still + // mutable. + fields.put(descriptor, new ArrayList((List) otherValue)); } else { + // Concatenate the lists. + ((List) value).addAll((List) otherValue); + } + } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { + Object value = fields.get(descriptor); + if (value == null) { fields.put(descriptor, otherValue); + } else { + // Merge the messages. + fields.put( + descriptor, + descriptor.internalMergeFrom( + ((MessageLite) value).toBuilder(), (MessageLite) otherValue) + .build()); } + + } else { + fields.put(descriptor, otherValue); } } @@ -468,8 +517,13 @@ final class FieldSet<FieldDescriptorType extends /** See {@link Message#writeTo(CodedOutputStream)}. */ public void writeTo(final CodedOutputStream output) throws IOException { - for (final Map.Entry<FieldDescriptorType, Object> entry: - fields.entrySet()) { + for (int i = 0; i < fields.getNumArrayEntries(); i++) { + final Map.Entry<FieldDescriptorType, Object> entry = + fields.getArrayEntryAt(i); + writeField(entry.getKey(), entry.getValue(), output); + } + for (final Map.Entry<FieldDescriptorType, Object> entry : + fields.getOverflowEntries()) { writeField(entry.getKey(), entry.getValue(), output); } } @@ -479,16 +533,25 @@ final class FieldSet<FieldDescriptorType extends */ public void writeMessageSetTo(final CodedOutputStream output) throws IOException { - for (final Map.Entry<FieldDescriptorType, Object> entry: - fields.entrySet()) { - final FieldDescriptorType descriptor = entry.getKey(); - if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && - !descriptor.isRepeated() && !descriptor.isPacked()) { - output.writeMessageSetExtension(entry.getKey().getNumber(), - (MessageLite) entry.getValue()); - } else { - writeField(descriptor, entry.getValue(), output); - } + for (int i = 0; i < fields.getNumArrayEntries(); i++) { + writeMessageSetTo(fields.getArrayEntryAt(i), output); + } + for (final Map.Entry<FieldDescriptorType, Object> entry : + fields.getOverflowEntries()) { + writeMessageSetTo(entry, output); + } + } + + private void writeMessageSetTo( + final Map.Entry<FieldDescriptorType, Object> entry, + final CodedOutputStream output) throws IOException { + final FieldDescriptorType descriptor = entry.getKey(); + if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && + !descriptor.isRepeated() && !descriptor.isPacked()) { + output.writeMessageSetExtension(entry.getKey().getNumber(), + (MessageLite) entry.getValue()); + } else { + writeField(descriptor, entry.getValue(), output); } } @@ -593,8 +656,13 @@ final class FieldSet<FieldDescriptorType extends */ public int getSerializedSize() { int size = 0; - for (final Map.Entry<FieldDescriptorType, Object> entry: - fields.entrySet()) { + for (int i = 0; i < fields.getNumArrayEntries(); i++) { + final Map.Entry<FieldDescriptorType, Object> entry = + fields.getArrayEntryAt(i); + size += computeFieldSize(entry.getKey(), entry.getValue()); + } + for (final Map.Entry<FieldDescriptorType, Object> entry : + fields.getOverflowEntries()) { size += computeFieldSize(entry.getKey(), entry.getValue()); } return size; @@ -605,20 +673,28 @@ final class FieldSet<FieldDescriptorType extends */ public int getMessageSetSerializedSize() { int size = 0; - for (final Map.Entry<FieldDescriptorType, Object> entry: - fields.entrySet()) { - final FieldDescriptorType descriptor = entry.getKey(); - if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && - !descriptor.isRepeated() && !descriptor.isPacked()) { - size += CodedOutputStream.computeMessageSetExtensionSize( - entry.getKey().getNumber(), (MessageLite) entry.getValue()); - } else { - size += computeFieldSize(descriptor, entry.getValue()); - } + for (int i = 0; i < fields.getNumArrayEntries(); i++) { + size += getMessageSetSerializedSize(fields.getArrayEntryAt(i)); + } + for (final Map.Entry<FieldDescriptorType, Object> entry : + fields.getOverflowEntries()) { + size += getMessageSetSerializedSize(entry); } return size; } + private int getMessageSetSerializedSize( + final Map.Entry<FieldDescriptorType, Object> entry) { + final FieldDescriptorType descriptor = entry.getKey(); + if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && + !descriptor.isRepeated() && !descriptor.isPacked()) { + return CodedOutputStream.computeMessageSetExtensionSize( + entry.getKey().getNumber(), (MessageLite) entry.getValue()); + } else { + return computeFieldSize(descriptor, entry.getValue()); + } + } + /** * Compute the number of bytes that would be needed to encode a * single tag/value pair of arbitrary type. diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java index 42ccbfd8..fc2e5303 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessage.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -35,8 +35,10 @@ import com.google.protobuf.Descriptors.EnumValueDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import java.io.IOException; -import java.lang.reflect.Method; +import java.io.ObjectStreamException; +import java.io.Serializable; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -52,10 +54,35 @@ import java.util.TreeMap; * * @author kenton@google.com Kenton Varda */ -public abstract class GeneratedMessage extends AbstractMessage { - protected GeneratedMessage() {} +public abstract class GeneratedMessage extends AbstractMessage + implements Serializable { + + private final UnknownFieldSet unknownFields; + + /** + * For testing. Allows a test to disable the optimization that avoids using + * field builders for nested messages until they are requested. By disabling + * this optimization, existing tests can be reused to test the field builders. + */ + protected static boolean alwaysUseFieldBuilders = false; - private UnknownFieldSet unknownFields = UnknownFieldSet.getDefaultInstance(); + protected GeneratedMessage() { + this.unknownFields = UnknownFieldSet.getDefaultInstance(); + } + + protected GeneratedMessage(Builder<?> builder) { + this.unknownFields = builder.getUnknownFields(); + } + + /** + * For testing. Allows a test to disable the optimization that avoids using + * field builders for nested messages until they are requested. By disabling + * this optimization, existing tests can be reused to test the field builders. + * See {@link RepeatedFieldBuilder} and {@link SingleFieldBuilder}. + */ + static void enableAlwaysUseFieldBuildersForTesting() { + alwaysUseFieldBuilders = true; + } /** * Get the FieldAccessorTable for this type. We can't have the message @@ -64,6 +91,7 @@ public abstract class GeneratedMessage extends AbstractMessage { */ protected abstract FieldAccessorTable internalGetFieldAccessorTable(); + //@Override (Java 1.6 override semantics, but we must support 1.5) public Descriptor getDescriptorForType() { return internalGetFieldAccessorTable().descriptor; } @@ -118,36 +146,115 @@ public abstract class GeneratedMessage extends AbstractMessage { return true; } + //@Override (Java 1.6 override semantics, but we must support 1.5) public Map<FieldDescriptor, Object> getAllFields() { return Collections.unmodifiableMap(getAllFieldsMutable()); } + //@Override (Java 1.6 override semantics, but we must support 1.5) public boolean hasField(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field).has(this); } + //@Override (Java 1.6 override semantics, but we must support 1.5) public Object getField(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field).get(this); } + //@Override (Java 1.6 override semantics, but we must support 1.5) public int getRepeatedFieldCount(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field) .getRepeatedCount(this); } + //@Override (Java 1.6 override semantics, but we must support 1.5) public Object getRepeatedField(final FieldDescriptor field, final int index) { return internalGetFieldAccessorTable().getField(field) .getRepeated(this, index); } + //@Override (Java 1.6 override semantics, but we must support 1.5) public final UnknownFieldSet getUnknownFields() { return unknownFields; } + protected abstract Message.Builder newBuilderForType(BuilderParent parent); + + /** + * Interface for the parent of a Builder that allows the builder to + * communicate invalidations back to the parent for use when using nested + * builders. + */ + protected interface BuilderParent { + + /** + * A builder becomes dirty whenever a field is modified -- including fields + * in nested builders -- and becomes clean when build() is called. Thus, + * when a builder becomes dirty, all its parents become dirty as well, and + * when it becomes clean, all its children become clean. The dirtiness + * state is used to invalidate certain cached values. + * <br> + * To this end, a builder calls markAsDirty() on its parent whenever it + * transitions from clean to dirty. The parent must propagate this call to + * its own parent, unless it was already dirty, in which case the + * grandparent must necessarily already be dirty as well. The parent can + * only transition back to "clean" after calling build() on all children. + */ + void markDirty(); + } + @SuppressWarnings("unchecked") public abstract static class Builder <BuilderType extends Builder> extends AbstractMessage.Builder<BuilderType> { - protected Builder() {} + + private BuilderParent builderParent; + + private BuilderParentImpl meAsParent; + + // Indicates that we've built a message and so we are now obligated + // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener. + private boolean isClean; + + private UnknownFieldSet unknownFields = + UnknownFieldSet.getDefaultInstance(); + + protected Builder() { + this(null); + } + + protected Builder(BuilderParent builderParent) { + this.builderParent = builderParent; + } + + void dispose() { + builderParent = null; + } + + /** + * Called by the subclass when a message is built. + */ + protected void onBuilt() { + if (builderParent != null) { + markClean(); + } + } + + /** + * Called by the subclass or a builder to notify us that a message was + * built and may be cached and therefore invalidations are needed. + */ + protected void markClean() { + this.isClean = true; + } + + /** + * Gets whether invalidations are needed + * + * @return whether invalidations are needed + */ + protected boolean isClean() { + return isClean; + } // 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 @@ -159,26 +266,50 @@ public abstract class GeneratedMessage extends AbstractMessage { } /** - * Get the message being built. We don't just pass this to the - * constructor because it becomes null when build() is called. + * Called by the initialization and clear code paths to allow subclasses to + * reset any of their builtin fields back to the initial values. */ - protected abstract GeneratedMessage internalGetResult(); + public BuilderType clear() { + unknownFields = UnknownFieldSet.getDefaultInstance(); + onChanged(); + return (BuilderType) this; + } /** * Get the FieldAccessorTable for this type. We can't have the message * class pass this in to the constructor because of bootstrapping trouble * with DescriptorProtos. */ - private FieldAccessorTable internalGetFieldAccessorTable() { - return internalGetResult().internalGetFieldAccessorTable(); - } + protected abstract FieldAccessorTable internalGetFieldAccessorTable(); + //@Override (Java 1.6 override semantics, but we must support 1.5) public Descriptor getDescriptorForType() { return internalGetFieldAccessorTable().descriptor; } + //@Override (Java 1.6 override semantics, but we must support 1.5) public Map<FieldDescriptor, Object> getAllFields() { - return internalGetResult().getAllFields(); + return Collections.unmodifiableMap(getAllFieldsMutable()); + } + + /** Internal helper which returns a mutable map. */ + private Map<FieldDescriptor, Object> getAllFieldsMutable() { + final TreeMap<FieldDescriptor, Object> result = + new TreeMap<FieldDescriptor, Object>(); + final Descriptor descriptor = internalGetFieldAccessorTable().descriptor; + for (final FieldDescriptor field : descriptor.getFields()) { + if (field.isRepeated()) { + final List value = (List) getField(field); + if (!value.isEmpty()) { + result.put(field, value); + } + } else { + if (hasField(field)) { + result.put(field, getField(field)); + } + } + } + return result; } public Message.Builder newBuilderForField( @@ -186,18 +317,20 @@ public abstract class GeneratedMessage extends AbstractMessage { return internalGetFieldAccessorTable().getField(field).newBuilder(); } + //@Override (Java 1.6 override semantics, but we must support 1.5) public boolean hasField(final FieldDescriptor field) { - return internalGetResult().hasField(field); + return internalGetFieldAccessorTable().getField(field).has(this); } + //@Override (Java 1.6 override semantics, but we must support 1.5) public Object getField(final FieldDescriptor field) { + Object object = internalGetFieldAccessorTable().getField(field).get(this); if (field.isRepeated()) { // The underlying list object is still modifiable at this point. // Make sure not to expose the modifiable list to the caller. - return Collections.unmodifiableList( - (List) internalGetResult().getField(field)); + return Collections.unmodifiableList((List) object); } else { - return internalGetResult().getField(field); + return object; } } @@ -207,18 +340,23 @@ public abstract class GeneratedMessage extends AbstractMessage { return (BuilderType) this; } + //@Override (Java 1.6 override semantics, but we must support 1.5) public BuilderType clearField(final FieldDescriptor field) { internalGetFieldAccessorTable().getField(field).clear(this); return (BuilderType) this; } + //@Override (Java 1.6 override semantics, but we must support 1.5) public int getRepeatedFieldCount(final FieldDescriptor field) { - return internalGetResult().getRepeatedFieldCount(field); + return internalGetFieldAccessorTable().getField(field) + .getRepeatedCount(this); } + //@Override (Java 1.6 override semantics, but we must support 1.5) public Object getRepeatedField(final FieldDescriptor field, final int index) { - return internalGetResult().getRepeatedField(field, index); + return internalGetFieldAccessorTable().getField(field) + .getRepeated(this, index); } public BuilderType setRepeatedField(final FieldDescriptor field, @@ -234,29 +372,57 @@ public abstract class GeneratedMessage extends AbstractMessage { return (BuilderType) this; } - public final UnknownFieldSet getUnknownFields() { - return internalGetResult().unknownFields; - } - public final BuilderType setUnknownFields( final UnknownFieldSet unknownFields) { - internalGetResult().unknownFields = unknownFields; + this.unknownFields = unknownFields; + onChanged(); return (BuilderType) this; } @Override public final BuilderType mergeUnknownFields( final UnknownFieldSet unknownFields) { - final GeneratedMessage result = internalGetResult(); - result.unknownFields = - UnknownFieldSet.newBuilder(result.unknownFields) + this.unknownFields = + UnknownFieldSet.newBuilder(this.unknownFields) .mergeFrom(unknownFields) .build(); + onChanged(); return (BuilderType) this; } + //@Override (Java 1.6 override semantics, but we must support 1.5) public boolean isInitialized() { - return internalGetResult().isInitialized(); + for (final FieldDescriptor field : getDescriptorForType().getFields()) { + // Check that all required fields are present. + if (field.isRequired()) { + if (!hasField(field)) { + return false; + } + } + // Check that embedded messages are initialized. + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.isRepeated()) { + @SuppressWarnings("unchecked") final + List<Message> messageList = (List<Message>) getField(field); + for (final Message element : messageList) { + if (!element.isInitialized()) { + return false; + } + } + } else { + if (hasField(field) && + !((Message) getField(field)).isInitialized()) { + return false; + } + } + } + } + return true; + } + + @Override + public final UnknownFieldSet getUnknownFields() { + return unknownFields; } /** @@ -270,11 +436,68 @@ public abstract class GeneratedMessage extends AbstractMessage { final int tag) throws IOException { return unknownFields.mergeFieldFrom(tag, input); } + + /** + * Implementation of {@link BuilderParent} for giving to our children. This + * small inner class makes it so we don't publicly expose the BuilderParent + * methods. + */ + private class BuilderParentImpl implements BuilderParent { + + @Override + public void markDirty() { + onChanged(); + } + } + + /** + * Gets the {@link BuilderParent} for giving to our children. + * @return The builder parent for our children. + */ + protected BuilderParent getParentForChildren() { + if (meAsParent == null) { + meAsParent = new BuilderParentImpl(); + } + return meAsParent; + } + + /** + * Called when a the builder or one of its nested children has changed + * and any parent should be notified of its invalidation. + */ + protected final void onChanged() { + if (isClean && builderParent != null) { + builderParent.markDirty(); + + // Don't keep dispatching invalidations until build is called again. + isClean = false; + } + } } // ================================================================= // Extensions-related stuff + public interface ExtendableMessageOrBuilder< + MessageType extends ExtendableMessage> extends MessageOrBuilder { + + /** Check if a singular extension is present. */ + <Type> boolean hasExtension( + GeneratedExtension<MessageType, Type> extension); + + /** Get the number of elements in a repeated extension. */ + <Type> int getExtensionCount( + GeneratedExtension<MessageType, List<Type>> extension); + + /** Get the value of an extension. */ + <Type> Type getExtension(GeneratedExtension<MessageType, Type> extension); + + /** Get one element of a repeated extension. */ + <Type> Type getExtension( + GeneratedExtension<MessageType, List<Type>> extension, + int index); + } + /** * Generated message classes for message types that contain extension ranges * subclass this. @@ -312,9 +535,20 @@ public abstract class GeneratedMessage extends AbstractMessage { */ public abstract static class ExtendableMessage< MessageType extends ExtendableMessage> - extends GeneratedMessage { - protected ExtendableMessage() {} - private final FieldSet<FieldDescriptor> extensions = FieldSet.newFieldSet(); + extends GeneratedMessage + implements ExtendableMessageOrBuilder<MessageType> { + + private final FieldSet<FieldDescriptor> extensions; + + protected ExtendableMessage() { + this.extensions = FieldSet.newFieldSet(); + } + + protected ExtendableMessage( + ExtendableBuilder<MessageType, ?> builder) { + super(builder); + this.extensions = builder.buildExtensions(); + } private void verifyExtensionContainingType( final GeneratedExtension<MessageType, ?> extension) { @@ -330,13 +564,15 @@ public abstract class GeneratedMessage extends AbstractMessage { } /** Check if a singular extension is present. */ - public final boolean hasExtension( - final GeneratedExtension<MessageType, ?> extension) { + //@Override (Java 1.6 override semantics, but we must support 1.5) + public final <Type> boolean hasExtension( + final GeneratedExtension<MessageType, Type> extension) { verifyExtensionContainingType(extension); return extensions.hasField(extension.getDescriptor()); } /** Get the number of elements in a repeated extension. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> int getExtensionCount( final GeneratedExtension<MessageType, List<Type>> extension) { verifyExtensionContainingType(extension); @@ -345,6 +581,7 @@ public abstract class GeneratedMessage extends AbstractMessage { } /** Get the value of an extension. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) @SuppressWarnings("unchecked") public final <Type> Type getExtension( final GeneratedExtension<MessageType, Type> extension) { @@ -367,6 +604,7 @@ public abstract class GeneratedMessage extends AbstractMessage { } /** Get one element of a repeated extension. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) @SuppressWarnings("unchecked") public final <Type> Type getExtension( final GeneratedExtension<MessageType, List<Type>> extension, @@ -448,10 +686,14 @@ public abstract class GeneratedMessage extends AbstractMessage { // --------------------------------------------------------------- // Reflection + protected Map<FieldDescriptor, Object> getExtensionFields() { + return extensions.getAllFields(); + } + @Override public Map<FieldDescriptor, Object> getAllFields() { final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable(); - result.putAll(extensions.getAllFields()); + result.putAll(getExtensionFields()); return Collections.unmodifiableMap(result); } @@ -556,9 +798,24 @@ public abstract class GeneratedMessage extends AbstractMessage { public abstract static class ExtendableBuilder< MessageType extends ExtendableMessage, BuilderType extends ExtendableBuilder> - extends Builder<BuilderType> { + extends Builder<BuilderType> + implements ExtendableMessageOrBuilder<MessageType> { + + private FieldSet<FieldDescriptor> extensions = FieldSet.emptySet(); + protected ExtendableBuilder() {} + protected ExtendableBuilder( + BuilderParent parent) { + super(parent); + } + + @Override + public BuilderType clear() { + extensions = FieldSet.emptySet(); + 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 dummy clone() implementation makes it go away. @@ -568,43 +825,84 @@ public abstract class GeneratedMessage extends AbstractMessage { "This is supposed to be overridden by subclasses."); } - @Override - protected abstract ExtendableMessage<MessageType> internalGetResult(); + private void ensureExtensionsIsMutable() { + if (extensions.isImmutable()) { + extensions = extensions.clone(); + } + } - /** Check if a singular extension is present. */ - public final boolean hasExtension( + private void verifyExtensionContainingType( final GeneratedExtension<MessageType, ?> extension) { - return internalGetResult().hasExtension(extension); + if (extension.getDescriptor().getContainingType() != + getDescriptorForType()) { + // This can only happen if someone uses unchecked operations. + throw new IllegalArgumentException( + "Extension is for type \"" + + extension.getDescriptor().getContainingType().getFullName() + + "\" which does not match message type \"" + + getDescriptorForType().getFullName() + "\"."); + } + } + + /** Check if a singular extension is present. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) + public final <Type> boolean hasExtension( + final GeneratedExtension<MessageType, Type> extension) { + verifyExtensionContainingType(extension); + return extensions.hasField(extension.getDescriptor()); } /** Get the number of elements in a repeated extension. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> int getExtensionCount( final GeneratedExtension<MessageType, List<Type>> extension) { - return internalGetResult().getExtensionCount(extension); + verifyExtensionContainingType(extension); + final FieldDescriptor descriptor = extension.getDescriptor(); + return extensions.getRepeatedFieldCount(descriptor); } /** Get the value of an extension. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> Type getExtension( final GeneratedExtension<MessageType, Type> extension) { - return internalGetResult().getExtension(extension); + verifyExtensionContainingType(extension); + FieldDescriptor descriptor = extension.getDescriptor(); + final Object value = extensions.getField(descriptor); + if (value == null) { + if (descriptor.isRepeated()) { + return (Type) Collections.emptyList(); + } else if (descriptor.getJavaType() == + FieldDescriptor.JavaType.MESSAGE) { + return (Type) extension.getMessageDefaultInstance(); + } else { + return (Type) extension.fromReflectionType( + descriptor.getDefaultValue()); + } + } else { + return (Type) extension.fromReflectionType(value); + } } /** Get one element of a repeated extension. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> Type getExtension( final GeneratedExtension<MessageType, List<Type>> extension, final int index) { - return internalGetResult().getExtension(extension, index); + verifyExtensionContainingType(extension); + FieldDescriptor descriptor = extension.getDescriptor(); + return (Type) extension.singularFromReflectionType( + extensions.getRepeatedField(descriptor, index)); } /** Set the value of an extension. */ public final <Type> BuilderType setExtension( final GeneratedExtension<MessageType, Type> extension, final Type value) { - final ExtendableMessage<MessageType> message = internalGetResult(); - message.verifyExtensionContainingType(extension); + verifyExtensionContainingType(extension); + ensureExtensionsIsMutable(); final FieldDescriptor descriptor = extension.getDescriptor(); - message.extensions.setField(descriptor, - extension.toReflectionType(value)); + extensions.setField(descriptor, extension.toReflectionType(value)); + onChanged(); return (BuilderType) this; } @@ -612,12 +910,13 @@ public abstract class GeneratedMessage extends AbstractMessage { public final <Type> BuilderType setExtension( final GeneratedExtension<MessageType, List<Type>> extension, final int index, final Type value) { - final ExtendableMessage<MessageType> message = internalGetResult(); - message.verifyExtensionContainingType(extension); + verifyExtensionContainingType(extension); + ensureExtensionsIsMutable(); final FieldDescriptor descriptor = extension.getDescriptor(); - message.extensions.setRepeatedField( + extensions.setRepeatedField( descriptor, index, extension.singularToReflectionType(value)); + onChanged(); return (BuilderType) this; } @@ -625,23 +924,44 @@ public abstract class GeneratedMessage extends AbstractMessage { public final <Type> BuilderType addExtension( final GeneratedExtension<MessageType, List<Type>> extension, final Type value) { - final ExtendableMessage<MessageType> message = internalGetResult(); - message.verifyExtensionContainingType(extension); + verifyExtensionContainingType(extension); + ensureExtensionsIsMutable(); final FieldDescriptor descriptor = extension.getDescriptor(); - message.extensions.addRepeatedField( + extensions.addRepeatedField( descriptor, extension.singularToReflectionType(value)); + onChanged(); return (BuilderType) this; } /** Clear an extension. */ public final <Type> BuilderType clearExtension( final GeneratedExtension<MessageType, ?> extension) { - final ExtendableMessage<MessageType> message = internalGetResult(); - message.verifyExtensionContainingType(extension); - message.extensions.clearField(extension.getDescriptor()); + verifyExtensionContainingType(extension); + ensureExtensionsIsMutable(); + extensions.clearField(extension.getDescriptor()); + onChanged(); return (BuilderType) this; } + /** Called by subclasses to check if all extensions are initialized. */ + protected boolean extensionsAreInitialized() { + return extensions.isInitialized(); + } + + /** + * Called by the build code path to create a copy of the extensions for + * building the message. + */ + private FieldSet<FieldDescriptor> buildExtensions() { + extensions.makeImmutable(); + return extensions; + } + + @Override + public boolean isInitialized() { + return super.isInitialized() && extensionsAreInitialized(); + } + /** * Called by subclasses to parse an unknown field or an extension. * @return {@code true} unless the tag is an end-group tag. @@ -659,16 +979,73 @@ public abstract class GeneratedMessage extends AbstractMessage { // --------------------------------------------------------------- // Reflection - // We don't have to override the get*() methods here because they already - // just forward to the underlying message. + @Override + public Map<FieldDescriptor, Object> getAllFields() { + final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable(); + result.putAll(extensions.getAllFields()); + return Collections.unmodifiableMap(result); + } + + @Override + public Object getField(final FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + final Object value = extensions.getField(field); + if (value == null) { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + // Lacking an ExtensionRegistry, we have no way to determine the + // extension's real type, so we return a DynamicMessage. + return DynamicMessage.getDefaultInstance(field.getMessageType()); + } else { + return field.getDefaultValue(); + } + } else { + return value; + } + } else { + return super.getField(field); + } + } + + @Override + public int getRepeatedFieldCount(final FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + return extensions.getRepeatedFieldCount(field); + } else { + return super.getRepeatedFieldCount(field); + } + } + + @Override + public Object getRepeatedField(final FieldDescriptor field, + final int index) { + if (field.isExtension()) { + verifyContainingType(field); + return extensions.getRepeatedField(field, index); + } else { + return super.getRepeatedField(field, index); + } + } + + @Override + public boolean hasField(final FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + return extensions.hasField(field); + } else { + return super.hasField(field); + } + } @Override public BuilderType setField(final FieldDescriptor field, final Object value) { if (field.isExtension()) { - final ExtendableMessage<MessageType> message = internalGetResult(); - message.verifyContainingType(field); - message.extensions.setField(field, value); + verifyContainingType(field); + ensureExtensionsIsMutable(); + extensions.setField(field, value); + onChanged(); return (BuilderType) this; } else { return super.setField(field, value); @@ -678,9 +1055,10 @@ public abstract class GeneratedMessage extends AbstractMessage { @Override public BuilderType clearField(final FieldDescriptor field) { if (field.isExtension()) { - final ExtendableMessage<MessageType> message = internalGetResult(); - message.verifyContainingType(field); - message.extensions.clearField(field); + verifyContainingType(field); + ensureExtensionsIsMutable(); + extensions.clearField(field); + onChanged(); return (BuilderType) this; } else { return super.clearField(field); @@ -691,9 +1069,10 @@ public abstract class GeneratedMessage extends AbstractMessage { public BuilderType setRepeatedField(final FieldDescriptor field, final int index, final Object value) { if (field.isExtension()) { - final ExtendableMessage<MessageType> message = internalGetResult(); - message.verifyContainingType(field); - message.extensions.setRepeatedField(field, index, value); + verifyContainingType(field); + ensureExtensionsIsMutable(); + extensions.setRepeatedField(field, index, value); + onChanged(); return (BuilderType) this; } else { return super.setRepeatedField(field, index, value); @@ -704,9 +1083,10 @@ public abstract class GeneratedMessage extends AbstractMessage { public BuilderType addRepeatedField(final FieldDescriptor field, final Object value) { if (field.isExtension()) { - final ExtendableMessage<MessageType> message = internalGetResult(); - message.verifyContainingType(field); - message.extensions.addRepeatedField(field, value); + verifyContainingType(field); + ensureExtensionsIsMutable(); + extensions.addRepeatedField(field, value); + onChanged(); return (BuilderType) this; } else { return super.addRepeatedField(field, value); @@ -714,17 +1094,63 @@ public abstract class GeneratedMessage extends AbstractMessage { } protected final void mergeExtensionFields(final ExtendableMessage other) { - internalGetResult().extensions.mergeFrom(other.extensions); + ensureExtensionsIsMutable(); + extensions.mergeFrom(other.extensions); + onChanged(); + } + + private void verifyContainingType(final FieldDescriptor field) { + if (field.getContainingType() != getDescriptorForType()) { + throw new IllegalArgumentException( + "FieldDescriptor does not match message type."); + } } } // ----------------------------------------------------------------- + /** + * Gets the descriptor for an extension. The implementation depends on whether + * the extension is scoped in the top level of a file or scoped in a Message. + */ + private static interface ExtensionDescriptorRetriever { + FieldDescriptor getDescriptor(); + } + /** For use by generated code only. */ public static <ContainingType extends Message, Type> GeneratedExtension<ContainingType, Type> - newGeneratedExtension() { - return new GeneratedExtension<ContainingType, Type>(); + newMessageScopedGeneratedExtension(final Message scope, + final int descriptorIndex, + final Class singularType, + final Message defaultInstance) { + // For extensions scoped within a Message, we use the Message to resolve + // the outer class's descriptor, from which the extension descriptor is + // obtained. + return new GeneratedExtension<ContainingType, Type>( + new ExtensionDescriptorRetriever() { + @Override + public FieldDescriptor getDescriptor() { + return scope.getDescriptorForType().getExtensions() + .get(descriptorIndex); + } + }, + singularType, + defaultInstance); + } + + /** For use by generated code only. */ + public static <ContainingType extends Message, Type> + GeneratedExtension<ContainingType, Type> + newFileScopedGeneratedExtension(final Class singularType, + final Message defaultInstance) { + // For extensions scoped within a file, we rely on the outer class's + // static initializer to call internalInit() on the extension when the + // descriptor is available. + return new GeneratedExtension<ContainingType, Type>( + null, // ExtensionDescriptorRetriever is initialized in internalInit(); + singularType, + defaultInstance); } /** @@ -757,64 +1183,67 @@ public abstract class GeneratedMessage extends AbstractMessage { // TODO(kenton): Find ways to avoid using Java reflection within this // class. Also try to avoid suppressing unchecked warnings. - // We can't always initialize a GeneratedExtension when we first construct - // it due to initialization order difficulties (namely, the descriptor may - // not have been constructed yet, since it is often constructed by the - // initializer of a separate module). So, we construct an uninitialized - // GeneratedExtension once, then call internalInit() on it later. Generated - // code will always call internalInit() on all extensions as part of the - // static initialization code, and internalInit() throws an exception if - // called more than once, so this method is useless to users. - private GeneratedExtension() {} - - /** For use by generated code only. */ - public void internalInit(final FieldDescriptor descriptor, - final Class<?> type) { - if (this.descriptor != null) { - throw new IllegalStateException("Already initialized."); - } - - if (!descriptor.isExtension()) { + // We can't always initialize the descriptor of a GeneratedExtension when + // we first construct it due to initialization order difficulties (namely, + // the descriptor may not have been constructed yet, since it is often + // constructed by the initializer of a separate module). + // + // In the case of nested extensions, we initialize the + // ExtensionDescriptorRetriever with an instance that uses the scoping + // Message's default instance to retrieve the extension's descriptor. + // + // In the case of non-nested extensions, we initialize the + // ExtensionDescriptorRetriever to null and rely on the outer class's static + // initializer to call internalInit() after the descriptor has been parsed. + private GeneratedExtension(ExtensionDescriptorRetriever descriptorRetriever, + Class singularType, + Message messageDefaultInstance) { + if (Message.class.isAssignableFrom(singularType) && + !singularType.isInstance(messageDefaultInstance)) { throw new IllegalArgumentException( - "GeneratedExtension given a regular (non-extension) field."); + "Bad messageDefaultInstance for " + singularType.getName()); } + this.descriptorRetriever = descriptorRetriever; + this.singularType = singularType; + this.messageDefaultInstance = messageDefaultInstance; - this.descriptor = descriptor; - this.type = type; + if (ProtocolMessageEnum.class.isAssignableFrom(singularType)) { + this.enumValueOf = getMethodOrDie(singularType, "valueOf", + EnumValueDescriptor.class); + this.enumGetValueDescriptor = + getMethodOrDie(singularType, "getValueDescriptor"); + } else { + this.enumValueOf = null; + this.enumGetValueDescriptor = null; + } + } - switch (descriptor.getJavaType()) { - case MESSAGE: - enumValueOf = null; - enumGetValueDescriptor = null; - 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", - EnumValueDescriptor.class); - enumGetValueDescriptor = getMethodOrDie(type, "getValueDescriptor"); - messageDefaultInstance = null; - break; - default: - enumValueOf = null; - enumGetValueDescriptor = null; - messageDefaultInstance = null; - break; + /** For use by generated code only. */ + public void internalInit(final FieldDescriptor descriptor) { + if (descriptorRetriever != null) { + throw new IllegalStateException("Already initialized."); } + descriptorRetriever = new ExtensionDescriptorRetriever() { + @Override + public FieldDescriptor getDescriptor() { + return descriptor; + } + }; } - private FieldDescriptor descriptor; - private Class<?> type; - private Method enumValueOf; - private Method enumGetValueDescriptor; - private Message messageDefaultInstance; + private ExtensionDescriptorRetriever descriptorRetriever; + private final Class singularType; + private final Message messageDefaultInstance; + private final Method enumValueOf; + private final Method enumGetValueDescriptor; - public FieldDescriptor getDescriptor() { return descriptor; } + public FieldDescriptor getDescriptor() { + if (descriptorRetriever == null) { + throw new IllegalStateException( + "getDescriptor() called before internalInit()"); + } + return descriptorRetriever.getDescriptor(); + } /** * If the extension is an embedded message or group, returns the default @@ -832,6 +1261,7 @@ public abstract class GeneratedMessage extends AbstractMessage { */ @SuppressWarnings("unchecked") private Object fromReflectionType(final Object value) { + FieldDescriptor descriptor = getDescriptor(); if (descriptor.isRepeated()) { if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE || descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { @@ -854,9 +1284,10 @@ public abstract class GeneratedMessage extends AbstractMessage { * type, this converts a single element. */ private Object singularFromReflectionType(final Object value) { + FieldDescriptor descriptor = getDescriptor(); switch (descriptor.getJavaType()) { case MESSAGE: - if (type.isInstance(value)) { + if (singularType.isInstance(value)) { return value; } else { // It seems the copy of the embedded message stored inside the @@ -883,6 +1314,7 @@ public abstract class GeneratedMessage extends AbstractMessage { */ @SuppressWarnings("unchecked") private Object toReflectionType(final Object value) { + FieldDescriptor descriptor = getDescriptor(); if (descriptor.isRepeated()) { if (descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { // Must convert the whole list. @@ -904,6 +1336,7 @@ public abstract class GeneratedMessage extends AbstractMessage { * type, this converts a single element. */ private Object singularToReflectionType(final Object value) { + FieldDescriptor descriptor = getDescriptor(); switch (descriptor.getJavaType()) { case ENUM: return invokeOrDie(enumGetValueDescriptor, value); @@ -1025,13 +1458,17 @@ public abstract class GeneratedMessage extends AbstractMessage { */ private interface FieldAccessor { Object get(GeneratedMessage message); + Object get(GeneratedMessage.Builder builder); void set(Builder builder, Object value); Object getRepeated(GeneratedMessage message, int index); + Object getRepeated(GeneratedMessage.Builder builder, int index); void setRepeated(Builder builder, int index, Object value); void addRepeated(Builder builder, Object value); boolean has(GeneratedMessage message); + boolean has(GeneratedMessage.Builder builder); int getRepeatedCount(GeneratedMessage message); + int getRepeatedCount(GeneratedMessage.Builder builder); void clear(Builder builder); Message.Builder newBuilder(); } @@ -1044,10 +1481,13 @@ public abstract class GeneratedMessage extends AbstractMessage { final Class<? extends GeneratedMessage> messageClass, final Class<? extends Builder> builderClass) { getMethod = getMethodOrDie(messageClass, "get" + camelCaseName); + getMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName); type = getMethod.getReturnType(); setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type); hasMethod = - getMethodOrDie(messageClass, "has" + camelCaseName); + getMethodOrDie(messageClass, "has" + camelCaseName); + hasMethodBuilder = + getMethodOrDie(builderClass, "has" + camelCaseName); clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); } @@ -1056,13 +1496,18 @@ public abstract class GeneratedMessage extends AbstractMessage { // checks. protected final Class<?> type; protected final Method getMethod; + protected final Method getMethodBuilder; protected final Method setMethod; protected final Method hasMethod; + protected final Method hasMethodBuilder; protected final Method clearMethod; public Object get(final GeneratedMessage message) { return invokeOrDie(getMethod, message); } + public Object get(GeneratedMessage.Builder builder) { + return invokeOrDie(getMethodBuilder, builder); + } public void set(final Builder builder, final Object value) { invokeOrDie(setMethod, builder, value); } @@ -1071,6 +1516,10 @@ public abstract class GeneratedMessage extends AbstractMessage { throw new UnsupportedOperationException( "getRepeatedField() called on a singular field."); } + public Object getRepeated(GeneratedMessage.Builder builder, int index) { + throw new UnsupportedOperationException( + "getRepeatedField() called on a singular field."); + } public void setRepeated(final Builder builder, final int index, final Object value) { throw new UnsupportedOperationException( @@ -1083,10 +1532,17 @@ public abstract class GeneratedMessage extends AbstractMessage { public boolean has(final GeneratedMessage message) { return (Boolean) invokeOrDie(hasMethod, message); } + public boolean has(GeneratedMessage.Builder builder) { + return (Boolean) invokeOrDie(hasMethodBuilder, builder); + } public int getRepeatedCount(final GeneratedMessage message) { throw new UnsupportedOperationException( "getRepeatedFieldSize() called on a singular field."); } + public int getRepeatedCount(GeneratedMessage.Builder builder) { + throw new UnsupportedOperationException( + "getRepeatedFieldSize() called on a singular field."); + } public void clear(final Builder builder) { invokeOrDie(clearMethod, builder); } @@ -1097,38 +1553,51 @@ public abstract class GeneratedMessage extends AbstractMessage { } private static class RepeatedFieldAccessor implements FieldAccessor { + protected final Class type; + protected final Method getMethod; + protected final Method getMethodBuilder; + protected final Method getRepeatedMethod; + protected final Method getRepeatedMethodBuilder; + protected final Method setRepeatedMethod; + protected final Method addRepeatedMethod; + protected final Method getCountMethod; + protected final Method getCountMethodBuilder; + protected final Method clearMethod; + RepeatedFieldAccessor( final FieldDescriptor descriptor, final String camelCaseName, final Class<? extends GeneratedMessage> messageClass, final Class<? extends Builder> builderClass) { getMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "List"); + getMethodBuilder = getMethodOrDie(builderClass, + "get" + camelCaseName + "List"); + getRepeatedMethod = - getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE); + getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE); + getRepeatedMethodBuilder = + getMethodOrDie(builderClass, "get" + camelCaseName, Integer.TYPE); type = getRepeatedMethod.getReturnType(); setRepeatedMethod = - getMethodOrDie(builderClass, "set" + camelCaseName, - Integer.TYPE, type); + getMethodOrDie(builderClass, "set" + camelCaseName, + Integer.TYPE, type); addRepeatedMethod = - getMethodOrDie(builderClass, "add" + camelCaseName, type); + getMethodOrDie(builderClass, "add" + camelCaseName, type); getCountMethod = - getMethodOrDie(messageClass, "get" + camelCaseName + "Count"); + getMethodOrDie(messageClass, "get" + camelCaseName + "Count"); + getCountMethodBuilder = + getMethodOrDie(builderClass, "get" + camelCaseName + "Count"); clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); } - protected final Class<?> type; - protected final Method getMethod; - protected final Method getRepeatedMethod; - protected final Method setRepeatedMethod; - protected final Method addRepeatedMethod; - protected final Method getCountMethod; - protected final Method clearMethod; - public Object get(final GeneratedMessage message) { return invokeOrDie(getMethod, message); } + public Object get(GeneratedMessage.Builder builder) { + return invokeOrDie(getMethodBuilder, builder); + } public void set(final Builder builder, final Object value) { // Add all the elements individually. This serves two purposes: // 1) Verifies that each element has the correct type. @@ -1143,6 +1612,9 @@ public abstract class GeneratedMessage extends AbstractMessage { final int index) { return invokeOrDie(getRepeatedMethod, message, index); } + public Object getRepeated(GeneratedMessage.Builder builder, int index) { + return invokeOrDie(getRepeatedMethodBuilder, builder, index); + } public void setRepeated(final Builder builder, final int index, final Object value) { invokeOrDie(setRepeatedMethod, builder, index, value); @@ -1154,9 +1626,16 @@ public abstract class GeneratedMessage extends AbstractMessage { throw new UnsupportedOperationException( "hasField() called on a singular field."); } + public boolean has(GeneratedMessage.Builder builder) { + throw new UnsupportedOperationException( + "hasField() called on a singular field."); + } public int getRepeatedCount(final GeneratedMessage message) { return (Integer) invokeOrDie(getCountMethod, message); } + public int getRepeatedCount(GeneratedMessage.Builder builder) { + return (Integer) invokeOrDie(getCountMethodBuilder, builder); + } public void clear(final Builder builder) { invokeOrDie(clearMethod, builder); } @@ -1189,6 +1668,12 @@ public abstract class GeneratedMessage extends AbstractMessage { public Object get(final GeneratedMessage message) { return invokeOrDie(getValueDescriptorMethod, super.get(message)); } + + @Override + public Object get(final GeneratedMessage.Builder builder) { + return invokeOrDie(getValueDescriptorMethod, super.get(builder)); + } + @Override public void set(final Builder builder, final Object value) { super.set(builder, invokeOrDie(valueOfMethod, null, value)); @@ -1221,6 +1706,17 @@ public abstract class GeneratedMessage extends AbstractMessage { } return Collections.unmodifiableList(newList); } + + @Override + @SuppressWarnings("unchecked") + public Object get(final GeneratedMessage.Builder builder) { + final List newList = new ArrayList(); + for (final Object element : (List) super.get(builder)) { + newList.add(invokeOrDie(getValueDescriptorMethod, element)); + } + return Collections.unmodifiableList(newList); + } + @Override public Object getRepeated(final GeneratedMessage message, final int index) { @@ -1228,6 +1724,12 @@ public abstract class GeneratedMessage extends AbstractMessage { super.getRepeated(message, index)); } @Override + public Object getRepeated(final GeneratedMessage.Builder builder, + final int index) { + return invokeOrDie(getValueDescriptorMethod, + super.getRepeated(builder, index)); + } + @Override public void setRepeated(final Builder builder, final int index, final Object value) { super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null, @@ -1318,4 +1820,14 @@ public abstract class GeneratedMessage extends AbstractMessage { } } } + + /** + * Replaces this object in the output stream with a serialized form. + * Part of Java's serialization magic. Generated sub-classes must override + * this method by calling <code>return super.writeReplace();</code> + * @return a SerializedForm of this message + */ + protected Object writeReplace() throws ObjectStreamException { + return new GeneratedMessageLite.SerializedForm(this); + } } diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java index 9a799b44..30a75715 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java @@ -31,6 +31,10 @@ package com.google.protobuf; import java.io.IOException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -41,8 +45,14 @@ import java.util.Map; * * @author kenton@google.com Kenton Varda */ -public abstract class GeneratedMessageLite extends AbstractMessageLite { - protected GeneratedMessageLite() {} +public abstract class GeneratedMessageLite extends AbstractMessageLite + implements Serializable { + + protected GeneratedMessageLite() { + } + + protected GeneratedMessageLite(Builder builder) { + } @SuppressWarnings("unchecked") public abstract static class Builder<MessageType extends GeneratedMessageLite, @@ -50,6 +60,11 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { extends AbstractMessageLite.Builder<BuilderType> { protected Builder() {} + //@Override (Java 1.6 override semantics, but we must support 1.5) + public BuilderType clear() { + return (BuilderType) this; + } + // 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. @@ -66,12 +81,6 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { public abstract MessageType getDefaultInstanceForType(); /** - * Get the message being built. We don't just pass this to the - * constructor because it becomes null when build() is called. - */ - protected abstract MessageType internalGetResult(); - - /** * Called by subclasses to parse an unknown field. * @return {@code true} unless the tag is an end-group tag. */ @@ -87,14 +96,45 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { // Extensions-related stuff /** + * Lite equivalent of {@link com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder}. + */ + public interface ExtendableMessageOrBuilder< + MessageType extends ExtendableMessage> extends MessageLiteOrBuilder { + + /** Check if a singular extension is present. */ + <Type> boolean hasExtension( + GeneratedExtension<MessageType, Type> extension); + + /** Get the number of elements in a repeated extension. */ + <Type> int getExtensionCount( + GeneratedExtension<MessageType, List<Type>> extension); + + /** Get the value of an extension. */ + <Type> Type getExtension(GeneratedExtension<MessageType, Type> extension); + + /** Get one element of a repeated extension. */ + <Type> Type getExtension( + GeneratedExtension<MessageType, List<Type>> extension, + int index); + } + + /** * Lite equivalent of {@link GeneratedMessage.ExtendableMessage}. */ public abstract static class ExtendableMessage< MessageType extends ExtendableMessage<MessageType>> - extends GeneratedMessageLite { - protected ExtendableMessage() {} - private final FieldSet<ExtensionDescriptor> extensions = - FieldSet.newFieldSet(); + extends GeneratedMessageLite + implements ExtendableMessageOrBuilder<MessageType> { + + private final FieldSet<ExtensionDescriptor> extensions; + + protected ExtendableMessage() { + this.extensions = FieldSet.newFieldSet(); + } + + protected ExtendableMessage(ExtendableBuilder<MessageType, ?> builder) { + this.extensions = builder.buildExtensions(); + } private void verifyExtensionContainingType( final GeneratedExtension<MessageType, ?> extension) { @@ -108,13 +148,15 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { } /** Check if a singular extension is present. */ - public final boolean hasExtension( - final GeneratedExtension<MessageType, ?> extension) { + //@Override (Java 1.6 override semantics, but we must support 1.5) + public final <Type> boolean hasExtension( + final GeneratedExtension<MessageType, Type> extension) { verifyExtensionContainingType(extension); return extensions.hasField(extension.descriptor); } /** Get the number of elements in a repeated extension. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> int getExtensionCount( final GeneratedExtension<MessageType, List<Type>> extension) { verifyExtensionContainingType(extension); @@ -122,6 +164,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { } /** Get the value of an extension. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) @SuppressWarnings("unchecked") public final <Type> Type getExtension( final GeneratedExtension<MessageType, Type> extension) { @@ -135,6 +178,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { } /** Get one element of a repeated extension. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) @SuppressWarnings("unchecked") public final <Type> Type getExtension( final GeneratedExtension<MessageType, List<Type>> extension, @@ -214,53 +258,104 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { public abstract static class ExtendableBuilder< MessageType extends ExtendableMessage<MessageType>, BuilderType extends ExtendableBuilder<MessageType, BuilderType>> - extends Builder<MessageType, BuilderType> { + extends Builder<MessageType, BuilderType> + implements ExtendableMessageOrBuilder<MessageType> { protected ExtendableBuilder() {} - // 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. + private FieldSet<ExtensionDescriptor> extensions = FieldSet.emptySet(); + private boolean extensionsIsMutable; + @Override - public BuilderType clone() { - throw new UnsupportedOperationException( - "This is supposed to be overridden by subclasses."); + public BuilderType clear() { + extensions.clear(); + extensionsIsMutable = false; + return super.clear(); } - @Override - protected abstract MessageType internalGetResult(); + private void ensureExtensionsIsMutable() { + if (!extensionsIsMutable) { + extensions = extensions.clone(); + extensionsIsMutable = true; + } + } - /** Check if a singular extension is present. */ - public final boolean hasExtension( + /** + * Called by the build code path to create a copy of the extensions for + * building the message. + */ + private FieldSet<ExtensionDescriptor> buildExtensions() { + extensions.makeImmutable(); + extensionsIsMutable = false; + return extensions; + } + + private void verifyExtensionContainingType( final GeneratedExtension<MessageType, ?> extension) { - return internalGetResult().hasExtension(extension); + if (extension.getContainingTypeDefaultInstance() != + getDefaultInstanceForType()) { + // This can only happen if someone uses unchecked operations. + throw new IllegalArgumentException( + "This extension is for a different message type. Please make " + + "sure that you are not suppressing any generics type warnings."); + } + } + + /** Check if a singular extension is present. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) + public final <Type> boolean hasExtension( + final GeneratedExtension<MessageType, Type> extension) { + verifyExtensionContainingType(extension); + return extensions.hasField(extension.descriptor); } /** Get the number of elements in a repeated extension. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> int getExtensionCount( final GeneratedExtension<MessageType, List<Type>> extension) { - return internalGetResult().getExtensionCount(extension); + verifyExtensionContainingType(extension); + return extensions.getRepeatedFieldCount(extension.descriptor); } /** Get the value of an extension. */ + //@Override (Java 1.6 override semantics, but we must support 1.5) + @SuppressWarnings("unchecked") public final <Type> Type getExtension( final GeneratedExtension<MessageType, Type> extension) { - return internalGetResult().getExtension(extension); + verifyExtensionContainingType(extension); + final Object value = extensions.getField(extension.descriptor); + if (value == null) { + return extension.defaultValue; + } else { + return (Type) value; + } } /** Get one element of a repeated extension. */ + @SuppressWarnings("unchecked") + //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> Type getExtension( final GeneratedExtension<MessageType, List<Type>> extension, final int index) { - return internalGetResult().getExtension(extension, index); + verifyExtensionContainingType(extension); + return (Type) extensions.getRepeatedField(extension.descriptor, 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() { + throw new UnsupportedOperationException( + "This is supposed to be overridden by subclasses."); } /** Set the value of an extension. */ public final <Type> BuilderType setExtension( final GeneratedExtension<MessageType, Type> extension, final Type value) { - final ExtendableMessage<MessageType> message = internalGetResult(); - message.verifyExtensionContainingType(extension); - message.extensions.setField(extension.descriptor, value); + verifyExtensionContainingType(extension); + ensureExtensionsIsMutable(); + extensions.setField(extension.descriptor, value); return (BuilderType) this; } @@ -268,9 +363,9 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { public final <Type> BuilderType setExtension( final GeneratedExtension<MessageType, List<Type>> extension, final int index, final Type value) { - final ExtendableMessage<MessageType> message = internalGetResult(); - message.verifyExtensionContainingType(extension); - message.extensions.setRepeatedField(extension.descriptor, index, value); + verifyExtensionContainingType(extension); + ensureExtensionsIsMutable(); + extensions.setRepeatedField(extension.descriptor, index, value); return (BuilderType) this; } @@ -278,21 +373,26 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { public final <Type> BuilderType addExtension( final GeneratedExtension<MessageType, List<Type>> extension, final Type value) { - final ExtendableMessage<MessageType> message = internalGetResult(); - message.verifyExtensionContainingType(extension); - message.extensions.addRepeatedField(extension.descriptor, value); + verifyExtensionContainingType(extension); + ensureExtensionsIsMutable(); + extensions.addRepeatedField(extension.descriptor, value); return (BuilderType) this; } /** Clear an extension. */ public final <Type> BuilderType clearExtension( final GeneratedExtension<MessageType, ?> extension) { - final ExtendableMessage<MessageType> message = internalGetResult(); - message.verifyExtensionContainingType(extension); - message.extensions.clearField(extension.descriptor); + verifyExtensionContainingType(extension); + ensureExtensionsIsMutable(); + extensions.clearField(extension.descriptor); return (BuilderType) this; } + /** Called by subclasses to check if all extensions are initialized. */ + protected boolean extensionsAreInitialized() { + return extensions.isInitialized(); + } + /** * Called by subclasses to parse an unknown field or an extension. * @return {@code true} unless the tag is an end-group tag. @@ -302,9 +402,6 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { final CodedInputStream input, final ExtensionRegistryLite extensionRegistry, final int tag) throws IOException { - final FieldSet<ExtensionDescriptor> extensions = - ((ExtendableMessage) internalGetResult()).extensions; - final int wireType = WireFormat.getTagWireType(tag); final int fieldNumber = WireFormat.getTagFieldNumber(tag); @@ -347,6 +444,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { // enum, drop it (don't even add it to unknownFields). return true; } + ensureExtensionsIsMutable(); extensions.addRepeatedField(extension.descriptor, value); } } else { @@ -354,6 +452,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { final Object value = FieldSet.readPrimitiveField(input, extension.descriptor.getLiteType()); + ensureExtensionsIsMutable(); extensions.addRepeatedField(extension.descriptor, value); } } @@ -400,8 +499,10 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { } if (extension.descriptor.isRepeated()) { + ensureExtensionsIsMutable(); extensions.addRepeatedField(extension.descriptor, value); } else { + ensureExtensionsIsMutable(); extensions.setField(extension.descriptor, value); } } @@ -410,8 +511,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { } protected final void mergeExtensionFields(final MessageType other) { - ((ExtendableMessage) internalGetResult()).extensions.mergeFrom( - ((ExtendableMessage) other).extensions); + ensureExtensionsIsMutable(); + extensions.mergeFrom(((ExtendableMessage) other).extensions); } } @@ -420,8 +521,40 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { /** For use by generated code only. */ public static <ContainingType extends MessageLite, Type> GeneratedExtension<ContainingType, Type> - newGeneratedExtension() { - return new GeneratedExtension<ContainingType, Type>(); + newSingularGeneratedExtension( + final ContainingType containingTypeDefaultInstance, + final Type defaultValue, + final MessageLite messageDefaultInstance, + final Internal.EnumLiteMap<?> enumTypeMap, + final int number, + final WireFormat.FieldType type) { + return new GeneratedExtension<ContainingType, Type>( + containingTypeDefaultInstance, + defaultValue, + messageDefaultInstance, + new ExtensionDescriptor(enumTypeMap, number, type, + false /* isRepeated */, + false /* isPacked */)); + } + + /** For use by generated code only. */ + public static <ContainingType extends MessageLite, Type> + GeneratedExtension<ContainingType, Type> + newRepeatedGeneratedExtension( + final ContainingType containingTypeDefaultInstance, + final MessageLite messageDefaultInstance, + final Internal.EnumLiteMap<?> enumTypeMap, + final int number, + final WireFormat.FieldType type, + final boolean isPacked) { + @SuppressWarnings("unchecked") // Subclasses ensure Type is a List + Type emptyList = (Type) Collections.emptyList(); + return new GeneratedExtension<ContainingType, Type>( + containingTypeDefaultInstance, + emptyList, + messageDefaultInstance, + new ExtensionDescriptor( + enumTypeMap, number, type, true /* isRepeated */, isPacked)); } private static final class ExtensionDescriptor @@ -489,60 +622,33 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { */ public static final class GeneratedExtension< ContainingType extends MessageLite, Type> { - // We can't always initialize a GeneratedExtension when we first construct - // it due to initialization order difficulties (namely, the default - // instances may not have been constructed yet). So, we construct an - // uninitialized GeneratedExtension once, then call internalInit() on it - // later. Generated code will always call internalInit() on all extensions - // as part of the static initialization code, and internalInit() throws an - // exception if called more than once, so this method is useless to users. - private GeneratedExtension() {} - - private void internalInit( + + private GeneratedExtension( final ContainingType containingTypeDefaultInstance, final Type defaultValue, final MessageLite messageDefaultInstance, final ExtensionDescriptor descriptor) { + // Defensive checks to verify the correct initialization order of + // GeneratedExtensions and their related GeneratedMessages. + if (containingTypeDefaultInstance == null) { + throw new IllegalArgumentException( + "Null containingTypeDefaultInstance"); + } + if (descriptor.getLiteType() == WireFormat.FieldType.MESSAGE && + messageDefaultInstance == null) { + throw new IllegalArgumentException( + "Null messageDefaultInstance"); + } this.containingTypeDefaultInstance = containingTypeDefaultInstance; this.defaultValue = defaultValue; this.messageDefaultInstance = messageDefaultInstance; this.descriptor = descriptor; } - /** For use by generated code only. */ - public void internalInitSingular( - final ContainingType containingTypeDefaultInstance, - final Type defaultValue, - final MessageLite messageDefaultInstance, - final Internal.EnumLiteMap<?> enumTypeMap, - final int number, - final WireFormat.FieldType type) { - internalInit( - containingTypeDefaultInstance, defaultValue, messageDefaultInstance, - new ExtensionDescriptor(enumTypeMap, number, type, - false /* isRepeated */, false /* isPacked */)); - } - - /** For use by generated code only. */ - @SuppressWarnings("unchecked") - public void internalInitRepeated( - final ContainingType containingTypeDefaultInstance, - final MessageLite messageDefaultInstance, - final Internal.EnumLiteMap<?> enumTypeMap, - final int number, - final WireFormat.FieldType type, - final boolean isPacked) { - internalInit( - containingTypeDefaultInstance, (Type) Collections.emptyList(), - messageDefaultInstance, - new ExtensionDescriptor( - enumTypeMap, number, type, true /* isRepeated */, isPacked)); - } - - private ContainingType containingTypeDefaultInstance; - private Type defaultValue; - private MessageLite messageDefaultInstance; - private ExtensionDescriptor descriptor; + private final ContainingType containingTypeDefaultInstance; + private final Type defaultValue; + private final MessageLite messageDefaultInstance; + private final ExtensionDescriptor descriptor; /** * Default instance of the type being extended, used to identify that type. @@ -564,4 +670,61 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite { return messageDefaultInstance; } } + + /** + * A serialized (serializable) form of the generated message. Stores the + * message as a class name and a byte array. + */ + static final class SerializedForm implements Serializable { + private static final long serialVersionUID = 0L; + + private String messageClassName; + private byte[] asBytes; + + /** + * Creates the serialized form by calling {@link com.google.protobuf.MessageLite#toByteArray}. + * @param regularForm the message to serialize + */ + SerializedForm(MessageLite regularForm) { + messageClassName = regularForm.getClass().getName(); + asBytes = regularForm.toByteArray(); + } + + /** + * When read from an ObjectInputStream, this method converts this object + * back to the regular form. Part of Java's serialization magic. + * @return a GeneratedMessage of the type that was serialized + */ + @SuppressWarnings("unchecked") + protected Object readResolve() throws ObjectStreamException { + try { + Class messageClass = Class.forName(messageClassName); + Method newBuilder = messageClass.getMethod("newBuilder"); + MessageLite.Builder builder = + (MessageLite.Builder) newBuilder.invoke(null); + builder.mergeFrom(asBytes); + return builder.buildPartial(); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Unable to find proto buffer class", e); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Unable to find newBuilder method", e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Unable to call newBuilder method", e); + } catch (InvocationTargetException e) { + throw new RuntimeException("Error calling newBuilder", e.getCause()); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException("Unable to understand proto buffer", e); + } + } + } + + /** + * Replaces this object in the output stream with a serialized form. + * Part of Java's serialization magic. Generated sub-classes must override + * this method by calling <code>return super.writeReplace();</code> + * @return a SerializedForm of this message + */ + protected Object writeReplace() throws ObjectStreamException { + return new SerializedForm(this); + } } diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java index 965465e1..05eab57a 100644 --- a/java/src/main/java/com/google/protobuf/Internal.java +++ b/java/src/main/java/com/google/protobuf/Internal.java @@ -100,6 +100,91 @@ public class Internal { } /** + * Helper called by generated code to determine if a byte array is a valid + * UTF-8 encoded string such that the original bytes can be converted to + * a String object and then back to a byte array round tripping the bytes + * without loss. + * <p> + * This is inspired by UTF_8.java in sun.nio.cs. + * + * @param byteString the string to check + * @return whether the byte array is round trippable + */ + public static boolean isValidUtf8(ByteString byteString) { + int index = 0; + int size = byteString.size(); + // To avoid the masking, we could change this to use bytes; + // Then X > 0xC2 gets turned into X < -0xC2; X < 0x80 + // gets turned into X >= 0, etc. + + while (index < size) { + int byte1 = byteString.byteAt(index++) & 0xFF; + if (byte1 < 0x80) { + // fast loop for single bytes + continue; + + // we know from this point on that we have 2-4 byte forms + } else if (byte1 < 0xC2 || byte1 > 0xF4) { + // catch illegal first bytes: < C2 or > F4 + return false; + } + if (index >= size) { + // fail if we run out of bytes + return false; + } + int byte2 = byteString.byteAt(index++) & 0xFF; + if (byte2 < 0x80 || byte2 > 0xBF) { + // general trail-byte test + return false; + } + if (byte1 <= 0xDF) { + // two-byte form; general trail-byte test is sufficient + continue; + } + + // we know from this point on that we have 3 or 4 byte forms + if (index >= size) { + // fail if we run out of bytes + return false; + } + int byte3 = byteString.byteAt(index++) & 0xFF; + if (byte3 < 0x80 || byte3 > 0xBF) { + // general trail-byte test + return false; + } + if (byte1 <= 0xEF) { + // three-byte form. Vastly more frequent than four-byte forms + // The following has an extra test, but not worth restructuring + if (byte1 == 0xE0 && byte2 < 0xA0 || + byte1 == 0xED && byte2 > 0x9F) { + // check special cases of byte2 + return false; + } + + } else { + // four-byte form + + if (index >= size) { + // fail if we run out of bytes + return false; + } + int byte4 = byteString.byteAt(index++) & 0xFF; + if (byte4 < 0x80 || byte4 > 0xBF) { + // general trail-byte test + return false; + } + // The following has an extra test, but not worth restructuring + if (byte1 == 0xF0 && byte2 < 0x90 || + byte1 == 0xF4 && byte2 > 0x8F) { + // check special cases of byte2 + return false; + } + } + } + return true; + } + + /** * Interface for an enum value or value descriptor, to be used in FieldSet. * The lite library stores enum values directly in FieldSets but the full * library stores EnumValueDescriptors in order to better support reflection. diff --git a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java new file mode 100644 index 00000000..1683a640 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java @@ -0,0 +1,155 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import java.util.List; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.RandomAccess; +import java.util.Collection; + +/** + * An implementation of {@link LazyStringList} that wraps an ArrayList. Each + * element is either a ByteString or a String. It caches the last one requested + * which is most likely the one needed next. This minimizes memory usage while + * satisfying the most common use cases. + * <p> + * <strong>Note that this implementation is not synchronized.</strong> + * If multiple threads access an <tt>ArrayList</tt> instance concurrently, + * and at least one of the threads modifies the list structurally, it + * <i>must</i> be synchronized externally. (A structural modification is + * any operation that adds or deletes one or more elements, or explicitly + * resizes the backing array; merely setting the value of an element is not + * a structural modification.) This is typically accomplished by + * synchronizing on some object that naturally encapsulates the list. + * <p> + * If the implementation is accessed via concurrent reads, this is thread safe. + * Conversions are done in a thread safe manner. It's possible that the + * conversion may happen more than once if two threads attempt to access the + * same element and the modifications were not visible to each other, but this + * will not result in any corruption of the list or change in behavior other + * than performance. + * + * @author jonp@google.com (Jon Perlow) + */ +public class LazyStringArrayList extends AbstractList<String> + implements LazyStringList, RandomAccess { + + public final static LazyStringList EMPTY = new UnmodifiableLazyStringList( + new LazyStringArrayList()); + + private final List<Object> list; + + public LazyStringArrayList() { + list = new ArrayList<Object>(); + } + + public LazyStringArrayList(List<String> from) { + list = new ArrayList<Object>(from); + } + + @Override + public String get(int index) { + Object o = list.get(index); + if (o instanceof String) { + return (String) o; + } else { + ByteString bs = (ByteString) o; + String s = bs.toStringUtf8(); + if (Internal.isValidUtf8(bs)) { + list.set(index, s); + } + return s; + } + } + + @Override + public int size() { + return list.size(); + } + + @Override + public String set(int index, String s) { + Object o = list.set(index, s); + return asString(o); + } + + @Override + public void add(int index, String element) { + list.add(index, element); + modCount++; + } + + @Override + public boolean addAll(int index, Collection<? extends String> c) { + boolean ret = list.addAll(index, c); + modCount++; + return ret; + } + + @Override + public String remove(int index) { + Object o = list.remove(index); + modCount++; + return asString(o); + } + + public void clear() { + list.clear(); + modCount++; + } + + // @Override + public void add(ByteString element) { + list.add(element); + modCount++; + } + + // @Override + public ByteString getByteString(int index) { + Object o = list.get(index); + if (o instanceof String) { + ByteString b = ByteString.copyFromUtf8((String) o); + list.set(index, b); + return b; + } else { + return (ByteString) o; + } + } + + private String asString(Object o) { + if (o instanceof String) { + return (String) o; + } else { + return ((ByteString) o).toStringUtf8(); + } + } +} diff --git a/java/src/main/java/com/google/protobuf/LazyStringList.java b/java/src/main/java/com/google/protobuf/LazyStringList.java new file mode 100644 index 00000000..97139ca6 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/LazyStringList.java @@ -0,0 +1,72 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import java.util.List; + +/** + * An interface extending List<String> that also provides access to the + * items of the list as UTF8-encoded ByteString objects. This is used by the + * protocol buffer implementation to support lazily converting bytes parsed + * over the wire to String objects until needed and also increases the + * efficiency of serialization if the String was never requested as the + * ByteString is already cached. + * <p> + * This only adds additional methods that are required for the use in the + * protocol buffer code in order to be able successfuly round trip byte arrays + * through parsing and serialization without conversion to strings. It's not + * attempting to support the functionality of say List<ByteString>, hence + * why only these two very specific methods are added. + * + * @author jonp@google.com (Jon Perlow) + */ +public interface LazyStringList extends List<String> { + + /** + * Returns the element at the specified position in this list as a ByteString. + * + * @param index index of the element to return + * @return the element at the specified position in this list + * @throws IndexOutOfBoundsException if the index is out of range + * (<tt>index < 0 || index >= size()</tt>) + */ + ByteString getByteString(int index); + + /** + * Appends the specified element to the end of this list (optional + * operation). + * + * @param element element to be appended to this list + * @throws UnsupportedOperationException if the <tt>add</tt> operation + * is not supported by this list + */ + void add(ByteString element); +} diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java index 8c29e212..67c4148e 100644 --- a/java/src/main/java/com/google/protobuf/Message.java +++ b/java/src/main/java/com/google/protobuf/Message.java @@ -48,69 +48,7 @@ import java.util.Map; * * @author kenton@google.com Kenton Varda */ -public interface Message extends MessageLite { - /** - * Get the message's type's descriptor. This differs from the - * {@code getDescriptor()} method of generated message classes in that - * this method is an abstract method of the {@code Message} interface - * whereas {@code getDescriptor()} is a static method of a specific class. - * They return the same thing. - */ - Descriptors.Descriptor getDescriptorForType(); - - // (From MessageLite, re-declared here only for return type covariance.) - Message getDefaultInstanceForType(); - - /** - * Returns a collection of all the fields in this message which are set - * and their corresponding values. A singular ("required" or "optional") - * field is set iff hasField() returns true for that field. A "repeated" - * field is set iff getRepeatedFieldSize() is greater than zero. The - * values are exactly what would be returned by calling - * {@link #getField(Descriptors.FieldDescriptor)} for each field. The map - * is guaranteed to be a sorted map, so iterating over it will return fields - * in order by field number. - */ - Map<Descriptors.FieldDescriptor, Object> getAllFields(); - - /** - * Returns true if the given field is set. This is exactly equivalent to - * calling the generated "has" accessor method corresponding to the field. - * @throws IllegalArgumentException The field is a repeated field, or - * {@code field.getContainingType() != getDescriptorForType()}. - */ - boolean hasField(Descriptors.FieldDescriptor field); - - /** - * Obtains the value of the given field, or the default value if it is - * not set. For primitive fields, the boxed primitive value is returned. - * For enum fields, the EnumValueDescriptor for the value is returend. For - * embedded message fields, the sub-message is returned. For repeated - * fields, a java.util.List is returned. - */ - Object getField(Descriptors.FieldDescriptor field); - - /** - * Gets the number of elements of a repeated field. This is exactly - * equivalent to calling the generated "Count" accessor method corresponding - * to the field. - * @throws IllegalArgumentException The field is not a repeated field, or - * {@code field.getContainingType() != getDescriptorForType()}. - */ - int getRepeatedFieldCount(Descriptors.FieldDescriptor field); - - /** - * Gets an element of a repeated field. For primitive fields, the boxed - * primitive value is returned. For enum fields, the EnumValueDescriptor - * for the value is returend. For embedded message fields, the sub-message - * is returned. - * @throws IllegalArgumentException The field is not a repeated field, or - * {@code field.getContainingType() != getDescriptorForType()}. - */ - Object getRepeatedField(Descriptors.FieldDescriptor field, int index); - - /** Get the {@link UnknownFieldSet} for this message. */ - UnknownFieldSet getUnknownFields(); +public interface Message extends MessageLite, MessageOrBuilder { // ----------------------------------------------------------------- // Comparison and hashing @@ -119,7 +57,8 @@ public interface Message extends MessageLite { * Compares the specified object with this message for equality. Returns * <tt>true</tt> if the given object is a message of the same type (as * defined by {@code getDescriptorForType()}) and has identical values for - * all of its fields. + * all of its fields. Subclasses must implement this; inheriting + * {@code Object.equals()} is incorrect. * * @param other object to be compared for equality with this message * @return <tt>true</tt> if the specified object is equal to this message @@ -129,8 +68,9 @@ public interface Message extends MessageLite { /** * Returns the hash code value for this message. The hash code of a message - * is defined to be <tt>getDescriptor().hashCode() ^ map.hashCode()</tt>, - * where <tt>map</tt> is a map of field numbers to field values. + * should mix the message's type (object identity of the decsriptor) with its + * contents (known and unknown field values). Subclasses must implement this; + * inheriting {@code Object.hashCode()} is incorrect. * * @return the hash code value for this message * @see Map#hashCode() @@ -158,7 +98,7 @@ public interface Message extends MessageLite { /** * Abstract interface implemented by Protocol Message builders. */ - interface Builder extends MessageLite.Builder { + interface Builder extends MessageLite.Builder, MessageOrBuilder { // (From MessageLite.Builder, re-declared here only for return type // covariance.) Builder clear(); @@ -197,17 +137,6 @@ public interface Message extends MessageLite { */ Descriptors.Descriptor getDescriptorForType(); - // (From MessageLite.Builder, re-declared here only for return type - // covariance.) - Message getDefaultInstanceForType(); - - /** - * Like {@link Message#getAllFields()}. The returned map may or may not - * reflect future changes to the builder. Either way, the returned map is - * itself unmodifiable. - */ - Map<Descriptors.FieldDescriptor, Object> getAllFields(); - /** * Create a Builder for messages of the appropriate type for the given * field. Messages built with this can then be passed to setField(), @@ -215,12 +144,6 @@ public interface Message extends MessageLite { */ Builder newBuilderForField(Descriptors.FieldDescriptor field); - /** Like {@link Message#hasField(Descriptors.FieldDescriptor)} */ - boolean hasField(Descriptors.FieldDescriptor field); - - /** Like {@link Message#getField(Descriptors.FieldDescriptor)} */ - Object getField(Descriptors.FieldDescriptor field); - /** * Sets a field to the given value. The value must be of the correct type * for this field, i.e. the same type that @@ -235,16 +158,6 @@ public interface Message extends MessageLite { Builder clearField(Descriptors.FieldDescriptor field); /** - * Like {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)} - */ - int getRepeatedFieldCount(Descriptors.FieldDescriptor field); - - /** - * Like {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} - */ - Object getRepeatedField(Descriptors.FieldDescriptor field, int index); - - /** * Sets an element of a repeated field to the given value. The value must * be of the correct type for this field, i.e. the same type that * {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} would @@ -262,9 +175,6 @@ public interface Message extends MessageLite { */ Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value); - /** Get the {@link UnknownFieldSet} for this message. */ - UnknownFieldSet getUnknownFields(); - /** Set the {@link UnknownFieldSet} for this message. */ Builder setUnknownFields(UnknownFieldSet unknownFields); diff --git a/java/src/main/java/com/google/protobuf/MessageLite.java b/java/src/main/java/com/google/protobuf/MessageLite.java index cf7f39e2..31b8256e 100644 --- a/java/src/main/java/com/google/protobuf/MessageLite.java +++ b/java/src/main/java/com/google/protobuf/MessageLite.java @@ -64,22 +64,8 @@ import java.io.OutputStream; * * @author kenton@google.com Kenton Varda */ -public interface MessageLite { - /** - * Get an instance of the type with all fields set to their default values. - * This may or may not be a singleton. This differs from the - * {@code getDefaultInstance()} method of generated message classes in that - * this method is an abstract method of the {@code MessageLite} interface - * whereas {@code getDefaultInstance()} is a static method of a specific - * class. They return the same thing. - */ - MessageLite getDefaultInstanceForType(); +public interface MessageLite extends MessageLiteOrBuilder { - /** - * Returns true if all required fields in the message and all embedded - * messages are set, false otherwise. - */ - boolean isInitialized(); /** * Serializes the message and writes it to {@code output}. This does not @@ -153,7 +139,7 @@ public interface MessageLite { /** * Abstract interface implemented by Protocol Message builders. */ - interface Builder extends Cloneable { + interface Builder extends MessageLiteOrBuilder, Cloneable { /** Resets all fields to their default values. */ Builder clear(); @@ -187,12 +173,6 @@ public interface MessageLite { Builder clone(); /** - * Returns true if all required fields in the message and all embedded - * messages are set, false otherwise. - */ - boolean isInitialized(); - - /** * Parses a message of this type from the input and merges it with this * message, as if using {@link Builder#mergeFrom(MessageLite)}. * @@ -230,12 +210,6 @@ public interface MessageLite { ExtensionRegistryLite extensionRegistry) throws IOException; - /** - * Get the message's type's default instance. - * See {@link MessageLite#getDefaultInstanceForType()}. - */ - MessageLite getDefaultInstanceForType(); - // --------------------------------------------------------------- // Convenience methods. @@ -243,6 +217,8 @@ public interface MessageLite { * Parse {@code data} as a message of this type and merge it with the * message being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. + * + * @return this */ Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException; @@ -250,6 +226,8 @@ public interface MessageLite { * Parse {@code data} as a message of this type and merge it with the * message being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. + * + * @return this */ Builder mergeFrom(ByteString data, ExtensionRegistryLite extensionRegistry) @@ -259,6 +237,8 @@ public interface MessageLite { * Parse {@code data} as a message of this type and merge it with the * message being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. + * + * @return this */ Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException; @@ -266,6 +246,8 @@ public interface MessageLite { * Parse {@code data} as a message of this type and merge it with the * message being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. + * + * @return this */ Builder mergeFrom(byte[] data, int off, int len) throws InvalidProtocolBufferException; @@ -274,6 +256,8 @@ public interface MessageLite { * Parse {@code data} as a message of this type and merge it with the * message being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. + * + * @return this */ Builder mergeFrom(byte[] data, ExtensionRegistryLite extensionRegistry) @@ -283,6 +267,8 @@ public interface MessageLite { * Parse {@code data} as a message of this type and merge it with the * message being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. + * + * @return this */ Builder mergeFrom(byte[] data, int off, int len, ExtensionRegistryLite extensionRegistry) @@ -299,6 +285,8 @@ public interface MessageLite { * and {@link #mergeDelimitedFrom(InputStream)} to read it. * <p> * Despite usually reading the entire input, this does not close the stream. + * + * @return this */ Builder mergeFrom(InputStream input) throws IOException; @@ -306,6 +294,8 @@ public interface MessageLite { * Parse a message of this type from {@code input} and merge it with the * message being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. + * + * @return this */ Builder mergeFrom(InputStream input, ExtensionRegistryLite extensionRegistry) diff --git a/java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java b/java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java new file mode 100644 index 00000000..a2a7448a --- /dev/null +++ b/java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java @@ -0,0 +1,56 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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; + +/** + * Base interface for methods common to {@link MessageLite} + * and {@link MessageLite.Builder} to provide type equivalency. + * + * @author jonp@google.com (Jon Perlow) + */ +public interface MessageLiteOrBuilder { + /** + * Get an instance of the type with all fields set to their default values. + * This may or may not be a singleton. This differs from the + * {@code getDefaultInstance()} method of generated message classes in that + * this method is an abstract method of the {@code MessageLite} interface + * whereas {@code getDefaultInstance()} is a static method of a specific + * class. They return the same thing. + */ + MessageLite getDefaultInstanceForType(); + + /** + * Returns true if all required fields in the message and all embedded + * messages are set, false otherwise. + */ + boolean isInitialized(); + +} diff --git a/java/src/main/java/com/google/protobuf/MessageOrBuilder.java b/java/src/main/java/com/google/protobuf/MessageOrBuilder.java new file mode 100644 index 00000000..0132e7ca --- /dev/null +++ b/java/src/main/java/com/google/protobuf/MessageOrBuilder.java @@ -0,0 +1,110 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import java.util.Map; + +/** + * Base interface for methods common to {@link Message} and + * {@link Message.Builder} to provide type equivalency. + * + * @author jonp@google.com (Jon Perlow) + */ +public interface MessageOrBuilder extends MessageLiteOrBuilder { + + // (From MessageLite, re-declared here only for return type covariance.) + //@Override (Java 1.6 override semantics, but we must support 1.5) + Message getDefaultInstanceForType(); + + /** + * Get the message's type's descriptor. This differs from the + * {@code getDescriptor()} method of generated message classes in that + * this method is an abstract method of the {@code Message} interface + * whereas {@code getDescriptor()} is a static method of a specific class. + * They return the same thing. + */ + Descriptors.Descriptor getDescriptorForType(); + + /** + * Returns a collection of all the fields in this message which are set + * and their corresponding values. A singular ("required" or "optional") + * field is set iff hasField() returns true for that field. A "repeated" + * field is set iff getRepeatedFieldSize() is greater than zero. The + * values are exactly what would be returned by calling + * {@link #getField(Descriptors.FieldDescriptor)} for each field. The map + * is guaranteed to be a sorted map, so iterating over it will return fields + * in order by field number. + * <br> + * If this is for a builder, the returned map may or may not reflect future + * changes to the builder. Either way, the returned map is itself + * unmodifiable. + */ + Map<Descriptors.FieldDescriptor, Object> getAllFields(); + + /** + * Returns true if the given field is set. This is exactly equivalent to + * calling the generated "has" accessor method corresponding to the field. + * @throws IllegalArgumentException The field is a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + boolean hasField(Descriptors.FieldDescriptor field); + + /** + * Obtains the value of the given field, or the default value if it is + * not set. For primitive fields, the boxed primitive value is returned. + * For enum fields, the EnumValueDescriptor for the value is returend. For + * embedded message fields, the sub-message is returned. For repeated + * fields, a java.util.List is returned. + */ + Object getField(Descriptors.FieldDescriptor field); + + /** + * Gets the number of elements of a repeated field. This is exactly + * equivalent to calling the generated "Count" accessor method corresponding + * to the field. + * @throws IllegalArgumentException The field is not a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + int getRepeatedFieldCount(Descriptors.FieldDescriptor field); + + /** + * Gets an element of a repeated field. For primitive fields, the boxed + * primitive value is returned. For enum fields, the EnumValueDescriptor + * for the value is returend. For embedded message fields, the sub-message + * is returned. + * @throws IllegalArgumentException The field is not a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + Object getRepeatedField(Descriptors.FieldDescriptor field, int index); + + /** Get the {@link UnknownFieldSet} for this message. */ + UnknownFieldSet getUnknownFields(); +} diff --git a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java new file mode 100644 index 00000000..0772eaca --- /dev/null +++ b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java @@ -0,0 +1,696 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * <code>RepeatedFieldBuilder</code> implements a structure that a protocol + * message uses to hold a repeated field of other protocol messages. It supports + * the classical use case of adding immutable {@link Message}'s to the + * repeated field and is highly optimized around this (no extra memory + * allocations and sharing of immutable arrays). + * <br> + * It also supports the additional use case of adding a {@link Message.Builder} + * to the repeated field and deferring conversion of that <code>Builder</code> + * to an immutable <code>Message</code>. In this way, it's possible to maintain + * a tree of <code>Builder</code>'s that acts as a fully read/write data + * structure. + * <br> + * Logically, one can think of a tree of builders as converting the entire tree + * to messages when build is called on the root or when any method is called + * that desires a Message instead of a Builder. In terms of the implementation, + * the <code>SingleFieldBuilder</code> and <code>RepeatedFieldBuilder</code> + * classes cache messages that were created so that messages only need to be + * created when some change occured in its builder or a builder for one of its + * descendants. + * + * @param <MType> the type of message for the field + * @param <BType> the type of builder for the field + * @param <IType> the common interface for the message and the builder + * + * @author jonp@google.com (Jon Perlow) + */ +public class RepeatedFieldBuilder + <MType extends GeneratedMessage, + BType extends GeneratedMessage.Builder, + IType extends MessageOrBuilder> + implements GeneratedMessage.BuilderParent { + + // Parent to send changes to. + private GeneratedMessage.BuilderParent parent; + + // List of messages. Never null. It may be immutable, in which case + // isMessagesListImmutable will be true. See note below. + private List<MType> messages; + + // Whether messages is an mutable array that can be modified. + private boolean isMessagesListMutable; + + // List of builders. May be null, in which case, no nested builders were + // created. If not null, entries represent the builder for that index. + private List<SingleFieldBuilder<MType, BType, IType>> builders; + + // Here are the invariants for messages and builders: + // 1. messages is never null and its count corresponds to the number of items + // in the repeated field. + // 2. If builders is non-null, messages and builders MUST always + // contain the same number of items. + // 3. Entries in either array can be null, but for any index, there MUST be + // either a Message in messages or a builder in builders. + // 4. If the builder at an index is non-null, the builder is + // authoritative. This is the case where a Builder was set on the index. + // Any message in the messages array MUST be ignored. + // t. If the builder at an index is null, the message in the messages + // list is authoritative. This is the case where a Message (not a Builder) + // was set directly for an index. + + // Indicates that we've built a message and so we are now obligated + // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener. + private boolean isClean; + + // A view of this builder that exposes a List interface of messages. This is + // initialized on demand. This is fully backed by this object and all changes + // are reflected in it. Access to any item converts it to a message if it + // was a builder. + private MessageExternalList<MType, BType, IType> externalMessageList; + + // A view of this builder that exposes a List interface of builders. This is + // initialized on demand. This is fully backed by this object and all changes + // are reflected in it. Access to any item converts it to a builder if it + // was a message. + private BuilderExternalList<MType, BType, IType> externalBuilderList; + + // A view of this builder that exposes a List interface of the interface + // implemented by messages and builders. This is initialized on demand. This + // is fully backed by this object and all changes are reflected in it. + // Access to any item returns either a builder or message depending on + // what is most efficient. + private MessageOrBuilderExternalList<MType, BType, IType> + externalMessageOrBuilderList; + + /** + * Constructs a new builder with an empty list of messages. + * + * @param messages the current list of messages + * @param isMessagesListMutable Whether the messages list is mutable + * @param parent a listener to notify of changes + * @param isClean whether the builder is initially marked clean + */ + public RepeatedFieldBuilder( + List<MType> messages, + boolean isMessagesListMutable, + GeneratedMessage.BuilderParent parent, + boolean isClean) { + this.messages = messages; + this.isMessagesListMutable = isMessagesListMutable; + this.parent = parent; + this.isClean = isClean; + } + + public void dispose() { + // Null out parent so we stop sending it invalidations. + parent = null; + } + + /** + * Ensures that the list of messages is mutable so it can be updated. If it's + * immutable, a copy is made. + */ + private void ensureMutableMessageList() { + if (!isMessagesListMutable) { + messages = new ArrayList<MType>(messages); + isMessagesListMutable = true; + } + } + + /** + * Ensures that the list of builders is not null. If it's null, the list is + * created and initialized to be the same size as the messages list with + * null entries. + */ + private void ensureBuilders() { + if (this.builders == null) { + this.builders = + new ArrayList<SingleFieldBuilder<MType, BType, IType>>( + messages.size()); + for (int i = 0; i < messages.size(); i++) { + builders.add(null); + } + } + } + + /** + * Gets the count of items in the list. + * + * @return the count of items in the list. + */ + public int getCount() { + return messages.size(); + } + + /** + * Gets whether the list is empty. + * + * @return whether the list is empty + */ + public boolean isEmpty() { + return messages.isEmpty(); + } + + /** + * Get the message at the specified index. If the message is currently stored + * as a <code>Builder</code>, it is converted to a <code>Message</code> by + * calling {@link Message.Builder#buildPartial} on it. + * + * @param index the index of the message to get + * @return the message for the specified index + */ + public MType getMessage(int index) { + return getMessage(index, false); + } + + /** + * Get the message at the specified index. If the message is currently stored + * as a <code>Builder</code>, it is converted to a <code>Message</code> by + * calling {@link Message.Builder#buildPartial} on it. + * + * @param index the index of the message to get + * @param forBuild this is being called for build so we want to make sure + * we SingleFieldBuilder.build to send dirty invalidations + * @return the message for the specified index + */ + private MType getMessage(int index, boolean forBuild) { + if (this.builders == null) { + // We don't have any builders -- return the current Message. + // This is the case where no builder was created, so we MUST have a + // Message. + return messages.get(index); + } + + SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); + if (builder == null) { + // We don't have a builder -- return the current message. + // This is the case where no builder was created for the entry at index, + // so we MUST have a message. + return messages.get(index); + + } else { + return forBuild ? builder.build() : builder.getMessage(); + } + } + + /** + * Gets a builder for the specified index. If no builder has been created for + * that index, a builder is created on demand by calling + * {@link Message#toBuilder}. + * + * @param index the index of the message to get + * @return The builder for that index + */ + public BType getBuilder(int index) { + ensureBuilders(); + SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); + if (builder == null) { + MType message = messages.get(index); + builder = new SingleFieldBuilder<MType, BType, IType>( + message, this, isClean); + builders.set(index, builder); + } + return builder.getBuilder(); + } + + /** + * Gets the base class interface for the specified index. This may either be + * a builder or a message. It will return whatever is more efficient. + * + * @param index the index of the message to get + * @return the message or builder for the index as the base class interface + */ + @SuppressWarnings("unchecked") + public IType getMessageOrBuilder(int index) { + if (this.builders == null) { + // We don't have any builders -- return the current Message. + // This is the case where no builder was created, so we MUST have a + // Message. + return (IType) messages.get(index); + } + + SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); + if (builder == null) { + // We don't have a builder -- return the current message. + // This is the case where no builder was created for the entry at index, + // so we MUST have a message. + return (IType) messages.get(index); + + } else { + return builder.getMessageOrBuilder(); + } + } + + /** + * Sets a message at the specified index replacing the existing item at + * that index. + * + * @param index the index to set. + * @param message the message to set + * @return the builder + */ + public RepeatedFieldBuilder<MType, BType, IType> setMessage( + int index, MType message) { + if (message == null) { + throw new NullPointerException(); + } + ensureMutableMessageList(); + messages.set(index, message); + if (builders != null) { + SingleFieldBuilder<MType, BType, IType> entry = + builders.set(index, null); + if (entry != null) { + entry.dispose(); + } + } + onChanged(); + incrementModCounts(); + return this; + } + + /** + * Appends the specified element to the end of this list. + * + * @param message the message to add + * @return the builder + */ + public RepeatedFieldBuilder<MType, BType, IType> addMessage( + MType message) { + if (message == null) { + throw new NullPointerException(); + } + ensureMutableMessageList(); + messages.add(message); + if (builders != null) { + builders.add(null); + } + onChanged(); + incrementModCounts(); + return this; + } + + /** + * Inserts the specified message at the specified position in this list. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices). + * + * @param index the index at which to insert the message + * @param message the message to add + * @return the builder + */ + public RepeatedFieldBuilder<MType, BType, IType> addMessage( + int index, MType message) { + if (message == null) { + throw new NullPointerException(); + } + ensureMutableMessageList(); + messages.add(index, message); + if (builders != null) { + builders.add(index, null); + } + onChanged(); + incrementModCounts(); + return this; + } + + /** + * Appends all of the messages in the specified collection to the end of + * this list, in the order that they are returned by the specified + * collection's iterator. + * + * @param values the messages to add + * @return the builder + */ + public RepeatedFieldBuilder<MType, BType, IType> addAllMessages( + Iterable<? extends MType> values) { + for (final MType value : values) { + if (value == null) { + throw new NullPointerException(); + } + } + if (values instanceof Collection) { + @SuppressWarnings("unchecked") final + Collection<MType> collection = (Collection<MType>) values; + if (collection.size() == 0) { + return this; + } + ensureMutableMessageList(); + for (MType value : values) { + addMessage(value); + } + } else { + ensureMutableMessageList(); + for (MType value : values) { + addMessage(value); + } + } + onChanged(); + incrementModCounts(); + return this; + } + + /** + * Appends a new builder to the end of this list and returns the builder. + * + * @param message the message to add which is the basis of the builder + * @return the new builder + */ + public BType addBuilder(MType message) { + ensureMutableMessageList(); + ensureBuilders(); + SingleFieldBuilder<MType, BType, IType> builder = + new SingleFieldBuilder<MType, BType, IType>( + message, this, isClean); + messages.add(null); + builders.add(builder); + onChanged(); + incrementModCounts(); + return builder.getBuilder(); + } + + /** + * Inserts a new builder at the specified position in this list. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices). + * + * @param index the index at which to insert the builder + * @param message the message to add which is the basis of the builder + * @return the builder + */ + public BType addBuilder(int index, MType message) { + ensureMutableMessageList(); + ensureBuilders(); + SingleFieldBuilder<MType, BType, IType> builder = + new SingleFieldBuilder<MType, BType, IType>( + message, this, isClean); + messages.add(index, null); + builders.add(index, builder); + onChanged(); + incrementModCounts(); + return builder.getBuilder(); + } + + /** + * Removes the element at the specified position in this list. Shifts any + * subsequent elements to the left (subtracts one from their indices). + * Returns the element that was removed from the list. + * + * @param index the index at which to remove the message + */ + public void remove(int index) { + ensureMutableMessageList(); + messages.remove(index); + if (builders != null) { + SingleFieldBuilder<MType, BType, IType> entry = + builders.remove(index); + if (entry != null) { + entry.dispose(); + } + } + onChanged(); + incrementModCounts(); + } + + /** + * Removes all of the elements from this list. + * The list will be empty after this call returns. + */ + public void clear() { + messages = Collections.emptyList(); + isMessagesListMutable = false; + if (builders != null) { + for (SingleFieldBuilder<MType, BType, IType> entry : + builders) { + if (entry != null) { + entry.dispose(); + } + } + builders = null; + } + onChanged(); + incrementModCounts(); + } + + /** + * Builds the list of messages from the builder and returns them. + * + * @return an immutable list of messages + */ + public List<MType> build() { + // Now that build has been called, we are required to dispatch + // invalidations. + isClean = true; + + if (!isMessagesListMutable && builders == null) { + // We still have an immutable list and we never created a builder. + return messages; + } + + boolean allMessagesInSync = true; + if (!isMessagesListMutable) { + // We still have an immutable list. Let's see if any of them are out + // of sync with their builders. + for (int i = 0; i < messages.size(); i++) { + Message message = messages.get(i); + SingleFieldBuilder<MType, BType, IType> builder = builders.get(i); + if (builder != null) { + if (builder.build() != message) { + allMessagesInSync = false; + break; + } + } + } + if (allMessagesInSync) { + // Immutable list is still in sync. + return messages; + } + } + + // Need to make sure messages is up to date + ensureMutableMessageList(); + for (int i = 0; i < messages.size(); i++) { + messages.set(i, getMessage(i, true)); + } + + // We're going to return our list as immutable so we mark that we can + // no longer update it. + messages = Collections.unmodifiableList(messages); + isMessagesListMutable = false; + return messages; + } + + /** + * Gets a view of the builder as a list of messages. The returned list is live + * and will reflect any changes to the underlying builder. + * + * @return the messages in the list + */ + public List<MType> getMessageList() { + if (externalMessageList == null) { + externalMessageList = + new MessageExternalList<MType, BType, IType>(this); + } + return externalMessageList; + } + + /** + * Gets a view of the builder as a list of builders. This returned list is + * live and will reflect any changes to the underlying builder. + * + * @return the builders in the list + */ + public List<BType> getBuilderList() { + if (externalBuilderList == null) { + externalBuilderList = + new BuilderExternalList<MType, BType, IType>(this); + } + return externalBuilderList; + } + + /** + * Gets a view of the builder as a list of MessageOrBuilders. This returned + * list is live and will reflect any changes to the underlying builder. + * + * @return the builders in the list + */ + public List<IType> getMessageOrBuilderList() { + if (externalMessageOrBuilderList == null) { + externalMessageOrBuilderList = + new MessageOrBuilderExternalList<MType, BType, IType>(this); + } + return externalMessageOrBuilderList; + } + + /** + * Called when a the builder or one of its nested children has changed + * and any parent should be notified of its invalidation. + */ + private void onChanged() { + if (isClean && parent != null) { + parent.markDirty(); + + // Don't keep dispatching invalidations until build is called again. + isClean = false; + } + } + + @Override + public void markDirty() { + onChanged(); + } + + /** + * Increments the mod counts so that an ConcurrentModificationException can + * be thrown if calling code tries to modify the builder while its iterating + * the list. + */ + private void incrementModCounts() { + if (externalMessageList != null) { + externalMessageList.incrementModCount(); + } + if (externalBuilderList != null) { + externalBuilderList.incrementModCount(); + } + if (externalMessageOrBuilderList != null) { + externalMessageOrBuilderList.incrementModCount(); + } + } + + /** + * Provides a live view of the builder as a list of messages. + * + * @param <MType> the type of message for the field + * @param <BType> the type of builder for the field + * @param <IType> the common interface for the message and the builder + */ + private static class MessageExternalList< + MType extends GeneratedMessage, + BType extends GeneratedMessage.Builder, + IType extends MessageOrBuilder> + extends AbstractList<MType> implements List<MType> { + + RepeatedFieldBuilder<MType, BType, IType> builder; + + MessageExternalList( + RepeatedFieldBuilder<MType, BType, IType> builder) { + this.builder = builder; + } + + public int size() { + return this.builder.getCount(); + } + + public MType get(int index) { + return builder.getMessage(index); + } + + void incrementModCount() { + modCount++; + } + } + + /** + * Provides a live view of the builder as a list of builders. + * + * @param <MType> the type of message for the field + * @param <BType> the type of builder for the field + * @param <IType> the common interface for the message and the builder + */ + private static class BuilderExternalList< + MType extends GeneratedMessage, + BType extends GeneratedMessage.Builder, + IType extends MessageOrBuilder> + extends AbstractList<BType> implements List<BType> { + + RepeatedFieldBuilder<MType, BType, IType> builder; + + BuilderExternalList( + RepeatedFieldBuilder<MType, BType, IType> builder) { + this.builder = builder; + } + + public int size() { + return this.builder.getCount(); + } + + public BType get(int index) { + return builder.getBuilder(index); + } + + void incrementModCount() { + modCount++; + } + } + + /** + * Provides a live view of the builder as a list of builders. + * + * @param <MType> the type of message for the field + * @param <BType> the type of builder for the field + * @param <IType> the common interface for the message and the builder + */ + private static class MessageOrBuilderExternalList< + MType extends GeneratedMessage, + BType extends GeneratedMessage.Builder, + IType extends MessageOrBuilder> + extends AbstractList<IType> implements List<IType> { + + RepeatedFieldBuilder<MType, BType, IType> builder; + + MessageOrBuilderExternalList( + RepeatedFieldBuilder<MType, BType, IType> builder) { + this.builder = builder; + } + + public int size() { + return this.builder.getCount(); + } + + public IType get(int index) { + return builder.getMessageOrBuilder(index); + } + + void incrementModCount() { + modCount++; + } + } +} diff --git a/java/src/main/java/com/google/protobuf/ServiceException.java b/java/src/main/java/com/google/protobuf/ServiceException.java index c043a775..cde669d6 100644 --- a/java/src/main/java/com/google/protobuf/ServiceException.java +++ b/java/src/main/java/com/google/protobuf/ServiceException.java @@ -35,10 +35,18 @@ package com.google.protobuf; * * @author cpovirk@google.com (Chris Povirk) */ -public final class ServiceException extends Exception { +public class ServiceException extends Exception { private static final long serialVersionUID = -1219262335729891920L; public ServiceException(final String message) { super(message); } + + public ServiceException(final Throwable cause) { + super(cause); + } + + public ServiceException(final String message, final Throwable cause) { + super(message, cause); + } } diff --git a/java/src/main/java/com/google/protobuf/SingleFieldBuilder.java b/java/src/main/java/com/google/protobuf/SingleFieldBuilder.java new file mode 100644 index 00000000..a92042e8 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/SingleFieldBuilder.java @@ -0,0 +1,241 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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; + +/** + * <code>SingleFieldBuilder</code> implements a structure that a protocol + * message uses to hold a single field of another protocol message. It supports + * the classical use case of setting an immutable {@link Message} as the value + * of the field and is highly optimized around this. + * <br> + * It also supports the additional use case of setting a {@link Message.Builder} + * as the field and deferring conversion of that <code>Builder</code> + * to an immutable <code>Message</code>. In this way, it's possible to maintain + * a tree of <code>Builder</code>'s that acts as a fully read/write data + * structure. + * <br> + * Logically, one can think of a tree of builders as converting the entire tree + * to messages when build is called on the root or when any method is called + * that desires a Message instead of a Builder. In terms of the implementation, + * the <code>SingleFieldBuilder</code> and <code>RepeatedFieldBuilder</code> + * classes cache messages that were created so that messages only need to be + * created when some change occured in its builder or a builder for one of its + * descendants. + * + * @param <MType> the type of message for the field + * @param <BType> the type of builder for the field + * @param <IType> the common interface for the message and the builder + * + * @author jonp@google.com (Jon Perlow) + */ +public class SingleFieldBuilder + <MType extends GeneratedMessage, + BType extends GeneratedMessage.Builder, + IType extends MessageOrBuilder> + implements GeneratedMessage.BuilderParent { + + // Parent to send changes to. + private GeneratedMessage.BuilderParent parent; + + // Invariant: one of builder or message fields must be non-null. + + // If set, this is the case where we are backed by a builder. In this case, + // message field represents a cached message for the builder (or null if + // there is no cached message). + private BType builder; + + // If builder is non-null, this represents a cached message from the builder. + // If builder is null, this is the authoritative message for the field. + private MType message; + + // Indicates that we've built a message and so we are now obligated + // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener. + private boolean isClean; + + public SingleFieldBuilder( + MType message, + GeneratedMessage.BuilderParent parent, + boolean isClean) { + if (message == null) { + throw new NullPointerException(); + } + this.message = message; + this.parent = parent; + this.isClean = isClean; + } + + public void dispose() { + // Null out parent so we stop sending it invalidations. + parent = null; + } + + /** + * Get the message for the field. If the message is currently stored + * as a <code>Builder</code>, it is converted to a <code>Message</code> by + * calling {@link Message.Builder#buildPartial} on it. If no message has + * been set, returns the default instance of the message. + * + * @return the message for the field + */ + @SuppressWarnings("unchecked") + public MType getMessage() { + if (message == null) { + // If message is null, the invariant is that we must be have a builder. + message = (MType) builder.buildPartial(); + } + return message; + } + + /** + * Builds the message and returns it. + * + * @return the message + */ + public MType build() { + // Now that build has been called, we are required to dispatch + // invalidations. + isClean = true; + return getMessage(); + } + + /** + * Gets a builder for the field. If no builder has been created yet, a + * builder is created on demand by calling {@link Message#toBuilder}. + * + * @return The builder for the field + */ + @SuppressWarnings("unchecked") + public BType getBuilder() { + if (builder == null) { + // builder.mergeFrom() on a fresh builder + // does not create any sub-objects with independent clean/dirty states, + // therefore setting the builder itself to clean without actually calling + // build() cannot break any invariants. + builder = (BType) message.newBuilderForType(this); + builder.mergeFrom(message); // no-op if message is the default message + builder.markClean(); + } + return builder; + } + + /** + * Gets the base class interface for the field. This may either be a builder + * or a message. It will return whatever is more efficient. + * + * @return the message or builder for the field as the base class interface + */ + @SuppressWarnings("unchecked") + public IType getMessageOrBuilder() { + if (builder != null) { + return (IType) builder; + } else { + return (IType) message; + } + } + + /** + * Sets a message for the field replacing any existing value. + * + * @param message the message to set + * @return the builder + */ + public SingleFieldBuilder<MType, BType, IType> setMessage( + MType message) { + if (message == null) { + throw new NullPointerException(); + } + this.message = message; + if (builder != null) { + builder.dispose(); + builder = null; + } + onChanged(); + return this; + } + + /** + * Merges the field from another field. + * + * @param value the value to merge from + * @return the builder + */ + public SingleFieldBuilder<MType, BType, IType> mergeFrom( + MType value) { + if (builder == null && message == message.getDefaultInstanceForType()) { + message = value; + } else { + getBuilder().mergeFrom(value); + } + onChanged(); + return this; + } + + /** + * Clears the value of the field. + * + * @return the builder + */ + @SuppressWarnings("unchecked") + public SingleFieldBuilder<MType, BType, IType> clear() { + message = (MType) (message != null ? + message.getDefaultInstanceForType() : + builder.getDefaultInstanceForType()); + if (builder != null) { + builder.dispose(); + builder = null; + } + onChanged(); + return this; + } + + /** + * Called when a the builder or one of its nested children has changed + * and any parent should be notified of its invalidation. + */ + private void onChanged() { + // If builder is null, this is the case where onChanged is being called + // from setMessage or clear. + if (builder != null) { + message = null; + } + if (isClean && parent != null) { + parent.markDirty(); + + // Don't keep dispatching invalidations until build is called again. + isClean = false; + } + } + + @Override + public void markDirty() { + onChanged(); + } +} diff --git a/java/src/main/java/com/google/protobuf/SmallSortedMap.java b/java/src/main/java/com/google/protobuf/SmallSortedMap.java new file mode 100644 index 00000000..ccc20163 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/SmallSortedMap.java @@ -0,0 +1,618 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.TreeMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.SortedMap; + +/** + * A custom map implementation from FieldDescriptor to Object optimized to + * minimize the number of memory allocations for instances with a small number + * of mappings. The implementation stores the first {@code k} mappings in an + * array for a configurable value of {@code k}, allowing direct access to the + * corresponding {@code Entry}s without the need to create an Iterator. The + * remaining entries are stored in an overflow map. Iteration over the entries + * in the map should be done as follows: + * + * <pre> + * for (int i = 0; i < fieldMap.getNumArrayEntries(); i++) { + * process(fieldMap.getArrayEntryAt(i)); + * } + * for (Map.Entry<K, V> entry : fieldMap.getOverflowEntries()) { + * process(entry); + * } + * </pre> + * + * The resulting iteration is in order of ascending field tag number. The + * object returned by {@link #entrySet()} adheres to the same contract but is + * less efficient as it necessarily involves creating an object for iteration. + * <p> + * The tradeoff for this memory efficiency is that the worst case running time + * of the {@code put()} operation is {@code O(k + lg n)}, which happens when + * entries are added in descending order. {@code k} should be chosen such that + * it covers enough common cases without adversely affecting larger maps. In + * practice, the worst case scenario does not happen for extensions because + * extension fields are serialized and deserialized in order of ascending tag + * number, but the worst case scenario can happen for DynamicMessages. + * <p> + * The running time for all other operations is similar to that of + * {@code TreeMap}. + * <p> + * Instances are not thread-safe until {@link #makeImmutable()} is called, + * after which any modifying operation will result in an + * {@link UnsupportedOperationException}. + * + * @author darick@google.com Darick Tong + */ +// This class is final for all intents and purposes because the constructor is +// private. However, the FieldDescriptor-specific logic is encapsulated in +// a subclass to aid testability of the core logic. +class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> { + + /** + * Creates a new instance for mapping FieldDescriptors to their values. + * The {@link #makeImmutable()} implementation will convert the List values + * of any repeated fields to unmodifiable lists. + * + * @param arraySize The size of the entry array containing the + * lexicographically smallest mappings. + */ + static <FieldDescriptorType extends + FieldSet.FieldDescriptorLite<FieldDescriptorType>> + SmallSortedMap<FieldDescriptorType, Object> newFieldMap(int arraySize) { + return new SmallSortedMap<FieldDescriptorType, Object>(arraySize) { + @Override + @SuppressWarnings("unchecked") + public void makeImmutable() { + if (!isImmutable()) { + for (int i = 0; i < getNumArrayEntries(); i++) { + final Map.Entry<FieldDescriptorType, Object> entry = + getArrayEntryAt(i); + if (entry.getKey().isRepeated()) { + final List value = (List) entry.getValue(); + entry.setValue(Collections.unmodifiableList(value)); + } + } + for (Map.Entry<FieldDescriptorType, Object> entry : + getOverflowEntries()) { + if (entry.getKey().isRepeated()) { + final List value = (List) entry.getValue(); + entry.setValue(Collections.unmodifiableList(value)); + } + } + } + super.makeImmutable(); + } + }; + } + + /** + * Creates a new instance for testing. + * + * @param arraySize The size of the entry array containing the + * lexicographically smallest mappings. + */ + static <K extends Comparable<K>, V> SmallSortedMap<K, V> newInstanceForTest( + int arraySize) { + return new SmallSortedMap<K, V>(arraySize); + } + + private final int maxArraySize; + // The "entry array" is actually a List because generic arrays are not + // allowed. ArrayList also nicely handles the entry shifting on inserts and + // removes. + private List<Entry> entryList; + private Map<K, V> overflowEntries; + private boolean isImmutable; + // The EntrySet is a stateless view of the Map. It's initialized the first + // time it is requested and reused henceforth. + private volatile EntrySet lazyEntrySet; + + /** + * @code arraySize Size of the array in which the lexicographically smallest + * mappings are stored. (i.e. the {@code k} referred to in the class + * documentation). + */ + private SmallSortedMap(int arraySize) { + this.maxArraySize = arraySize; + this.entryList = Collections.emptyList(); + this.overflowEntries = Collections.emptyMap(); + } + + /** Make this map immutable from this point forward. */ + public void makeImmutable() { + if (!isImmutable) { + // Note: There's no need to wrap the entryList in an unmodifiableList + // because none of the list's accessors are exposed. The iterator() of + // overflowEntries, on the other hand, is exposed so it must be made + // unmodifiable. + overflowEntries = overflowEntries.isEmpty() ? + Collections.<K, V>emptyMap() : + Collections.unmodifiableMap(overflowEntries); + isImmutable = true; + } + } + + /** @return Whether {@link #makeImmutable()} has been called. */ + public boolean isImmutable() { + return isImmutable; + } + + /** @return The number of entries in the entry array. */ + public int getNumArrayEntries() { + return entryList.size(); + } + + /** @return The array entry at the given {@code index}. */ + public Map.Entry<K, V> getArrayEntryAt(int index) { + return entryList.get(index); + } + + /** @return There number of overflow entries. */ + public int getNumOverflowEntries() { + return overflowEntries.size(); + } + + /** @return An iterable over the overflow entries. */ + public Iterable<Map.Entry<K, V>> getOverflowEntries() { + return overflowEntries.isEmpty() ? + EmptySet.<Map.Entry<K, V>>iterable() : + overflowEntries.entrySet(); + } + + @Override + public int size() { + return entryList.size() + overflowEntries.size(); + } + + /** + * The implementation throws a {@code ClassCastException} if o is not an + * object of type {@code K}. + * + * {@inheritDoc} + */ + @Override + public boolean containsKey(Object o) { + @SuppressWarnings("unchecked") + final K key = (K) o; + return binarySearchInArray(key) >= 0 || overflowEntries.containsKey(key); + } + + /** + * The implementation throws a {@code ClassCastException} if o is not an + * object of type {@code K}. + * + * {@inheritDoc} + */ + @Override + public V get(Object o) { + @SuppressWarnings("unchecked") + final K key = (K) o; + final int index = binarySearchInArray(key); + if (index >= 0) { + return entryList.get(index).getValue(); + } + return overflowEntries.get(key); + } + + @Override + public V put(K key, V value) { + checkMutable(); + final int index = binarySearchInArray(key); + if (index >= 0) { + // Replace existing array entry. + return entryList.get(index).setValue(value); + } + ensureEntryArrayMutable(); + final int insertionPoint = -(index + 1); + if (insertionPoint >= maxArraySize) { + // Put directly in overflow. + return getOverflowEntriesMutable().put(key, value); + } + // Insert new Entry in array. + if (entryList.size() == maxArraySize) { + // Shift the last array entry into overflow. + final Entry lastEntryInArray = entryList.remove(maxArraySize - 1); + getOverflowEntriesMutable().put(lastEntryInArray.getKey(), + lastEntryInArray.getValue()); + } + entryList.add(insertionPoint, new Entry(key, value)); + return null; + } + + @Override + public void clear() { + checkMutable(); + if (!entryList.isEmpty()) { + entryList.clear(); + } + if (!overflowEntries.isEmpty()) { + overflowEntries.clear(); + } + } + + /** + * The implementation throws a {@code ClassCastException} if o is not an + * object of type {@code K}. + * + * {@inheritDoc} + */ + @Override + public V remove(Object o) { + checkMutable(); + @SuppressWarnings("unchecked") + final K key = (K) o; + final int index = binarySearchInArray(key); + if (index >= 0) { + return removeArrayEntryAt(index); + } + // overflowEntries might be Collections.unmodifiableMap(), so only + // call remove() if it is non-empty. + if (overflowEntries.isEmpty()) { + return null; + } else { + return overflowEntries.remove(key); + } + } + + private V removeArrayEntryAt(int index) { + checkMutable(); + final V removed = entryList.remove(index).getValue(); + if (!overflowEntries.isEmpty()) { + // Shift the first entry in the overflow to be the last entry in the + // array. + final Iterator<Map.Entry<K, V>> iterator = + getOverflowEntriesMutable().entrySet().iterator(); + entryList.add(new Entry(iterator.next())); + iterator.remove(); + } + return removed; + } + + /** + * @param key The key to find in the entry array. + * @return The returned integer position follows the same semantics as the + * value returned by {@link java.util.Arrays#binarySearch()}. + */ + private int binarySearchInArray(K key) { + int left = 0; + int right = entryList.size() - 1; + + // Optimization: For the common case in which entries are added in + // ascending tag order, check the largest element in the array before + // doing a full binary search. + if (right >= 0) { + int cmp = key.compareTo(entryList.get(right).getKey()); + if (cmp > 0) { + return -(right + 2); // Insert point is after "right". + } else if (cmp == 0) { + return right; + } + } + + while (left <= right) { + int mid = (left + right) / 2; + int cmp = key.compareTo(entryList.get(mid).getKey()); + if (cmp < 0) { + right = mid - 1; + } else if (cmp > 0) { + left = mid + 1; + } else { + return mid; + } + } + return -(left + 1); + } + + /** + * Similar to the AbstractMap implementation of {@code keySet()} and + * {@code values()}, the entry set is created the first time this method is + * called, and returned in response to all subsequent calls. + * + * {@inheritDoc} + */ + @Override + public Set<Map.Entry<K, V>> entrySet() { + if (lazyEntrySet == null) { + lazyEntrySet = new EntrySet(); + } + return lazyEntrySet; + } + + /** + * @throws UnsupportedOperationException if {@link #makeImmutable()} has + * has been called. + */ + private void checkMutable() { + if (isImmutable) { + throw new UnsupportedOperationException(); + } + } + + /** + * @return a {@link SortedMap} to which overflow entries mappings can be + * added or removed. + * @throws UnsupportedOperationException if {@link #makeImmutable()} has been + * called. + */ + @SuppressWarnings("unchecked") + private SortedMap<K, V> getOverflowEntriesMutable() { + checkMutable(); + if (overflowEntries.isEmpty() && !(overflowEntries instanceof TreeMap)) { + overflowEntries = new TreeMap<K, V>(); + } + return (SortedMap<K, V>) overflowEntries; + } + + /** + * Lazily creates the entry list. Any code that adds to the list must first + * call this method. + */ + private void ensureEntryArrayMutable() { + checkMutable(); + if (entryList.isEmpty() && !(entryList instanceof ArrayList)) { + entryList = new ArrayList<Entry>(maxArraySize); + } + } + + /** + * Entry implementation that implements Comparable in order to support + * binary search witin the entry array. Also checks mutability in + * {@link #setValue()}. + */ + private class Entry implements Map.Entry<K, V>, Comparable<Entry> { + + private final K key; + private V value; + + Entry(Map.Entry<K, V> copy) { + this(copy.getKey(), copy.getValue()); + } + + Entry(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public int compareTo(Entry other) { + return getKey().compareTo(other.getKey()); + } + + @Override + public V setValue(V newValue) { + checkMutable(); + final V oldValue = this.value; + this.value = newValue; + return oldValue; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof Map.Entry)) { + return false; + } + @SuppressWarnings("unchecked") + Map.Entry<?, ?> other = (Map.Entry<?, ?>) o; + return equals(key, other.getKey()) && equals(value, other.getValue()); + } + + @Override + public int hashCode() { + return (key == null ? 0 : key.hashCode()) ^ + (value == null ? 0 : value.hashCode()); + } + + @Override + public String toString() { + return key + "=" + value; + } + + /** equals() that handles null values. */ + private boolean equals(Object o1, Object o2) { + return o1 == null ? o2 == null : o1.equals(o2); + } + } + + /** + * Stateless view of the entries in the field map. + */ + private class EntrySet extends AbstractSet<Map.Entry<K, V>> { + + @Override + public Iterator<Map.Entry<K, V>> iterator() { + return new EntryIterator(); + } + + @Override + public int size() { + return SmallSortedMap.this.size(); + } + + /** + * Throws a {@link ClassCastException} if o is not of the expected type. + * + * {@inheritDoc} + */ + @Override + public boolean contains(Object o) { + @SuppressWarnings("unchecked") + final Map.Entry<K, V> entry = (Map.Entry<K, V>) o; + final V existing = get(entry.getKey()); + final V value = entry.getValue(); + return existing == value || + (existing != null && existing.equals(value)); + } + + @Override + public boolean add(Map.Entry<K, V> entry) { + if (!contains(entry)) { + put(entry.getKey(), entry.getValue()); + return true; + } + return false; + } + + /** + * Throws a {@link ClassCastException} if o is not of the expected type. + * + * {@inheritDoc} + */ + @Override + public boolean remove(Object o) { + @SuppressWarnings("unchecked") + final Map.Entry<K, V> entry = (Map.Entry<K, V>) o; + if (contains(entry)) { + SmallSortedMap.this.remove(entry.getKey()); + return true; + } + return false; + } + + @Override + public void clear() { + SmallSortedMap.this.clear(); + } + } + + /** + * Iterator implementation that switches from the entry array to the overflow + * entries appropriately. + */ + private class EntryIterator implements Iterator<Map.Entry<K, V>> { + + private int pos = -1; + private boolean nextCalledBeforeRemove; + private Iterator<Map.Entry<K, V>> lazyOverflowIterator; + + @Override + public boolean hasNext() { + return (pos + 1) < entryList.size() || + getOverflowIterator().hasNext(); + } + + @Override + public Map.Entry<K, V> next() { + nextCalledBeforeRemove = true; + // Always increment pos so that we know whether the last returned value + // was from the array or from overflow. + if (++pos < entryList.size()) { + return entryList.get(pos); + } + return getOverflowIterator().next(); + } + + @Override + public void remove() { + if (!nextCalledBeforeRemove) { + throw new IllegalStateException("remove() was called before next()"); + } + nextCalledBeforeRemove = false; + checkMutable(); + + if (pos < entryList.size()) { + removeArrayEntryAt(pos--); + } else { + getOverflowIterator().remove(); + } + } + + /** + * It is important to create the overflow iterator only after the array + * entries have been iterated over because the overflow entry set changes + * when the client calls remove() on the array entries, which invalidates + * any existing iterators. + */ + private Iterator<Map.Entry<K, V>> getOverflowIterator() { + if (lazyOverflowIterator == null) { + lazyOverflowIterator = overflowEntries.entrySet().iterator(); + } + return lazyOverflowIterator; + } + } + + /** + * Helper class that holds immutable instances of an Iterable/Iterator that + * we return when the overflow entries is empty. This eliminates the creation + * of an Iterator object when there is nothing to iterate over. + */ + private static class EmptySet { + + private static final Iterator<Object> ITERATOR = new Iterator<Object>() { + @Override + public boolean hasNext() { + return false; + } + @Override + public Object next() { + throw new NoSuchElementException(); + } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + + private static final Iterable<Object> ITERABLE = new Iterable<Object>() { + @Override + public Iterator<Object> iterator() { + return ITERATOR; + } + }; + + @SuppressWarnings("unchecked") + static <T> Iterable<T> iterable() { + return (Iterable<T>) ITERABLE; + } + } +} diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java index 7ca2b4bf..d5fbdabf 100644 --- a/java/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -46,15 +46,17 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * Provide ascii text parsing and formatting support for proto2 instances. + * Provide text parsing and formatting support for proto2 instances. * The implementation largely follows google/protobuf/text_format.cc. * * @author wenboz@google.com Wenbo Zhu * @author kenton@google.com Kenton Varda */ public final class TextFormat { - private TextFormat() { - } + private TextFormat() {} + + private static final Printer DEFAULT_PRINTER = new Printer(false); + private static final Printer SINGLE_LINE_PRINTER = new Printer(true); /** * Outputs a textual representation of the Protocol Message supplied into @@ -63,16 +65,44 @@ public final class TextFormat { */ public static void print(final Message message, final Appendable output) throws IOException { - final TextGenerator generator = new TextGenerator(output); - print(message, generator); + DEFAULT_PRINTER.print(message, new TextGenerator(output)); } /** Outputs a textual representation of {@code fields} to {@code output}. */ public static void print(final UnknownFieldSet fields, final Appendable output) throws IOException { - final TextGenerator generator = new TextGenerator(output); - printUnknownFields(fields, generator); + DEFAULT_PRINTER.printUnknownFields(fields, new TextGenerator(output)); + } + + /** + * Generates a human readable form of this message, useful for debugging and + * other purposes, with no newline characters. + */ + public static String shortDebugString(final Message message) { + try { + final StringBuilder sb = new StringBuilder(); + SINGLE_LINE_PRINTER.print(message, new TextGenerator(sb)); + // Single line mode currently might have an extra space at the end. + return sb.toString().trim(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + /** + * Generates a human readable form of the unknown fields, useful for debugging + * and other purposes, with no newline characters. + */ + public static String shortDebugString(final UnknownFieldSet fields) { + try { + final StringBuilder sb = new StringBuilder(); + SINGLE_LINE_PRINTER.printUnknownFields(fields, new TextGenerator(sb)); + // Single line mode currently might have an extra space at the end. + return sb.toString().trim(); + } catch (IOException e) { + throw new IllegalStateException(e); + } } /** @@ -85,9 +115,7 @@ public final class TextFormat { print(message, text); return text.toString(); } catch (IOException e) { - throw new RuntimeException( - "Writing to a StringBuilder threw an IOException (should never " + - "happen).", e); + throw new IllegalStateException(e); } } @@ -101,28 +129,15 @@ public final class TextFormat { print(fields, text); return text.toString(); } catch (IOException e) { - throw new RuntimeException( - "Writing to a StringBuilder threw an IOException (should never " + - "happen).", e); + throw new IllegalStateException(e); } } - private static void print(final Message message, - final TextGenerator generator) - throws IOException { - for (final Map.Entry<FieldDescriptor, Object> field : - message.getAllFields().entrySet()) { - printField(field.getKey(), field.getValue(), generator); - } - printUnknownFields(message.getUnknownFields(), generator); - } - public static void printField(final FieldDescriptor field, final Object value, final Appendable output) throws IOException { - final TextGenerator generator = new TextGenerator(output); - printField(field, value, generator); + DEFAULT_PRINTER.printField(field, value, new TextGenerator(output)); } public static String printFieldToString(final FieldDescriptor field, @@ -132,157 +147,263 @@ public final class TextFormat { printField(field, value, text); return text.toString(); } catch (IOException e) { - throw new RuntimeException( - "Writing to a StringBuilder threw an IOException (should never " + - "happen).", e); + throw new IllegalStateException(e); } } - private static void printField(final FieldDescriptor field, - final Object value, - final TextGenerator generator) - throws IOException { - if (field.isRepeated()) { - // Repeated field. Print each element. - for (final Object element : (List<?>) value) { - printSingleField(field, element, generator); - } - } else { - printSingleField(field, value, generator); + /** + * Outputs a textual representation of the value of given field value. + * + * @param field the descriptor of the field + * @param value the value of the field + * @param output the output to which to append the formatted value + * @throws ClassCastException if the value is not appropriate for the + * given field descriptor + * @throws IOException if there is an exception writing to the output + */ + public static void printFieldValue(final FieldDescriptor field, + final Object value, + final Appendable output) + throws IOException { + DEFAULT_PRINTER.printFieldValue(field, value, new TextGenerator(output)); + } + + /** + * Outputs a textual representation of the value of an unknown field. + * + * @param tag the field's tag number + * @param value the value of the field + * @param output the output to which to append the formatted value + * @throws ClassCastException if the value is not appropriate for the + * given field descriptor + * @throws IOException if there is an exception writing to the output + */ + public static void printUnknownFieldValue(final int tag, + final Object value, + final Appendable output) + throws IOException { + printUnknownFieldValue(tag, value, new TextGenerator(output)); + } + + private static void printUnknownFieldValue(final int tag, + final Object value, + final TextGenerator generator) + throws IOException { + switch (WireFormat.getTagWireType(tag)) { + case WireFormat.WIRETYPE_VARINT: + generator.print(unsignedToString((Long) value)); + break; + case WireFormat.WIRETYPE_FIXED32: + generator.print( + String.format((Locale) null, "0x%08x", (Integer) value)); + break; + case WireFormat.WIRETYPE_FIXED64: + generator.print(String.format((Locale) null, "0x%016x", (Long) value)); + break; + case WireFormat.WIRETYPE_LENGTH_DELIMITED: + generator.print("\""); + generator.print(escapeBytes((ByteString) value)); + generator.print("\""); + break; + case WireFormat.WIRETYPE_START_GROUP: + DEFAULT_PRINTER.printUnknownFields((UnknownFieldSet) value, generator); + break; + default: + throw new IllegalArgumentException("Bad tag: " + tag); } } - private static void printSingleField(final FieldDescriptor field, - final Object value, - final TextGenerator generator) - throws IOException { - if (field.isExtension()) { - generator.print("["); - // We special-case MessageSet elements for compatibility with proto1. - if (field.getContainingType().getOptions().getMessageSetWireFormat() - && (field.getType() == FieldDescriptor.Type.MESSAGE) - && (field.isOptional()) - // object equality - && (field.getExtensionScope() == field.getMessageType())) { - generator.print(field.getMessageType().getFullName()); - } else { - generator.print(field.getFullName()); + /** Helper class for converting protobufs to text. */ + private static final class Printer { + /** Whether to omit newlines from the output. */ + final boolean singleLineMode; + + private Printer(final boolean singleLineMode) { + this.singleLineMode = singleLineMode; + } + + private void print(final Message message, final TextGenerator generator) + throws IOException { + for (Map.Entry<FieldDescriptor, Object> field + : message.getAllFields().entrySet()) { + printField(field.getKey(), field.getValue(), generator); } - generator.print("]"); - } else { - if (field.getType() == FieldDescriptor.Type.GROUP) { - // Groups must be serialized with their original capitalization. - generator.print(field.getMessageType().getName()); + printUnknownFields(message.getUnknownFields(), generator); + } + + private void printField(final FieldDescriptor field, final Object value, + final TextGenerator generator) throws IOException { + if (field.isRepeated()) { + // Repeated field. Print each element. + for (Object element : (List<?>) value) { + printSingleField(field, element, generator); + } } else { - generator.print(field.getName()); + printSingleField(field, value, generator); } } - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - generator.print(" {\n"); - generator.indent(); - } else { - generator.print(": "); - } + private void printSingleField(final FieldDescriptor field, + final Object value, + final TextGenerator generator) + throws IOException { + if (field.isExtension()) { + generator.print("["); + // We special-case MessageSet elements for compatibility with proto1. + if (field.getContainingType().getOptions().getMessageSetWireFormat() + && (field.getType() == FieldDescriptor.Type.MESSAGE) + && (field.isOptional()) + // object equality + && (field.getExtensionScope() == field.getMessageType())) { + generator.print(field.getMessageType().getFullName()); + } else { + generator.print(field.getFullName()); + } + generator.print("]"); + } else { + if (field.getType() == FieldDescriptor.Type.GROUP) { + // Groups must be serialized with their original capitalization. + generator.print(field.getMessageType().getName()); + } else { + generator.print(field.getName()); + } + } - printFieldValue(field, value, generator); + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (singleLineMode) { + generator.print(" { "); + } else { + generator.print(" {\n"); + generator.indent(); + } + } else { + generator.print(": "); + } - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - generator.outdent(); - generator.print("}"); + printFieldValue(field, value, generator); + + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (singleLineMode) { + generator.print("} "); + } else { + generator.outdent(); + generator.print("}\n"); + } + } else { + if (singleLineMode) { + generator.print(" "); + } else { + generator.print("\n"); + } + } } - generator.print("\n"); - } - private static void printFieldValue(final FieldDescriptor field, - final Object value, - final TextGenerator generator) - throws IOException { - switch (field.getType()) { - case INT32: - case INT64: - case SINT32: - case SINT64: - case SFIXED32: - case SFIXED64: - case FLOAT: - case DOUBLE: - case BOOL: - // Good old toString() does what we want for these types. - generator.print(value.toString()); - break; + private void printFieldValue(final FieldDescriptor field, + final Object value, + final TextGenerator generator) + throws IOException { + switch (field.getType()) { + case INT32: + case SINT32: + case SFIXED32: + generator.print(((Integer) value).toString()); + break; - case UINT32: - case FIXED32: - generator.print(unsignedToString((Integer) value)); - break; + case INT64: + case SINT64: + case SFIXED64: + generator.print(((Long) value).toString()); + break; - case UINT64: - case FIXED64: - generator.print(unsignedToString((Long) value)); - break; + case BOOL: + generator.print(((Boolean) value).toString()); + break; - case STRING: - generator.print("\""); - generator.print(escapeText((String) value)); - generator.print("\""); - break; + case FLOAT: + generator.print(((Float) value).toString()); + break; - case BYTES: - generator.print("\""); - generator.print(escapeBytes((ByteString) value)); - generator.print("\""); - break; + case DOUBLE: + generator.print(((Double) value).toString()); + break; - case ENUM: - generator.print(((EnumValueDescriptor) value).getName()); - break; + case UINT32: + case FIXED32: + generator.print(unsignedToString((Integer) value)); + break; - case MESSAGE: - case GROUP: - print((Message) value, generator); - break; - } - } + case UINT64: + case FIXED64: + generator.print(unsignedToString((Long) value)); + break; - private static void printUnknownFields(final UnknownFieldSet unknownFields, - final TextGenerator generator) - throws IOException { - for (final Map.Entry<Integer, UnknownFieldSet.Field> entry : - unknownFields.asMap().entrySet()) { - final UnknownFieldSet.Field field = entry.getValue(); + case STRING: + generator.print("\""); + generator.print(escapeText((String) value)); + generator.print("\""); + break; - for (final long value : field.getVarintList()) { - generator.print(entry.getKey().toString()); - generator.print(": "); - generator.print(unsignedToString(value)); - generator.print("\n"); + case BYTES: + generator.print("\""); + generator.print(escapeBytes((ByteString) value)); + generator.print("\""); + break; + + case ENUM: + generator.print(((EnumValueDescriptor) value).getName()); + break; + + case MESSAGE: + case GROUP: + print((Message) value, generator); + break; } - for (final int value : field.getFixed32List()) { - generator.print(entry.getKey().toString()); - generator.print(": "); - generator.print(String.format((Locale) null, "0x%08x", value)); - generator.print("\n"); + } + + private void printUnknownFields(final UnknownFieldSet unknownFields, + final TextGenerator generator) + throws IOException { + for (Map.Entry<Integer, UnknownFieldSet.Field> entry : + unknownFields.asMap().entrySet()) { + final int number = entry.getKey(); + final UnknownFieldSet.Field field = entry.getValue(); + printUnknownField(number, WireFormat.WIRETYPE_VARINT, + field.getVarintList(), generator); + printUnknownField(number, WireFormat.WIRETYPE_FIXED32, + field.getFixed32List(), generator); + printUnknownField(number, WireFormat.WIRETYPE_FIXED64, + field.getFixed64List(), generator); + printUnknownField(number, WireFormat.WIRETYPE_LENGTH_DELIMITED, + field.getLengthDelimitedList(), generator); + for (final UnknownFieldSet value : field.getGroupList()) { + generator.print(entry.getKey().toString()); + if (singleLineMode) { + generator.print(" { "); + } else { + generator.print(" {\n"); + generator.indent(); + } + printUnknownFields(value, generator); + if (singleLineMode) { + generator.print("} "); + } else { + generator.outdent(); + generator.print("}\n"); + } + } } - for (final long value : field.getFixed64List()) { - generator.print(entry.getKey().toString()); + } + + private void printUnknownField(final int number, + final int wireType, + final List<?> values, + final TextGenerator generator) + throws IOException { + for (final Object value : values) { + generator.print(String.valueOf(number)); generator.print(": "); - generator.print(String.format((Locale) null, "0x%016x", value)); - generator.print("\n"); - } - for (final ByteString value : field.getLengthDelimitedList()) { - generator.print(entry.getKey().toString()); - generator.print(": \""); - generator.print(escapeBytes(value)); - generator.print("\"\n"); - } - for (final UnknownFieldSet value : field.getGroupList()) { - generator.print(entry.getKey().toString()); - generator.print(" {\n"); - generator.indent(); - printUnknownFields(value, generator); - generator.outdent(); - generator.print("}\n"); + printUnknownFieldValue(wireType, value, generator); + generator.print(singleLineMode ? " " : "\n"); } } } @@ -312,9 +433,9 @@ public final class TextFormat { * An inner class for writing text to the output stream. */ private static final class TextGenerator { - private Appendable output; - private boolean atStartOfLine = true; + private final Appendable output; private final StringBuilder indent = new StringBuilder(); + private boolean atStartOfLine = true; private TextGenerator(final Appendable output) { this.output = output; @@ -670,10 +791,14 @@ public final class TextFormat { * Otherwise, throw a {@link ParseException}. */ public boolean consumeBoolean() throws ParseException { - if (currentToken.equals("true")) { + if (currentToken.equals("true") || + currentToken.equals("t") || + currentToken.equals("1")) { nextToken(); return true; - } else if (currentToken.equals("false")) { + } else if (currentToken.equals("false") || + currentToken.equals("f") || + currentToken.equals("0")) { nextToken(); return false; } else { @@ -1063,6 +1188,9 @@ public final class TextFormat { case '\'': builder.append("\\\'"); break; case '"' : builder.append("\\\""); break; default: + // Note: Bytes with the high-order bit set should be escaped. Since + // bytes are signed, such bytes will compare less than 0x20, hence + // the following line is correct. if (b >= 0x20) { builder.append((char) b); } else { @@ -1082,27 +1210,37 @@ public final class TextFormat { * {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with * "\x") are also recognized. */ - static ByteString unescapeBytes(final CharSequence input) + static ByteString unescapeBytes(final CharSequence charString) throws InvalidEscapeSequenceException { - final byte[] result = new byte[input.length()]; + // First convert the Java characater sequence to UTF-8 bytes. + ByteString input = ByteString.copyFromUtf8(charString.toString()); + // Then unescape certain byte sequences introduced by ASCII '\\'. The valid + // escapes can all be expressed with ASCII characters, so it is safe to + // operate on bytes here. + // + // Unescaping the input byte array will result in a byte sequence that's no + // longer than the input. That's because each escape sequence is between + // two and four bytes long and stands for a single byte. + final byte[] result = new byte[input.size()]; int pos = 0; - for (int i = 0; i < input.length(); i++) { - char c = input.charAt(i); + for (int i = 0; i < input.size(); i++) { + byte c = input.byteAt(i); if (c == '\\') { - if (i + 1 < input.length()) { + if (i + 1 < input.size()) { ++i; - c = input.charAt(i); + c = input.byteAt(i); if (isOctal(c)) { // Octal escape. int code = digitValue(c); - if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) { + if (i + 1 < input.size() && isOctal(input.byteAt(i + 1))) { ++i; - code = code * 8 + digitValue(input.charAt(i)); + code = code * 8 + digitValue(input.byteAt(i)); } - if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) { + if (i + 1 < input.size() && isOctal(input.byteAt(i + 1))) { ++i; - code = code * 8 + digitValue(input.charAt(i)); + code = code * 8 + digitValue(input.byteAt(i)); } + // TODO: Check that 0 <= code && code <= 0xFF. result[pos++] = (byte)code; } else { switch (c) { @@ -1120,31 +1258,31 @@ public final class TextFormat { case 'x': // hex escape int code = 0; - if (i + 1 < input.length() && isHex(input.charAt(i + 1))) { + if (i + 1 < input.size() && isHex(input.byteAt(i + 1))) { ++i; - code = digitValue(input.charAt(i)); + code = digitValue(input.byteAt(i)); } else { throw new InvalidEscapeSequenceException( - "Invalid escape sequence: '\\x' with no digits"); + "Invalid escape sequence: '\\x' with no digits"); } - if (i + 1 < input.length() && isHex(input.charAt(i + 1))) { + if (i + 1 < input.size() && isHex(input.byteAt(i + 1))) { ++i; - code = code * 16 + digitValue(input.charAt(i)); + code = code * 16 + digitValue(input.byteAt(i)); } result[pos++] = (byte)code; break; default: throw new InvalidEscapeSequenceException( - "Invalid escape sequence: '\\" + c + '\''); + "Invalid escape sequence: '\\" + (char)c + '\''); } } } else { throw new InvalidEscapeSequenceException( - "Invalid escape sequence: '\\' at end of string."); + "Invalid escape sequence: '\\' at end of string."); } } else { - result[pos++] = (byte)c; + result[pos++] = c; } } @@ -1182,12 +1320,12 @@ public final class TextFormat { } /** Is this an octal digit? */ - private static boolean isOctal(final char c) { + private static boolean isOctal(final byte c) { return '0' <= c && c <= '7'; } /** Is this a hex digit? */ - private static boolean isHex(final char c) { + private static boolean isHex(final byte c) { return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'); @@ -1198,7 +1336,7 @@ public final class TextFormat { * numeric value. This is like {@code Character.digit()} but we don't accept * non-ASCII digits. */ - private static int digitValue(final char c) { + private static int digitValue(final byte c) { if ('0' <= c && c <= '9') { return c - '0'; } else if ('a' <= c && c <= 'z') { diff --git a/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java b/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java new file mode 100644 index 00000000..ee8fe190 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java @@ -0,0 +1,146 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import java.util.AbstractList; +import java.util.RandomAccess; +import java.util.ListIterator; +import java.util.Iterator; + +/** + * An implementation of {@link LazyStringList} that wraps another + * {@link LazyStringList} such that it cannot be modified via the wrapper. + * + * @author jonp@google.com (Jon Perlow) + */ +public class UnmodifiableLazyStringList extends AbstractList<String> + implements LazyStringList, RandomAccess { + + private final LazyStringList list; + + public UnmodifiableLazyStringList(LazyStringList list) { + this.list = list; + } + + @Override + public String get(int index) { + return list.get(index); + } + + @Override + public int size() { + return list.size(); + } + + @Override + public ByteString getByteString(int index) { + return list.getByteString(index); + } + + @Override + public void add(ByteString element) { + throw new UnsupportedOperationException(); + } + + @Override + public ListIterator<String> listIterator(final int index) { + return new ListIterator<String>() { + ListIterator<String> iter = list.listIterator(index); + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + + @Override + public String next() { + return iter.next(); + } + + @Override + public boolean hasPrevious() { + return iter.hasPrevious(); + } + + @Override + public String previous() { + return iter.previous(); + } + + @Override + public int nextIndex() { + return iter.nextIndex(); + } + + @Override + public int previousIndex() { + return iter.previousIndex(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public void set(String o) { + throw new UnsupportedOperationException(); + } + + @Override + public void add(String o) { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public Iterator<String> iterator() { + return new Iterator<String>() { + Iterator<String> iter = list.iterator(); + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + + @Override + public String next() { + return iter.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } +} diff --git a/java/src/main/java/com/google/protobuf/WireFormat.java b/java/src/main/java/com/google/protobuf/WireFormat.java index c46f7b0a..a30f2a3c 100644 --- a/java/src/main/java/com/google/protobuf/WireFormat.java +++ b/java/src/main/java/com/google/protobuf/WireFormat.java @@ -45,12 +45,12 @@ public final class WireFormat { // Do not allow instantiation. private WireFormat() {} - static final int WIRETYPE_VARINT = 0; - static final int WIRETYPE_FIXED64 = 1; - static final int WIRETYPE_LENGTH_DELIMITED = 2; - static final int WIRETYPE_START_GROUP = 3; - static final int WIRETYPE_END_GROUP = 4; - static final int WIRETYPE_FIXED32 = 5; + public static final int WIRETYPE_VARINT = 0; + public static final int WIRETYPE_FIXED64 = 1; + public static final int WIRETYPE_LENGTH_DELIMITED = 2; + public static final int WIRETYPE_START_GROUP = 3; + public static final int WIRETYPE_END_GROUP = 4; + public static final int WIRETYPE_FIXED32 = 5; static final int TAG_TYPE_BITS = 3; static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1; diff --git a/java/src/test/java/com/google/protobuf/AbstractMessageTest.java b/java/src/test/java/com/google/protobuf/AbstractMessageTest.java index c44d6605..d53ce8d7 100644 --- a/java/src/test/java/com/google/protobuf/AbstractMessageTest.java +++ b/java/src/test/java/com/google/protobuf/AbstractMessageTest.java @@ -366,7 +366,7 @@ public class AbstractMessageTest extends TestCase { // ----------------------------------------------------------------- // Tests for equals and hashCode - + public void testEqualsAndHashCode() throws Exception { TestAllTypes a = TestUtil.getAllSet(); TestAllTypes b = TestAllTypes.newBuilder().build(); @@ -382,7 +382,7 @@ public class AbstractMessageTest extends TestCase { checkEqualsIsConsistent(d); checkEqualsIsConsistent(e); checkEqualsIsConsistent(f); - + checkNotEqual(a, b); checkNotEqual(a, c); checkNotEqual(a, d); @@ -413,19 +413,20 @@ public class AbstractMessageTest extends TestCase { checkEqualsIsConsistent(eUnknownFields); checkEqualsIsConsistent(fUnknownFields); - // Subseqent reconstitutions should be identical + // Subsequent reconstitutions should be identical UnittestProto.TestEmptyMessage eUnknownFields2 = UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray()); checkEqualsIsConsistent(eUnknownFields, eUnknownFields2); } - + + /** * Asserts that the given proto has symetric equals and hashCode methods. */ private void checkEqualsIsConsistent(Message message) { // Object should be equal to itself. assertEquals(message, message); - + // Object should be equal to a dynamic copy of itself. DynamicMessage dynamic = DynamicMessage.newBuilder(message).build(); checkEqualsIsConsistent(message, dynamic); @@ -442,7 +443,7 @@ public class AbstractMessageTest extends TestCase { /** * Asserts that the given protos are not equal and have different hash codes. - * + * * @warning It's valid for non-equal objects to have the same hash code, so * this test is stricter than it needs to be. However, this should happen * relatively rarely. diff --git a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java index 6acd3223..83f7f8da 100644 --- a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java +++ b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java @@ -325,6 +325,27 @@ public class CodedInputStreamTest extends TestCase { assertEquals(2, input.readRawByte()); } + /** + * Test that a bug in skipRawBytes() has been fixed: if the skip skips + * past the end of a buffer with a limit that has been set past the end of + * that buffer, this should not break things. + */ + public void testSkipRawBytesPastEndOfBufferWithLimit() throws Exception { + byte[] rawBytes = new byte[] { 1, 2, 3, 4, 5 }; + CodedInputStream input = CodedInputStream.newInstance( + new SmallBlockInputStream(rawBytes, 3)); + + int limit = input.pushLimit(4); + // In order to expose the bug we need to read at least one byte to prime the + // buffer inside the CodedInputStream. + assertEquals(1, input.readRawByte()); + // Skip to the end of the limit. + input.skipRawBytes(3); + assertTrue(input.isAtEnd()); + input.popLimit(limit); + assertEquals(5, input.readRawByte()); + } + public void testReadHugeBlob() throws Exception { // Allocate and initialize a 1MB blob. byte[] blob = new byte[1 << 20]; diff --git a/java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java b/java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java new file mode 100644 index 00000000..1f8bb445 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java @@ -0,0 +1,80 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestDeprecatedFields; + +import junit.framework.TestCase; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; +/** + * Test field deprecation + * + * @author birdo@google.com (Roberto Scaramuzzi) + */ +public class DeprecatedFieldTest extends TestCase { + private String[] deprecatedGetterNames = { + "hasDeprecatedInt32", + "getDeprecatedInt32"}; + + private String[] deprecatedBuilderGetterNames = { + "hasDeprecatedInt32", + "getDeprecatedInt32", + "clearDeprecatedInt32"}; + + private String[] deprecatedBuilderSetterNames = { + "setDeprecatedInt32"}; + + public void testDeprecatedField() throws Exception { + Class<?> deprecatedFields = TestDeprecatedFields.class; + Class<?> deprecatedFieldsBuilder = TestDeprecatedFields.Builder.class; + for (String name : deprecatedGetterNames) { + Method method = deprecatedFields.getMethod(name); + assertTrue("Method " + name + " should be deprecated", + isDeprecated(method)); + } + for (String name : deprecatedBuilderGetterNames) { + Method method = deprecatedFieldsBuilder.getMethod(name); + assertTrue("Method " + name + " should be deprecated", + isDeprecated(method)); + } + for (String name : deprecatedBuilderSetterNames) { + Method method = deprecatedFieldsBuilder.getMethod(name, int.class); + assertTrue("Method " + name + " should be deprecated", + isDeprecated(method)); + } + } + + private boolean isDeprecated(AnnotatedElement annotated) { + return annotated.isAnnotationPresent(Deprecated.class); + } +} diff --git a/java/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/src/test/java/com/google/protobuf/DescriptorsTest.java index c77af5d2..65d06e32 100644 --- a/java/src/test/java/com/google/protobuf/DescriptorsTest.java +++ b/java/src/test/java/com/google/protobuf/DescriptorsTest.java @@ -44,6 +44,7 @@ import com.google.protobuf.Descriptors.MethodDescriptor; import com.google.protobuf.test.UnittestImport; import com.google.protobuf.test.UnittestImport.ImportEnum; +import com.google.protobuf.test.UnittestImport.ImportMessage; import protobuf_unittest.UnittestProto; import protobuf_unittest.UnittestProto.ForeignEnum; import protobuf_unittest.UnittestProto.ForeignMessage; diff --git a/java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java b/java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java new file mode 100644 index 00000000..108a28e1 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java @@ -0,0 +1,48 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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; + +/** + * A prerun for a test suite that allows running the full protocol buffer + * tests in a mode that disables the optimization for not using + * {@link RepeatedFieldBuilder} and {@link SingleFieldBuilder} until they are + * requested. This allows us to run all the tests through both code paths + * and ensures that both code paths produce identical results. + * + * @author jonp@google.com (Jon Perlow) + */ +public class ForceFieldBuildersPreRun implements Runnable { + + @Override + public void run() { + GeneratedMessage.enableAlwaysUseFieldBuildersForTesting(); + } +} diff --git a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java index 73c71f31..3675e003 100644 --- a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java +++ b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java @@ -30,26 +30,43 @@ package com.google.protobuf; +import com.google.protobuf.UnittestLite.TestAllExtensionsLite; +import com.google.protobuf.test.UnittestImport; +import protobuf_unittest.EnumWithNoOuter; +import protobuf_unittest.MessageWithNoOuter; +import protobuf_unittest.MultipleFilesTestProto; +import protobuf_unittest.NestedExtension.MyNestedExtension; +import protobuf_unittest.NestedExtensionLite.MyNestedExtensionLite; +import protobuf_unittest.NonNestedExtension; +import protobuf_unittest.NonNestedExtension.MessageToBeExtended; +import protobuf_unittest.NonNestedExtension.MyNonNestedExtension; +import protobuf_unittest.NonNestedExtensionLite; +import protobuf_unittest.NonNestedExtensionLite.MessageLiteToBeExtended; +import protobuf_unittest.NonNestedExtensionLite.MyNonNestedExtensionLite; +import protobuf_unittest.ServiceWithNoOuter; import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize; import protobuf_unittest.UnittestOptimizeFor.TestOptionalOptimizedForSize; import protobuf_unittest.UnittestOptimizeFor.TestRequiredOptimizedForSize; import protobuf_unittest.UnittestProto; -import protobuf_unittest.UnittestProto.ForeignMessage; import protobuf_unittest.UnittestProto.ForeignEnum; -import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.ForeignMessage; +import protobuf_unittest.UnittestProto.ForeignMessageOrBuilder; import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder; 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; -import protobuf_unittest.ServiceWithNoOuter; -import com.google.protobuf.UnittestLite; -import com.google.protobuf.UnittestLite.TestAllExtensionsLite; import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * Unit test for generated messages and generated code. See also @@ -68,32 +85,111 @@ public class GeneratedMessageTest extends TestCase { TestAllTypes.newBuilder().getDefaultInstanceForType()); } - public void testAccessors() throws Exception { + public void testMessageOrBuilder() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TestUtil.setAllFields(builder); TestAllTypes message = builder.build(); TestUtil.assertAllFieldsSet(message); } - public void testDoubleBuildError() throws Exception { + public void testUsingBuilderMultipleTimes() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); - builder.build(); - try { - builder.build(); - fail("Should have thrown exception."); - } catch (IllegalStateException e) { - // Success. - } + // primitive field scalar and repeated + builder.setOptionalSfixed64(100); + builder.addRepeatedInt32(100); + // enum field scalar and repeated + builder.setOptionalImportEnum(UnittestImport.ImportEnum.IMPORT_BAR); + builder.addRepeatedImportEnum(UnittestImport.ImportEnum.IMPORT_BAR); + // proto field scalar and repeated + builder.setOptionalForeignMessage(ForeignMessage.newBuilder().setC(1)); + builder.addRepeatedForeignMessage(ForeignMessage.newBuilder().setC(1)); + + TestAllTypes value1 = builder.build(); + + assertEquals(100, value1.getOptionalSfixed64()); + assertEquals(100, value1.getRepeatedInt32(0)); + assertEquals(UnittestImport.ImportEnum.IMPORT_BAR, + value1.getOptionalImportEnum()); + assertEquals(UnittestImport.ImportEnum.IMPORT_BAR, + value1.getRepeatedImportEnum(0)); + assertEquals(1, value1.getOptionalForeignMessage().getC()); + assertEquals(1, value1.getRepeatedForeignMessage(0).getC()); + + // Make sure that builder didn't update previously created values + builder.setOptionalSfixed64(200); + builder.setRepeatedInt32(0, 200); + builder.setOptionalImportEnum(UnittestImport.ImportEnum.IMPORT_FOO); + builder.setRepeatedImportEnum(0, UnittestImport.ImportEnum.IMPORT_FOO); + builder.setOptionalForeignMessage(ForeignMessage.newBuilder().setC(2)); + builder.setRepeatedForeignMessage(0, ForeignMessage.newBuilder().setC(2)); + + TestAllTypes value2 = builder.build(); + + // Make sure value1 didn't change. + assertEquals(100, value1.getOptionalSfixed64()); + assertEquals(100, value1.getRepeatedInt32(0)); + assertEquals(UnittestImport.ImportEnum.IMPORT_BAR, + value1.getOptionalImportEnum()); + assertEquals(UnittestImport.ImportEnum.IMPORT_BAR, + value1.getRepeatedImportEnum(0)); + assertEquals(1, value1.getOptionalForeignMessage().getC()); + assertEquals(1, value1.getRepeatedForeignMessage(0).getC()); + + // Make sure value2 is correct + assertEquals(200, value2.getOptionalSfixed64()); + assertEquals(200, value2.getRepeatedInt32(0)); + assertEquals(UnittestImport.ImportEnum.IMPORT_FOO, + value2.getOptionalImportEnum()); + assertEquals(UnittestImport.ImportEnum.IMPORT_FOO, + value2.getRepeatedImportEnum(0)); + assertEquals(2, value2.getOptionalForeignMessage().getC()); + assertEquals(2, value2.getRepeatedForeignMessage(0).getC()); + } + + public void testProtosShareRepeatedArraysIfDidntChange() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + builder.addRepeatedInt32(100); + builder.addRepeatedImportEnum(UnittestImport.ImportEnum.IMPORT_BAR); + builder.addRepeatedForeignMessage(ForeignMessage.getDefaultInstance()); + + TestAllTypes value1 = builder.build(); + TestAllTypes value2 = value1.toBuilder().build(); + + assertSame(value1.getRepeatedInt32List(), value2.getRepeatedInt32List()); + assertSame(value1.getRepeatedImportEnumList(), + value2.getRepeatedImportEnumList()); + assertSame(value1.getRepeatedForeignMessageList(), + value2.getRepeatedForeignMessageList()); } - public void testClearAfterBuildError() throws Exception { + public void testRepeatedArraysAreImmutable() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); - builder.build(); - try { - builder.clear(); - fail("Should have thrown exception."); - } catch (IllegalStateException e) { - // Success. + builder.addRepeatedInt32(100); + builder.addRepeatedImportEnum(UnittestImport.ImportEnum.IMPORT_BAR); + builder.addRepeatedForeignMessage(ForeignMessage.getDefaultInstance()); + assertIsUnmodifiable(builder.getRepeatedInt32List()); + assertIsUnmodifiable(builder.getRepeatedImportEnumList()); + assertIsUnmodifiable(builder.getRepeatedForeignMessageList()); + assertIsUnmodifiable(builder.getRepeatedFloatList()); + + + TestAllTypes value = builder.build(); + assertIsUnmodifiable(value.getRepeatedInt32List()); + assertIsUnmodifiable(value.getRepeatedImportEnumList()); + assertIsUnmodifiable(value.getRepeatedForeignMessageList()); + assertIsUnmodifiable(value.getRepeatedFloatList()); + } + + private void assertIsUnmodifiable(List<?> list) { + if (list == Collections.emptyList()) { + // OKAY -- Need to check this b/c EmptyList allows you to call clear. + } else { + try { + list.clear(); + fail("List wasn't immutable"); + } catch (UnsupportedOperationException e) { + // good + } } } @@ -316,9 +412,19 @@ public class GeneratedMessageTest extends TestCase { assertTrue(Float.isNaN(message.getNanFloat())); } + public void testClear() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.assertClear(builder); + TestUtil.setAllFields(builder); + builder.clear(); + TestUtil.assertClear(builder); + } + public void testReflectionGetters() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TestUtil.setAllFields(builder); + reflectionTester.assertAllFieldsSetViaReflection(builder); + TestAllTypes message = builder.build(); reflectionTester.assertAllFieldsSetViaReflection(message); } @@ -326,6 +432,8 @@ public class GeneratedMessageTest extends TestCase { public void testReflectionSetters() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); reflectionTester.setAllFieldsViaReflection(builder); + TestUtil.assertAllFieldsSet(builder); + TestAllTypes message = builder.build(); TestUtil.assertAllFieldsSet(message); } @@ -339,6 +447,8 @@ public class GeneratedMessageTest extends TestCase { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); reflectionTester.setAllFieldsViaReflection(builder); reflectionTester.modifyRepeatedFieldsViaReflection(builder); + TestUtil.assertRepeatedFieldsModified(builder); + TestAllTypes message = builder.build(); TestUtil.assertRepeatedFieldsModified(message); } @@ -391,7 +501,7 @@ public class GeneratedMessageTest extends TestCase { new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(), TestUtil.getExtensionRegistry()); - public void testExtensionAccessors() throws Exception { + public void testExtensionMessageOrBuilder() throws Exception { TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); TestUtil.setAllExtensions(builder); TestAllExtensions message = builder.build(); @@ -414,6 +524,8 @@ public class GeneratedMessageTest extends TestCase { public void testExtensionReflectionGetters() throws Exception { TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); TestUtil.setAllExtensions(builder); + extensionsReflectionTester.assertAllFieldsSetViaReflection(builder); + TestAllExtensions message = builder.build(); extensionsReflectionTester.assertAllFieldsSetViaReflection(message); } @@ -421,6 +533,8 @@ public class GeneratedMessageTest extends TestCase { public void testExtensionReflectionSetters() throws Exception { TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); extensionsReflectionTester.setAllFieldsViaReflection(builder); + TestUtil.assertAllExtensionsSet(builder); + TestAllExtensions message = builder.build(); TestUtil.assertAllExtensionsSet(message); } @@ -434,6 +548,8 @@ public class GeneratedMessageTest extends TestCase { TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); extensionsReflectionTester.setAllFieldsViaReflection(builder); extensionsReflectionTester.modifyRepeatedFieldsViaReflection(builder); + TestUtil.assertRepeatedExtensionsModified(builder); + TestAllExtensions message = builder.build(); TestUtil.assertRepeatedExtensionsModified(message); } @@ -491,9 +607,11 @@ public class GeneratedMessageTest extends TestCase { // lite fields directly since they are implemented exactly the same as // regular fields. - public void testLiteExtensionAccessors() throws Exception { + public void testLiteExtensionMessageOrBuilder() throws Exception { TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder(); TestUtil.setAllExtensions(builder); + TestUtil.assertAllExtensionsSet(builder); + TestAllExtensionsLite message = builder.build(); TestUtil.assertAllExtensionsSet(message); } @@ -502,6 +620,8 @@ public class GeneratedMessageTest extends TestCase { TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder(); TestUtil.setAllExtensions(builder); TestUtil.modifyRepeatedExtensions(builder); + TestUtil.assertRepeatedExtensionsModified(builder); + TestAllExtensionsLite message = builder.build(); TestUtil.assertRepeatedExtensionsModified(message); } @@ -609,6 +729,7 @@ public class GeneratedMessageTest extends TestCase { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TestUtil.setAllFields(builder); TestAllTypes message = builder.build(); + TestUtil.assertAllFieldsSet(message); TestUtil.assertAllFieldsSet(message.toBuilder().build()); } @@ -646,4 +767,207 @@ public class GeneratedMessageTest extends TestCase { assertTrue(message.getA() != null); assertTrue(message.getA() == message); } + + public void testSerialize() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestAllTypes expected = builder.build(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(expected); + out.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + TestAllTypes actual = (TestAllTypes) in.readObject(); + assertEquals(expected, actual); + } + + public void testSerializePartial() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestAllTypes expected = builder.buildPartial(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(expected); + out.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + TestAllTypes actual = (TestAllTypes) in.readObject(); + assertEquals(expected, actual); + } + + public void testEnumValues() { + assertEquals( + TestAllTypes.NestedEnum.BAR.getNumber(), + TestAllTypes.NestedEnum.BAR_VALUE); + assertEquals( + TestAllTypes.NestedEnum.BAZ.getNumber(), + TestAllTypes.NestedEnum.BAZ_VALUE); + assertEquals( + TestAllTypes.NestedEnum.FOO.getNumber(), + TestAllTypes.NestedEnum.FOO_VALUE); + } + + public void testNonNestedExtensionInitialization() { + assertTrue(NonNestedExtension.nonNestedExtension + .getMessageDefaultInstance() instanceof MyNonNestedExtension); + assertEquals("nonNestedExtension", + NonNestedExtension.nonNestedExtension.getDescriptor().getName()); + } + + public void testNestedExtensionInitialization() { + assertTrue(MyNestedExtension.recursiveExtension.getMessageDefaultInstance() + instanceof MessageToBeExtended); + assertEquals("recursiveExtension", + MyNestedExtension.recursiveExtension.getDescriptor().getName()); + } + + public void testNonNestedExtensionLiteInitialization() { + assertTrue(NonNestedExtensionLite.nonNestedExtensionLite + .getMessageDefaultInstance() instanceof MyNonNestedExtensionLite); + } + + public void testNestedExtensionLiteInitialization() { + assertTrue(MyNestedExtensionLite.recursiveExtensionLite + .getMessageDefaultInstance() instanceof MessageLiteToBeExtended); + } + + public void testInvalidations() throws Exception { + GeneratedMessage.enableAlwaysUseFieldBuildersForTesting(); + TestAllTypes.NestedMessage nestedMessage1 = + TestAllTypes.NestedMessage.newBuilder().build(); + TestAllTypes.NestedMessage nestedMessage2 = + TestAllTypes.NestedMessage.newBuilder().build(); + + // Set all three flavors (enum, primitive, message and singular/repeated) + // and verify no invalidations fired + TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); + + TestAllTypes.Builder builder = (TestAllTypes.Builder) + ((GeneratedMessage) TestAllTypes.getDefaultInstance()). + newBuilderForType(mockParent); + builder.setOptionalInt32(1); + builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR); + builder.setOptionalNestedMessage(nestedMessage1); + builder.addRepeatedInt32(1); + builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.BAR); + builder.addRepeatedNestedMessage(nestedMessage1); + assertEquals(0, mockParent.getInvalidationCount()); + + // Now tell it we want changes and make sure it's only fired once + // And do this for each flavor + + // primitive single + builder.buildPartial(); + builder.setOptionalInt32(2); + builder.setOptionalInt32(3); + assertEquals(1, mockParent.getInvalidationCount()); + + // enum single + builder.buildPartial(); + builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAZ); + builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR); + assertEquals(2, mockParent.getInvalidationCount()); + + // message single + builder.buildPartial(); + builder.setOptionalNestedMessage(nestedMessage2); + builder.setOptionalNestedMessage(nestedMessage1); + assertEquals(3, mockParent.getInvalidationCount()); + + // primitive repated + builder.buildPartial(); + builder.addRepeatedInt32(2); + builder.addRepeatedInt32(3); + assertEquals(4, mockParent.getInvalidationCount()); + + // enum repeated + builder.buildPartial(); + builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.BAZ); + builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.BAZ); + assertEquals(5, mockParent.getInvalidationCount()); + + // message repeated + builder.buildPartial(); + builder.addRepeatedNestedMessage(nestedMessage2); + builder.addRepeatedNestedMessage(nestedMessage1); + assertEquals(6, mockParent.getInvalidationCount()); + + } + + public void testInvalidations_Extensions() throws Exception { + TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); + + TestAllExtensions.Builder builder = (TestAllExtensions.Builder) + ((GeneratedMessage) TestAllExtensions.getDefaultInstance()). + newBuilderForType(mockParent); + + builder.addExtension(UnittestProto.repeatedInt32Extension, 1); + builder.setExtension(UnittestProto.repeatedInt32Extension, 0, 2); + builder.clearExtension(UnittestProto.repeatedInt32Extension); + assertEquals(0, mockParent.getInvalidationCount()); + + // Now tell it we want changes and make sure it's only fired once + builder.buildPartial(); + builder.addExtension(UnittestProto.repeatedInt32Extension, 2); + builder.addExtension(UnittestProto.repeatedInt32Extension, 3); + assertEquals(1, mockParent.getInvalidationCount()); + + builder.buildPartial(); + builder.setExtension(UnittestProto.repeatedInt32Extension, 0, 4); + builder.setExtension(UnittestProto.repeatedInt32Extension, 1, 5); + assertEquals(2, mockParent.getInvalidationCount()); + + builder.buildPartial(); + builder.clearExtension(UnittestProto.repeatedInt32Extension); + builder.clearExtension(UnittestProto.repeatedInt32Extension); + assertEquals(3, mockParent.getInvalidationCount()); + } + + public void testBaseMessageOrBuilder() { + // Mostly just makes sure the base interface exists and has some methods. + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestAllTypes message = builder.buildPartial(); + TestAllTypesOrBuilder builderAsInterface = (TestAllTypesOrBuilder) builder; + TestAllTypesOrBuilder messageAsInterface = (TestAllTypesOrBuilder) message; + + assertEquals( + messageAsInterface.getDefaultBool(), + messageAsInterface.getDefaultBool()); + assertEquals( + messageAsInterface.getOptionalDouble(), + messageAsInterface.getOptionalDouble()); + } + + public void testMessageOrBuilderGetters() { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + + // single fields + assertSame(ForeignMessage.getDefaultInstance(), + builder.getOptionalForeignMessageOrBuilder()); + ForeignMessage.Builder subBuilder = + builder.getOptionalForeignMessageBuilder(); + assertSame(subBuilder, builder.getOptionalForeignMessageOrBuilder()); + + // repeated fields + ForeignMessage m0 = ForeignMessage.newBuilder().buildPartial(); + ForeignMessage m1 = ForeignMessage.newBuilder().buildPartial(); + ForeignMessage m2 = ForeignMessage.newBuilder().buildPartial(); + builder.addRepeatedForeignMessage(m0); + builder.addRepeatedForeignMessage(m1); + builder.addRepeatedForeignMessage(m2); + assertSame(m0, builder.getRepeatedForeignMessageOrBuilder(0)); + assertSame(m1, builder.getRepeatedForeignMessageOrBuilder(1)); + assertSame(m2, builder.getRepeatedForeignMessageOrBuilder(2)); + ForeignMessage.Builder b0 = builder.getRepeatedForeignMessageBuilder(0); + ForeignMessage.Builder b1 = builder.getRepeatedForeignMessageBuilder(1); + assertSame(b0, builder.getRepeatedForeignMessageOrBuilder(0)); + assertSame(b1, builder.getRepeatedForeignMessageOrBuilder(1)); + assertSame(m2, builder.getRepeatedForeignMessageOrBuilder(2)); + + List<? extends ForeignMessageOrBuilder> messageOrBuilderList = + builder.getRepeatedForeignMessageOrBuilderList(); + assertSame(b0, messageOrBuilderList.get(0)); + assertSame(b1, messageOrBuilderList.get(1)); + assertSame(m2, messageOrBuilderList.get(2)); + } } diff --git a/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java b/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java new file mode 100644 index 00000000..4dcdc74d --- /dev/null +++ b/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java @@ -0,0 +1,118 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import junit.framework.TestCase; + +/** + * Tests for {@link LazyStringArrayList}. + * + * @author jonp@google.com (Jon Perlow) + */ +public class LazyStringArrayListTest extends TestCase { + + private static String STRING_A = "A"; + private static String STRING_B = "B"; + private static String STRING_C = "C"; + + private static ByteString BYTE_STRING_A = ByteString.copyFromUtf8("A"); + private static ByteString BYTE_STRING_B = ByteString.copyFromUtf8("B"); + private static ByteString BYTE_STRING_C = ByteString.copyFromUtf8("C"); + + public void testJustStrings() { + LazyStringArrayList list = new LazyStringArrayList(); + list.add(STRING_A); + list.add(STRING_B); + list.add(STRING_C); + + assertEquals(3, list.size()); + assertSame(STRING_A, list.get(0)); + assertSame(STRING_B, list.get(1)); + assertSame(STRING_C, list.get(2)); + + list.set(1, STRING_C); + assertSame(STRING_C, list.get(1)); + + list.remove(1); + assertSame(STRING_A, list.get(0)); + assertSame(STRING_C, list.get(1)); + } + + public void testJustByteString() { + LazyStringArrayList list = new LazyStringArrayList(); + list.add(BYTE_STRING_A); + list.add(BYTE_STRING_B); + list.add(BYTE_STRING_C); + + assertEquals(3, list.size()); + assertSame(BYTE_STRING_A, list.getByteString(0)); + assertSame(BYTE_STRING_B, list.getByteString(1)); + assertSame(BYTE_STRING_C, list.getByteString(2)); + + list.remove(1); + assertSame(BYTE_STRING_A, list.getByteString(0)); + assertSame(BYTE_STRING_C, list.getByteString(1)); + } + + public void testConversionBackAndForth() { + LazyStringArrayList list = new LazyStringArrayList(); + list.add(STRING_A); + list.add(BYTE_STRING_B); + list.add(BYTE_STRING_C); + + // String a should be the same because it was originally a string + assertSame(STRING_A, list.get(0)); + + // String b and c should be different because the string has to be computed + // from the ByteString + String bPrime = list.get(1); + assertNotSame(STRING_B, bPrime); + assertEquals(STRING_B, bPrime); + String cPrime = list.get(2); + assertNotSame(STRING_C, cPrime); + assertEquals(STRING_C, cPrime); + + // String c and c should stay the same once cached. + assertSame(bPrime, list.get(1)); + assertSame(cPrime, list.get(2)); + + // ByteString needs to be computed from string for both a and b + ByteString aPrimeByteString = list.getByteString(0); + assertEquals(BYTE_STRING_A, aPrimeByteString); + ByteString bPrimeByteString = list.getByteString(1); + assertNotSame(BYTE_STRING_B, bPrimeByteString); + assertEquals(BYTE_STRING_B, list.getByteString(1)); + + // Once cached, ByteString should stay cached. + assertSame(aPrimeByteString, list.getByteString(0)); + assertSame(bPrimeByteString, list.getByteString(1)); + } +} diff --git a/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java b/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java new file mode 100644 index 00000000..e6870b51 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java @@ -0,0 +1,115 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + + +import protobuf_unittest.UnittestProto; + +import junit.framework.TestCase; + +import java.io.IOException; + +/** + * Tests to make sure the lazy conversion of UTF8-encoded byte arrays to + * strings works correctly. + * + * @author jonp@google.com (Jon Perlow) + */ +public class LazyStringEndToEndTest extends TestCase { + + private static ByteString TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8 = + ByteString.copyFrom(new byte[] { + 114, 4, -1, 0, -1, 0, -30, 2, 4, -1, + 0, -1, 0, -30, 2, 4, -1, 0, -1, 0, }); + + /** + * Tests that an invalid UTF8 string will roundtrip through a parse + * and serialization. + */ + public void testParseAndSerialize() throws InvalidProtocolBufferException { + UnittestProto.TestAllTypes tV2 = UnittestProto.TestAllTypes.parseFrom( + TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8); + ByteString bytes = tV2.toByteString(); + assertEquals(TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8, bytes); + + tV2.getOptionalString(); + bytes = tV2.toByteString(); + assertEquals(TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8, bytes); + } + + public void testParseAndWrite() throws IOException { + UnittestProto.TestAllTypes tV2 = UnittestProto.TestAllTypes.parseFrom( + TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8); + byte[] sink = new byte[TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8.size()]; + CodedOutputStream outputStream = CodedOutputStream.newInstance(sink); + tV2.writeTo(outputStream); + outputStream.flush(); + assertEquals( + TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8, + ByteString.copyFrom(sink)); + } + + public void testCaching() { + String a = "a"; + String b = "b"; + String c = "c"; + UnittestProto.TestAllTypes proto = UnittestProto.TestAllTypes.newBuilder() + .setOptionalString(a) + .addRepeatedString(b) + .addRepeatedString(c) + .build(); + + // String should be the one we passed it. + assertSame(a, proto.getOptionalString()); + assertSame(b, proto.getRepeatedString(0)); + assertSame(c, proto.getRepeatedString(1)); + + + // There's no way to directly observe that the ByteString is cached + // correctly on serialization, but we can observe that it had to recompute + // the string after serialization. + proto.toByteString(); + String aPrime = proto.getOptionalString(); + assertNotSame(a, aPrime); + assertEquals(a, aPrime); + String bPrime = proto.getRepeatedString(0); + assertNotSame(b, bPrime); + assertEquals(b, bPrime); + String cPrime = proto.getRepeatedString(1); + assertNotSame(c, cPrime); + assertEquals(c, cPrime); + + // And now the string should stay cached. + assertSame(aPrime, proto.getOptionalString()); + assertSame(bPrime, proto.getRepeatedString(0)); + assertSame(cPrime, proto.getRepeatedString(1)); + } +} diff --git a/java/src/test/java/com/google/protobuf/LiteTest.java b/java/src/test/java/com/google/protobuf/LiteTest.java index 728bad9d..4e1003d9 100644 --- a/java/src/test/java/com/google/protobuf/LiteTest.java +++ b/java/src/test/java/com/google/protobuf/LiteTest.java @@ -37,6 +37,11 @@ import com.google.protobuf.UnittestLite.TestNestedExtensionLite; import junit.framework.TestCase; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + /** * Test lite runtime. * @@ -113,4 +118,28 @@ public class LiteTest extends TestCase { assertEquals(7, message2.getExtension( UnittestLite.optionalNestedMessageExtensionLite).getBb()); } + + public void testSerialize() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + TestAllTypesLite expected = + TestAllTypesLite.newBuilder() + .setOptionalInt32(123) + .addRepeatedString("hello") + .setOptionalNestedMessage( + TestAllTypesLite.NestedMessage.newBuilder().setBb(7)) + .build(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(expected); + out.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + TestAllTypesLite actual = (TestAllTypesLite) in.readObject(); + assertEquals(expected.getOptionalInt32(), actual.getOptionalInt32()); + assertEquals(expected.getRepeatedStringCount(), + actual.getRepeatedStringCount()); + assertEquals(expected.getRepeatedString(0), + actual.getRepeatedString(0)); + assertEquals(expected.getOptionalNestedMessage().getBb(), + actual.getOptionalNestedMessage().getBb()); + } } diff --git a/java/src/test/java/com/google/protobuf/NestedBuildersTest.java b/java/src/test/java/com/google/protobuf/NestedBuildersTest.java new file mode 100644 index 00000000..f5375801 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/NestedBuildersTest.java @@ -0,0 +1,185 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import protobuf_unittest.Vehicle; +import protobuf_unittest.Wheel; + +import junit.framework.TestCase; + +import java.util.List; +import java.util.ArrayList; + +/** + * Test cases that exercise end-to-end use cases involving + * {@link SingleFieldBuilder} and {@link RepeatedFieldBuilder}. + * + * @author jonp@google.com (Jon Perlow) + */ +public class NestedBuildersTest extends TestCase { + + public void testMessagesAndBuilders() { + Vehicle.Builder vehicleBuilder = Vehicle.newBuilder(); + vehicleBuilder.addWheelBuilder() + .setRadius(4) + .setWidth(1); + vehicleBuilder.addWheelBuilder() + .setRadius(4) + .setWidth(2); + vehicleBuilder.addWheelBuilder() + .setRadius(4) + .setWidth(3); + vehicleBuilder.addWheelBuilder() + .setRadius(4) + .setWidth(4); + vehicleBuilder.getEngineBuilder() + .setLiters(10); + + Vehicle vehicle = vehicleBuilder.build(); + assertEquals(4, vehicle.getWheelCount()); + for (int i = 0; i < 4; i++) { + Wheel wheel = vehicle.getWheel(i); + assertEquals(4, wheel.getRadius()); + assertEquals(i + 1, wheel.getWidth()); + } + assertEquals(10, vehicle.getEngine().getLiters()); + + for (int i = 0; i < 4; i++) { + vehicleBuilder.getWheelBuilder(i) + .setRadius(5) + .setWidth(i + 10); + } + vehicleBuilder.getEngineBuilder().setLiters(20); + + vehicle = vehicleBuilder.build(); + for (int i = 0; i < 4; i++) { + Wheel wheel = vehicle.getWheel(i); + assertEquals(5, wheel.getRadius()); + assertEquals(i + 10, wheel.getWidth()); + } + assertEquals(20, vehicle.getEngine().getLiters()); + assertTrue(vehicle.hasEngine()); + } + + public void testMessagesAreCached() { + Vehicle.Builder vehicleBuilder = Vehicle.newBuilder(); + vehicleBuilder.addWheelBuilder() + .setRadius(1) + .setWidth(2); + vehicleBuilder.addWheelBuilder() + .setRadius(3) + .setWidth(4); + vehicleBuilder.addWheelBuilder() + .setRadius(5) + .setWidth(6); + vehicleBuilder.addWheelBuilder() + .setRadius(7) + .setWidth(8); + + // Make sure messages are cached. + List<Wheel> wheels = new ArrayList<Wheel>(vehicleBuilder.getWheelList()); + for (int i = 0; i < wheels.size(); i++) { + assertSame(wheels.get(i), vehicleBuilder.getWheel(i)); + } + + // Now get builders and check they didn't change. + for (int i = 0; i < wheels.size(); i++) { + vehicleBuilder.getWheel(i); + } + for (int i = 0; i < wheels.size(); i++) { + assertSame(wheels.get(i), vehicleBuilder.getWheel(i)); + } + + // Change just one + vehicleBuilder.getWheelBuilder(3) + .setRadius(20).setWidth(20); + + // Now get wheels and check that only that one changed + for (int i = 0; i < wheels.size(); i++) { + if (i < 3) { + assertSame(wheels.get(i), vehicleBuilder.getWheel(i)); + } else { + assertNotSame(wheels.get(i), vehicleBuilder.getWheel(i)); + } + } + } + + public void testRemove_WithNestedBuilders() { + Vehicle.Builder vehicleBuilder = Vehicle.newBuilder(); + vehicleBuilder.addWheelBuilder() + .setRadius(1) + .setWidth(1); + vehicleBuilder.addWheelBuilder() + .setRadius(2) + .setWidth(2); + vehicleBuilder.removeWheel(0); + + assertEquals(1, vehicleBuilder.getWheelCount()); + assertEquals(2, vehicleBuilder.getWheel(0).getRadius()); + } + + public void testRemove_WithNestedMessages() { + Vehicle.Builder vehicleBuilder = Vehicle.newBuilder(); + vehicleBuilder.addWheel(Wheel.newBuilder() + .setRadius(1) + .setWidth(1)); + vehicleBuilder.addWheel(Wheel.newBuilder() + .setRadius(2) + .setWidth(2)); + vehicleBuilder.removeWheel(0); + + assertEquals(1, vehicleBuilder.getWheelCount()); + assertEquals(2, vehicleBuilder.getWheel(0).getRadius()); + } + + public void testMerge() { + Vehicle vehicle1 = Vehicle.newBuilder() + .addWheel(Wheel.newBuilder().setRadius(1).build()) + .addWheel(Wheel.newBuilder().setRadius(2).build()) + .build(); + + Vehicle vehicle2 = Vehicle.newBuilder() + .mergeFrom(vehicle1) + .build(); + // List should be the same -- no allocation + assertSame(vehicle1.getWheelList(), vehicle2.getWheelList()); + + Vehicle vehicle3 = vehicle1.toBuilder().build(); + assertSame(vehicle1.getWheelList(), vehicle3.getWheelList()); + } + + public void testGettingBuilderMarksFieldAsHaving() { + Vehicle.Builder vehicleBuilder = Vehicle.newBuilder(); + vehicleBuilder.getEngineBuilder(); + Vehicle vehicle = vehicleBuilder.buildPartial(); + assertTrue(vehicle.hasEngine()); + } +} diff --git a/java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java b/java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java new file mode 100644 index 00000000..cdcdcb25 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java @@ -0,0 +1,190 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.List; + +/** + * Tests for {@link RepeatedFieldBuilder}. This tests basic functionality. + * More extensive testing is provided via other tests that exercise the + * builder. + * + * @author jonp@google.com (Jon Perlow) + */ +public class RepeatedFieldBuilderTest extends TestCase { + + public void testBasicUse() { + TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); + RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent); + builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build()); + builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build()); + assertEquals(0, builder.getMessage(0).getOptionalInt32()); + assertEquals(1, builder.getMessage(1).getOptionalInt32()); + + List<TestAllTypes> list = builder.build(); + assertEquals(2, list.size()); + assertEquals(0, list.get(0).getOptionalInt32()); + assertEquals(1, list.get(1).getOptionalInt32()); + assertIsUnmodifiable(list); + + // Make sure it doesn't change. + List<TestAllTypes> list2 = builder.build(); + assertSame(list, list2); + assertEquals(0, mockParent.getInvalidationCount()); + } + + public void testGoingBackAndForth() { + TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); + RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent); + builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build()); + builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build()); + assertEquals(0, builder.getMessage(0).getOptionalInt32()); + assertEquals(1, builder.getMessage(1).getOptionalInt32()); + + // Convert to list + List<TestAllTypes> list = builder.build(); + assertEquals(2, list.size()); + assertEquals(0, list.get(0).getOptionalInt32()); + assertEquals(1, list.get(1).getOptionalInt32()); + assertIsUnmodifiable(list); + + // Update 0th item + assertEquals(0, mockParent.getInvalidationCount()); + builder.getBuilder(0).setOptionalString("foo"); + assertEquals(1, mockParent.getInvalidationCount()); + list = builder.build(); + assertEquals(2, list.size()); + assertEquals(0, list.get(0).getOptionalInt32()); + assertEquals("foo", list.get(0).getOptionalString()); + assertEquals(1, list.get(1).getOptionalInt32()); + assertIsUnmodifiable(list); + assertEquals(1, mockParent.getInvalidationCount()); + } + + public void testVariousMethods() { + TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); + RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent); + builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build()); + builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(2).build()); + builder.addBuilder(0, TestAllTypes.getDefaultInstance()) + .setOptionalInt32(0); + builder.addBuilder(TestAllTypes.getDefaultInstance()).setOptionalInt32(3); + + assertEquals(0, builder.getMessage(0).getOptionalInt32()); + assertEquals(1, builder.getMessage(1).getOptionalInt32()); + assertEquals(2, builder.getMessage(2).getOptionalInt32()); + assertEquals(3, builder.getMessage(3).getOptionalInt32()); + + assertEquals(0, mockParent.getInvalidationCount()); + List<TestAllTypes> messages = builder.build(); + assertEquals(4, messages.size()); + assertSame(messages, builder.build()); // expect same list + + // Remove a message. + builder.remove(2); + assertEquals(1, mockParent.getInvalidationCount()); + assertEquals(3, builder.getCount()); + assertEquals(0, builder.getMessage(0).getOptionalInt32()); + assertEquals(1, builder.getMessage(1).getOptionalInt32()); + assertEquals(3, builder.getMessage(2).getOptionalInt32()); + + // Remove a builder. + builder.remove(0); + assertEquals(1, mockParent.getInvalidationCount()); + assertEquals(2, builder.getCount()); + assertEquals(1, builder.getMessage(0).getOptionalInt32()); + assertEquals(3, builder.getMessage(1).getOptionalInt32()); + + // Test clear. + builder.clear(); + assertEquals(1, mockParent.getInvalidationCount()); + assertEquals(0, builder.getCount()); + assertTrue(builder.isEmpty()); + } + + public void testLists() { + TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); + RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent); + builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build()); + builder.addMessage(0, + TestAllTypes.newBuilder().setOptionalInt32(0).build()); + assertEquals(0, builder.getMessage(0).getOptionalInt32()); + assertEquals(1, builder.getMessage(1).getOptionalInt32()); + + // Use list of builders. + List<TestAllTypes.Builder> builders = builder.getBuilderList(); + assertEquals(0, builders.get(0).getOptionalInt32()); + assertEquals(1, builders.get(1).getOptionalInt32()); + builders.get(0).setOptionalInt32(10); + builders.get(1).setOptionalInt32(11); + + // Use list of protos + List<TestAllTypes> protos = builder.getMessageList(); + assertEquals(10, protos.get(0).getOptionalInt32()); + assertEquals(11, protos.get(1).getOptionalInt32()); + + // Add an item to the builders and verify it's updated in both + builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(12).build()); + assertEquals(3, builders.size()); + assertEquals(3, protos.size()); + } + + private void assertIsUnmodifiable(List<?> list) { + if (list == Collections.emptyList()) { + // OKAY -- Need to check this b/c EmptyList allows you to call clear. + } else { + try { + list.clear(); + fail("List wasn't immutable"); + } catch (UnsupportedOperationException e) { + // good + } + } + } + + private RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder> + newRepeatedFieldBuilder(GeneratedMessage.BuilderParent parent) { + return new RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder>(Collections.<TestAllTypes>emptyList(), false, + parent, false); + } +} diff --git a/java/src/test/java/com/google/protobuf/ServiceTest.java b/java/src/test/java/com/google/protobuf/ServiceTest.java index 802eb0e0..4be84f5b 100644 --- a/java/src/test/java/com/google/protobuf/ServiceTest.java +++ b/java/src/test/java/com/google/protobuf/ServiceTest.java @@ -244,6 +244,14 @@ public class ServiceTest extends TestCase { // make assumptions, so I'm just going to accept any character as the // separator. assertTrue(fullName.startsWith(outerName)); + + if (!Service.class.isAssignableFrom(innerClass) && + !Message.class.isAssignableFrom(innerClass) && + !ProtocolMessageEnum.class.isAssignableFrom(innerClass)) { + // Ignore any classes not generated by the base code generator. + continue; + } + innerClassNames.add(fullName.substring(outerName.length() + 1)); } diff --git a/java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java b/java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java new file mode 100644 index 00000000..5c2f7e75 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java @@ -0,0 +1,155 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder; + +import junit.framework.TestCase; + +/** + * Tests for {@link SingleFieldBuilder}. This tests basic functionality. + * More extensive testing is provided via other tests that exercise the + * builder. + * + * @author jonp@google.com (Jon Perlow) + */ +public class SingleFieldBuilderTest extends TestCase { + + public void testBasicUseAndInvalidations() { + TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); + SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder> builder = + new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder>( + TestAllTypes.getDefaultInstance(), + mockParent, + false); + assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage()); + assertEquals(TestAllTypes.getDefaultInstance(), + builder.getBuilder().buildPartial()); + assertEquals(0, mockParent.getInvalidationCount()); + + builder.getBuilder().setOptionalInt32(10); + assertEquals(0, mockParent.getInvalidationCount()); + TestAllTypes message = builder.build(); + assertEquals(10, message.getOptionalInt32()); + + // Test that we receive invalidations now that build has been called. + assertEquals(0, mockParent.getInvalidationCount()); + builder.getBuilder().setOptionalInt32(20); + assertEquals(1, mockParent.getInvalidationCount()); + + // Test that we don't keep getting invalidations on every change + builder.getBuilder().setOptionalInt32(30); + assertEquals(1, mockParent.getInvalidationCount()); + + } + + public void testSetMessage() { + TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); + SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder> builder = + new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder>( + TestAllTypes.getDefaultInstance(), + mockParent, + false); + builder.setMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build()); + assertEquals(0, builder.getMessage().getOptionalInt32()); + + // Update message using the builder + builder.getBuilder().setOptionalInt32(1); + assertEquals(0, mockParent.getInvalidationCount()); + assertEquals(1, builder.getBuilder().getOptionalInt32()); + assertEquals(1, builder.getMessage().getOptionalInt32()); + builder.build(); + builder.getBuilder().setOptionalInt32(2); + assertEquals(2, builder.getBuilder().getOptionalInt32()); + assertEquals(2, builder.getMessage().getOptionalInt32()); + + // Make sure message stays cached + assertSame(builder.getMessage(), builder.getMessage()); + } + + public void testClear() { + TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); + SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder> builder = + new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder>( + TestAllTypes.getDefaultInstance(), + mockParent, + false); + builder.setMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build()); + assertNotSame(TestAllTypes.getDefaultInstance(), builder.getMessage()); + builder.clear(); + assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage()); + + builder.getBuilder().setOptionalInt32(1); + assertNotSame(TestAllTypes.getDefaultInstance(), builder.getMessage()); + builder.clear(); + assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage()); + } + + public void testMerge() { + TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); + SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder> builder = + new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder, + TestAllTypesOrBuilder>( + TestAllTypes.getDefaultInstance(), + mockParent, + false); + + // Merge into default field. + builder.mergeFrom(TestAllTypes.getDefaultInstance()); + assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage()); + + // Merge into non-default field on existing builder. + builder.getBuilder().setOptionalInt32(2); + builder.mergeFrom(TestAllTypes.newBuilder() + .setOptionalDouble(4.0) + .buildPartial()); + assertEquals(2, builder.getMessage().getOptionalInt32()); + assertEquals(4.0, builder.getMessage().getOptionalDouble()); + + // Merge into non-default field on existing message + builder.setMessage(TestAllTypes.newBuilder() + .setOptionalInt32(10) + .buildPartial()); + builder.mergeFrom(TestAllTypes.newBuilder() + .setOptionalDouble(5.0) + .buildPartial()); + assertEquals(10, builder.getMessage().getOptionalInt32()); + assertEquals(5.0, builder.getMessage().getOptionalDouble()); + } +} diff --git a/java/src/test/java/com/google/protobuf/SmallSortedMapTest.java b/java/src/test/java/com/google/protobuf/SmallSortedMapTest.java new file mode 100644 index 00000000..922115fc --- /dev/null +++ b/java/src/test/java/com/google/protobuf/SmallSortedMapTest.java @@ -0,0 +1,378 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import junit.framework.TestCase; + +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * @author darick@google.com Darick Tong + */ +public class SmallSortedMapTest extends TestCase { + + public void testPutAndGetArrayEntriesOnly() { + runPutAndGetTest(3); + } + + public void testPutAndGetOverflowEntries() { + runPutAndGetTest(6); + } + + private void runPutAndGetTest(int numElements) { + // Test with even and odd arraySize + SmallSortedMap<Integer, Integer> map1 = + SmallSortedMap.newInstanceForTest(3); + SmallSortedMap<Integer, Integer> map2 = + SmallSortedMap.newInstanceForTest(4); + SmallSortedMap<Integer, Integer> map3 = + SmallSortedMap.newInstanceForTest(3); + SmallSortedMap<Integer, Integer> map4 = + SmallSortedMap.newInstanceForTest(4); + + // Test with puts in ascending order. + for (int i = 0; i < numElements; i++) { + assertNull(map1.put(i, i + 1)); + assertNull(map2.put(i, i + 1)); + } + // Test with puts in descending order. + for (int i = numElements - 1; i >= 0; i--) { + assertNull(map3.put(i, i + 1)); + assertNull(map4.put(i, i + 1)); + } + + assertEquals(Math.min(3, numElements), map1.getNumArrayEntries()); + assertEquals(Math.min(4, numElements), map2.getNumArrayEntries()); + assertEquals(Math.min(3, numElements), map3.getNumArrayEntries()); + assertEquals(Math.min(4, numElements), map4.getNumArrayEntries()); + + List<SmallSortedMap<Integer, Integer>> allMaps = + new ArrayList<SmallSortedMap<Integer, Integer>>(); + allMaps.add(map1); + allMaps.add(map2); + allMaps.add(map3); + allMaps.add(map4); + + for (SmallSortedMap<Integer, Integer> map : allMaps) { + assertEquals(numElements, map.size()); + for (int i = 0; i < numElements; i++) { + assertEquals(new Integer(i + 1), map.get(i)); + } + } + + assertEquals(map1, map2); + assertEquals(map2, map3); + assertEquals(map3, map4); + } + + public void testReplacingPut() { + SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3); + for (int i = 0; i < 6; i++) { + assertNull(map.put(i, i + 1)); + assertNull(map.remove(i + 1)); + } + for (int i = 0; i < 6; i++) { + assertEquals(new Integer(i + 1), map.put(i, i + 2)); + } + } + + public void testRemove() { + SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3); + for (int i = 0; i < 6; i++) { + assertNull(map.put(i, i + 1)); + assertNull(map.remove(i + 1)); + } + + assertEquals(3, map.getNumArrayEntries()); + assertEquals(3, map.getNumOverflowEntries()); + assertEquals(6, map.size()); + assertEquals(makeSortedKeySet(0, 1, 2, 3, 4, 5), map.keySet()); + + assertEquals(new Integer(2), map.remove(1)); + assertEquals(3, map.getNumArrayEntries()); + assertEquals(2, map.getNumOverflowEntries()); + assertEquals(5, map.size()); + assertEquals(makeSortedKeySet(0, 2, 3, 4, 5), map.keySet()); + + assertEquals(new Integer(5), map.remove(4)); + assertEquals(3, map.getNumArrayEntries()); + assertEquals(1, map.getNumOverflowEntries()); + assertEquals(4, map.size()); + assertEquals(makeSortedKeySet(0, 2, 3, 5), map.keySet()); + + assertEquals(new Integer(4), map.remove(3)); + assertEquals(3, map.getNumArrayEntries()); + assertEquals(0, map.getNumOverflowEntries()); + assertEquals(3, map.size()); + assertEquals(makeSortedKeySet(0, 2, 5), map.keySet()); + + assertNull(map.remove(3)); + assertEquals(3, map.getNumArrayEntries()); + assertEquals(0, map.getNumOverflowEntries()); + assertEquals(3, map.size()); + + assertEquals(new Integer(1), map.remove(0)); + assertEquals(2, map.getNumArrayEntries()); + assertEquals(0, map.getNumOverflowEntries()); + assertEquals(2, map.size()); + } + + public void testClear() { + SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3); + for (int i = 0; i < 6; i++) { + assertNull(map.put(i, i + 1)); + } + map.clear(); + assertEquals(0, map.getNumArrayEntries()); + assertEquals(0, map.getNumOverflowEntries()); + assertEquals(0, map.size()); + } + + public void testGetArrayEntryAndOverflowEntries() { + SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3); + for (int i = 0; i < 6; i++) { + assertNull(map.put(i, i + 1)); + } + assertEquals(3, map.getNumArrayEntries()); + for (int i = 0; i < 3; i++) { + Map.Entry<Integer, Integer> entry = map.getArrayEntryAt(i); + assertEquals(new Integer(i), entry.getKey()); + assertEquals(new Integer(i + 1), entry.getValue()); + } + Iterator<Map.Entry<Integer, Integer>> it = + map.getOverflowEntries().iterator(); + for (int i = 3; i < 6; i++) { + assertTrue(it.hasNext()); + Map.Entry<Integer, Integer> entry = it.next(); + assertEquals(new Integer(i), entry.getKey()); + assertEquals(new Integer(i + 1), entry.getValue()); + } + assertFalse(it.hasNext()); + } + + public void testEntrySetContains() { + SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3); + for (int i = 0; i < 6; i++) { + assertNull(map.put(i, i + 1)); + } + Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet(); + for (int i = 0; i < 6; i++) { + assertTrue( + entrySet.contains(new SimpleEntry<Integer, Integer>(i, i + 1))); + assertFalse( + entrySet.contains(new SimpleEntry<Integer, Integer>(i, i))); + } + } + + public void testEntrySetAdd() { + SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3); + Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet(); + for (int i = 0; i < 6; i++) { + Map.Entry<Integer, Integer> entry = + new SimpleEntry<Integer, Integer>(i, i + 1); + assertTrue(entrySet.add(entry)); + assertFalse(entrySet.add(entry)); + } + for (int i = 0; i < 6; i++) { + assertEquals(new Integer(i + 1), map.get(i)); + } + assertEquals(3, map.getNumArrayEntries()); + assertEquals(3, map.getNumOverflowEntries()); + assertEquals(6, map.size()); + } + + public void testEntrySetRemove() { + SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3); + Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet(); + for (int i = 0; i < 6; i++) { + assertNull(map.put(i, i + 1)); + } + for (int i = 0; i < 6; i++) { + Map.Entry<Integer, Integer> entry = + new SimpleEntry<Integer, Integer>(i, i + 1); + assertTrue(entrySet.remove(entry)); + assertFalse(entrySet.remove(entry)); + } + assertTrue(map.isEmpty()); + assertEquals(0, map.getNumArrayEntries()); + assertEquals(0, map.getNumOverflowEntries()); + assertEquals(0, map.size()); + } + + public void testEntrySetClear() { + SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3); + for (int i = 0; i < 6; i++) { + assertNull(map.put(i, i + 1)); + } + map.entrySet().clear(); + assertTrue(map.isEmpty()); + assertEquals(0, map.getNumArrayEntries()); + assertEquals(0, map.getNumOverflowEntries()); + assertEquals(0, map.size()); + } + + public void testEntrySetIteratorNext() { + SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3); + for (int i = 0; i < 6; i++) { + assertNull(map.put(i, i + 1)); + } + Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator(); + for (int i = 0; i < 6; i++) { + assertTrue(it.hasNext()); + Map.Entry<Integer, Integer> entry = it.next(); + assertEquals(new Integer(i), entry.getKey()); + assertEquals(new Integer(i + 1), entry.getValue()); + } + assertFalse(it.hasNext()); + } + + public void testEntrySetIteratorRemove() { + SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3); + for (int i = 0; i < 6; i++) { + assertNull(map.put(i, i + 1)); + } + Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator(); + for (int i = 0; i < 6; i++) { + assertTrue(map.containsKey(i)); + it.next(); + it.remove(); + assertFalse(map.containsKey(i)); + assertEquals(6 - i - 1, map.size()); + } + } + + public void testMapEntryModification() { + SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3); + for (int i = 0; i < 6; i++) { + assertNull(map.put(i, i + 1)); + } + Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator(); + for (int i = 0; i < 6; i++) { + Map.Entry<Integer, Integer> entry = it.next(); + entry.setValue(i + 23); + } + for (int i = 0; i < 6; i++) { + assertEquals(new Integer(i + 23), map.get(i)); + } + } + + public void testMakeImmutable() { + SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3); + for (int i = 0; i < 6; i++) { + assertNull(map.put(i, i + 1)); + } + map.makeImmutable(); + assertEquals(new Integer(1), map.get(0)); + assertEquals(6, map.size()); + + try { + map.put(23, 23); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + } + + Map<Integer, Integer> other = new HashMap<Integer, Integer>(); + other.put(23, 23); + try { + map.putAll(other); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + } + + try { + map.remove(0); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + } + + try { + map.clear(); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + } + + Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet(); + try { + entrySet.clear(); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + } + + Iterator<Map.Entry<Integer, Integer>> it = entrySet.iterator(); + while (it.hasNext()) { + Map.Entry<Integer, Integer> entry = it.next(); + try { + entry.setValue(0); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + } + try { + it.remove(); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + } + } + + Set<Integer> keySet = map.keySet(); + try { + keySet.clear(); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + } + + Iterator<Integer> keys = keySet.iterator(); + while (keys.hasNext()) { + Integer key = keys.next(); + try { + keySet.remove(key); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + } + try { + keys.remove(); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + } + } + } + + private Set<Integer> makeSortedKeySet(Integer... keys) { + return new TreeSet<Integer>(Arrays.<Integer>asList(keys)); + } +} diff --git a/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java b/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java new file mode 100644 index 00000000..6feec4e6 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java @@ -0,0 +1,49 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import junit.framework.TestCase; + +/** + * Tests that proto2 api generation doesn't cause compile errors when + * compiling protocol buffers that have names that would otherwise conflict + * if not fully qualified (like @Deprecated and @Override). + * + * @author jonp@google.com (Jon Perlow) + */ +public class TestBadIdentifiers extends TestCase { + + public void testCompilation() { + // If this compiles, it means the generation was correct. + TestBadIdentifiersProto.Deprecated.newBuilder(); + TestBadIdentifiersProto.Override.newBuilder(); + } +} diff --git a/java/src/test/java/com/google/protobuf/TestUtil.java b/java/src/test/java/com/google/protobuf/TestUtil.java index 2b8b2af8..7c458d75 100644 --- a/java/src/test/java/com/google/protobuf/TestUtil.java +++ b/java/src/test/java/com/google/protobuf/TestUtil.java @@ -214,7 +214,9 @@ import static com.google.protobuf.UnittestLite.packedBoolExtensionLite; import static com.google.protobuf.UnittestLite.packedEnumExtensionLite; import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestAllExtensionsOrBuilder; import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder; import protobuf_unittest.UnittestProto.TestPackedExtensions; import protobuf_unittest.UnittestProto.TestPackedTypes; import protobuf_unittest.UnittestProto.TestUnpackedTypes; @@ -225,6 +227,7 @@ import com.google.protobuf.test.UnittestImport.ImportEnum; import com.google.protobuf.UnittestLite.TestAllTypesLite; import com.google.protobuf.UnittestLite.TestAllExtensionsLite; +import com.google.protobuf.UnittestLite.TestAllExtensionsLiteOrBuilder; import com.google.protobuf.UnittestLite.TestPackedExtensionsLite; import com.google.protobuf.UnittestLite.ForeignMessageLite; import com.google.protobuf.UnittestLite.ForeignEnumLite; @@ -244,9 +247,12 @@ import java.io.RandomAccessFile; * set all fields of a message, serialize it, parse it, and check that all * fields are set. * + * <p>This code is not to be used outside of {@code com.google.protobuf} and + * subpackages. + * * @author kenton@google.com Kenton Varda */ -class TestUtil { +public final class TestUtil { private TestUtil() {} /** Helper to convert a String to ByteString. */ @@ -485,7 +491,7 @@ class TestUtil { * Assert (using {@code junit.framework.Assert}} that all fields of * {@code message} are set to the values assigned by {@code setAllFields}. */ - public static void assertAllFieldsSet(TestAllTypes message) { + public static void assertAllFieldsSet(TestAllTypesOrBuilder message) { Assert.assertTrue(message.hasOptionalInt32 ()); Assert.assertTrue(message.hasOptionalInt64 ()); Assert.assertTrue(message.hasOptionalUint32 ()); @@ -682,13 +688,12 @@ class TestUtil { } // ------------------------------------------------------------------- - /** * Assert (using {@code junit.framework.Assert}} that all fields of * {@code message} are cleared, and that getting the fields returns their * default values. */ - public static void assertClear(TestAllTypes message) { + public static void assertClear(TestAllTypesOrBuilder message) { // hasBlah() should initially be false for all optional fields. Assert.assertFalse(message.hasOptionalInt32 ()); Assert.assertFalse(message.hasOptionalInt64 ()); @@ -838,7 +843,8 @@ class TestUtil { * {@code message} are set to the values assigned by {@code setAllFields} * followed by {@code modifyRepeatedFields}. */ - public static void assertRepeatedFieldsModified(TestAllTypes message) { + public static void assertRepeatedFieldsModified( + TestAllTypesOrBuilder message) { // ModifyRepeatedFields only sets the second repeated element of each // field. In addition to verifying this, we also verify that the first // element and size were *not* modified. @@ -1352,7 +1358,8 @@ class TestUtil { * Assert (using {@code junit.framework.Assert}} that all extensions of * {@code message} are set to the values assigned by {@code setAllExtensions}. */ - public static void assertAllExtensionsSet(TestAllExtensions message) { + public static void assertAllExtensionsSet( + TestAllExtensionsOrBuilder message) { Assert.assertTrue(message.hasExtension(optionalInt32Extension )); Assert.assertTrue(message.hasExtension(optionalInt64Extension )); Assert.assertTrue(message.hasExtension(optionalUint32Extension )); @@ -1567,7 +1574,7 @@ class TestUtil { * {@code message} are cleared, and that getting the extensions returns their * default values. */ - public static void assertExtensionsClear(TestAllExtensions message) { + public static void assertExtensionsClear(TestAllExtensionsOrBuilder message) { // hasBlah() should initially be false for all optional fields. Assert.assertFalse(message.hasExtension(optionalInt32Extension )); Assert.assertFalse(message.hasExtension(optionalInt64Extension )); @@ -1752,7 +1759,7 @@ class TestUtil { * followed by {@code modifyRepeatedExtensions}. */ public static void assertRepeatedExtensionsModified( - TestAllExtensions message) { + TestAllExtensionsOrBuilder message) { // ModifyRepeatedFields only sets the second repeated element of each // field. In addition to verifying this, we also verify that the first // element and size were *not* modified. @@ -2106,7 +2113,8 @@ class TestUtil { * Assert (using {@code junit.framework.Assert}} that all extensions of * {@code message} are set to the values assigned by {@code setAllExtensions}. */ - public static void assertAllExtensionsSet(TestAllExtensionsLite message) { + public static void assertAllExtensionsSet( + TestAllExtensionsLiteOrBuilder message) { Assert.assertTrue(message.hasExtension(optionalInt32ExtensionLite )); Assert.assertTrue(message.hasExtension(optionalInt64ExtensionLite )); Assert.assertTrue(message.hasExtension(optionalUint32ExtensionLite )); @@ -2321,7 +2329,8 @@ class TestUtil { * {@code message} are cleared, and that getting the extensions returns their * default values. */ - public static void assertExtensionsClear(TestAllExtensionsLite message) { + public static void assertExtensionsClear( + TestAllExtensionsLiteOrBuilder message) { // hasBlah() should initially be false for all optional fields. Assert.assertFalse(message.hasExtension(optionalInt32ExtensionLite )); Assert.assertFalse(message.hasExtension(optionalInt64ExtensionLite )); @@ -2478,7 +2487,7 @@ class TestUtil { * followed by {@code modifyRepeatedExtensions}. */ public static void assertRepeatedExtensionsModified( - TestAllExtensionsLite message) { + TestAllExtensionsLiteOrBuilder message) { // ModifyRepeatedFields only sets the second repeated element of each // field. In addition to verifying this, we also verify that the first // element and size were *not* modified. @@ -3015,7 +3024,7 @@ class TestUtil { * {@code message} are set to the values assigned by {@code setAllFields}, * using the {@link Message} reflection interface. */ - public void assertAllFieldsSetViaReflection(Message message) { + public void assertAllFieldsSetViaReflection(MessageOrBuilder message) { Assert.assertTrue(message.hasField(f("optional_int32" ))); Assert.assertTrue(message.hasField(f("optional_int64" ))); Assert.assertTrue(message.hasField(f("optional_uint32" ))); @@ -3248,7 +3257,7 @@ class TestUtil { * {@code message} are cleared, and that getting the fields returns their * default values, using the {@link Message} reflection interface. */ - public void assertClearViaReflection(Message message) { + public void assertClearViaReflection(MessageOrBuilder message) { // has_blah() should initially be false for all optional fields. Assert.assertFalse(message.hasField(f("optional_int32" ))); Assert.assertFalse(message.hasField(f("optional_int64" ))); @@ -3405,9 +3414,11 @@ class TestUtil { Assert.assertEquals("123", message.getField(f("default_cord"))); } + // --------------------------------------------------------------- - public void assertRepeatedFieldsModifiedViaReflection(Message message) { + public void assertRepeatedFieldsModifiedViaReflection( + MessageOrBuilder message) { // ModifyRepeatedFields only sets the second repeated element of each // field. In addition to verifying this, we also verify that the first // element and size were *not* modified. @@ -3543,7 +3554,7 @@ class TestUtil { message.addRepeatedField(f("packed_enum" ), foreignBaz); } - public void assertPackedFieldsSetViaReflection(Message message) { + public void assertPackedFieldsSetViaReflection(MessageOrBuilder message) { Assert.assertEquals(2, message.getRepeatedFieldCount(f("packed_int32" ))); Assert.assertEquals(2, message.getRepeatedFieldCount(f("packed_int64" ))); Assert.assertEquals(2, message.getRepeatedFieldCount(f("packed_uint32" ))); @@ -3699,7 +3710,7 @@ class TestUtil { /** * @param filePath The path relative to - * {@link com.google.testing.util.TestUtil#getDefaultSrcDir}. + * {@link #getTestDataDir}. */ public static String readTextFromFile(String filePath) { return readBytesFromFile(filePath).toStringUtf8(); @@ -3728,8 +3739,8 @@ class TestUtil { } /** - * @param filePath The path relative to - * {@link com.google.testing.util.TestUtil#getDefaultSrcDir}. + * @param filename The path relative to + * {@link #getTestDataDir}. */ public static ByteString readBytesFromFile(String filename) { File fullPath = new File(getTestDataDir(), filename); @@ -3749,7 +3760,7 @@ class TestUtil { /** * Get the bytes of the "golden message". This is a serialized TestAllTypes * with all fields set as they would be by - * {@link setAllFields(TestAllTypes.Builder)}, but it is loaded from a file + * {@link #setAllFields(TestAllTypes.Builder)}, but it is loaded from a file * on disk rather than generated dynamically. The file is actually generated * by C++ code, so testing against it verifies compatibility with C++. */ @@ -3764,7 +3775,7 @@ class TestUtil { /** * Get the bytes of the "golden packed fields message". This is a serialized * TestPackedTypes with all fields set as they would be by - * {@link setPackedFields(TestPackedTypes.Builder)}, but it is loaded from a + * {@link #setPackedFields(TestPackedTypes.Builder)}, but it is loaded from a * file on disk rather than generated dynamically. The file is actually * generated by C++ code, so testing against it verifies compatibility with * C++. @@ -3777,4 +3788,24 @@ class TestUtil { return goldenPackedFieldsMessage; } private static ByteString goldenPackedFieldsMessage = null; + + /** + * Mock implementation of {@link GeneratedMessage.BuilderParent} for testing. + * + * @author jonp@google.com (Jon Perlow) + */ + public static class MockBuilderParent + implements GeneratedMessage.BuilderParent { + + private int invalidations; + + @Override + public void markDirty() { + invalidations++; + } + + public int getInvalidationCount() { + return invalidations; + } + } } diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/src/test/java/com/google/protobuf/TextFormatTest.java index ad48157a..47690d1f 100644 --- a/java/src/test/java/com/google/protobuf/TextFormatTest.java +++ b/java/src/test/java/com/google/protobuf/TextFormatTest.java @@ -31,14 +31,14 @@ package com.google.protobuf; import com.google.protobuf.Descriptors.FieldDescriptor; -import protobuf_unittest.UnittestProto.OneString; -import protobuf_unittest.UnittestProto.TestAllTypes; -import protobuf_unittest.UnittestProto.TestAllExtensions; -import protobuf_unittest.UnittestProto.TestEmptyMessage; -import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage; import protobuf_unittest.UnittestMset.TestMessageSet; import protobuf_unittest.UnittestMset.TestMessageSetExtension1; import protobuf_unittest.UnittestMset.TestMessageSetExtension2; +import protobuf_unittest.UnittestProto.OneString; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage; +import protobuf_unittest.UnittestProto.TestEmptyMessage; import junit.framework.TestCase; @@ -133,40 +133,44 @@ public class TextFormatTest extends TestCase { assertEquals(allExtensionsSetText, javaText); } + // Creates an example unknown field set. + private UnknownFieldSet makeUnknownFieldSet() { + return UnknownFieldSet.newBuilder() + .addField(5, + UnknownFieldSet.Field.newBuilder() + .addVarint(1) + .addFixed32(2) + .addFixed64(3) + .addLengthDelimited(ByteString.copyFromUtf8("4")) + .addGroup( + UnknownFieldSet.newBuilder() + .addField(10, + UnknownFieldSet.Field.newBuilder() + .addVarint(5) + .build()) + .build()) + .build()) + .addField(8, + UnknownFieldSet.Field.newBuilder() + .addVarint(1) + .addVarint(2) + .addVarint(3) + .build()) + .addField(15, + UnknownFieldSet.Field.newBuilder() + .addVarint(0xABCDEF1234567890L) + .addFixed32(0xABCD1234) + .addFixed64(0xABCDEF1234567890L) + .build()) + .build(); + } + public void testPrintUnknownFields() throws Exception { // Test printing of unknown fields in a message. TestEmptyMessage message = TestEmptyMessage.newBuilder() - .setUnknownFields( - UnknownFieldSet.newBuilder() - .addField(5, - UnknownFieldSet.Field.newBuilder() - .addVarint(1) - .addFixed32(2) - .addFixed64(3) - .addLengthDelimited(ByteString.copyFromUtf8("4")) - .addGroup( - UnknownFieldSet.newBuilder() - .addField(10, - UnknownFieldSet.Field.newBuilder() - .addVarint(5) - .build()) - .build()) - .build()) - .addField(8, - UnknownFieldSet.Field.newBuilder() - .addVarint(1) - .addVarint(2) - .addVarint(3) - .build()) - .addField(15, - UnknownFieldSet.Field.newBuilder() - .addVarint(0xABCDEF1234567890L) - .addFixed32(0xABCD1234) - .addFixed64(0xABCDEF1234567890L) - .build()) - .build()) + .setUnknownFields(makeUnknownFieldSet()) .build(); assertEquals( @@ -409,6 +413,9 @@ public class TextFormatTest extends TestCase { "1:16: Expected \"true\" or \"false\".", "optional_bool: maybe"); assertParseError( + "1:16: Expected \"true\" or \"false\".", + "optional_bool: 2"); + assertParseError( "1:18: Expected string.", "optional_string: 123"); assertParseError( @@ -485,6 +492,11 @@ public class TextFormatTest extends TestCase { assertEquals(bytes(0xe1, 0x88, 0xb4), TextFormat.unescapeBytes("\\xe1\\x88\\xb4")); + // Handling of strings with unescaped Unicode characters > 255. + final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c"; + ByteString zhByteString = ByteString.copyFromUtf8(zh); + assertEquals(zhByteString, TextFormat.unescapeBytes(zh)); + // Errors. try { TextFormat.unescapeText("\\x"); @@ -626,6 +638,13 @@ public class TextFormatTest extends TestCase { } } + public void testParseString() throws Exception { + final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c"; + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge("optional_string: \"" + zh + "\"", builder); + assertEquals(zh, builder.getOptionalString()); + } + public void testParseLongString() throws Exception { String longText = "123456789012345678901234567890123456789012345678901234567890" + @@ -654,9 +673,80 @@ public class TextFormatTest extends TestCase { assertEquals(longText, builder.getOptionalString()); } + public void testParseBoolean() throws Exception { + String goodText = + "repeated_bool: t repeated_bool : 0\n" + + "repeated_bool :f repeated_bool:1"; + String goodTextCanonical = + "repeated_bool: true\n" + + "repeated_bool: false\n" + + "repeated_bool: false\n" + + "repeated_bool: true\n"; + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge(goodText, builder); + assertEquals(goodTextCanonical, builder.build().toString()); + + try { + TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder(); + TextFormat.merge("optional_bool:2", badBuilder); + fail("Should have thrown an exception."); + } catch (TextFormat.ParseException e) { + // success + } + try { + TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder(); + TextFormat.merge("optional_bool: foo", badBuilder); + fail("Should have thrown an exception."); + } catch (TextFormat.ParseException e) { + // success + } + } + public void testParseAdjacentStringLiterals() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TextFormat.merge("optional_string: \"foo\" 'corge' \"grault\"", builder); assertEquals("foocorgegrault", builder.getOptionalString()); } + + public void testPrintFieldValue() throws Exception { + assertPrintFieldValue("\"Hello\"", "Hello", "repeated_string"); + assertPrintFieldValue("123.0", 123f, "repeated_float"); + assertPrintFieldValue("123.0", 123d, "repeated_double"); + assertPrintFieldValue("123", 123, "repeated_int32"); + assertPrintFieldValue("123", 123L, "repeated_int64"); + assertPrintFieldValue("true", true, "repeated_bool"); + assertPrintFieldValue("4294967295", 0xFFFFFFFF, "repeated_uint32"); + assertPrintFieldValue("18446744073709551615", 0xFFFFFFFFFFFFFFFFL, + "repeated_uint64"); + assertPrintFieldValue("\"\\001\\002\\003\"", + ByteString.copyFrom(new byte[] {1, 2, 3}), "repeated_bytes"); + } + + private void assertPrintFieldValue(String expect, Object value, + String fieldName) throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + StringBuilder sb = new StringBuilder(); + TextFormat.printFieldValue( + TestAllTypes.getDescriptor().findFieldByName(fieldName), + value, sb); + assertEquals(expect, sb.toString()); + } + + public void testShortDebugString() { + assertEquals("optional_nested_message { bb: 42 } repeated_int32: 1" + + " repeated_uint32: 2", + TextFormat.shortDebugString(TestAllTypes.newBuilder() + .addRepeatedInt32(1) + .addRepeatedUint32(2) + .setOptionalNestedMessage( + NestedMessage.newBuilder().setBb(42).build()) + .build())); + } + + public void testShortDebugString_unknown() { + assertEquals("5: 1 5: 0x00000002 5: 0x0000000000000003 5: \"4\" 5 { 10: 5 }" + + " 8: 1 8: 2 8: 3 15: 12379813812177893520 15: 0xabcd1234 15:" + + " 0xabcdef1234567890", + TextFormat.shortDebugString(makeUnknownFieldSet())); + } } diff --git a/java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java b/java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java new file mode 100644 index 00000000..ed5d069e --- /dev/null +++ b/java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java @@ -0,0 +1,152 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import junit.framework.TestCase; + +import java.util.Iterator; +import java.util.ListIterator; + +/** + * Tests for {@link UnmodifiableLazyStringList}. + * + * @author jonp@google.com (Jon Perlow) + */ +public class UnmodifiableLazyStringListTest extends TestCase { + + private static String STRING_A = "A"; + private static String STRING_B = "B"; + private static String STRING_C = "C"; + + private static ByteString BYTE_STRING_A = ByteString.copyFromUtf8("A"); + private static ByteString BYTE_STRING_B = ByteString.copyFromUtf8("B"); + private static ByteString BYTE_STRING_C = ByteString.copyFromUtf8("C"); + + public void testReadOnlyMethods() { + LazyStringArrayList rawList = createSampleList(); + UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList); + assertEquals(3, list.size()); + assertSame(STRING_A, list.get(0)); + assertSame(STRING_B, list.get(1)); + assertSame(STRING_C, list.get(2)); + assertEquals(BYTE_STRING_A, list.getByteString(0)); + assertEquals(BYTE_STRING_B, list.getByteString(1)); + assertEquals(BYTE_STRING_C, list.getByteString(2)); + } + + public void testModifyMethods() { + LazyStringArrayList rawList = createSampleList(); + UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList); + + try { + list.remove(0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(3, list.size()); + + try { + list.add(STRING_B); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(3, list.size()); + + try { + list.set(1, STRING_B); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + public void testIterator() { + LazyStringArrayList rawList = createSampleList(); + UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList); + + Iterator<String> iter = list.iterator(); + int count = 0; + while (iter.hasNext()) { + iter.next(); + count++; + try { + iter.remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + assertEquals(3, count); + + } + + public void testListIterator() { + LazyStringArrayList rawList = createSampleList(); + UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList); + + ListIterator<String> iter = list.listIterator(); + int count = 0; + while (iter.hasNext()) { + iter.next(); + count++; + try { + iter.remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + iter.set("bar"); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + iter.add("bar"); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + assertEquals(3, count); + + } + + private LazyStringArrayList createSampleList() { + LazyStringArrayList rawList = new LazyStringArrayList(); + rawList.add(STRING_A); + rawList.add(STRING_B); + rawList.add(STRING_C); + return rawList; + } +} diff --git a/java/src/test/java/com/google/protobuf/multiple_files_test.proto b/java/src/test/java/com/google/protobuf/multiple_files_test.proto index 060f159a..9a040145 100644 --- a/java/src/test/java/com/google/protobuf/multiple_files_test.proto +++ b/java/src/test/java/com/google/protobuf/multiple_files_test.proto @@ -33,6 +33,10 @@ // A proto file which tests the java_multiple_files option. +// Some generic_services option(s) added automatically. +// See: http://go/proto2-generic-services-default +option java_generic_services = true; // auto-added + import "google/protobuf/unittest.proto"; package protobuf_unittest; diff --git a/java/src/test/java/com/google/protobuf/nested_builders_test.proto b/java/src/test/java/com/google/protobuf/nested_builders_test.proto new file mode 100644 index 00000000..abffb9d2 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/nested_builders_test.proto @@ -0,0 +1,53 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: jonp@google.com (Jon Perlow) +// + +package protobuf_unittest; + +option java_multiple_files = true; +option java_outer_classname = "NestedBuilders"; + + +message Vehicle { + optional Engine engine = 1; + repeated Wheel wheel = 2; +} + +message Engine { + optional int32 cylinder = 1; + optional int32 liters = 2; +} + +message Wheel { + optional int32 radius = 1; + optional int32 width = 2; +} diff --git a/java/src/test/java/com/google/protobuf/nested_extension.proto b/java/src/test/java/com/google/protobuf/nested_extension.proto new file mode 100644 index 00000000..9fe5d560 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/nested_extension.proto @@ -0,0 +1,45 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: Darick Tong (darick@google.com) +// +// A proto file with nested extensions. Note that this must be defined in +// a separate file to properly test the initialization of the outer class. + + +import "com/google/protobuf/non_nested_extension.proto"; + +package protobuf_unittest; + +message MyNestedExtension { + extend MessageToBeExtended { + optional MessageToBeExtended recursiveExtension = 2; + } +} diff --git a/java/src/test/java/com/google/protobuf/nested_extension_lite.proto b/java/src/test/java/com/google/protobuf/nested_extension_lite.proto new file mode 100644 index 00000000..16ee46e5 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/nested_extension_lite.proto @@ -0,0 +1,48 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: Darick Tong (darick@google.com) +// +// A proto file with nested extensions for a MessageLite messages. Note that +// this must be defined in a separate file to properly test the initialization +// of the outer class. + + +package protobuf_unittest; + +option optimize_for = LITE_RUNTIME; + +import "com/google/protobuf/non_nested_extension_lite.proto"; + +message MyNestedExtensionLite { + extend MessageLiteToBeExtended { + optional MessageLiteToBeExtended recursiveExtensionLite = 3; + } +} diff --git a/java/src/test/java/com/google/protobuf/non_nested_extension.proto b/java/src/test/java/com/google/protobuf/non_nested_extension.proto new file mode 100644 index 00000000..f61b419b --- /dev/null +++ b/java/src/test/java/com/google/protobuf/non_nested_extension.proto @@ -0,0 +1,48 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: Darick Tong (darick@google.com) +// +// A proto file with extensions. + + +package protobuf_unittest; + +message MessageToBeExtended { + extensions 1 to max; +} + +message MyNonNestedExtension { +} + +extend MessageToBeExtended { + optional MyNonNestedExtension nonNestedExtension = 1; +} + diff --git a/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto b/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto new file mode 100644 index 00000000..3c82659b --- /dev/null +++ b/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto @@ -0,0 +1,50 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: Darick Tong (darick@google.com) +// +// A proto file with extensions for a MessageLite messages. + + +package protobuf_unittest; + +option optimize_for = LITE_RUNTIME; + +message MessageLiteToBeExtended { + extensions 1 to max; +} + +message MyNonNestedExtensionLite { +} + +extend MessageLiteToBeExtended { + optional MyNonNestedExtensionLite nonNestedExtensionLite = 1; +} + diff --git a/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto b/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto new file mode 100644 index 00000000..5cc42af8 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto @@ -0,0 +1,66 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: jonp@google.com (Jon Perlow) + +// This file tests that various identifiers work as field and type names even +// though the same identifiers are used internally by the java code generator. + + +// Some generic_services option(s) added automatically. +// See: http://go/proto2-generic-services-default +option java_generic_services = true; // auto-added + +package io_protocol_tests; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "TestBadIdentifiersProto"; + +message TestMessage { +} + +message Deprecated { + enum TestEnum { + FOO = 1; + } + + optional int32 field1 = 1 [deprecated=true]; + optional TestEnum field2 = 2 [deprecated=true]; + optional TestMessage field3 = 3 [deprecated=true]; +} + +message Override { + optional int32 override = 1; +} + +service TestConflictingMethodNames { + rpc Override(TestMessage) returns (TestMessage); +} + diff --git a/python/google/protobuf/descriptor.py b/python/google/protobuf/descriptor.py index aa4ab969..cf609bee 100755 --- a/python/google/protobuf/descriptor.py +++ b/python/google/protobuf/descriptor.py @@ -28,16 +28,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# TODO(robinson): We probably need to provide deep-copy methods for -# descriptor types. When a FieldDescriptor is passed into -# Descriptor.__init__(), we should make a deep copy and then set -# containing_type on it. Alternatively, we could just get -# rid of containing_type (iit's not needed for reflection.py, at least). -# -# TODO(robinson): Print method? -# -# TODO(robinson): Useful __repr__? - """Descriptors essentially contain exactly the information found in a .proto file, in types that make this information accessible in Python. """ @@ -45,6 +35,13 @@ file, in types that make this information accessible in Python. __author__ = 'robinson@google.com (Will Robinson)' +from google.protobuf.internal import api_implementation + + +if api_implementation.Type() == 'cpp': + from google.protobuf.internal import cpp_message + + class Error(Exception): """Base error for this module.""" @@ -396,6 +393,13 @@ class FieldDescriptor(DescriptorBase): self.enum_type = enum_type self.is_extension = is_extension self.extension_scope = extension_scope + if api_implementation.Type() == 'cpp': + if is_extension: + self._cdescriptor = cpp_message.GetExtensionDescriptor(full_name) + else: + self._cdescriptor = cpp_message.GetFieldDescriptor(full_name) + else: + self._cdescriptor = None class EnumDescriptor(_NestedDescriptorBase): @@ -567,9 +571,13 @@ class FileDescriptor(DescriptorBase): """Constructor.""" super(FileDescriptor, self).__init__(options, 'FileOptions') + self.message_types_by_name = {} self.name = name self.package = package self.serialized_pb = serialized_pb + if (api_implementation.Type() == 'cpp' and + self.serialized_pb is not None): + cpp_message.BuildFile(self.serialized_pb) def CopyToProto(self, proto): """Copies this to a descriptor_pb2.FileDescriptorProto. diff --git a/python/google/protobuf/internal/api_implementation.py b/python/google/protobuf/internal/api_implementation.py new file mode 100755 index 00000000..b3e412e2 --- /dev/null +++ b/python/google/protobuf/internal/api_implementation.py @@ -0,0 +1,64 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# http://code.google.com/p/protobuf/ +# +# 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. + +""" +This module is the central entity that determines which implementation of the +API is used. +""" + +__author__ = 'petar@google.com (Petar Petrov)' + +import os +# This environment variable can be used to switch to a certain implementation +# of the Python API. Right now only 'python' and 'cpp' are valid values. Any +# other value will be ignored. +_implementation_type = os.getenv('PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION', + 'python') + + +if _implementation_type != 'python': + # For now, by default use the pure-Python implementation. + # The code below checks if the C extension is available and + # uses it if it is available. + _implementation_type = 'cpp' + ## Determine automatically which implementation to use. + #try: + # from google.protobuf.internal import cpp_message + # _implementation_type = 'cpp' + #except ImportError, e: + # _implementation_type = 'python' + + +# Usage of this function is discouraged. Clients shouldn't care which +# implementation of the API is in use. Note that there is no guarantee +# that differences between APIs will be maintained. +# Please don't use this function if possible. +def Type(): + return _implementation_type diff --git a/python/google/protobuf/internal/containers.py b/python/google/protobuf/internal/containers.py index 5cc7d6d0..097a3c26 100755 --- a/python/google/protobuf/internal/containers.py +++ b/python/google/protobuf/internal/containers.py @@ -72,9 +72,15 @@ class BaseContainer(object): # The concrete classes should define __eq__. return not self == other + def __hash__(self): + raise TypeError('unhashable object') + def __repr__(self): return repr(self._values) + def sort(self, sort_function=cmp): + self._values.sort(sort_function) + class RepeatedScalarFieldContainer(BaseContainer): @@ -198,28 +204,37 @@ class RepeatedCompositeFieldContainer(BaseContainer): super(RepeatedCompositeFieldContainer, self).__init__(message_listener) self._message_descriptor = message_descriptor - def add(self): - new_element = self._message_descriptor._concrete_class() + def add(self, **kwargs): + """Adds a new element at the end of the list and returns it. Keyword + arguments may be used to initialize the element. + """ + new_element = self._message_descriptor._concrete_class(**kwargs) new_element._SetListener(self._message_listener) self._values.append(new_element) if not self._message_listener.dirty: self._message_listener.Modified() return new_element - def MergeFrom(self, other): - """Appends the contents of another repeated field of the same type to this - one, copying each individual message. + def extend(self, elem_seq): + """Extends by appending the given sequence of elements of the same type + as this one, copying each individual message. """ message_class = self._message_descriptor._concrete_class listener = self._message_listener values = self._values - for message in other._values: + for message in elem_seq: new_element = message_class() new_element._SetListener(listener) new_element.MergeFrom(message) values.append(new_element) listener.Modified() + def MergeFrom(self, other): + """Appends the contents of another repeated field of the same type to this + one, copying each individual message. + """ + self.extend(other._values) + def __getslice__(self, start, stop): """Retrieves the subset of items from between the specified indices.""" return self._values[start:stop] diff --git a/python/google/protobuf/internal/cpp_message.py b/python/google/protobuf/internal/cpp_message.py new file mode 100755 index 00000000..3f426502 --- /dev/null +++ b/python/google/protobuf/internal/cpp_message.py @@ -0,0 +1,616 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Contains helper functions used to create protocol message classes from +Descriptor objects at runtime backed by the protocol buffer C++ API. +""" + +__author__ = 'petar@google.com (Petar Petrov)' + +import operator +from google.protobuf.internal import _net_proto2___python +from google.protobuf import message + + +_LABEL_REPEATED = _net_proto2___python.LABEL_REPEATED +_LABEL_OPTIONAL = _net_proto2___python.LABEL_OPTIONAL +_CPPTYPE_MESSAGE = _net_proto2___python.CPPTYPE_MESSAGE +_TYPE_MESSAGE = _net_proto2___python.TYPE_MESSAGE + + +def GetDescriptorPool(): + """Creates a new DescriptorPool C++ object.""" + return _net_proto2___python.NewCDescriptorPool() + + +_pool = GetDescriptorPool() + + +def GetFieldDescriptor(full_field_name): + """Searches for a field descriptor given a full field name.""" + return _pool.FindFieldByName(full_field_name) + + +def BuildFile(content): + """Registers a new proto file in the underlying C++ descriptor pool.""" + _net_proto2___python.BuildFile(content) + + +def GetExtensionDescriptor(full_extension_name): + """Searches for extension descriptor given a full field name.""" + return _pool.FindExtensionByName(full_extension_name) + + +def NewCMessage(full_message_name): + """Creates a new C++ protocol message by its name.""" + return _net_proto2___python.NewCMessage(full_message_name) + + +def ScalarProperty(cdescriptor): + """Returns a scalar property for the given descriptor.""" + + def Getter(self): + return self._cmsg.GetScalar(cdescriptor) + + def Setter(self, value): + self._cmsg.SetScalar(cdescriptor, value) + + return property(Getter, Setter) + + +def CompositeProperty(cdescriptor, message_type): + """Returns a Python property the given composite field.""" + + def Getter(self): + sub_message = self._composite_fields.get(cdescriptor.name, None) + if sub_message is None: + cmessage = self._cmsg.NewSubMessage(cdescriptor) + sub_message = message_type._concrete_class(__cmessage=cmessage) + self._composite_fields[cdescriptor.name] = sub_message + return sub_message + + return property(Getter) + + +class RepeatedScalarContainer(object): + """Container for repeated scalar fields.""" + + __slots__ = ['_message', '_cfield_descriptor', '_cmsg'] + + def __init__(self, msg, cfield_descriptor): + self._message = msg + self._cmsg = msg._cmsg + self._cfield_descriptor = cfield_descriptor + + def append(self, value): + self._cmsg.AddRepeatedScalar( + self._cfield_descriptor, value) + + def extend(self, sequence): + for element in sequence: + self.append(element) + + def insert(self, key, value): + values = self[slice(None, None, None)] + values.insert(key, value) + self._cmsg.AssignRepeatedScalar(self._cfield_descriptor, values) + + def remove(self, value): + values = self[slice(None, None, None)] + values.remove(value) + self._cmsg.AssignRepeatedScalar(self._cfield_descriptor, values) + + def __setitem__(self, key, value): + values = self[slice(None, None, None)] + values[key] = value + self._cmsg.AssignRepeatedScalar(self._cfield_descriptor, values) + + def __getitem__(self, key): + return self._cmsg.GetRepeatedScalar(self._cfield_descriptor, key) + + def __delitem__(self, key): + self._cmsg.DeleteRepeatedField(self._cfield_descriptor, key) + + def __len__(self): + return len(self[slice(None, None, None)]) + + def __eq__(self, other): + if self is other: + return True + if not operator.isSequenceType(other): + raise TypeError( + 'Can only compare repeated scalar fields against sequences.') + # We are presumably comparing against some other sequence type. + return other == self[slice(None, None, None)] + + def __ne__(self, other): + return not self == other + + def __hash__(self): + raise TypeError('unhashable object') + + def sort(self, sort_function=cmp): + values = self[slice(None, None, None)] + values.sort(sort_function) + self._cmsg.AssignRepeatedScalar(self._cfield_descriptor, values) + + +def RepeatedScalarProperty(cdescriptor): + """Returns a Python property the given repeated scalar field.""" + + def Getter(self): + container = self._composite_fields.get(cdescriptor.name, None) + if container is None: + container = RepeatedScalarContainer(self, cdescriptor) + self._composite_fields[cdescriptor.name] = container + return container + + def Setter(self, new_value): + raise AttributeError('Assignment not allowed to repeated field ' + '"%s" in protocol message object.' % cdescriptor.name) + + doc = 'Magic attribute generated for "%s" proto field.' % cdescriptor.name + return property(Getter, Setter, doc=doc) + + +class RepeatedCompositeContainer(object): + """Container for repeated composite fields.""" + + __slots__ = ['_message', '_subclass', '_cfield_descriptor', '_cmsg'] + + def __init__(self, msg, cfield_descriptor, subclass): + self._message = msg + self._cmsg = msg._cmsg + self._subclass = subclass + self._cfield_descriptor = cfield_descriptor + + def add(self, **kwargs): + cmessage = self._cmsg.AddMessage(self._cfield_descriptor) + return self._subclass(__cmessage=cmessage, __owner=self._message, **kwargs) + + def extend(self, elem_seq): + """Extends by appending the given sequence of elements of the same type + as this one, copying each individual message. + """ + for message in elem_seq: + self.add().MergeFrom(message) + + def MergeFrom(self, other): + for message in other[:]: + self.add().MergeFrom(message) + + def __getitem__(self, key): + cmessages = self._cmsg.GetRepeatedMessage( + self._cfield_descriptor, key) + subclass = self._subclass + if not isinstance(cmessages, list): + return subclass(__cmessage=cmessages, __owner=self._message) + + return [subclass(__cmessage=m, __owner=self._message) for m in cmessages] + + def __delitem__(self, key): + self._cmsg.DeleteRepeatedField( + self._cfield_descriptor, key) + + def __len__(self): + return self._cmsg.FieldLength(self._cfield_descriptor) + + def __eq__(self, other): + """Compares the current instance with another one.""" + if self is other: + return True + if not isinstance(other, self.__class__): + raise TypeError('Can only compare repeated composite fields against ' + 'other repeated composite fields.') + messages = self[slice(None, None, None)] + other_messages = other[slice(None, None, None)] + return messages == other_messages + + def __hash__(self): + raise TypeError('unhashable object') + + def sort(self, sort_function=cmp): + messages = [] + for index in range(len(self)): + # messages[i][0] is where the i-th element of the new array has to come + # from. + # messages[i][1] is where the i-th element of the old array has to go. + messages.append([index, 0, self[index]]) + messages.sort(lambda x,y: sort_function(x[2], y[2])) + + # Remember which position each elements has to move to. + for i in range(len(messages)): + messages[messages[i][0]][1] = i + + # Apply the transposition. + for i in range(len(messages)): + from_position = messages[i][0] + if i == from_position: + continue + self._cmsg.SwapRepeatedFieldElements( + self._cfield_descriptor, i, from_position) + messages[messages[i][1]][0] = from_position + + +def RepeatedCompositeProperty(cdescriptor, message_type): + """Returns a Python property for the given repeated composite field.""" + + def Getter(self): + container = self._composite_fields.get(cdescriptor.name, None) + if container is None: + container = RepeatedCompositeContainer( + self, cdescriptor, message_type._concrete_class) + self._composite_fields[cdescriptor.name] = container + return container + + def Setter(self, new_value): + raise AttributeError('Assignment not allowed to repeated field ' + '"%s" in protocol message object.' % cdescriptor.name) + + doc = 'Magic attribute generated for "%s" proto field.' % cdescriptor.name + return property(Getter, Setter, doc=doc) + + +class ExtensionDict(object): + """Extension dictionary added to each protocol message.""" + + def __init__(self, msg): + self._message = msg + self._cmsg = msg._cmsg + self._values = {} + + def __setitem__(self, extension, value): + from google.protobuf import descriptor + if not isinstance(extension, descriptor.FieldDescriptor): + raise KeyError('Bad extension %r.' % (extension,)) + cdescriptor = extension._cdescriptor + if (cdescriptor.label != _LABEL_OPTIONAL or + cdescriptor.cpp_type == _CPPTYPE_MESSAGE): + raise TypeError('Extension %r is repeated and/or a composite type.' % ( + extension.full_name,)) + self._cmsg.SetScalar(cdescriptor, value) + self._values[extension] = value + + def __getitem__(self, extension): + from google.protobuf import descriptor + if not isinstance(extension, descriptor.FieldDescriptor): + raise KeyError('Bad extension %r.' % (extension,)) + + cdescriptor = extension._cdescriptor + if (cdescriptor.label != _LABEL_REPEATED and + cdescriptor.cpp_type != _CPPTYPE_MESSAGE): + return self._cmsg.GetScalar(cdescriptor) + + ext = self._values.get(extension, None) + if ext is not None: + return ext + + ext = self._CreateNewHandle(extension) + self._values[extension] = ext + return ext + + def ClearExtension(self, extension): + from google.protobuf import descriptor + if not isinstance(extension, descriptor.FieldDescriptor): + raise KeyError('Bad extension %r.' % (extension,)) + self._cmsg.ClearFieldByDescriptor(extension._cdescriptor) + if extension in self._values: + del self._values[extension] + + def HasExtension(self, extension): + from google.protobuf import descriptor + if not isinstance(extension, descriptor.FieldDescriptor): + raise KeyError('Bad extension %r.' % (extension,)) + return self._cmsg.HasFieldByDescriptor(extension._cdescriptor) + + def _FindExtensionByName(self, name): + """Tries to find a known extension with the specified name. + + Args: + name: Extension full name. + + Returns: + Extension field descriptor. + """ + return self._message._extensions_by_name.get(name, None) + + def _CreateNewHandle(self, extension): + cdescriptor = extension._cdescriptor + if (cdescriptor.label != _LABEL_REPEATED and + cdescriptor.cpp_type == _CPPTYPE_MESSAGE): + cmessage = self._cmsg.NewSubMessage(cdescriptor) + return extension.message_type._concrete_class(__cmessage=cmessage) + + if cdescriptor.label == _LABEL_REPEATED: + if cdescriptor.cpp_type == _CPPTYPE_MESSAGE: + return RepeatedCompositeContainer( + self._message, cdescriptor, extension.message_type._concrete_class) + else: + return RepeatedScalarContainer(self._message, cdescriptor) + # This shouldn't happen! + assert False + return None + + +def NewMessage(message_descriptor, dictionary): + """Creates a new protocol message *class*.""" + _AddClassAttributesForNestedExtensions(message_descriptor, dictionary) + _AddEnumValues(message_descriptor, dictionary) + _AddDescriptors(message_descriptor, dictionary) + + +def InitMessage(message_descriptor, cls): + """Constructs a new message instance (called before instance's __init__).""" + cls._extensions_by_name = {} + _AddInitMethod(message_descriptor, cls) + _AddMessageMethods(message_descriptor, cls) + _AddPropertiesForExtensions(message_descriptor, cls) + + +def _AddDescriptors(message_descriptor, dictionary): + """Sets up a new protocol message class dictionary. + + Args: + message_descriptor: A Descriptor instance describing this message type. + dictionary: Class dictionary to which we'll add a '__slots__' entry. + """ + dictionary['__descriptors'] = {} + for field in message_descriptor.fields: + dictionary['__descriptors'][field.name] = GetFieldDescriptor( + field.full_name) + + dictionary['__slots__'] = list(dictionary['__descriptors'].iterkeys()) + [ + '_cmsg', '_owner', '_composite_fields', 'Extensions'] + + +def _AddEnumValues(message_descriptor, dictionary): + """Sets class-level attributes for all enum fields defined in this message. + + Args: + message_descriptor: Descriptor object for this message type. + dictionary: Class dictionary that should be populated. + """ + for enum_type in message_descriptor.enum_types: + for enum_value in enum_type.values: + dictionary[enum_value.name] = enum_value.number + + +def _AddClassAttributesForNestedExtensions(message_descriptor, dictionary): + """Adds class attributes for the nested extensions.""" + extension_dict = message_descriptor.extensions_by_name + for extension_name, extension_field in extension_dict.iteritems(): + assert extension_name not in dictionary + dictionary[extension_name] = extension_field + + +def _AddInitMethod(message_descriptor, cls): + """Adds an __init__ method to cls.""" + + # Create and attach message field properties to the message class. + # This can be done just once per message class, since property setters and + # getters are passed the message instance. + # This makes message instantiation extremely fast, and at the same time it + # doesn't require the creation of property objects for each message instance, + # which saves a lot of memory. + for field in message_descriptor.fields: + field_cdescriptor = cls.__descriptors[field.name] + if field.label == _LABEL_REPEATED: + if field.cpp_type == _CPPTYPE_MESSAGE: + value = RepeatedCompositeProperty(field_cdescriptor, field.message_type) + else: + value = RepeatedScalarProperty(field_cdescriptor) + elif field.cpp_type == _CPPTYPE_MESSAGE: + value = CompositeProperty(field_cdescriptor, field.message_type) + else: + value = ScalarProperty(field_cdescriptor) + setattr(cls, field.name, value) + + # Attach a constant with the field number. + constant_name = field.name.upper() + '_FIELD_NUMBER' + setattr(cls, constant_name, field.number) + + def Init(self, **kwargs): + """Message constructor.""" + cmessage = kwargs.pop('__cmessage', None) + if cmessage is None: + self._cmsg = NewCMessage(message_descriptor.full_name) + else: + self._cmsg = cmessage + + # Keep a reference to the owner, as the owner keeps a reference to the + # underlying protocol buffer message. + owner = kwargs.pop('__owner', None) + if owner is not None: + self._owner = owner + + self.Extensions = ExtensionDict(self) + self._composite_fields = {} + + for field_name, field_value in kwargs.iteritems(): + field_cdescriptor = self.__descriptors.get(field_name, None) + if field_cdescriptor is None: + raise ValueError('Protocol message has no "%s" field.' % field_name) + if field_cdescriptor.label == _LABEL_REPEATED: + if field_cdescriptor.cpp_type == _CPPTYPE_MESSAGE: + for val in field_value: + getattr(self, field_name).add().MergeFrom(val) + else: + getattr(self, field_name).extend(field_value) + elif field_cdescriptor.cpp_type == _CPPTYPE_MESSAGE: + getattr(self, field_name).MergeFrom(field_value) + else: + setattr(self, field_name, field_value) + + Init.__module__ = None + Init.__doc__ = None + cls.__init__ = Init + + +def _IsMessageSetExtension(field): + """Checks if a field is a message set extension.""" + return (field.is_extension and + field.containing_type.has_options and + field.containing_type.GetOptions().message_set_wire_format and + field.type == _TYPE_MESSAGE and + field.message_type == field.extension_scope and + field.label == _LABEL_OPTIONAL) + + +def _AddMessageMethods(message_descriptor, cls): + """Adds the methods to a protocol message class.""" + if message_descriptor.is_extendable: + + def ClearExtension(self, extension): + self.Extensions.ClearExtension(extension) + + def HasExtension(self, extension): + return self.Extensions.HasExtension(extension) + + def HasField(self, field_name): + return self._cmsg.HasField(field_name) + + def ClearField(self, field_name): + if field_name in self._composite_fields: + del self._composite_fields[field_name] + self._cmsg.ClearField(field_name) + + def Clear(self): + return self._cmsg.Clear() + + def IsInitialized(self, errors=None): + if self._cmsg.IsInitialized(): + return True + if errors is not None: + errors.extend(self.FindInitializationErrors()); + return False + + def SerializeToString(self): + if not self.IsInitialized(): + raise message.EncodeError( + 'Message is missing required fields: ' + + ','.join(self.FindInitializationErrors())) + return self._cmsg.SerializeToString() + + def SerializePartialToString(self): + return self._cmsg.SerializePartialToString() + + def ParseFromString(self, serialized): + self.Clear() + self.MergeFromString(serialized) + + def MergeFromString(self, serialized): + byte_size = self._cmsg.MergeFromString(serialized) + if byte_size < 0: + raise message.DecodeError('Unable to merge from string.') + return byte_size + + def MergeFrom(self, msg): + if not isinstance(msg, cls): + raise TypeError( + "Parameter to MergeFrom() must be instance of same class.") + self._cmsg.MergeFrom(msg._cmsg) + + def CopyFrom(self, msg): + self._cmsg.CopyFrom(msg._cmsg) + + def ByteSize(self): + return self._cmsg.ByteSize() + + def SetInParent(self): + return self._cmsg.SetInParent() + + def ListFields(self): + all_fields = [] + field_list = self._cmsg.ListFields() + fields_by_name = cls.DESCRIPTOR.fields_by_name + for is_extension, field_name in field_list: + if is_extension: + extension = cls._extensions_by_name[field_name] + all_fields.append((extension, self.Extensions[extension])) + else: + field_descriptor = fields_by_name[field_name] + all_fields.append( + (field_descriptor, getattr(self, field_name))) + all_fields.sort(key=lambda item: item[0].number) + return all_fields + + def FindInitializationErrors(self): + return self._cmsg.FindInitializationErrors() + + def __str__(self): + return self._cmsg.DebugString() + + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, self.__class__): + return False + return self.ListFields() == other.ListFields() + + def __ne__(self, other): + return not self == other + + def __hash__(self): + raise TypeError('unhashable object') + + def __unicode__(self): + return text_format.MessageToString(self, as_utf8=True).decode('utf-8') + + # Attach the local methods to the message class. + for key, value in locals().copy().iteritems(): + if key not in ('key', 'value', '__builtins__', '__name__', '__doc__'): + setattr(cls, key, value) + + # Static methods: + + def RegisterExtension(extension_handle): + extension_handle.containing_type = cls.DESCRIPTOR + cls._extensions_by_name[extension_handle.full_name] = extension_handle + + if _IsMessageSetExtension(extension_handle): + # MessageSet extension. Also register under type name. + cls._extensions_by_name[ + extension_handle.message_type.full_name] = extension_handle + cls.RegisterExtension = staticmethod(RegisterExtension) + + def FromString(string): + msg = cls() + msg.MergeFromString(string) + return msg + cls.FromString = staticmethod(FromString) + + + +def _AddPropertiesForExtensions(message_descriptor, cls): + """Adds properties for all fields in this protocol message type.""" + extension_dict = message_descriptor.extensions_by_name + for extension_name, extension_field in extension_dict.iteritems(): + constant_name = extension_name.upper() + '_FIELD_NUMBER' + setattr(cls, constant_name, extension_field.number) diff --git a/python/google/protobuf/internal/decoder.py b/python/google/protobuf/internal/decoder.py index 461a30c0..55f746f5 100755 --- a/python/google/protobuf/internal/decoder.py +++ b/python/google/protobuf/internal/decoder.py @@ -86,6 +86,13 @@ from google.protobuf.internal import wire_format from google.protobuf import message +# This will overflow and thus become IEEE-754 "infinity". We would use +# "float('inf')" but it doesn't work on Windows pre-Python-2.6. +_POS_INF = 1e10000 +_NEG_INF = -_POS_INF +_NAN = _POS_INF * 0 + + # This is not for optimization, but rather to avoid conflicts with local # variables named "message". _DecodeError = message.DecodeError @@ -269,6 +276,72 @@ def _StructPackDecoder(wire_type, format): return _SimpleDecoder(wire_type, InnerDecode) +def _FloatDecoder(): + """Returns a decoder for a float field. + + This code works around a bug in struct.unpack for non-finite 32-bit + floating-point values. + """ + + local_unpack = struct.unpack + + def InnerDecode(buffer, pos): + # We expect a 32-bit value in little-endian byte order. Bit 1 is the sign + # bit, bits 2-9 represent the exponent, and bits 10-32 are the significand. + new_pos = pos + 4 + float_bytes = buffer[pos:new_pos] + + # If this value has all its exponent bits set, then it's non-finite. + # In Python 2.4, struct.unpack will convert it to a finite 64-bit value. + # To avoid that, we parse it specially. + if ((float_bytes[3] in '\x7F\xFF') + and (float_bytes[2] >= '\x80')): + # If at least one significand bit is set... + if float_bytes[0:3] != '\x00\x00\x80': + return (_NAN, new_pos) + # If sign bit is set... + if float_bytes[3] == '\xFF': + return (_NEG_INF, new_pos) + return (_POS_INF, new_pos) + + # Note that we expect someone up-stack to catch struct.error and convert + # it to _DecodeError -- this way we don't have to set up exception- + # handling blocks every time we parse one value. + result = local_unpack('<f', float_bytes)[0] + return (result, new_pos) + return _SimpleDecoder(wire_format.WIRETYPE_FIXED32, InnerDecode) + + +def _DoubleDecoder(): + """Returns a decoder for a double field. + + This code works around a bug in struct.unpack for not-a-number. + """ + + local_unpack = struct.unpack + + def InnerDecode(buffer, pos): + # We expect a 64-bit value in little-endian byte order. Bit 1 is the sign + # bit, bits 2-12 represent the exponent, and bits 13-64 are the significand. + new_pos = pos + 8 + double_bytes = buffer[pos:new_pos] + + # If this value has all its exponent bits set and at least one significand + # bit set, it's not a number. In Python 2.4, struct.unpack will treat it + # as inf or -inf. To avoid that, we treat it specially. + if ((double_bytes[7] in '\x7F\xFF') + and (double_bytes[6] >= '\xF0') + and (double_bytes[0:7] != '\x00\x00\x00\x00\x00\x00\xF0')): + return (_NAN, new_pos) + + # Note that we expect someone up-stack to catch struct.error and convert + # it to _DecodeError -- this way we don't have to set up exception- + # handling blocks every time we parse one value. + result = local_unpack('<d', double_bytes)[0] + return (result, new_pos) + return _SimpleDecoder(wire_format.WIRETYPE_FIXED64, InnerDecode) + + # -------------------------------------------------------------------- @@ -294,8 +367,8 @@ Fixed32Decoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED32, '<I') Fixed64Decoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED64, '<Q') SFixed32Decoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED32, '<i') SFixed64Decoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED64, '<q') -FloatDecoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED32, '<f') -DoubleDecoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED64, '<d') +FloatDecoder = _FloatDecoder() +DoubleDecoder = _DoubleDecoder() BoolDecoder = _ModifiedDecoder( wire_format.WIRETYPE_VARINT, _DecodeVarint, bool) diff --git a/python/google/protobuf/internal/encoder.py b/python/google/protobuf/internal/encoder.py index aa05d5b3..777975e8 100755 --- a/python/google/protobuf/internal/encoder.py +++ b/python/google/protobuf/internal/encoder.py @@ -70,6 +70,12 @@ import struct from google.protobuf.internal import wire_format +# This will overflow and thus become IEEE-754 "infinity". We would use +# "float('inf')" but it doesn't work on Windows pre-Python-2.6. +_POS_INF = 1e10000 +_NEG_INF = -_POS_INF + + def _VarintSize(value): """Compute the size of a varint value.""" if value <= 0x7f: return 1 @@ -502,6 +508,83 @@ def _StructPackEncoder(wire_type, format): return SpecificEncoder +def _FloatingPointEncoder(wire_type, format): + """Return a constructor for an encoder for float fields. + + This is like StructPackEncoder, but catches errors that may be due to + passing non-finite floating-point values to struct.pack, and makes a + second attempt to encode those values. + + Args: + wire_type: The field's wire type, for encoding tags. + format: The format string to pass to struct.pack(). + """ + + value_size = struct.calcsize(format) + if value_size == 4: + def EncodeNonFiniteOrRaise(write, value): + # Remember that the serialized form uses little-endian byte order. + if value == _POS_INF: + write('\x00\x00\x80\x7F') + elif value == _NEG_INF: + write('\x00\x00\x80\xFF') + elif value != value: # NaN + write('\x00\x00\xC0\x7F') + else: + raise + elif value_size == 8: + def EncodeNonFiniteOrRaise(write, value): + if value == _POS_INF: + write('\x00\x00\x00\x00\x00\x00\xF0\x7F') + elif value == _NEG_INF: + write('\x00\x00\x00\x00\x00\x00\xF0\xFF') + elif value != value: # NaN + write('\x00\x00\x00\x00\x00\x00\xF8\x7F') + else: + raise + else: + raise ValueError('Can\'t encode floating-point values that are ' + '%d bytes long (only 4 or 8)' % value_size) + + def SpecificEncoder(field_number, is_repeated, is_packed): + local_struct_pack = struct.pack + if is_packed: + tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + local_EncodeVarint = _EncodeVarint + def EncodePackedField(write, value): + write(tag_bytes) + local_EncodeVarint(write, len(value) * value_size) + for element in value: + # This try/except block is going to be faster than any code that + # we could write to check whether element is finite. + try: + write(local_struct_pack(format, element)) + except SystemError: + EncodeNonFiniteOrRaise(write, element) + return EncodePackedField + elif is_repeated: + tag_bytes = TagBytes(field_number, wire_type) + def EncodeRepeatedField(write, value): + for element in value: + write(tag_bytes) + try: + write(local_struct_pack(format, element)) + except SystemError: + EncodeNonFiniteOrRaise(write, element) + return EncodeRepeatedField + else: + tag_bytes = TagBytes(field_number, wire_type) + def EncodeField(write, value): + write(tag_bytes) + try: + write(local_struct_pack(format, value)) + except SystemError: + EncodeNonFiniteOrRaise(write, value) + return EncodeField + + return SpecificEncoder + + # ==================================================================== # Here we declare an encoder constructor for each field type. These work # very similarly to sizer constructors, described earlier. @@ -525,8 +608,8 @@ Fixed32Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED32, '<I') Fixed64Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED64, '<Q') SFixed32Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED32, '<i') SFixed64Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED64, '<q') -FloatEncoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED32, '<f') -DoubleEncoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED64, '<d') +FloatEncoder = _FloatingPointEncoder(wire_format.WIRETYPE_FIXED32, '<f') +DoubleEncoder = _FloatingPointEncoder(wire_format.WIRETYPE_FIXED64, '<d') def BoolEncoder(field_number, is_repeated, is_packed): diff --git a/python/google/protobuf/internal/generator_test.py b/python/google/protobuf/internal/generator_test.py index 78360b53..e4387c85 100755 --- a/python/google/protobuf/internal/generator_test.py +++ b/python/google/protobuf/internal/generator_test.py @@ -42,11 +42,12 @@ further ensures that we can use Python protocol message objects as we expect. __author__ = 'robinson@google.com (Will Robinson)' import unittest +from google.protobuf import unittest_custom_options_pb2 from google.protobuf import unittest_import_pb2 from google.protobuf import unittest_mset_pb2 from google.protobuf import unittest_pb2 from google.protobuf import unittest_no_generic_services_pb2 - +from google.protobuf import service MAX_EXTENSION = 536870912 @@ -140,6 +141,13 @@ class GeneratorTest(unittest.TestCase): proto = unittest_mset_pb2.TestMessageSet() self.assertTrue(proto.DESCRIPTOR.GetOptions().message_set_wire_format) + def testMessageWithCustomOptions(self): + proto = unittest_custom_options_pb2.TestMessageWithCustomOptions() + enum_options = proto.DESCRIPTOR.enum_types_by_name['AnEnum'].GetOptions() + self.assertTrue(enum_options is not None) + # TODO(gps): We really should test for the presense of the enum_opt1 + # extension and for its value to be set to -789. + def testNestedTypes(self): self.assertEquals( set(unittest_pb2.TestAllTypes.DESCRIPTOR.nested_types), @@ -208,12 +216,27 @@ class GeneratorTest(unittest.TestCase): self.assertFalse(unittest_pb2.DESCRIPTOR.serialized_pb is None) def testNoGenericServices(self): - # unittest_no_generic_services.proto should contain defs for everything - # except services. self.assertTrue(hasattr(unittest_no_generic_services_pb2, "TestMessage")) self.assertTrue(hasattr(unittest_no_generic_services_pb2, "FOO")) self.assertTrue(hasattr(unittest_no_generic_services_pb2, "test_extension")) - self.assertFalse(hasattr(unittest_no_generic_services_pb2, "TestService")) + + # Make sure unittest_no_generic_services_pb2 has no services subclassing + # Proto2 Service class. + if hasattr(unittest_no_generic_services_pb2, "TestService"): + self.assertFalse(issubclass(unittest_no_generic_services_pb2.TestService, + service.Service)) + + def testMessageTypesByName(self): + file_type = unittest_pb2.DESCRIPTOR + self.assertEqual( + unittest_pb2._TESTALLTYPES, + file_type.message_types_by_name[unittest_pb2._TESTALLTYPES.name]) + + # Nested messages shouldn't be included in the message_types_by_name + # dictionary (like in the C++ API). + self.assertFalse( + unittest_pb2._TESTALLTYPES_NESTEDMESSAGE.name in + file_type.message_types_by_name) if __name__ == '__main__': diff --git a/python/google/protobuf/internal/message_test.py b/python/google/protobuf/internal/message_test.py index 73a9a3a3..65174373 100755 --- a/python/google/protobuf/internal/message_test.py +++ b/python/google/protobuf/internal/message_test.py @@ -43,11 +43,25 @@ abstract interface. __author__ = 'gps@google.com (Gregory P. Smith)' +import copy +import math import unittest from google.protobuf import unittest_import_pb2 from google.protobuf import unittest_pb2 from google.protobuf.internal import test_util +# Python pre-2.6 does not have isinf() or isnan() functions, so we have +# to provide our own. +def isnan(val): + # NaN is never equal to itself. + return val != val +def isinf(val): + # Infinity times zero equals NaN. + return not isnan(val) and isnan(val * 0) +def IsPosInf(val): + return isinf(val) and (val > 0) +def IsNegInf(val): + return isinf(val) and (val < 0) class MessageTest(unittest.TestCase): @@ -57,6 +71,8 @@ class MessageTest(unittest.TestCase): golden_message.ParseFromString(golden_data) test_util.ExpectAllFieldsSet(self, golden_message) self.assertTrue(golden_message.SerializeToString() == golden_data) + golden_copy = copy.deepcopy(golden_message) + self.assertTrue(golden_copy.SerializeToString() == golden_data) def testGoldenExtensions(self): golden_data = test_util.GoldenFile('golden_message').read() @@ -66,6 +82,8 @@ class MessageTest(unittest.TestCase): test_util.SetAllExtensions(all_set) self.assertEquals(all_set, golden_message) self.assertTrue(golden_message.SerializeToString() == golden_data) + golden_copy = copy.deepcopy(golden_message) + self.assertTrue(golden_copy.SerializeToString() == golden_data) def testGoldenPackedMessage(self): golden_data = test_util.GoldenFile('golden_packed_fields_message').read() @@ -75,6 +93,8 @@ class MessageTest(unittest.TestCase): test_util.SetAllPackedFields(all_set) self.assertEquals(all_set, golden_message) self.assertTrue(all_set.SerializeToString() == golden_data) + golden_copy = copy.deepcopy(golden_message) + self.assertTrue(golden_copy.SerializeToString() == golden_data) def testGoldenPackedExtensions(self): golden_data = test_util.GoldenFile('golden_packed_fields_message').read() @@ -84,6 +104,240 @@ class MessageTest(unittest.TestCase): test_util.SetAllPackedExtensions(all_set) self.assertEquals(all_set, golden_message) self.assertTrue(all_set.SerializeToString() == golden_data) + golden_copy = copy.deepcopy(golden_message) + self.assertTrue(golden_copy.SerializeToString() == golden_data) + + def testPositiveInfinity(self): + golden_data = ('\x5D\x00\x00\x80\x7F' + '\x61\x00\x00\x00\x00\x00\x00\xF0\x7F' + '\xCD\x02\x00\x00\x80\x7F' + '\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\x7F') + golden_message = unittest_pb2.TestAllTypes() + golden_message.ParseFromString(golden_data) + self.assertTrue(IsPosInf(golden_message.optional_float)) + self.assertTrue(IsPosInf(golden_message.optional_double)) + self.assertTrue(IsPosInf(golden_message.repeated_float[0])) + self.assertTrue(IsPosInf(golden_message.repeated_double[0])) + self.assertTrue(golden_message.SerializeToString() == golden_data) + + def testNegativeInfinity(self): + golden_data = ('\x5D\x00\x00\x80\xFF' + '\x61\x00\x00\x00\x00\x00\x00\xF0\xFF' + '\xCD\x02\x00\x00\x80\xFF' + '\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\xFF') + golden_message = unittest_pb2.TestAllTypes() + golden_message.ParseFromString(golden_data) + self.assertTrue(IsNegInf(golden_message.optional_float)) + self.assertTrue(IsNegInf(golden_message.optional_double)) + self.assertTrue(IsNegInf(golden_message.repeated_float[0])) + self.assertTrue(IsNegInf(golden_message.repeated_double[0])) + self.assertTrue(golden_message.SerializeToString() == golden_data) + + def testNotANumber(self): + golden_data = ('\x5D\x00\x00\xC0\x7F' + '\x61\x00\x00\x00\x00\x00\x00\xF8\x7F' + '\xCD\x02\x00\x00\xC0\x7F' + '\xD1\x02\x00\x00\x00\x00\x00\x00\xF8\x7F') + golden_message = unittest_pb2.TestAllTypes() + golden_message.ParseFromString(golden_data) + self.assertTrue(isnan(golden_message.optional_float)) + self.assertTrue(isnan(golden_message.optional_double)) + self.assertTrue(isnan(golden_message.repeated_float[0])) + self.assertTrue(isnan(golden_message.repeated_double[0])) + self.assertTrue(golden_message.SerializeToString() == golden_data) + + def testPositiveInfinityPacked(self): + golden_data = ('\xA2\x06\x04\x00\x00\x80\x7F' + '\xAA\x06\x08\x00\x00\x00\x00\x00\x00\xF0\x7F') + golden_message = unittest_pb2.TestPackedTypes() + golden_message.ParseFromString(golden_data) + self.assertTrue(IsPosInf(golden_message.packed_float[0])) + self.assertTrue(IsPosInf(golden_message.packed_double[0])) + self.assertTrue(golden_message.SerializeToString() == golden_data) + + def testNegativeInfinityPacked(self): + golden_data = ('\xA2\x06\x04\x00\x00\x80\xFF' + '\xAA\x06\x08\x00\x00\x00\x00\x00\x00\xF0\xFF') + golden_message = unittest_pb2.TestPackedTypes() + golden_message.ParseFromString(golden_data) + self.assertTrue(IsNegInf(golden_message.packed_float[0])) + self.assertTrue(IsNegInf(golden_message.packed_double[0])) + self.assertTrue(golden_message.SerializeToString() == golden_data) + + def testNotANumberPacked(self): + golden_data = ('\xA2\x06\x04\x00\x00\xC0\x7F' + '\xAA\x06\x08\x00\x00\x00\x00\x00\x00\xF8\x7F') + golden_message = unittest_pb2.TestPackedTypes() + golden_message.ParseFromString(golden_data) + self.assertTrue(isnan(golden_message.packed_float[0])) + self.assertTrue(isnan(golden_message.packed_double[0])) + self.assertTrue(golden_message.SerializeToString() == golden_data) + + def testExtremeFloatValues(self): + message = unittest_pb2.TestAllTypes() + + # Most positive exponent, no significand bits set. + kMostPosExponentNoSigBits = math.pow(2, 127) + message.optional_float = kMostPosExponentNoSigBits + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_float == kMostPosExponentNoSigBits) + + # Most positive exponent, one significand bit set. + kMostPosExponentOneSigBit = 1.5 * math.pow(2, 127) + message.optional_float = kMostPosExponentOneSigBit + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_float == kMostPosExponentOneSigBit) + + # Repeat last two cases with values of same magnitude, but negative. + message.optional_float = -kMostPosExponentNoSigBits + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_float == -kMostPosExponentNoSigBits) + + message.optional_float = -kMostPosExponentOneSigBit + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_float == -kMostPosExponentOneSigBit) + + # Most negative exponent, no significand bits set. + kMostNegExponentNoSigBits = math.pow(2, -127) + message.optional_float = kMostNegExponentNoSigBits + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_float == kMostNegExponentNoSigBits) + + # Most negative exponent, one significand bit set. + kMostNegExponentOneSigBit = 1.5 * math.pow(2, -127) + message.optional_float = kMostNegExponentOneSigBit + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_float == kMostNegExponentOneSigBit) + + # Repeat last two cases with values of the same magnitude, but negative. + message.optional_float = -kMostNegExponentNoSigBits + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_float == -kMostNegExponentNoSigBits) + + message.optional_float = -kMostNegExponentOneSigBit + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_float == -kMostNegExponentOneSigBit) + + def testExtremeFloatValues(self): + message = unittest_pb2.TestAllTypes() + + # Most positive exponent, no significand bits set. + kMostPosExponentNoSigBits = math.pow(2, 1023) + message.optional_double = kMostPosExponentNoSigBits + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_double == kMostPosExponentNoSigBits) + + # Most positive exponent, one significand bit set. + kMostPosExponentOneSigBit = 1.5 * math.pow(2, 1023) + message.optional_double = kMostPosExponentOneSigBit + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_double == kMostPosExponentOneSigBit) + + # Repeat last two cases with values of same magnitude, but negative. + message.optional_double = -kMostPosExponentNoSigBits + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_double == -kMostPosExponentNoSigBits) + + message.optional_double = -kMostPosExponentOneSigBit + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_double == -kMostPosExponentOneSigBit) + + # Most negative exponent, no significand bits set. + kMostNegExponentNoSigBits = math.pow(2, -1023) + message.optional_double = kMostNegExponentNoSigBits + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_double == kMostNegExponentNoSigBits) + + # Most negative exponent, one significand bit set. + kMostNegExponentOneSigBit = 1.5 * math.pow(2, -1023) + message.optional_double = kMostNegExponentOneSigBit + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_double == kMostNegExponentOneSigBit) + + # Repeat last two cases with values of the same magnitude, but negative. + message.optional_double = -kMostNegExponentNoSigBits + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_double == -kMostNegExponentNoSigBits) + + message.optional_double = -kMostNegExponentOneSigBit + message.ParseFromString(message.SerializeToString()) + self.assertTrue(message.optional_double == -kMostNegExponentOneSigBit) + + def testSortingRepeatedScalarFieldsDefaultComparator(self): + """Check some different types with the default comparator.""" + message = unittest_pb2.TestAllTypes() + + # TODO(mattp): would testing more scalar types strengthen test? + message.repeated_int32.append(1) + message.repeated_int32.append(3) + message.repeated_int32.append(2) + message.repeated_int32.sort() + self.assertEqual(message.repeated_int32[0], 1) + self.assertEqual(message.repeated_int32[1], 2) + self.assertEqual(message.repeated_int32[2], 3) + + message.repeated_float.append(1.1) + message.repeated_float.append(1.3) + message.repeated_float.append(1.2) + message.repeated_float.sort() + self.assertAlmostEqual(message.repeated_float[0], 1.1) + self.assertAlmostEqual(message.repeated_float[1], 1.2) + self.assertAlmostEqual(message.repeated_float[2], 1.3) + + message.repeated_string.append('a') + message.repeated_string.append('c') + message.repeated_string.append('b') + message.repeated_string.sort() + self.assertEqual(message.repeated_string[0], 'a') + self.assertEqual(message.repeated_string[1], 'b') + self.assertEqual(message.repeated_string[2], 'c') + + message.repeated_bytes.append('a') + message.repeated_bytes.append('c') + message.repeated_bytes.append('b') + message.repeated_bytes.sort() + self.assertEqual(message.repeated_bytes[0], 'a') + self.assertEqual(message.repeated_bytes[1], 'b') + self.assertEqual(message.repeated_bytes[2], 'c') + + def testSortingRepeatedScalarFieldsCustomComparator(self): + """Check some different types with custom comparator.""" + message = unittest_pb2.TestAllTypes() + + message.repeated_int32.append(-3) + message.repeated_int32.append(-2) + message.repeated_int32.append(-1) + message.repeated_int32.sort(lambda x,y: cmp(abs(x), abs(y))) + self.assertEqual(message.repeated_int32[0], -1) + self.assertEqual(message.repeated_int32[1], -2) + self.assertEqual(message.repeated_int32[2], -3) + + message.repeated_string.append('aaa') + message.repeated_string.append('bb') + message.repeated_string.append('c') + message.repeated_string.sort(lambda x,y: cmp(len(x), len(y))) + self.assertEqual(message.repeated_string[0], 'c') + self.assertEqual(message.repeated_string[1], 'bb') + self.assertEqual(message.repeated_string[2], 'aaa') + + def testSortingRepeatedCompositeFieldsCustomComparator(self): + """Check passing a custom comparator to sort a repeated composite field.""" + message = unittest_pb2.TestAllTypes() + + message.repeated_nested_message.add().bb = 1 + message.repeated_nested_message.add().bb = 3 + message.repeated_nested_message.add().bb = 2 + message.repeated_nested_message.add().bb = 6 + message.repeated_nested_message.add().bb = 5 + message.repeated_nested_message.add().bb = 4 + message.repeated_nested_message.sort(lambda x,y: cmp(x.bb, y.bb)) + self.assertEqual(message.repeated_nested_message[0].bb, 1) + self.assertEqual(message.repeated_nested_message[1].bb, 2) + self.assertEqual(message.repeated_nested_message[2].bb, 3) + self.assertEqual(message.repeated_nested_message[3].bb, 4) + self.assertEqual(message.repeated_nested_message[4].bb, 5) + self.assertEqual(message.repeated_nested_message[5].bb, 6) + if __name__ == '__main__': unittest.main() diff --git a/python/google/protobuf/internal/python_message.py b/python/google/protobuf/internal/python_message.py new file mode 100755 index 00000000..66fca918 --- /dev/null +++ b/python/google/protobuf/internal/python_message.py @@ -0,0 +1,1098 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# http://code.google.com/p/protobuf/ +# +# 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. + +# This code is meant to work on Python 2.4 and above only. +# +# TODO(robinson): Helpers for verbose, common checks like seeing if a +# descriptor's cpp_type is CPPTYPE_MESSAGE. + +"""Contains a metaclass and helper functions used to create +protocol message classes from Descriptor objects at runtime. + +Recall that a metaclass is the "type" of a class. +(A class is to a metaclass what an instance is to a class.) + +In this case, we use the GeneratedProtocolMessageType metaclass +to inject all the useful functionality into the classes +output by the protocol compiler at compile-time. + +The upshot of all this is that the real implementation +details for ALL pure-Python protocol buffers are *here in +this file*. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO +import struct +import weakref + +# We use "as" to avoid name collisions with variables. +from google.protobuf.internal import containers +from google.protobuf.internal import decoder +from google.protobuf.internal import encoder +from google.protobuf.internal import message_listener as message_listener_mod +from google.protobuf.internal import type_checkers +from google.protobuf.internal import wire_format +from google.protobuf import descriptor as descriptor_mod +from google.protobuf import message as message_mod +from google.protobuf import text_format + +_FieldDescriptor = descriptor_mod.FieldDescriptor + + +def NewMessage(descriptor, dictionary): + _AddClassAttributesForNestedExtensions(descriptor, dictionary) + _AddSlots(descriptor, dictionary) + + +def InitMessage(descriptor, cls): + cls._decoders_by_tag = {} + cls._extensions_by_name = {} + cls._extensions_by_number = {} + if (descriptor.has_options and + descriptor.GetOptions().message_set_wire_format): + cls._decoders_by_tag[decoder.MESSAGE_SET_ITEM_TAG] = ( + decoder.MessageSetItemDecoder(cls._extensions_by_number)) + + # Attach stuff to each FieldDescriptor for quick lookup later on. + for field in descriptor.fields: + _AttachFieldHelpers(cls, field) + + _AddEnumValues(descriptor, cls) + _AddInitMethod(descriptor, cls) + _AddPropertiesForFields(descriptor, cls) + _AddPropertiesForExtensions(descriptor, cls) + _AddStaticMethods(cls) + _AddMessageMethods(descriptor, cls) + _AddPrivateHelperMethods(cls) + + +# Stateless helpers for GeneratedProtocolMessageType below. +# Outside clients should not access these directly. +# +# I opted not to make any of these methods on the metaclass, to make it more +# clear that I'm not really using any state there and to keep clients from +# thinking that they have direct access to these construction helpers. + + +def _PropertyName(proto_field_name): + """Returns the name of the public property attribute which + clients can use to get and (in some cases) set the value + of a protocol message field. + + Args: + proto_field_name: The protocol message field name, exactly + as it appears (or would appear) in a .proto file. + """ + # TODO(robinson): Escape Python keywords (e.g., yield), and test this support. + # nnorwitz makes my day by writing: + # """ + # FYI. See the keyword module in the stdlib. This could be as simple as: + # + # if keyword.iskeyword(proto_field_name): + # return proto_field_name + "_" + # return proto_field_name + # """ + # Kenton says: The above is a BAD IDEA. People rely on being able to use + # getattr() and setattr() to reflectively manipulate field values. If we + # rename the properties, then every such user has to also make sure to apply + # the same transformation. Note that currently if you name a field "yield", + # you can still access it just fine using getattr/setattr -- it's not even + # that cumbersome to do so. + # TODO(kenton): Remove this method entirely if/when everyone agrees with my + # position. + return proto_field_name + + +def _VerifyExtensionHandle(message, extension_handle): + """Verify that the given extension handle is valid.""" + + if not isinstance(extension_handle, _FieldDescriptor): + raise KeyError('HasExtension() expects an extension handle, got: %s' % + extension_handle) + + if not extension_handle.is_extension: + raise KeyError('"%s" is not an extension.' % extension_handle.full_name) + + if extension_handle.containing_type is not message.DESCRIPTOR: + raise KeyError('Extension "%s" extends message type "%s", but this ' + 'message is of type "%s".' % + (extension_handle.full_name, + extension_handle.containing_type.full_name, + message.DESCRIPTOR.full_name)) + + +def _AddSlots(message_descriptor, dictionary): + """Adds a __slots__ entry to dictionary, containing the names of all valid + attributes for this message type. + + Args: + message_descriptor: A Descriptor instance describing this message type. + dictionary: Class dictionary to which we'll add a '__slots__' entry. + """ + dictionary['__slots__'] = ['_cached_byte_size', + '_cached_byte_size_dirty', + '_fields', + '_is_present_in_parent', + '_listener', + '_listener_for_children', + '__weakref__'] + + +def _IsMessageSetExtension(field): + return (field.is_extension and + field.containing_type.has_options and + field.containing_type.GetOptions().message_set_wire_format and + field.type == _FieldDescriptor.TYPE_MESSAGE and + field.message_type == field.extension_scope and + field.label == _FieldDescriptor.LABEL_OPTIONAL) + + +def _AttachFieldHelpers(cls, field_descriptor): + is_repeated = (field_descriptor.label == _FieldDescriptor.LABEL_REPEATED) + is_packed = (field_descriptor.has_options and + field_descriptor.GetOptions().packed) + + if _IsMessageSetExtension(field_descriptor): + field_encoder = encoder.MessageSetItemEncoder(field_descriptor.number) + sizer = encoder.MessageSetItemSizer(field_descriptor.number) + else: + field_encoder = type_checkers.TYPE_TO_ENCODER[field_descriptor.type]( + field_descriptor.number, is_repeated, is_packed) + sizer = type_checkers.TYPE_TO_SIZER[field_descriptor.type]( + field_descriptor.number, is_repeated, is_packed) + + field_descriptor._encoder = field_encoder + field_descriptor._sizer = sizer + field_descriptor._default_constructor = _DefaultValueConstructorForField( + field_descriptor) + + def AddDecoder(wiretype, is_packed): + tag_bytes = encoder.TagBytes(field_descriptor.number, wiretype) + cls._decoders_by_tag[tag_bytes] = ( + type_checkers.TYPE_TO_DECODER[field_descriptor.type]( + field_descriptor.number, is_repeated, is_packed, + field_descriptor, field_descriptor._default_constructor)) + + AddDecoder(type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type], + False) + + if is_repeated and wire_format.IsTypePackable(field_descriptor.type): + # To support wire compatibility of adding packed = true, add a decoder for + # packed values regardless of the field's options. + AddDecoder(wire_format.WIRETYPE_LENGTH_DELIMITED, True) + + +def _AddClassAttributesForNestedExtensions(descriptor, dictionary): + extension_dict = descriptor.extensions_by_name + for extension_name, extension_field in extension_dict.iteritems(): + assert extension_name not in dictionary + dictionary[extension_name] = extension_field + + +def _AddEnumValues(descriptor, cls): + """Sets class-level attributes for all enum fields defined in this message. + + Args: + descriptor: Descriptor object for this message type. + cls: Class we're constructing for this message type. + """ + for enum_type in descriptor.enum_types: + for enum_value in enum_type.values: + setattr(cls, enum_value.name, enum_value.number) + + +def _DefaultValueConstructorForField(field): + """Returns a function which returns a default value for a field. + + Args: + field: FieldDescriptor object for this field. + + The returned function has one argument: + message: Message instance containing this field, or a weakref proxy + of same. + + That function in turn returns a default value for this field. The default + value may refer back to |message| via a weak reference. + """ + + if field.label == _FieldDescriptor.LABEL_REPEATED: + if field.default_value != []: + raise ValueError('Repeated field default value not empty list: %s' % ( + field.default_value)) + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + # We can't look at _concrete_class yet since it might not have + # been set. (Depends on order in which we initialize the classes). + message_type = field.message_type + def MakeRepeatedMessageDefault(message): + return containers.RepeatedCompositeFieldContainer( + message._listener_for_children, field.message_type) + return MakeRepeatedMessageDefault + else: + type_checker = type_checkers.GetTypeChecker(field.cpp_type, field.type) + def MakeRepeatedScalarDefault(message): + return containers.RepeatedScalarFieldContainer( + message._listener_for_children, type_checker) + return MakeRepeatedScalarDefault + + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + # _concrete_class may not yet be initialized. + message_type = field.message_type + def MakeSubMessageDefault(message): + result = message_type._concrete_class() + result._SetListener(message._listener_for_children) + return result + return MakeSubMessageDefault + + def MakeScalarDefault(message): + return field.default_value + return MakeScalarDefault + + +def _AddInitMethod(message_descriptor, cls): + """Adds an __init__ method to cls.""" + fields = message_descriptor.fields + def init(self, **kwargs): + self._cached_byte_size = 0 + self._cached_byte_size_dirty = len(kwargs) > 0 + self._fields = {} + self._is_present_in_parent = False + self._listener = message_listener_mod.NullMessageListener() + self._listener_for_children = _Listener(self) + for field_name, field_value in kwargs.iteritems(): + field = _GetFieldByName(message_descriptor, field_name) + if field is None: + raise TypeError("%s() got an unexpected keyword argument '%s'" % + (message_descriptor.name, field_name)) + if field.label == _FieldDescriptor.LABEL_REPEATED: + copy = field._default_constructor(self) + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: # Composite + for val in field_value: + copy.add().MergeFrom(val) + else: # Scalar + copy.extend(field_value) + self._fields[field] = copy + elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + copy = field._default_constructor(self) + copy.MergeFrom(field_value) + self._fields[field] = copy + else: + setattr(self, field_name, field_value) + + init.__module__ = None + init.__doc__ = None + cls.__init__ = init + + +def _GetFieldByName(message_descriptor, field_name): + """Returns a field descriptor by field name. + + Args: + message_descriptor: A Descriptor describing all fields in message. + field_name: The name of the field to retrieve. + Returns: + The field descriptor associated with the field name. + """ + try: + return message_descriptor.fields_by_name[field_name] + except KeyError: + raise ValueError('Protocol message has no "%s" field.' % field_name) + + +def _AddPropertiesForFields(descriptor, cls): + """Adds properties for all fields in this protocol message type.""" + for field in descriptor.fields: + _AddPropertiesForField(field, cls) + + if descriptor.is_extendable: + # _ExtensionDict is just an adaptor with no state so we allocate a new one + # every time it is accessed. + cls.Extensions = property(lambda self: _ExtensionDict(self)) + + +def _AddPropertiesForField(field, cls): + """Adds a public property for a protocol message field. + Clients can use this property to get and (in the case + of non-repeated scalar fields) directly set the value + of a protocol message field. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + # Catch it if we add other types that we should + # handle specially here. + assert _FieldDescriptor.MAX_CPPTYPE == 10 + + constant_name = field.name.upper() + "_FIELD_NUMBER" + setattr(cls, constant_name, field.number) + + if field.label == _FieldDescriptor.LABEL_REPEATED: + _AddPropertiesForRepeatedField(field, cls) + elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + _AddPropertiesForNonRepeatedCompositeField(field, cls) + else: + _AddPropertiesForNonRepeatedScalarField(field, cls) + + +def _AddPropertiesForRepeatedField(field, cls): + """Adds a public property for a "repeated" protocol message field. Clients + can use this property to get the value of the field, which will be either a + _RepeatedScalarFieldContainer or _RepeatedCompositeFieldContainer (see + below). + + Note that when clients add values to these containers, we perform + type-checking in the case of repeated scalar fields, and we also set any + necessary "has" bits as a side-effect. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + proto_field_name = field.name + property_name = _PropertyName(proto_field_name) + + def getter(self): + field_value = self._fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + + # Atomically check if another thread has preempted us and, if not, swap + # in the new object we just created. If someone has preempted us, we + # take that object and discard ours. + # WARNING: We are relying on setdefault() being atomic. This is true + # in CPython but we haven't investigated others. This warning appears + # in several other locations in this file. + field_value = self._fields.setdefault(field, field_value) + return field_value + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + # We define a setter just so we can throw an exception with a more + # helpful error message. + def setter(self, new_value): + raise AttributeError('Assignment not allowed to repeated field ' + '"%s" in protocol message object.' % proto_field_name) + + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, property(getter, setter, doc=doc)) + + +def _AddPropertiesForNonRepeatedScalarField(field, cls): + """Adds a public property for a nonrepeated, scalar protocol message field. + Clients can use this property to get and directly set the value of the field. + Note that when the client sets the value of a field by using this property, + all necessary "has" bits are set as a side-effect, and we also perform + type-checking. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + proto_field_name = field.name + property_name = _PropertyName(proto_field_name) + type_checker = type_checkers.GetTypeChecker(field.cpp_type, field.type) + default_value = field.default_value + valid_values = set() + + def getter(self): + return self._fields.get(field, default_value) + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + def setter(self, new_value): + type_checker.CheckValue(new_value) + self._fields[field] = new_value + # Check _cached_byte_size_dirty inline to improve performance, since scalar + # setters are called frequently. + if not self._cached_byte_size_dirty: + self._Modified() + + setter.__module__ = None + setter.__doc__ = 'Setter for %s.' % proto_field_name + + # Add a property to encapsulate the getter/setter. + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, property(getter, setter, doc=doc)) + + +def _AddPropertiesForNonRepeatedCompositeField(field, cls): + """Adds a public property for a nonrepeated, composite protocol message field. + A composite field is a "group" or "message" field. + + Clients can use this property to get the value of the field, but cannot + assign to the property directly. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + # TODO(robinson): Remove duplication with similar method + # for non-repeated scalars. + proto_field_name = field.name + property_name = _PropertyName(proto_field_name) + message_type = field.message_type + + def getter(self): + field_value = self._fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = message_type._concrete_class() + field_value._SetListener(self._listener_for_children) + + # Atomically check if another thread has preempted us and, if not, swap + # in the new object we just created. If someone has preempted us, we + # take that object and discard ours. + # WARNING: We are relying on setdefault() being atomic. This is true + # in CPython but we haven't investigated others. This warning appears + # in several other locations in this file. + field_value = self._fields.setdefault(field, field_value) + return field_value + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + # We define a setter just so we can throw an exception with a more + # helpful error message. + def setter(self, new_value): + raise AttributeError('Assignment not allowed to composite field ' + '"%s" in protocol message object.' % proto_field_name) + + # Add a property to encapsulate the getter. + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, property(getter, setter, doc=doc)) + + +def _AddPropertiesForExtensions(descriptor, cls): + """Adds properties for all fields in this protocol message type.""" + extension_dict = descriptor.extensions_by_name + for extension_name, extension_field in extension_dict.iteritems(): + constant_name = extension_name.upper() + "_FIELD_NUMBER" + setattr(cls, constant_name, extension_field.number) + + +def _AddStaticMethods(cls): + # TODO(robinson): This probably needs to be thread-safe(?) + def RegisterExtension(extension_handle): + extension_handle.containing_type = cls.DESCRIPTOR + _AttachFieldHelpers(cls, extension_handle) + + # Try to insert our extension, failing if an extension with the same number + # already exists. + actual_handle = cls._extensions_by_number.setdefault( + extension_handle.number, extension_handle) + if actual_handle is not extension_handle: + raise AssertionError( + 'Extensions "%s" and "%s" both try to extend message type "%s" with ' + 'field number %d.' % + (extension_handle.full_name, actual_handle.full_name, + cls.DESCRIPTOR.full_name, extension_handle.number)) + + cls._extensions_by_name[extension_handle.full_name] = extension_handle + + handle = extension_handle # avoid line wrapping + if _IsMessageSetExtension(handle): + # MessageSet extension. Also register under type name. + cls._extensions_by_name[ + extension_handle.message_type.full_name] = extension_handle + + cls.RegisterExtension = staticmethod(RegisterExtension) + + def FromString(s): + message = cls() + message.MergeFromString(s) + return message + cls.FromString = staticmethod(FromString) + + +def _IsPresent(item): + """Given a (FieldDescriptor, value) tuple from _fields, return true if the + value should be included in the list returned by ListFields().""" + + if item[0].label == _FieldDescriptor.LABEL_REPEATED: + return bool(item[1]) + elif item[0].cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + return item[1]._is_present_in_parent + else: + return True + + +def _AddListFieldsMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def ListFields(self): + all_fields = [item for item in self._fields.iteritems() if _IsPresent(item)] + all_fields.sort(key = lambda item: item[0].number) + return all_fields + + cls.ListFields = ListFields + + +def _AddHasFieldMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + singular_fields = {} + for field in message_descriptor.fields: + if field.label != _FieldDescriptor.LABEL_REPEATED: + singular_fields[field.name] = field + + def HasField(self, field_name): + try: + field = singular_fields[field_name] + except KeyError: + raise ValueError( + 'Protocol message has no singular "%s" field.' % field_name) + + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + value = self._fields.get(field) + return value is not None and value._is_present_in_parent + else: + return field in self._fields + cls.HasField = HasField + + +def _AddClearFieldMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def ClearField(self, field_name): + try: + field = message_descriptor.fields_by_name[field_name] + except KeyError: + raise ValueError('Protocol message has no "%s" field.' % field_name) + + if field in self._fields: + # Note: If the field is a sub-message, its listener will still point + # at us. That's fine, because the worst than can happen is that it + # will call _Modified() and invalidate our byte size. Big deal. + del self._fields[field] + + # Always call _Modified() -- even if nothing was changed, this is + # a mutating method, and thus calling it should cause the field to become + # present in the parent message. + self._Modified() + + cls.ClearField = ClearField + + +def _AddClearExtensionMethod(cls): + """Helper for _AddMessageMethods().""" + def ClearExtension(self, extension_handle): + _VerifyExtensionHandle(self, extension_handle) + + # Similar to ClearField(), above. + if extension_handle in self._fields: + del self._fields[extension_handle] + self._Modified() + cls.ClearExtension = ClearExtension + + +def _AddClearMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def Clear(self): + # Clear fields. + self._fields = {} + self._Modified() + cls.Clear = Clear + + +def _AddHasExtensionMethod(cls): + """Helper for _AddMessageMethods().""" + def HasExtension(self, extension_handle): + _VerifyExtensionHandle(self, extension_handle) + if extension_handle.label == _FieldDescriptor.LABEL_REPEATED: + raise KeyError('"%s" is repeated.' % extension_handle.full_name) + + if extension_handle.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + value = self._fields.get(extension_handle) + return value is not None and value._is_present_in_parent + else: + return extension_handle in self._fields + cls.HasExtension = HasExtension + + +def _AddEqualsMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def __eq__(self, other): + if (not isinstance(other, message_mod.Message) or + other.DESCRIPTOR != self.DESCRIPTOR): + return False + + if self is other: + return True + + return self.ListFields() == other.ListFields() + + cls.__eq__ = __eq__ + + +def _AddStrMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def __str__(self): + return text_format.MessageToString(self) + cls.__str__ = __str__ + + +def _AddUnicodeMethod(unused_message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def __unicode__(self): + return text_format.MessageToString(self, as_utf8=True).decode('utf-8') + cls.__unicode__ = __unicode__ + + +def _AddSetListenerMethod(cls): + """Helper for _AddMessageMethods().""" + def SetListener(self, listener): + if listener is None: + self._listener = message_listener_mod.NullMessageListener() + else: + self._listener = listener + cls._SetListener = SetListener + + +def _BytesForNonRepeatedElement(value, field_number, field_type): + """Returns the number of bytes needed to serialize a non-repeated element. + The returned byte count includes space for tag information and any + other additional space associated with serializing value. + + Args: + value: Value we're serializing. + field_number: Field number of this value. (Since the field number + is stored as part of a varint-encoded tag, this has an impact + on the total bytes required to serialize the value). + field_type: The type of the field. One of the TYPE_* constants + within FieldDescriptor. + """ + try: + fn = type_checkers.TYPE_TO_BYTE_SIZE_FN[field_type] + return fn(field_number, value) + except KeyError: + raise message_mod.EncodeError('Unrecognized field type: %d' % field_type) + + +def _AddByteSizeMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def ByteSize(self): + if not self._cached_byte_size_dirty: + return self._cached_byte_size + + size = 0 + for field_descriptor, field_value in self.ListFields(): + size += field_descriptor._sizer(field_value) + + self._cached_byte_size = size + self._cached_byte_size_dirty = False + self._listener_for_children.dirty = False + return size + + cls.ByteSize = ByteSize + + +def _AddSerializeToStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def SerializeToString(self): + # Check if the message has all of its required fields set. + errors = [] + if not self.IsInitialized(): + raise message_mod.EncodeError( + 'Message is missing required fields: ' + + ','.join(self.FindInitializationErrors())) + return self.SerializePartialToString() + cls.SerializeToString = SerializeToString + + +def _AddSerializePartialToStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def SerializePartialToString(self): + out = StringIO() + self._InternalSerialize(out.write) + return out.getvalue() + cls.SerializePartialToString = SerializePartialToString + + def InternalSerialize(self, write_bytes): + for field_descriptor, field_value in self.ListFields(): + field_descriptor._encoder(write_bytes, field_value) + cls._InternalSerialize = InternalSerialize + + +def _AddMergeFromStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def MergeFromString(self, serialized): + length = len(serialized) + try: + if self._InternalParse(serialized, 0, length) != length: + # The only reason _InternalParse would return early is if it + # encountered an end-group tag. + raise message_mod.DecodeError('Unexpected end-group tag.') + except IndexError: + raise message_mod.DecodeError('Truncated message.') + except struct.error, e: + raise message_mod.DecodeError(e) + return length # Return this for legacy reasons. + cls.MergeFromString = MergeFromString + + local_ReadTag = decoder.ReadTag + local_SkipField = decoder.SkipField + decoders_by_tag = cls._decoders_by_tag + + def InternalParse(self, buffer, pos, end): + self._Modified() + field_dict = self._fields + while pos != end: + (tag_bytes, new_pos) = local_ReadTag(buffer, pos) + field_decoder = decoders_by_tag.get(tag_bytes) + if field_decoder is None: + new_pos = local_SkipField(buffer, new_pos, end, tag_bytes) + if new_pos == -1: + return pos + pos = new_pos + else: + pos = field_decoder(buffer, new_pos, end, self, field_dict) + return pos + cls._InternalParse = InternalParse + + +def _AddIsInitializedMethod(message_descriptor, cls): + """Adds the IsInitialized and FindInitializationError methods to the + protocol message class.""" + + required_fields = [field for field in message_descriptor.fields + if field.label == _FieldDescriptor.LABEL_REQUIRED] + + def IsInitialized(self, errors=None): + """Checks if all required fields of a message are set. + + Args: + errors: A list which, if provided, will be populated with the field + paths of all missing required fields. + + Returns: + True iff the specified message has all required fields set. + """ + + # Performance is critical so we avoid HasField() and ListFields(). + + for field in required_fields: + if (field not in self._fields or + (field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE and + not self._fields[field]._is_present_in_parent)): + if errors is not None: + errors.extend(self.FindInitializationErrors()) + return False + + for field, value in self._fields.iteritems(): + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if field.label == _FieldDescriptor.LABEL_REPEATED: + for element in value: + if not element.IsInitialized(): + if errors is not None: + errors.extend(self.FindInitializationErrors()) + return False + elif value._is_present_in_parent and not value.IsInitialized(): + if errors is not None: + errors.extend(self.FindInitializationErrors()) + return False + + return True + + cls.IsInitialized = IsInitialized + + def FindInitializationErrors(self): + """Finds required fields which are not initialized. + + Returns: + A list of strings. Each string is a path to an uninitialized field from + the top-level message, e.g. "foo.bar[5].baz". + """ + + errors = [] # simplify things + + for field in required_fields: + if not self.HasField(field.name): + errors.append(field.name) + + for field, value in self.ListFields(): + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if field.is_extension: + name = "(%s)" % field.full_name + else: + name = field.name + + if field.label == _FieldDescriptor.LABEL_REPEATED: + for i in xrange(len(value)): + element = value[i] + prefix = "%s[%d]." % (name, i) + sub_errors = element.FindInitializationErrors() + errors += [ prefix + error for error in sub_errors ] + else: + prefix = name + "." + sub_errors = value.FindInitializationErrors() + errors += [ prefix + error for error in sub_errors ] + + return errors + + cls.FindInitializationErrors = FindInitializationErrors + + +def _AddMergeFromMethod(cls): + LABEL_REPEATED = _FieldDescriptor.LABEL_REPEATED + CPPTYPE_MESSAGE = _FieldDescriptor.CPPTYPE_MESSAGE + + def MergeFrom(self, msg): + if not isinstance(msg, cls): + raise TypeError( + "Parameter to MergeFrom() must be instance of same class.") + + assert msg is not self + self._Modified() + + fields = self._fields + + for field, value in msg._fields.iteritems(): + if field.label == LABEL_REPEATED: + field_value = fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + fields[field] = field_value + field_value.MergeFrom(value) + elif field.cpp_type == CPPTYPE_MESSAGE: + if value._is_present_in_parent: + field_value = fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + fields[field] = field_value + field_value.MergeFrom(value) + else: + self._fields[field] = value + cls.MergeFrom = MergeFrom + + +def _AddMessageMethods(message_descriptor, cls): + """Adds implementations of all Message methods to cls.""" + _AddListFieldsMethod(message_descriptor, cls) + _AddHasFieldMethod(message_descriptor, cls) + _AddClearFieldMethod(message_descriptor, cls) + if message_descriptor.is_extendable: + _AddClearExtensionMethod(cls) + _AddHasExtensionMethod(cls) + _AddClearMethod(message_descriptor, cls) + _AddEqualsMethod(message_descriptor, cls) + _AddStrMethod(message_descriptor, cls) + _AddUnicodeMethod(message_descriptor, cls) + _AddSetListenerMethod(cls) + _AddByteSizeMethod(message_descriptor, cls) + _AddSerializeToStringMethod(message_descriptor, cls) + _AddSerializePartialToStringMethod(message_descriptor, cls) + _AddMergeFromStringMethod(message_descriptor, cls) + _AddIsInitializedMethod(message_descriptor, cls) + _AddMergeFromMethod(cls) + + +def _AddPrivateHelperMethods(cls): + """Adds implementation of private helper methods to cls.""" + + def Modified(self): + """Sets the _cached_byte_size_dirty bit to true, + and propagates this to our listener iff this was a state change. + """ + + # Note: Some callers check _cached_byte_size_dirty before calling + # _Modified() as an extra optimization. So, if this method is ever + # changed such that it does stuff even when _cached_byte_size_dirty is + # already true, the callers need to be updated. + if not self._cached_byte_size_dirty: + self._cached_byte_size_dirty = True + self._listener_for_children.dirty = True + self._is_present_in_parent = True + self._listener.Modified() + + cls._Modified = Modified + cls.SetInParent = Modified + + +class _Listener(object): + + """MessageListener implementation that a parent message registers with its + child message. + + In order to support semantics like: + + foo.bar.baz.qux = 23 + assert foo.HasField('bar') + + ...child objects must have back references to their parents. + This helper class is at the heart of this support. + """ + + def __init__(self, parent_message): + """Args: + parent_message: The message whose _Modified() method we should call when + we receive Modified() messages. + """ + # This listener establishes a back reference from a child (contained) object + # to its parent (containing) object. We make this a weak reference to avoid + # creating cyclic garbage when the client finishes with the 'parent' object + # in the tree. + if isinstance(parent_message, weakref.ProxyType): + self._parent_message_weakref = parent_message + else: + self._parent_message_weakref = weakref.proxy(parent_message) + + # As an optimization, we also indicate directly on the listener whether + # or not the parent message is dirty. This way we can avoid traversing + # up the tree in the common case. + self.dirty = False + + def Modified(self): + if self.dirty: + return + try: + # Propagate the signal to our parents iff this is the first field set. + self._parent_message_weakref._Modified() + except ReferenceError: + # We can get here if a client has kept a reference to a child object, + # and is now setting a field on it, but the child's parent has been + # garbage-collected. This is not an error. + pass + + +# TODO(robinson): Move elsewhere? This file is getting pretty ridiculous... +# TODO(robinson): Unify error handling of "unknown extension" crap. +# TODO(robinson): Support iteritems()-style iteration over all +# extensions with the "has" bits turned on? +class _ExtensionDict(object): + + """Dict-like container for supporting an indexable "Extensions" + field on proto instances. + + Note that in all cases we expect extension handles to be + FieldDescriptors. + """ + + def __init__(self, extended_message): + """extended_message: Message instance for which we are the Extensions dict. + """ + + self._extended_message = extended_message + + def __getitem__(self, extension_handle): + """Returns the current value of the given extension handle.""" + + _VerifyExtensionHandle(self._extended_message, extension_handle) + + result = self._extended_message._fields.get(extension_handle) + if result is not None: + return result + + if extension_handle.label == _FieldDescriptor.LABEL_REPEATED: + result = extension_handle._default_constructor(self._extended_message) + elif extension_handle.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + result = extension_handle.message_type._concrete_class() + try: + result._SetListener(self._extended_message._listener_for_children) + except ReferenceError: + pass + else: + # Singular scalar -- just return the default without inserting into the + # dict. + return extension_handle.default_value + + # Atomically check if another thread has preempted us and, if not, swap + # in the new object we just created. If someone has preempted us, we + # take that object and discard ours. + # WARNING: We are relying on setdefault() being atomic. This is true + # in CPython but we haven't investigated others. This warning appears + # in several other locations in this file. + result = self._extended_message._fields.setdefault( + extension_handle, result) + + return result + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + + my_fields = self._extended_message.ListFields() + other_fields = other._extended_message.ListFields() + + # Get rid of non-extension fields. + my_fields = [ field for field in my_fields if field.is_extension ] + other_fields = [ field for field in other_fields if field.is_extension ] + + return my_fields == other_fields + + def __ne__(self, other): + return not self == other + + def __hash__(self): + raise TypeError('unhashable object') + + # Note that this is only meaningful for non-repeated, scalar extension + # fields. Note also that we may have to call _Modified() when we do + # successfully set a field this way, to set any necssary "has" bits in the + # ancestors of the extended message. + def __setitem__(self, extension_handle, value): + """If extension_handle specifies a non-repeated, scalar extension + field, sets the value of that field. + """ + + _VerifyExtensionHandle(self._extended_message, extension_handle) + + if (extension_handle.label == _FieldDescriptor.LABEL_REPEATED or + extension_handle.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE): + raise TypeError( + 'Cannot assign to extension "%s" because it is a repeated or ' + 'composite type.' % extension_handle.full_name) + + # It's slightly wasteful to lookup the type checker each time, + # but we expect this to be a vanishingly uncommon case anyway. + type_checker = type_checkers.GetTypeChecker( + extension_handle.cpp_type, extension_handle.type) + type_checker.CheckValue(value) + self._extended_message._fields[extension_handle] = value + self._extended_message._Modified() + + def _FindExtensionByName(self, name): + """Tries to find a known extension with the specified name. + + Args: + name: Extension full name. + + Returns: + Extension field descriptor. + """ + return self._extended_message._extensions_by_name.get(name, None) diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py index 54eeebe6..7b9d3398 100755 --- a/python/google/protobuf/internal/reflection_test.py +++ b/python/google/protobuf/internal/reflection_test.py @@ -41,8 +41,6 @@ import operator import struct import unittest -# TODO(robinson): When we split this test in two, only some of these imports -# will be necessary in each test. from google.protobuf import unittest_import_pb2 from google.protobuf import unittest_mset_pb2 from google.protobuf import unittest_pb2 @@ -50,6 +48,7 @@ from google.protobuf import descriptor_pb2 from google.protobuf import descriptor from google.protobuf import message from google.protobuf import reflection +from google.protobuf.internal import api_implementation from google.protobuf.internal import more_extensions_pb2 from google.protobuf.internal import more_messages_pb2 from google.protobuf.internal import wire_format @@ -104,10 +103,10 @@ class _MiniDecoder(object): class ReflectionTest(unittest.TestCase): - def assertIs(self, values, others): + def assertListsEqual(self, values, others): self.assertEqual(len(values), len(others)) for i in range(len(values)): - self.assertTrue(values[i] is others[i]) + self.assertEqual(values[i], others[i]) def testScalarConstructor(self): # Constructor with only scalar types should succeed. @@ -201,15 +200,24 @@ class ReflectionTest(unittest.TestCase): list(proto.repeated_foreign_message)) def testConstructorTypeError(self): - self.assertRaises(TypeError, unittest_pb2.TestAllTypes, optional_int32="foo") - self.assertRaises(TypeError, unittest_pb2.TestAllTypes, optional_string=1234) - self.assertRaises(TypeError, unittest_pb2.TestAllTypes, optional_nested_message=1234) - self.assertRaises(TypeError, unittest_pb2.TestAllTypes, repeated_int32=1234) - self.assertRaises(TypeError, unittest_pb2.TestAllTypes, repeated_int32=["foo"]) - self.assertRaises(TypeError, unittest_pb2.TestAllTypes, repeated_string=1234) - self.assertRaises(TypeError, unittest_pb2.TestAllTypes, repeated_string=[1234]) - self.assertRaises(TypeError, unittest_pb2.TestAllTypes, repeated_nested_message=1234) - self.assertRaises(TypeError, unittest_pb2.TestAllTypes, repeated_nested_message=[1234]) + self.assertRaises( + TypeError, unittest_pb2.TestAllTypes, optional_int32="foo") + self.assertRaises( + TypeError, unittest_pb2.TestAllTypes, optional_string=1234) + self.assertRaises( + TypeError, unittest_pb2.TestAllTypes, optional_nested_message=1234) + self.assertRaises( + TypeError, unittest_pb2.TestAllTypes, repeated_int32=1234) + self.assertRaises( + TypeError, unittest_pb2.TestAllTypes, repeated_int32=["foo"]) + self.assertRaises( + TypeError, unittest_pb2.TestAllTypes, repeated_string=1234) + self.assertRaises( + TypeError, unittest_pb2.TestAllTypes, repeated_string=[1234]) + self.assertRaises( + TypeError, unittest_pb2.TestAllTypes, repeated_nested_message=1234) + self.assertRaises( + TypeError, unittest_pb2.TestAllTypes, repeated_nested_message=[1234]) def testConstructorInvalidatesCachedByteSize(self): message = unittest_pb2.TestAllTypes(optional_int32 = 12) @@ -311,11 +319,14 @@ class ReflectionTest(unittest.TestCase): self.assertEqual(0, getattr(composite_field, scalar_field_name)) # Finally, ensure that modifications to the old composite field object - # don't have any effect on the parent. + # don't have any effect on the parent. Possible only with the pure-python + # implementation of the API. # # (NOTE that when we clear the composite field in the parent, we actually # don't recursively clear down the tree. Instead, we just disconnect the # cleared composite from the tree.) + if api_implementation.Type() != 'python': + return self.assertTrue(old_composite_field is not composite_field) setattr(old_composite_field, scalar_field_name, new_val) self.assertTrue(not composite_field.HasField(scalar_field_name)) @@ -337,6 +348,8 @@ class ReflectionTest(unittest.TestCase): nested.bb = 23 def testDisconnectingNestedMessageBeforeSettingField(self): + if api_implementation.Type() != 'python': + return proto = unittest_pb2.TestAllTypes() nested = proto.optional_nested_message proto.ClearField('optional_nested_message') # Should disconnect from parent @@ -526,7 +539,6 @@ class ReflectionTest(unittest.TestCase): # proto.nonexistent_field = 23 should fail as well. self.assertRaises(AttributeError, setattr, proto, 'nonexistent_field', 23) - # TODO(robinson): Add type-safety check for enums. def testSingleScalarTypeSafety(self): proto = unittest_pb2.TestAllTypes() self.assertRaises(TypeError, setattr, proto, 'optional_int32', 1.1) @@ -538,7 +550,9 @@ class ReflectionTest(unittest.TestCase): def TestMinAndMaxIntegers(field_name, expected_min, expected_max): pb = unittest_pb2.TestAllTypes() setattr(pb, field_name, expected_min) + self.assertEqual(expected_min, getattr(pb, field_name)) setattr(pb, field_name, expected_max) + self.assertEqual(expected_max, getattr(pb, field_name)) self.assertRaises(ValueError, setattr, pb, field_name, expected_min - 1) self.assertRaises(ValueError, setattr, pb, field_name, expected_max + 1) @@ -546,7 +560,33 @@ class ReflectionTest(unittest.TestCase): TestMinAndMaxIntegers('optional_uint32', 0, 0xffffffff) TestMinAndMaxIntegers('optional_int64', -(1 << 63), (1 << 63) - 1) TestMinAndMaxIntegers('optional_uint64', 0, 0xffffffffffffffff) - TestMinAndMaxIntegers('optional_nested_enum', -(1 << 31), (1 << 31) - 1) + + pb = unittest_pb2.TestAllTypes() + pb.optional_nested_enum = 1 + self.assertEqual(1, pb.optional_nested_enum) + + # Invalid enum values. + pb.optional_nested_enum = 0 + self.assertEqual(0, pb.optional_nested_enum) + + bytes_size_before = pb.ByteSize() + + pb.optional_nested_enum = 4 + self.assertEqual(4, pb.optional_nested_enum) + + pb.optional_nested_enum = 0 + self.assertEqual(0, pb.optional_nested_enum) + + # Make sure that setting the same enum field doesn't just add unknown + # fields (but overwrites them). + self.assertEqual(bytes_size_before, pb.ByteSize()) + + # Is the invalid value preserved after serialization? + serialized = pb.SerializeToString() + pb2 = unittest_pb2.TestAllTypes() + pb2.ParseFromString(serialized) + self.assertEqual(0, pb2.optional_nested_enum) + self.assertEqual(pb, pb2) def testRepeatedScalarTypeSafety(self): proto = unittest_pb2.TestAllTypes() @@ -560,11 +600,19 @@ class ReflectionTest(unittest.TestCase): self.assertRaises(IndexError, proto.repeated_int32.__setitem__, 500, 23) self.assertRaises(TypeError, proto.repeated_int32.__setitem__, 0, 'abc') + # Repeated enums tests. + #proto.repeated_nested_enum.append(0) + def testSingleScalarGettersAndSetters(self): proto = unittest_pb2.TestAllTypes() self.assertEqual(0, proto.optional_int32) proto.optional_int32 = 1 self.assertEqual(1, proto.optional_int32) + + proto.optional_uint64 = 0xffffffffffff + self.assertEqual(0xffffffffffff, proto.optional_uint64) + proto.optional_uint64 = 0xffffffffffffffff + self.assertEqual(0xffffffffffffffff, proto.optional_uint64) # TODO(robinson): Test all other scalar field types. def testSingleScalarClearField(self): @@ -645,11 +693,38 @@ class ReflectionTest(unittest.TestCase): del proto.repeated_int32[2:] self.assertEqual([5, 35], proto.repeated_int32) + # Test extending. + proto.repeated_int32.extend([3, 13]) + self.assertEqual([5, 35, 3, 13], proto.repeated_int32) + # Test clearing. proto.ClearField('repeated_int32') self.assertTrue(not proto.repeated_int32) self.assertEqual(0, len(proto.repeated_int32)) + proto.repeated_int32.append(1) + self.assertEqual(1, proto.repeated_int32[-1]) + # Test assignment to a negative index. + proto.repeated_int32[-1] = 2 + self.assertEqual(2, proto.repeated_int32[-1]) + + # Test deletion at negative indices. + proto.repeated_int32[:] = [0, 1, 2, 3] + del proto.repeated_int32[-1] + self.assertEqual([0, 1, 2], proto.repeated_int32) + + del proto.repeated_int32[-2] + self.assertEqual([0, 2], proto.repeated_int32) + + self.assertRaises(IndexError, proto.repeated_int32.__delitem__, -3) + self.assertRaises(IndexError, proto.repeated_int32.__delitem__, 300) + + del proto.repeated_int32[-2:-1] + self.assertEqual([2], proto.repeated_int32) + + del proto.repeated_int32[100:10000] + self.assertEqual([2], proto.repeated_int32) + def testRepeatedScalarsRemove(self): proto = unittest_pb2.TestAllTypes() @@ -687,7 +762,7 @@ class ReflectionTest(unittest.TestCase): m1 = proto.repeated_nested_message.add() self.assertTrue(proto.repeated_nested_message) self.assertEqual(2, len(proto.repeated_nested_message)) - self.assertIs([m0, m1], proto.repeated_nested_message) + self.assertListsEqual([m0, m1], proto.repeated_nested_message) self.assertTrue(isinstance(m0, unittest_pb2.TestAllTypes.NestedMessage)) # Test out-of-bounds indices. @@ -706,32 +781,57 @@ class ReflectionTest(unittest.TestCase): m2 = proto.repeated_nested_message.add() m3 = proto.repeated_nested_message.add() m4 = proto.repeated_nested_message.add() - self.assertIs([m1, m2, m3], proto.repeated_nested_message[1:4]) - self.assertIs([m0, m1, m2, m3, m4], proto.repeated_nested_message[:]) + self.assertListsEqual( + [m1, m2, m3], proto.repeated_nested_message[1:4]) + self.assertListsEqual( + [m0, m1, m2, m3, m4], proto.repeated_nested_message[:]) + self.assertListsEqual( + [m0, m1], proto.repeated_nested_message[:2]) + self.assertListsEqual( + [m2, m3, m4], proto.repeated_nested_message[2:]) + self.assertEqual( + m0, proto.repeated_nested_message[0]) + self.assertListsEqual( + [m0], proto.repeated_nested_message[:1]) # Test that we can use the field as an iterator. result = [] for i in proto.repeated_nested_message: result.append(i) - self.assertIs([m0, m1, m2, m3, m4], result) + self.assertListsEqual([m0, m1, m2, m3, m4], result) # Test single deletion. del proto.repeated_nested_message[2] - self.assertIs([m0, m1, m3, m4], proto.repeated_nested_message) + self.assertListsEqual([m0, m1, m3, m4], proto.repeated_nested_message) # Test slice deletion. del proto.repeated_nested_message[2:] - self.assertIs([m0, m1], proto.repeated_nested_message) + self.assertListsEqual([m0, m1], proto.repeated_nested_message) + + # Test extending. + n1 = unittest_pb2.TestAllTypes.NestedMessage(bb=1) + n2 = unittest_pb2.TestAllTypes.NestedMessage(bb=2) + proto.repeated_nested_message.extend([n1,n2]) + self.assertEqual(4, len(proto.repeated_nested_message)) + self.assertEqual(n1, proto.repeated_nested_message[2]) + self.assertEqual(n2, proto.repeated_nested_message[3]) # Test clearing. proto.ClearField('repeated_nested_message') self.assertTrue(not proto.repeated_nested_message) self.assertEqual(0, len(proto.repeated_nested_message)) + # Test constructing an element while adding it. + proto.repeated_nested_message.add(bb=23) + self.assertEqual(1, len(proto.repeated_nested_message)) + self.assertEqual(23, proto.repeated_nested_message[0].bb) + def testHandWrittenReflection(self): - # TODO(robinson): We probably need a better way to specify - # protocol types by hand. But then again, this isn't something - # we expect many people to do. Hmm. + # Hand written extensions are only supported by the pure-Python + # implementation of the API. + if api_implementation.Type() != 'python': + return + FieldDescriptor = descriptor.FieldDescriptor foo_field_descriptor = FieldDescriptor( name='foo_field', full_name='MyProto.foo_field', @@ -894,7 +994,7 @@ class ReflectionTest(unittest.TestCase): self.assertTrue(not toplevel.HasField('submessage')) foreign = toplevel.submessage.Extensions[ more_extensions_pb2.repeated_message_extension].add() - self.assertTrue(foreign is toplevel.submessage.Extensions[ + self.assertEqual(foreign, toplevel.submessage.Extensions[ more_extensions_pb2.repeated_message_extension][0]) self.assertTrue(toplevel.HasField('submessage')) @@ -997,6 +1097,12 @@ class ReflectionTest(unittest.TestCase): self.assertEqual(123, proto2.repeated_nested_message[1].bb) self.assertEqual(321, proto2.repeated_nested_message[2].bb) + proto3 = unittest_pb2.TestAllTypes() + proto3.repeated_nested_message.MergeFrom(proto2.repeated_nested_message) + self.assertEqual(999, proto3.repeated_nested_message[0].bb) + self.assertEqual(123, proto3.repeated_nested_message[1].bb) + self.assertEqual(321, proto3.repeated_nested_message[2].bb) + def testMergeFromAllFields(self): # With all fields set. proto1 = unittest_pb2.TestAllTypes() @@ -1126,6 +1232,15 @@ class ReflectionTest(unittest.TestCase): self.assertEqual(2, proto1.optional_int32) self.assertEqual('important-text', proto1.optional_string) + def testCopyFromBadType(self): + # The python implementation doesn't raise an exception in this + # case. In theory it should. + if api_implementation.Type() == 'python': + return + proto1 = unittest_pb2.TestAllTypes() + proto2 = unittest_pb2.TestAllExtensions() + self.assertRaises(TypeError, proto1.CopyFrom, proto2) + def testClear(self): proto = unittest_pb2.TestAllTypes() test_util.SetAllFields(proto) @@ -1231,9 +1346,10 @@ class ReflectionTest(unittest.TestCase): proto.optional_string = str('Testing') self.assertEqual(proto.optional_string, unicode('Testing')) - # Values of type 'str' are also accepted as long as they can be encoded in - # UTF-8. - self.assertEqual(type(proto.optional_string), str) + if api_implementation.Type() == 'python': + # Values of type 'str' are also accepted as long as they can be + # encoded in UTF-8. + self.assertEqual(type(proto.optional_string), str) # Try to assign a 'str' value which contains bytes that aren't 7-bit ASCII. self.assertRaises(ValueError, @@ -1271,7 +1387,7 @@ class ReflectionTest(unittest.TestCase): # Check that the type_id is the same as the tag ID in the .proto file. self.assertEqual(raw.item[0].type_id, 1547769) - # Check the actually bytes on the wire. + # Check the actual bytes on the wire. self.assertTrue( raw.item[0].message.endswith(test_utf8_bytes)) message2.MergeFromString(raw.item[0].message) @@ -1279,10 +1395,23 @@ class ReflectionTest(unittest.TestCase): self.assertEqual(type(message2.str), unicode) self.assertEqual(message2.str, test_utf8) - # How about if the bytes on the wire aren't a valid UTF-8 encoded string. + # The pure Python API throws an exception on MergeFromString(), + # if any of the string fields of the message can't be UTF-8 decoded. + # The C++ implementation of the API has no way to check that on + # MergeFromString and thus has no way to throw the exception. + # + # The pure Python API always returns objects of type 'unicode' (UTF-8 + # encoded), or 'str' (in 7 bit ASCII). bytes = raw.item[0].message.replace( test_utf8_bytes, len(test_utf8_bytes) * '\xff') - self.assertRaises(UnicodeDecodeError, message2.MergeFromString, bytes) + + unicode_decode_failed = False + try: + message2.MergeFromString(bytes) + except UnicodeDecodeError, e: + unicode_decode_failed = True + string_field = message2.str + self.assertTrue(unicode_decode_failed or type(string_field) == str) def testEmptyNestedMessage(self): proto = unittest_pb2.TestAllTypes() @@ -1325,6 +1454,9 @@ class TestAllTypesEqualityTest(unittest.TestCase): self.first_proto = unittest_pb2.TestAllTypes() self.second_proto = unittest_pb2.TestAllTypes() + def testNotHashable(self): + self.assertRaises(TypeError, hash, self.first_proto) + def testSelfEquality(self): self.assertEqual(self.first_proto, self.first_proto) @@ -1342,6 +1474,9 @@ class FullProtosEqualityTest(unittest.TestCase): test_util.SetAllFields(self.first_proto) test_util.SetAllFields(self.second_proto) + def testNotHashable(self): + self.assertRaises(TypeError, hash, self.first_proto) + def testNoneNotEqual(self): self.assertNotEqual(self.first_proto, None) self.assertNotEqual(None, self.second_proto) @@ -1410,9 +1545,6 @@ class FullProtosEqualityTest(unittest.TestCase): self.first_proto.ClearField('optional_nested_message') self.second_proto.optional_nested_message.ClearField('bb') self.assertNotEqual(self.first_proto, self.second_proto) - # TODO(robinson): Replace next two lines with method - # to set the "has" bit without changing the value, - # if/when such a method exists. self.first_proto.optional_nested_message.bb = 0 self.first_proto.optional_nested_message.ClearField('bb') self.assertEqual(self.first_proto, self.second_proto) @@ -1477,6 +1609,14 @@ class ByteSizeTest(unittest.TestCase): def testEmptyMessage(self): self.assertEqual(0, self.proto.ByteSize()) + def testSizedOnKwargs(self): + # Use a separate message to ensure testing right after creation. + proto = unittest_pb2.TestAllTypes() + self.assertEqual(0, proto.ByteSize()) + proto_kwargs = unittest_pb2.TestAllTypes(optional_int64 = 1) + # One byte for the tag, one to encode varint 1. + self.assertEqual(2, proto_kwargs.ByteSize()) + def testVarints(self): def Test(i, expected_varint_size): self.proto.Clear() @@ -1668,10 +1808,13 @@ class ByteSizeTest(unittest.TestCase): self.assertEqual(3, self.proto.ByteSize()) self.proto.ClearField('optional_foreign_message') self.assertEqual(0, self.proto.ByteSize()) - child = self.proto.optional_foreign_message - self.proto.ClearField('optional_foreign_message') - child.c = 128 - self.assertEqual(0, self.proto.ByteSize()) + + if api_implementation.Type() == 'python': + # This is only possible in pure-Python implementation of the API. + child = self.proto.optional_foreign_message + self.proto.ClearField('optional_foreign_message') + child.c = 128 + self.assertEqual(0, self.proto.ByteSize()) # Test within extension. extension = more_extensions_pb2.optional_message_extension @@ -1737,7 +1880,6 @@ class ByteSizeTest(unittest.TestCase): self.assertEqual(19, self.packed_extended_proto.ByteSize()) -# TODO(robinson): We need cross-language serialization consistency tests. # Issues to be sure to cover include: # * Handling of unrecognized tags ("uninterpreted_bytes"). # * Handling of MessageSets. @@ -1792,6 +1934,10 @@ class SerializationTest(unittest.TestCase): self.assertEqual(first_proto, second_proto) def testParseTruncated(self): + # This test is only applicable for the Python implementation of the API. + if api_implementation.Type() != 'python': + return + first_proto = unittest_pb2.TestAllTypes() test_util.SetAllFields(first_proto) serialized = first_proto.SerializeToString() diff --git a/python/google/protobuf/internal/text_format_test.py b/python/google/protobuf/internal/text_format_test.py index e0991cb1..a3547782 100755 --- a/python/google/protobuf/internal/text_format_test.py +++ b/python/google/protobuf/internal/text_format_test.py @@ -35,6 +35,7 @@ __author__ = 'kenton@google.com (Kenton Varda)' import difflib +import re import unittest from google.protobuf import text_format @@ -95,12 +96,13 @@ class TextFormatTest(unittest.TestCase): def testPrintExotic(self): message = unittest_pb2.TestAllTypes() - message.repeated_int64.append(-9223372036854775808); - message.repeated_uint64.append(18446744073709551615); - message.repeated_double.append(123.456); - message.repeated_double.append(1.23e22); - message.repeated_double.append(1.23e-18); - message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'\"'); + message.repeated_int64.append(-9223372036854775808) + message.repeated_uint64.append(18446744073709551615) + message.repeated_double.append(123.456) + message.repeated_double.append(1.23e22) + message.repeated_double.append(1.23e-18) + message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"') + message.repeated_string.append(u'\u00fc\ua71f') self.CompareToGoldenText( self.RemoveRedundantZeros(text_format.MessageToString(message)), 'repeated_int64: -9223372036854775808\n' @@ -109,7 +111,95 @@ class TextFormatTest(unittest.TestCase): 'repeated_double: 1.23e+22\n' 'repeated_double: 1.23e-18\n' 'repeated_string: ' - '\"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\\"\"\n') + '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n' + 'repeated_string: "\\303\\274\\352\\234\\237"\n') + + def testPrintNestedMessageAsOneLine(self): + message = unittest_pb2.TestAllTypes() + msg = message.repeated_nested_message.add() + msg.bb = 42; + self.CompareToGoldenText( + text_format.MessageToString(message, as_one_line=True), + 'repeated_nested_message { bb: 42 }') + + def testPrintRepeatedFieldsAsOneLine(self): + message = unittest_pb2.TestAllTypes() + message.repeated_int32.append(1) + message.repeated_int32.append(1) + message.repeated_int32.append(3) + message.repeated_string.append("Google") + message.repeated_string.append("Zurich") + self.CompareToGoldenText( + text_format.MessageToString(message, as_one_line=True), + 'repeated_int32: 1 repeated_int32: 1 repeated_int32: 3 ' + 'repeated_string: "Google" repeated_string: "Zurich"') + + def testPrintNestedNewLineInStringAsOneLine(self): + message = unittest_pb2.TestAllTypes() + message.optional_string = "a\nnew\nline" + self.CompareToGoldenText( + text_format.MessageToString(message, as_one_line=True), + 'optional_string: "a\\nnew\\nline"') + + def testPrintMessageSetAsOneLine(self): + message = unittest_mset_pb2.TestMessageSetContainer() + ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension + ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension + message.message_set.Extensions[ext1].i = 23 + message.message_set.Extensions[ext2].str = 'foo' + self.CompareToGoldenText( + text_format.MessageToString(message, as_one_line=True), + 'message_set {' + ' [protobuf_unittest.TestMessageSetExtension1] {' + ' i: 23' + ' }' + ' [protobuf_unittest.TestMessageSetExtension2] {' + ' str: \"foo\"' + ' }' + ' }') + + def testPrintExoticAsOneLine(self): + message = unittest_pb2.TestAllTypes() + message.repeated_int64.append(-9223372036854775808) + message.repeated_uint64.append(18446744073709551615) + message.repeated_double.append(123.456) + message.repeated_double.append(1.23e22) + message.repeated_double.append(1.23e-18) + message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"') + message.repeated_string.append(u'\u00fc\ua71f') + self.CompareToGoldenText( + self.RemoveRedundantZeros( + text_format.MessageToString(message, as_one_line=True)), + 'repeated_int64: -9223372036854775808' + ' repeated_uint64: 18446744073709551615' + ' repeated_double: 123.456' + ' repeated_double: 1.23e+22' + ' repeated_double: 1.23e-18' + ' repeated_string: ' + '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""' + ' repeated_string: "\\303\\274\\352\\234\\237"') + + def testRoundTripExoticAsOneLine(self): + message = unittest_pb2.TestAllTypes() + message.repeated_int64.append(-9223372036854775808) + message.repeated_uint64.append(18446744073709551615) + message.repeated_double.append(123.456) + message.repeated_double.append(1.23e22) + message.repeated_double.append(1.23e-18) + message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"') + message.repeated_string.append(u'\u00fc\ua71f') + + wire_text = text_format.MessageToString(message, as_one_line=True) + parsed_message = unittest_pb2.TestAllTypes() + text_format.Merge(wire_text, parsed_message) + self.assertEquals(message, parsed_message) + + def testPrintRawUtf8String(self): + message = unittest_pb2.TestAllTypes() + message.repeated_string.append(u'\u00fc\ua71f') + self.CompareToGoldenText( + text_format.MessageToString(message, as_utf8 = True), + 'repeated_string: "\303\274\352\234\237"\n') def testMessageToString(self): message = unittest_pb2.ForeignMessage() @@ -119,8 +209,12 @@ class TextFormatTest(unittest.TestCase): def RemoveRedundantZeros(self, text): # Some platforms print 1e+5 as 1e+005. This is fine, but we need to remove # these zeros in order to match the golden file. - return text.replace('e+0','e+').replace('e+0','e+') \ + text = text.replace('e+0','e+').replace('e+0','e+') \ .replace('e-0','e-').replace('e-0','e-') + # Floating point fields are printed with .0 suffix even if they are + # actualy integer numbers. + text = re.compile('\.0$', re.MULTILINE).sub('', text) + return text def testMergeGolden(self): golden_text = '\n'.join(self.ReadGolden('text_format_unittest_data.txt')) @@ -191,8 +285,11 @@ class TextFormatTest(unittest.TestCase): 'repeated_double: 1.23e+22\n' 'repeated_double: 1.23e-18\n' 'repeated_string: \n' - '\"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\\"\"\n' - 'repeated_string: "foo" \'corge\' "grault"') + '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n' + 'repeated_string: "foo" \'corge\' "grault"\n' + 'repeated_string: "\\303\\274\\352\\234\\237"\n' + 'repeated_string: "\\xc3\\xbc"\n' + 'repeated_string: "\xc3\xbc"\n') text_format.Merge(text, message) self.assertEqual(-9223372036854775808, message.repeated_int64[0]) @@ -201,8 +298,30 @@ class TextFormatTest(unittest.TestCase): self.assertEqual(1.23e22, message.repeated_double[1]) self.assertEqual(1.23e-18, message.repeated_double[2]) self.assertEqual( - '\000\001\a\b\f\n\r\t\v\\\'\"', message.repeated_string[0]) + '\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0]) self.assertEqual('foocorgegrault', message.repeated_string[1]) + self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2]) + self.assertEqual(u'\u00fc', message.repeated_string[3]) + + def testMergeEmptyText(self): + message = unittest_pb2.TestAllTypes() + text = '' + text_format.Merge(text, message) + self.assertEquals(unittest_pb2.TestAllTypes(), message) + + def testMergeInvalidUtf8(self): + message = unittest_pb2.TestAllTypes() + text = 'repeated_string: "\\xc3\\xc3"' + self.assertRaises(text_format.ParseError, text_format.Merge, text, message) + + def testMergeSingleWord(self): + message = unittest_pb2.TestAllTypes() + text = 'foo' + self.assertRaisesWithMessage( + text_format.ParseError, + ('1:1 : Message type "protobuf_unittest.TestAllTypes" has no field named ' + '"foo".'), + text_format.Merge, text, message) def testMergeUnknownField(self): message = unittest_pb2.TestAllTypes() @@ -297,7 +416,8 @@ class TokenizerTest(unittest.TestCase): 'identifiER_4 : 1.1e+2 ID5:-0.23 ID6:\'aaaa\\\'bbbb\'\n' 'ID7 : "aa\\"bb"\n\n\n\n ID8: {A:inf B:-inf C:true D:false}\n' 'ID9: 22 ID10: -111111111111111111 ID11: -22\n' - 'ID12: 2222222222222222222') + 'ID12: 2222222222222222222 ' + 'false_bool: 0 true_BOOL:t \n true_bool1: 1 false_BOOL1:f ' ) tokenizer = text_format._Tokenizer(text) methods = [(tokenizer.ConsumeIdentifier, 'identifier1'), ':', @@ -347,7 +467,19 @@ class TokenizerTest(unittest.TestCase): (tokenizer.ConsumeInt32, -22), (tokenizer.ConsumeIdentifier, 'ID12'), ':', - (tokenizer.ConsumeUint64, 2222222222222222222)] + (tokenizer.ConsumeUint64, 2222222222222222222), + (tokenizer.ConsumeIdentifier, 'false_bool'), + ':', + (tokenizer.ConsumeBool, False), + (tokenizer.ConsumeIdentifier, 'true_BOOL'), + ':', + (tokenizer.ConsumeBool, True), + (tokenizer.ConsumeIdentifier, 'true_bool1'), + ':', + (tokenizer.ConsumeBool, True), + (tokenizer.ConsumeIdentifier, 'false_BOOL1'), + ':', + (tokenizer.ConsumeBool, False)] i = 0 while not tokenizer.AtEnd(): diff --git a/python/google/protobuf/message.py b/python/google/protobuf/message.py index f8398474..54cf35ea 100755 --- a/python/google/protobuf/message.py +++ b/python/google/protobuf/message.py @@ -67,6 +67,11 @@ class Message(object): DESCRIPTOR = None + def __deepcopy__(self, memo=None): + clone = type(self)() + clone.MergeFrom(self) + return clone + def __eq__(self, other_msg): raise NotImplementedError @@ -74,9 +79,15 @@ class Message(object): # Can't just say self != other_msg, since that would infinitely recurse. :) return not self == other_msg + def __hash__(self): + raise TypeError('unhashable object') + def __str__(self): raise NotImplementedError + def __unicode__(self): + raise NotImplementedError + def MergeFrom(self, other_msg): """Merges the contents of the specified message into current message. diff --git a/python/google/protobuf/pyext/python-proto2.cc b/python/google/protobuf/pyext/python-proto2.cc new file mode 100644 index 00000000..f2799e62 --- /dev/null +++ b/python/google/protobuf/pyext/python-proto2.cc @@ -0,0 +1,1658 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: petar@google.com (Petar Petrov) + +#include <Python.h> +#include <map> +#include <string> +#include <vector> + +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/pyext/python_descriptor.h> +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/dynamic_message.h> +#include <google/protobuf/message.h> +#include <google/protobuf/unknown_field_set.h> +#include <google/protobuf/pyext/python_protobuf.h> + +/* Is 64bit */ +#define IS_64BIT (SIZEOF_LONG == 8) + +#define FIELD_BELONGS_TO_MESSAGE(field_descriptor, message) \ + ((message)->GetDescriptor() == (field_descriptor)->containing_type()) + +#define FIELD_IS_REPEATED(field_descriptor) \ + ((field_descriptor)->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED) + +#define GOOGLE_CHECK_GET_INT32(arg, value) \ + int32 value; \ + if (!CheckAndGetInteger(arg, &value, kint32min_py, kint32max_py)) { \ + return NULL; \ + } + +#define GOOGLE_CHECK_GET_INT64(arg, value) \ + int64 value; \ + if (!CheckAndGetInteger(arg, &value, kint64min_py, kint64max_py)) { \ + return NULL; \ + } + +#define GOOGLE_CHECK_GET_UINT32(arg, value) \ + uint32 value; \ + if (!CheckAndGetInteger(arg, &value, kPythonZero, kuint32max_py)) { \ + return NULL; \ + } + +#define GOOGLE_CHECK_GET_UINT64(arg, value) \ + uint64 value; \ + if (!CheckAndGetInteger(arg, &value, kPythonZero, kuint64max_py)) { \ + return NULL; \ + } + +#define GOOGLE_CHECK_GET_FLOAT(arg, value) \ + float value; \ + if (!CheckAndGetFloat(arg, &value)) { \ + return NULL; \ + } \ + +#define GOOGLE_CHECK_GET_DOUBLE(arg, value) \ + double value; \ + if (!CheckAndGetDouble(arg, &value)) { \ + return NULL; \ + } + +#define GOOGLE_CHECK_GET_BOOL(arg, value) \ + bool value; \ + if (!CheckAndGetBool(arg, &value)) { \ + return NULL; \ + } + +#define C(str) const_cast<char*>(str) + +// --- Globals: + +// Constants used for integer type range checking. +static PyObject* kPythonZero; +static PyObject* kint32min_py; +static PyObject* kint32max_py; +static PyObject* kuint32max_py; +static PyObject* kint64min_py; +static PyObject* kint64max_py; +static PyObject* kuint64max_py; + +namespace google { +namespace protobuf { +namespace python { + +// --- Support Routines: + +static void AddConstants(PyObject* module) { + struct NameValue { + char* name; + int32 value; + } constants[] = { + // Labels: + {"LABEL_OPTIONAL", google::protobuf::FieldDescriptor::LABEL_OPTIONAL}, + {"LABEL_REQUIRED", google::protobuf::FieldDescriptor::LABEL_REQUIRED}, + {"LABEL_REPEATED", google::protobuf::FieldDescriptor::LABEL_REPEATED}, + // CPP types: + {"CPPTYPE_MESSAGE", google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE}, + // Field Types: + {"TYPE_MESSAGE", google::protobuf::FieldDescriptor::TYPE_MESSAGE}, + // End. + {NULL, 0} + }; + + for (NameValue* constant = constants; + constant->name != NULL; constant++) { + PyModule_AddIntConstant(module, constant->name, constant->value); + } +} + +// --- CMessage Custom Type: + +// ------ Type Forward Declaration: + +struct CMessage; +struct CMessage_Type; + +static void CMessageDealloc(CMessage* self); +static int CMessageInit(CMessage* self, PyObject *args, PyObject *kwds); +static PyObject* CMessageStr(CMessage* self); + +static PyObject* CMessage_AddMessage(CMessage* self, PyObject* args); +static PyObject* CMessage_AddRepeatedScalar(CMessage* self, PyObject* args); +static PyObject* CMessage_AssignRepeatedScalar(CMessage* self, PyObject* args); +static PyObject* CMessage_ByteSize(CMessage* self, PyObject* args); +static PyObject* CMessage_Clear(CMessage* self, PyObject* args); +static PyObject* CMessage_ClearField(CMessage* self, PyObject* args); +static PyObject* CMessage_ClearFieldByDescriptor( + CMessage* self, PyObject* args); +static PyObject* CMessage_CopyFrom(CMessage* self, PyObject* args); +static PyObject* CMessage_DebugString(CMessage* self, PyObject* args); +static PyObject* CMessage_DeleteRepeatedField(CMessage* self, PyObject* args); +static PyObject* CMessage_Equals(CMessage* self, PyObject* args); +static PyObject* CMessage_FieldLength(CMessage* self, PyObject* args); +static PyObject* CMessage_FindInitializationErrors(CMessage* self); +static PyObject* CMessage_GetRepeatedMessage(CMessage* self, PyObject* args); +static PyObject* CMessage_GetRepeatedScalar(CMessage* self, PyObject* args); +static PyObject* CMessage_GetScalar(CMessage* self, PyObject* args); +static PyObject* CMessage_HasField(CMessage* self, PyObject* args); +static PyObject* CMessage_HasFieldByDescriptor(CMessage* self, PyObject* args); +static PyObject* CMessage_IsInitialized(CMessage* self, PyObject* args); +static PyObject* CMessage_ListFields(CMessage* self, PyObject* args); +static PyObject* CMessage_MergeFrom(CMessage* self, PyObject* args); +static PyObject* CMessage_MergeFromString(CMessage* self, PyObject* args); +static PyObject* CMessage_MutableMessage(CMessage* self, PyObject* args); +static PyObject* CMessage_NewSubMessage(CMessage* self, PyObject* args); +static PyObject* CMessage_SetScalar(CMessage* self, PyObject* args); +static PyObject* CMessage_SerializePartialToString( + CMessage* self, PyObject* args); +static PyObject* CMessage_SerializeToString(CMessage* self, PyObject* args); +static PyObject* CMessage_SetInParent(CMessage* self, PyObject* args); +static PyObject* CMessage_SwapRepeatedFieldElements( + CMessage* self, PyObject* args); + +// ------ Object Definition: + +typedef struct CMessage { + PyObject_HEAD + + struct CMessage* parent; // NULL if wasn't created from another message. + CFieldDescriptor* parent_field; + const char* full_name; + google::protobuf::Message* message; + bool free_message; + bool read_only; +} CMessage; + +// ------ Method Table: + +#define CMETHOD(name, args, doc) \ + { C(#name), (PyCFunction)CMessage_##name, args, C(doc) } +static PyMethodDef CMessageMethods[] = { + CMETHOD(AddMessage, METH_O, + "Adds a new message to a repeated composite field."), + CMETHOD(AddRepeatedScalar, METH_VARARGS, + "Adds a scalar to a repeated scalar field."), + CMETHOD(AssignRepeatedScalar, METH_VARARGS, + "Clears and sets the values of a repeated scalar field."), + CMETHOD(ByteSize, METH_NOARGS, + "Returns the size of the message in bytes."), + CMETHOD(Clear, METH_NOARGS, + "Clears a protocol message."), + CMETHOD(ClearField, METH_O, + "Clears a protocol message field by name."), + CMETHOD(ClearFieldByDescriptor, METH_O, + "Clears a protocol message field by descriptor."), + CMETHOD(CopyFrom, METH_O, + "Copies a protocol message into the current message."), + CMETHOD(DebugString, METH_NOARGS, + "Returns the debug string of a protocol message."), + CMETHOD(DeleteRepeatedField, METH_VARARGS, + "Deletes a slice of values from a repeated field."), + CMETHOD(Equals, METH_O, + "Checks if two protocol messages are equal (by identity)."), + CMETHOD(FieldLength, METH_O, + "Returns the number of elements in a repeated field."), + CMETHOD(FindInitializationErrors, METH_NOARGS, + "Returns the initialization errors of a message."), + CMETHOD(GetRepeatedMessage, METH_VARARGS, + "Returns a message from a repeated composite field."), + CMETHOD(GetRepeatedScalar, METH_VARARGS, + "Returns a scalar value from a repeated scalar field."), + CMETHOD(GetScalar, METH_O, + "Returns the scalar value of a field."), + CMETHOD(HasField, METH_O, + "Checks if a message field is set."), + CMETHOD(HasFieldByDescriptor, METH_O, + "Checks if a message field is set by given its descriptor"), + CMETHOD(IsInitialized, METH_NOARGS, + "Checks if all required fields of a protocol message are set."), + CMETHOD(ListFields, METH_NOARGS, + "Lists all set fields of a message."), + CMETHOD(MergeFrom, METH_O, + "Merges a protocol message into the current message."), + CMETHOD(MergeFromString, METH_O, + "Merges a serialized message into the current message."), + CMETHOD(MutableMessage, METH_O, + "Returns a new instance of a nested protocol message."), + CMETHOD(NewSubMessage, METH_O, + "Creates and returns a python message given the descriptor of a " + "composite field of the current message."), + CMETHOD(SetScalar, METH_VARARGS, + "Sets the value of a singular scalar field."), + CMETHOD(SerializePartialToString, METH_VARARGS, + "Serializes the message to a string, even if it isn't initialized."), + CMETHOD(SerializeToString, METH_NOARGS, + "Serializes the message to a string, only for initialized messages."), + CMETHOD(SetInParent, METH_NOARGS, + "Sets the has bit of the given field in its parent message."), + CMETHOD(SwapRepeatedFieldElements, METH_VARARGS, + "Swaps the elements in two positions in a repeated field."), + { NULL, NULL } +}; +#undef CMETHOD + +static PyMemberDef CMessageMembers[] = { + { C("full_name"), T_STRING, offsetof(CMessage, full_name), 0, "Full name" }, + { NULL } +}; + +// ------ Type Definition: + +// The definition for the type object that captures the type of CMessage +// in Python. +PyTypeObject CMessage_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + C("google3.net.google.protobuf.python.internal." + "_net_proto2___python." + "CMessage"), // tp_name + sizeof(CMessage), // tp_basicsize + 0, // tp_itemsize + (destructor)CMessageDealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + (reprfunc)CMessageStr, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + C("A ProtocolMessage"), // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + CMessageMethods, // tp_methods + CMessageMembers, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc)CMessageInit, // tp_init + PyType_GenericAlloc, // tp_alloc + PyType_GenericNew, // tp_new + PyObject_Del, // tp_free +}; + +// ------ Helper Functions: + +static void FormatTypeError(PyObject* arg, char* expected_types) { + PyObject* s = PyObject_Str(PyObject_Type(arg)); + PyObject* repr = PyObject_Repr(PyObject_Type(arg)); + PyErr_Format(PyExc_TypeError, + "%.100s has type %.100s, but expected one of: %s", + PyString_AS_STRING(repr), + PyString_AS_STRING(s), + expected_types); + Py_DECREF(s); + Py_DECREF(repr); +} + +template <class T> +static bool CheckAndGetInteger( + PyObject* arg, T* value, PyObject* min, PyObject* max) { + bool is_long = PyLong_Check(arg); + if (!PyInt_Check(arg) && !is_long) { + FormatTypeError(arg, "int, long"); + return false; + } + + if (PyObject_Compare(min, arg) > 0 || PyObject_Compare(max, arg) < 0) { + PyObject* s = PyObject_Str(arg); + PyErr_Format(PyExc_ValueError, + "Value out of range: %s", + PyString_AS_STRING(s)); + Py_DECREF(s); + return false; + } + if (is_long) { + if (min == kPythonZero) { + *value = static_cast<T>(PyLong_AsUnsignedLongLong(arg)); + } else { + *value = static_cast<T>(PyLong_AsLongLong(arg)); + } + } else { + *value = static_cast<T>(PyInt_AsLong(arg)); + } + return true; +} + +static bool CheckAndGetDouble(PyObject* arg, double* value) { + if (!PyInt_Check(arg) && !PyLong_Check(arg) && + !PyFloat_Check(arg)) { + FormatTypeError(arg, "int, long, float"); + return false; + } + *value = PyFloat_AsDouble(arg); + return true; +} + +static bool CheckAndGetFloat(PyObject* arg, float* value) { + double double_value; + if (!CheckAndGetDouble(arg, &double_value)) { + return false; + } + *value = static_cast<float>(double_value); + return true; +} + +static bool CheckAndGetBool(PyObject* arg, bool* value) { + if (!PyInt_Check(arg) && !PyBool_Check(arg) && !PyLong_Check(arg)) { + FormatTypeError(arg, "int, long, bool"); + return false; + } + *value = static_cast<bool>(PyInt_AsLong(arg)); + return true; +} + +google::protobuf::DynamicMessageFactory* global_message_factory = NULL; +static const google::protobuf::Message* CreateMessage(const char* message_type) { + string message_name(message_type); + const google::protobuf::Descriptor* descriptor = + GetDescriptorPool()->FindMessageTypeByName(message_name); + if (descriptor == NULL) { + return NULL; + } + return global_message_factory->GetPrototype(descriptor); +} + +static bool CheckAndSetString( + PyObject* arg, google::protobuf::Message* message, + const google::protobuf::FieldDescriptor* descriptor, + const google::protobuf::Reflection* reflection, + bool append, + int index) { + GOOGLE_DCHECK(descriptor->type() == google::protobuf::FieldDescriptor::TYPE_STRING || + descriptor->type() == google::protobuf::FieldDescriptor::TYPE_BYTES); + if (descriptor->type() == google::protobuf::FieldDescriptor::TYPE_STRING) { + if (!PyString_Check(arg) && !PyUnicode_Check(arg)) { + FormatTypeError(arg, "str, unicode"); + return false; + } + + if (PyString_Check(arg)) { + PyObject* unicode = PyUnicode_FromEncodedObject(arg, "ascii", NULL); + if (unicode == NULL) { + PyObject* repr = PyObject_Repr(arg); + PyErr_Format(PyExc_ValueError, + "%s has type str, but isn't in 7-bit ASCII " + "encoding. Non-ASCII strings must be converted to " + "unicode objects before being added.", + PyString_AS_STRING(repr)); + Py_DECREF(repr); + return false; + } else { + Py_DECREF(unicode); + } + } + } else if (!PyString_Check(arg)) { + FormatTypeError(arg, "str"); + return false; + } + + PyObject* encoded_string = NULL; + if (descriptor->type() == google::protobuf::FieldDescriptor::TYPE_STRING) { + if (PyString_Check(arg)) { + encoded_string = PyString_AsEncodedObject(arg, "utf-8", NULL); + } else { + encoded_string = PyUnicode_AsEncodedObject(arg, "utf-8", NULL); + } + } else { + // In this case field type is "bytes". + encoded_string = arg; + Py_INCREF(encoded_string); + } + + if (encoded_string == NULL) { + return false; + } + + char* value; + Py_ssize_t value_len; + if (PyString_AsStringAndSize(encoded_string, &value, &value_len) < 0) { + Py_DECREF(encoded_string); + return false; + } + + string value_string(value, value_len); + if (append) { + reflection->AddString(message, descriptor, value_string); + } else if (index < 0) { + reflection->SetString(message, descriptor, value_string); + } else { + reflection->SetRepeatedString(message, descriptor, index, value_string); + } + Py_DECREF(encoded_string); + return true; +} + +static PyObject* ToStringObject( + const google::protobuf::FieldDescriptor* descriptor, string value) { + if (descriptor->type() != google::protobuf::FieldDescriptor::TYPE_STRING) { + return PyString_FromStringAndSize(value.c_str(), value.length()); + } + + PyObject* result = PyUnicode_DecodeUTF8(value.c_str(), value.length(), NULL); + // If the string can't be decoded in UTF-8, just return a string object that + // contains the raw bytes. This can't happen if the value was assigned using + // the members of the Python message object, but can happen if the values were + // parsed from the wire (binary). + if (result == NULL) { + PyErr_Clear(); + result = PyString_FromStringAndSize(value.c_str(), value.length()); + } + return result; +} + +static void AssureWritable(CMessage* self) { + if (self == NULL || + self->parent == NULL || + self->parent_field == NULL) { + return; + } + + if (!self->read_only) { + return; + } + + AssureWritable(self->parent); + + google::protobuf::Message* message = self->parent->message; + const google::protobuf::Reflection* reflection = message->GetReflection(); + self->message = reflection->MutableMessage( + message, self->parent_field->descriptor, global_message_factory); + self->read_only = false; + self->parent = NULL; + self->parent_field = NULL; +} + +static PyObject* InternalGetScalar( + google::protobuf::Message* message, + const google::protobuf::FieldDescriptor* field_descriptor) { + const google::protobuf::Reflection* reflection = message->GetReflection(); + + if (!FIELD_BELONGS_TO_MESSAGE(field_descriptor, message)) { + PyErr_SetString( + PyExc_KeyError, "Field does not belong to message!"); + return NULL; + } + + PyObject* result = NULL; + switch (field_descriptor->cpp_type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: { + int32 value = reflection->GetInt32(*message, field_descriptor); + result = PyInt_FromLong(value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: { + int64 value = reflection->GetInt64(*message, field_descriptor); +#if IS_64BIT + result = PyInt_FromLong(value); +#else + result = PyLong_FromLongLong(value); +#endif + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: { + uint32 value = reflection->GetUInt32(*message, field_descriptor); +#if IS_64BIT + result = PyInt_FromLong(value); +#else + result = PyLong_FromLongLong(value); +#endif + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: { + uint64 value = reflection->GetUInt64(*message, field_descriptor); +#if IS_64BIT + if (value <= static_cast<uint64>(kint64max)) { + result = PyInt_FromLong(static_cast<uint64>(value)); + } +#else + if (value <= static_cast<uint32>(kint32max)) { + result = PyInt_FromLong(static_cast<uint32>(value)); + } +#endif + else { // NOLINT + result = PyLong_FromUnsignedLongLong(value); + } + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: { + float value = reflection->GetFloat(*message, field_descriptor); + result = PyFloat_FromDouble(value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: { + double value = reflection->GetDouble(*message, field_descriptor); + result = PyFloat_FromDouble(value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: { + bool value = reflection->GetBool(*message, field_descriptor); + result = PyBool_FromLong(value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: { + string value = reflection->GetString(*message, field_descriptor); + result = ToStringObject(field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: { + if (!message->GetReflection()->HasField(*message, field_descriptor)) { + // Look for the value in the unknown fields. + google::protobuf::UnknownFieldSet* unknown_field_set = + message->GetReflection()->MutableUnknownFields(message); + for (int i = 0; i < unknown_field_set->field_count(); ++i) { + if (unknown_field_set->field(i).number() == + field_descriptor->number()) { + result = PyInt_FromLong(unknown_field_set->field(i).varint()); + break; + } + } + } + + if (result == NULL) { + const google::protobuf::EnumValueDescriptor* enum_value = + message->GetReflection()->GetEnum(*message, field_descriptor); + result = PyInt_FromLong(enum_value->number()); + } + break; + } + default: + PyErr_Format( + PyExc_SystemError, "Getting a value from a field of unknown type %d", + field_descriptor->cpp_type()); + } + + return result; +} + +static PyObject* InternalSetScalar( + google::protobuf::Message* message, const google::protobuf::FieldDescriptor* field_descriptor, + PyObject* arg) { + const google::protobuf::Reflection* reflection = message->GetReflection(); + + if (!FIELD_BELONGS_TO_MESSAGE(field_descriptor, message)) { + PyErr_SetString( + PyExc_KeyError, "Field does not belong to message!"); + return NULL; + } + + switch (field_descriptor->cpp_type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: { + GOOGLE_CHECK_GET_INT32(arg, value); + reflection->SetInt32(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: { + GOOGLE_CHECK_GET_INT64(arg, value); + reflection->SetInt64(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: { + GOOGLE_CHECK_GET_UINT32(arg, value); + reflection->SetUInt32(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: { + GOOGLE_CHECK_GET_UINT64(arg, value); + reflection->SetUInt64(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: { + GOOGLE_CHECK_GET_FLOAT(arg, value); + reflection->SetFloat(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: { + GOOGLE_CHECK_GET_DOUBLE(arg, value); + reflection->SetDouble(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: { + GOOGLE_CHECK_GET_BOOL(arg, value); + reflection->SetBool(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: { + if (!CheckAndSetString( + arg, message, field_descriptor, reflection, false, -1)) { + return NULL; + } + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: { + GOOGLE_CHECK_GET_INT32(arg, value); + const google::protobuf::EnumDescriptor* enum_descriptor = + field_descriptor->enum_type(); + const google::protobuf::EnumValueDescriptor* enum_value = + enum_descriptor->FindValueByNumber(value); + if (enum_value != NULL) { + reflection->SetEnum(message, field_descriptor, enum_value); + } else { + bool added = false; + // Add the value to the unknown fields. + google::protobuf::UnknownFieldSet* unknown_field_set = + message->GetReflection()->MutableUnknownFields(message); + for (int i = 0; i < unknown_field_set->field_count(); ++i) { + if (unknown_field_set->field(i).number() == + field_descriptor->number()) { + unknown_field_set->mutable_field(i)->set_varint(value); + added = true; + break; + } + } + + if (!added) { + unknown_field_set->AddVarint(field_descriptor->number(), value); + } + reflection->ClearField(message, field_descriptor); + } + break; + } + default: + PyErr_Format( + PyExc_SystemError, "Setting value to a field of unknown type %d", + field_descriptor->cpp_type()); + } + + Py_RETURN_NONE; +} + +static PyObject* InternalAddRepeatedScalar( + google::protobuf::Message* message, const google::protobuf::FieldDescriptor* field_descriptor, + PyObject* arg) { + + if (!FIELD_BELONGS_TO_MESSAGE(field_descriptor, message)) { + PyErr_SetString( + PyExc_KeyError, "Field does not belong to message!"); + return NULL; + } + + const google::protobuf::Reflection* reflection = message->GetReflection(); + switch (field_descriptor->cpp_type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: { + GOOGLE_CHECK_GET_INT32(arg, value); + reflection->AddInt32(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: { + GOOGLE_CHECK_GET_INT64(arg, value); + reflection->AddInt64(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: { + GOOGLE_CHECK_GET_UINT32(arg, value); + reflection->AddUInt32(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: { + GOOGLE_CHECK_GET_UINT64(arg, value); + reflection->AddUInt64(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: { + GOOGLE_CHECK_GET_FLOAT(arg, value); + reflection->AddFloat(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: { + GOOGLE_CHECK_GET_DOUBLE(arg, value); + reflection->AddDouble(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: { + GOOGLE_CHECK_GET_BOOL(arg, value); + reflection->AddBool(message, field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: { + if (!CheckAndSetString( + arg, message, field_descriptor, reflection, true, -1)) { + return NULL; + } + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: { + GOOGLE_CHECK_GET_INT32(arg, value); + const google::protobuf::EnumDescriptor* enum_descriptor = + field_descriptor->enum_type(); + const google::protobuf::EnumValueDescriptor* enum_value = + enum_descriptor->FindValueByNumber(value); + if (enum_value != NULL) { + reflection->AddEnum(message, field_descriptor, enum_value); + } else { + PyObject* s = PyObject_Str(arg); + PyErr_Format(PyExc_ValueError, "Unknown enum value: %s", + PyString_AS_STRING(s)); + Py_DECREF(s); + return NULL; + } + break; + } + default: + PyErr_Format( + PyExc_SystemError, "Adding value to a field of unknown type %d", + field_descriptor->cpp_type()); + } + + Py_RETURN_NONE; +} + +static PyObject* InternalGetRepeatedScalar( + CMessage* cmessage, const google::protobuf::FieldDescriptor* field_descriptor, + int index) { + google::protobuf::Message* message = cmessage->message; + const google::protobuf::Reflection* reflection = message->GetReflection(); + + int field_size = reflection->FieldSize(*message, field_descriptor); + if (index < 0) { + index = field_size + index; + } + if (index < 0 || index >= field_size) { + PyErr_Format(PyExc_IndexError, + "list assignment index (%d) out of range", index); + return NULL; + } + + PyObject* result = NULL; + switch (field_descriptor->cpp_type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: { + int32 value = reflection->GetRepeatedInt32( + *message, field_descriptor, index); + result = PyInt_FromLong(value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: { + int64 value = reflection->GetRepeatedInt64( + *message, field_descriptor, index); + result = PyLong_FromLongLong(value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: { + uint32 value = reflection->GetRepeatedUInt32( + *message, field_descriptor, index); + result = PyLong_FromLongLong(value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: { + uint64 value = reflection->GetRepeatedUInt64( + *message, field_descriptor, index); + result = PyLong_FromUnsignedLongLong(value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: { + float value = reflection->GetRepeatedFloat( + *message, field_descriptor, index); + result = PyFloat_FromDouble(value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: { + double value = reflection->GetRepeatedDouble( + *message, field_descriptor, index); + result = PyFloat_FromDouble(value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: { + bool value = reflection->GetRepeatedBool( + *message, field_descriptor, index); + result = PyBool_FromLong(value ? 1 : 0); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: { + const google::protobuf::EnumValueDescriptor* enum_value = + message->GetReflection()->GetRepeatedEnum( + *message, field_descriptor, index); + result = PyInt_FromLong(enum_value->number()); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: { + string value = reflection->GetRepeatedString( + *message, field_descriptor, index); + result = ToStringObject(field_descriptor, value); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: { + CMessage* py_cmsg = PyObject_New(CMessage, &CMessage_Type); + if (py_cmsg == NULL) { + return NULL; + } + const google::protobuf::Message& msg = reflection->GetRepeatedMessage( + *message, field_descriptor, index); + py_cmsg->parent = cmessage; + py_cmsg->full_name = field_descriptor->full_name().c_str(); + py_cmsg->message = const_cast<google::protobuf::Message*>(&msg); + py_cmsg->free_message = false; + py_cmsg->read_only = false; + result = reinterpret_cast<PyObject*>(py_cmsg); + break; + } + default: + PyErr_Format( + PyExc_SystemError, + "Getting value from a repeated field of unknown type %d", + field_descriptor->cpp_type()); + } + + return result; +} + +static PyObject* InternalGetRepeatedScalarSlice( + CMessage* cmessage, const google::protobuf::FieldDescriptor* field_descriptor, + PyObject* slice) { + Py_ssize_t from; + Py_ssize_t to; + Py_ssize_t step; + Py_ssize_t length; + bool return_list = false; + google::protobuf::Message* message = cmessage->message; + + if (PyInt_Check(slice)) { + from = to = PyInt_AsLong(slice); + } else if (PyLong_Check(slice)) { + from = to = PyLong_AsLong(slice); + } else if (PySlice_Check(slice)) { + const google::protobuf::Reflection* reflection = message->GetReflection(); + length = reflection->FieldSize(*message, field_descriptor); + PySlice_GetIndices( + reinterpret_cast<PySliceObject*>(slice), length, &from, &to, &step); + return_list = true; + } else { + PyErr_SetString(PyExc_TypeError, "list indices must be integers"); + return NULL; + } + + if (!return_list) { + return InternalGetRepeatedScalar(cmessage, field_descriptor, from); + } + + PyObject* list = PyList_New(0); + if (list == NULL) { + return NULL; + } + + if (from <= to) { + if (step < 0) return list; + for (Py_ssize_t index = from; index < to; index += step) { + if (index < 0 || index >= length) break; + PyObject* s = InternalGetRepeatedScalar( + cmessage, field_descriptor, index); + PyList_Append(list, s); + Py_DECREF(s); + } + } else { + if (step > 0) return list; + for (Py_ssize_t index = from; index > to; index += step) { + if (index < 0 || index >= length) break; + PyObject* s = InternalGetRepeatedScalar( + cmessage, field_descriptor, index); + PyList_Append(list, s); + Py_DECREF(s); + } + } + return list; +} + +// ------ C Constructor/Destructor: + +static int CMessageInit(CMessage* self, PyObject *args, PyObject *kwds) { + self->message = NULL; + return 0; +} + +static void CMessageDealloc(CMessage* self) { + if (self->free_message) { + if (self->read_only) { + PyErr_WriteUnraisable(reinterpret_cast<PyObject*>(self)); + } + delete self->message; + } + self->ob_type->tp_free(reinterpret_cast<PyObject*>(self)); +} + +// ------ Methods: + +static PyObject* CMessage_Clear(CMessage* self, PyObject* args) { + AssureWritable(self); + self->message->Clear(); + Py_RETURN_NONE; +} + +static PyObject* CMessage_IsInitialized(CMessage* self, PyObject* args) { + return PyBool_FromLong(self->message->IsInitialized() ? 1 : 0); +} + +static PyObject* CMessage_HasField(CMessage* self, PyObject* arg) { + char* field_name; + if (PyString_AsStringAndSize(arg, &field_name, NULL) < 0) { + return NULL; + } + + google::protobuf::Message* message = self->message; + const google::protobuf::Descriptor* descriptor = message->GetDescriptor(); + const google::protobuf::FieldDescriptor* field_descriptor = + descriptor->FindFieldByName(field_name); + if (field_descriptor == NULL) { + PyErr_Format(PyExc_ValueError, "Unknown field %s.", field_name); + return NULL; + } + + bool has_field = + message->GetReflection()->HasField(*message, field_descriptor); + return PyBool_FromLong(has_field ? 1 : 0); +} + +static PyObject* CMessage_HasFieldByDescriptor(CMessage* self, PyObject* arg) { + CFieldDescriptor* cfield_descriptor = NULL; + if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), + &CFieldDescriptor_Type)) { + PyErr_SetString(PyExc_TypeError, "Must be a field descriptor"); + return NULL; + } + cfield_descriptor = reinterpret_cast<CFieldDescriptor*>(arg); + + google::protobuf::Message* message = self->message; + const google::protobuf::FieldDescriptor* field_descriptor = + cfield_descriptor->descriptor; + + if (!FIELD_BELONGS_TO_MESSAGE(field_descriptor, message)) { + PyErr_SetString(PyExc_KeyError, + "Field does not belong to message!"); + return NULL; + } + + if (FIELD_IS_REPEATED(field_descriptor)) { + PyErr_SetString(PyExc_KeyError, + "Field is repeated. A singular method is required."); + return NULL; + } + + bool has_field = + message->GetReflection()->HasField(*message, field_descriptor); + return PyBool_FromLong(has_field ? 1 : 0); +} + +static PyObject* CMessage_ClearFieldByDescriptor( + CMessage* self, PyObject* arg) { + CFieldDescriptor* cfield_descriptor = NULL; + if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), + &CFieldDescriptor_Type)) { + PyErr_SetString(PyExc_TypeError, "Must be a field descriptor"); + return NULL; + } + cfield_descriptor = reinterpret_cast<CFieldDescriptor*>(arg); + + google::protobuf::Message* message = self->message; + const google::protobuf::FieldDescriptor* field_descriptor = + cfield_descriptor->descriptor; + + if (!FIELD_BELONGS_TO_MESSAGE(field_descriptor, message)) { + PyErr_SetString(PyExc_KeyError, + "Field does not belong to message!"); + return NULL; + } + + message->GetReflection()->ClearField(message, field_descriptor); + Py_RETURN_NONE; +} + +static PyObject* CMessage_ClearField(CMessage* self, PyObject* arg) { + char* field_name; + if (PyString_AsStringAndSize(arg, &field_name, NULL) < 0) { + return NULL; + } + + google::protobuf::Message* message = self->message; + const google::protobuf::Descriptor* descriptor = message->GetDescriptor(); + const google::protobuf::FieldDescriptor* field_descriptor = + descriptor->FindFieldByName(field_name); + if (field_descriptor == NULL) { + PyErr_Format(PyExc_ValueError, "Unknown field %s.", field_name); + return NULL; + } + + message->GetReflection()->ClearField(message, field_descriptor); + Py_RETURN_NONE; +} + +static PyObject* CMessage_GetScalar(CMessage* self, PyObject* arg) { + CFieldDescriptor* cdescriptor = NULL; + if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), + &CFieldDescriptor_Type)) { + PyErr_SetString(PyExc_TypeError, "Must be a field descriptor"); + return NULL; + } + cdescriptor = reinterpret_cast<CFieldDescriptor*>(arg); + + google::protobuf::Message* message = self->message; + return InternalGetScalar(message, cdescriptor->descriptor); +} + +static PyObject* CMessage_GetRepeatedScalar(CMessage* self, PyObject* args) { + CFieldDescriptor* cfield_descriptor; + PyObject* slice; + if (!PyArg_ParseTuple(args, C("O!O:GetRepeatedScalar"), + &CFieldDescriptor_Type, &cfield_descriptor, &slice)) { + return NULL; + } + + return InternalGetRepeatedScalarSlice( + self, cfield_descriptor->descriptor, slice); +} + +static PyObject* CMessage_AssignRepeatedScalar(CMessage* self, PyObject* args) { + CFieldDescriptor* cfield_descriptor; + PyObject* slice; + if (!PyArg_ParseTuple(args, C("O!O:AssignRepeatedScalar"), + &CFieldDescriptor_Type, &cfield_descriptor, &slice)) { + return NULL; + } + + AssureWritable(self); + google::protobuf::Message* message = self->message; + message->GetReflection()->ClearField(message, cfield_descriptor->descriptor); + + PyObject* iter = PyObject_GetIter(slice); + PyObject* next; + while ((next = PyIter_Next(iter)) != NULL) { + if (InternalAddRepeatedScalar( + message, cfield_descriptor->descriptor, next) == NULL) { + Py_DECREF(iter); + return NULL; + } + } + Py_DECREF(iter); + Py_RETURN_NONE; +} + +static PyObject* CMessage_DeleteRepeatedField(CMessage* self, PyObject* args) { + CFieldDescriptor* cfield_descriptor; + PyObject* slice; + if (!PyArg_ParseTuple(args, C("O!O:DeleteRepeatedField"), + &CFieldDescriptor_Type, &cfield_descriptor, &slice)) { + return NULL; + } + AssureWritable(self); + + Py_ssize_t length, from, to, step, slice_length; + google::protobuf::Message* message = self->message; + const google::protobuf::FieldDescriptor* field_descriptor = + cfield_descriptor->descriptor; + const google::protobuf::Reflection* reflection = message->GetReflection(); + int min, max; + length = reflection->FieldSize(*message, field_descriptor); + + if (PyInt_Check(slice) || PyLong_Check(slice)) { + from = to = PyLong_AsLong(slice); + if (from < 0) { + from = to = length + from; + } + step = 1; + min = max = from; + + // Range check. + if (from < 0 || from >= length) { + PyErr_Format(PyExc_IndexError, "list assignment index out of range"); + return NULL; + } + } else if (PySlice_Check(slice)) { + from = to = step = slice_length = 0; + PySlice_GetIndicesEx( + reinterpret_cast<PySliceObject*>(slice), + length, &from, &to, &step, &slice_length); + if (from < to) { + min = from; + max = to - 1; + } else { + min = to + 1; + max = from; + } + } else { + PyErr_SetString(PyExc_TypeError, "list indices must be integers"); + return NULL; + } + + Py_ssize_t i = from; + std::vector<bool> to_delete(length, false); + while (i >= min && i <= max) { + to_delete[i] = true; + i += step; + } + + to = 0; + for (i = 0; i < length; ++i) { + if (!to_delete[i]) { + if (i != to) { + reflection->SwapElements(message, field_descriptor, i, to); + } + ++to; + } + } + + while (i > to) { + reflection->RemoveLast(message, field_descriptor); + --i; + } + + Py_RETURN_NONE; +} + + +static PyObject* CMessage_SetScalar(CMessage* self, PyObject* args) { + CFieldDescriptor* cfield_descriptor; + PyObject* arg; + if (!PyArg_ParseTuple(args, C("O!O:SetScalar"), + &CFieldDescriptor_Type, &cfield_descriptor, &arg)) { + return NULL; + } + AssureWritable(self); + + return InternalSetScalar(self->message, cfield_descriptor->descriptor, arg); +} + +static PyObject* CMessage_AddRepeatedScalar(CMessage* self, PyObject* args) { + CFieldDescriptor* cfield_descriptor; + PyObject* value; + if (!PyArg_ParseTuple(args, C("O!O:AddRepeatedScalar"), + &CFieldDescriptor_Type, &cfield_descriptor, &value)) { + return NULL; + } + AssureWritable(self); + + return InternalAddRepeatedScalar( + self->message, cfield_descriptor->descriptor, value); +} + +static PyObject* CMessage_FieldLength(CMessage* self, PyObject* arg) { + CFieldDescriptor* cfield_descriptor; + if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), + &CFieldDescriptor_Type)) { + PyErr_SetString(PyExc_TypeError, "Must be a field descriptor"); + return NULL; + } + cfield_descriptor = reinterpret_cast<CFieldDescriptor*>(arg); + + google::protobuf::Message* message = self->message; + int length = message->GetReflection()->FieldSize( + *message, cfield_descriptor->descriptor); + return PyInt_FromLong(length); +} + +static PyObject* CMessage_DebugString(CMessage* self, PyObject* args) { + return PyString_FromString(self->message->DebugString().c_str()); +} + +static PyObject* CMessage_SerializeToString(CMessage* self, PyObject* args) { + int size = self->message->ByteSize(); + if (size <= 0) { + return PyString_FromString(""); + } + PyObject* result = PyString_FromStringAndSize(NULL, size); + if (result == NULL) { + return NULL; + } + char* buffer = PyString_AS_STRING(result); + self->message->SerializeWithCachedSizesToArray( + reinterpret_cast<uint8*>(buffer)); + return result; +} + +static PyObject* CMessage_SerializePartialToString( + CMessage* self, PyObject* args) { + string contents; + self->message->SerializePartialToString(&contents); + return PyString_FromStringAndSize(contents.c_str(), contents.size()); +} + +static PyObject* CMessageStr(CMessage* self) { + char str[1024]; + str[sizeof(str) - 1] = 0; + snprintf(str, sizeof(str) - 1, "CMessage: <%p>", self->message); + return PyString_FromString(str); +} + +static PyObject* CMessage_MergeFrom(CMessage* self, PyObject* arg) { + CMessage* other_message; + if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), &CMessage_Type)) { + PyErr_SetString(PyExc_TypeError, "Must be a message"); + return NULL; + } + + other_message = reinterpret_cast<CMessage*>(arg); + if (other_message->message->GetDescriptor() != + self->message->GetDescriptor()) { + PyErr_Format(PyExc_TypeError, + "Tried to merge from a message with a different type. " + "to: %s, from: %s", + self->message->GetDescriptor()->full_name().c_str(), + other_message->message->GetDescriptor()->full_name().c_str()); + return NULL; + } + AssureWritable(self); + + self->message->MergeFrom(*other_message->message); + Py_RETURN_NONE; +} + +static PyObject* CMessage_CopyFrom(CMessage* self, PyObject* arg) { + CMessage* other_message; + if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), &CMessage_Type)) { + PyErr_SetString(PyExc_TypeError, "Must be a message"); + return NULL; + } + + other_message = reinterpret_cast<CMessage*>(arg); + if (other_message->message->GetDescriptor() != + self->message->GetDescriptor()) { + PyErr_Format(PyExc_TypeError, + "Tried to copy from a message with a different type. " + "to: %s, from: %s", + self->message->GetDescriptor()->full_name().c_str(), + other_message->message->GetDescriptor()->full_name().c_str()); + return NULL; + } + + AssureWritable(self); + + self->message->CopyFrom(*other_message->message); + Py_RETURN_NONE; +} + +static PyObject* CMessage_MergeFromString(CMessage* self, PyObject* arg) { + const void* data; + Py_ssize_t data_length; + if (PyObject_AsReadBuffer(arg, &data, &data_length) < 0) { + return NULL; + } + + AssureWritable(self); + google::protobuf::io::CodedInputStream input( + reinterpret_cast<const uint8*>(data), data_length); + bool success = self->message->MergePartialFromCodedStream(&input); + if (success) { + return PyInt_FromLong(self->message->ByteSize()); + } else { + return PyInt_FromLong(-1); + } +} + +static PyObject* CMessage_ByteSize(CMessage* self, PyObject* args) { + return PyLong_FromLong(self->message->ByteSize()); +} + +static PyObject* CMessage_SetInParent(CMessage* self, PyObject* args) { + AssureWritable(self); + Py_RETURN_NONE; +} + +static PyObject* CMessage_SwapRepeatedFieldElements( + CMessage* self, PyObject* args) { + CFieldDescriptor* cfield_descriptor; + int index1, index2; + if (!PyArg_ParseTuple(args, C("O!ii:SwapRepeatedFieldElements"), + &CFieldDescriptor_Type, &cfield_descriptor, + &index1, &index2)) { + return NULL; + } + + google::protobuf::Message* message = self->message; + const google::protobuf::Reflection* reflection = message->GetReflection(); + + reflection->SwapElements( + message, cfield_descriptor->descriptor, index1, index2); + Py_RETURN_NONE; +} + +static PyObject* CMessage_AddMessage(CMessage* self, PyObject* arg) { + CFieldDescriptor* cfield_descriptor; + if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), + &CFieldDescriptor_Type)) { + PyErr_SetString(PyExc_TypeError, "Must be a field descriptor"); + return NULL; + } + cfield_descriptor = reinterpret_cast<CFieldDescriptor*>(arg); + AssureWritable(self); + + CMessage* py_cmsg = PyObject_New(CMessage, &CMessage_Type); + if (py_cmsg == NULL) { + return NULL; + } + + google::protobuf::Message* message = self->message; + const google::protobuf::Reflection* reflection = message->GetReflection(); + google::protobuf::Message* sub_message = + reflection->AddMessage(message, cfield_descriptor->descriptor); + + py_cmsg->parent = NULL; + py_cmsg->full_name = sub_message->GetDescriptor()->full_name().c_str(); + py_cmsg->message = sub_message; + py_cmsg->free_message = false; + py_cmsg->read_only = false; + return reinterpret_cast<PyObject*>(py_cmsg); +} + +static PyObject* CMessage_GetRepeatedMessage(CMessage* self, PyObject* args) { + CFieldDescriptor* cfield_descriptor; + PyObject* slice; + if (!PyArg_ParseTuple(args, C("O!O:GetRepeatedMessage"), + &CFieldDescriptor_Type, &cfield_descriptor, &slice)) { + return NULL; + } + + return InternalGetRepeatedScalarSlice( + self, cfield_descriptor->descriptor, slice); +} + +static PyObject* CMessage_NewSubMessage(CMessage* self, PyObject* arg) { + CFieldDescriptor* cfield_descriptor; + if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), + &CFieldDescriptor_Type)) { + PyErr_SetString(PyExc_TypeError, "Must be a field descriptor"); + return NULL; + } + cfield_descriptor = reinterpret_cast<CFieldDescriptor*>(arg); + + CMessage* py_cmsg = PyObject_New(CMessage, &CMessage_Type); + if (py_cmsg == NULL) { + return NULL; + } + + google::protobuf::Message* message = self->message; + const google::protobuf::Reflection* reflection = message->GetReflection(); + const google::protobuf::Message& sub_message = + reflection->GetMessage(*message, cfield_descriptor->descriptor, + global_message_factory); + + py_cmsg->full_name = sub_message.GetDescriptor()->full_name().c_str(); + py_cmsg->parent = self; + py_cmsg->parent_field = cfield_descriptor; + py_cmsg->message = const_cast<google::protobuf::Message*>(&sub_message); + py_cmsg->free_message = false; + py_cmsg->read_only = true; + return reinterpret_cast<PyObject*>(py_cmsg); +} + +static PyObject* CMessage_MutableMessage(CMessage* self, PyObject* arg) { + CFieldDescriptor* cfield_descriptor; + if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), + &CFieldDescriptor_Type)) { + PyErr_SetString(PyExc_TypeError, "Must be a field descriptor"); + return NULL; + } + cfield_descriptor = reinterpret_cast<CFieldDescriptor*>(arg); + AssureWritable(self); + + CMessage* py_cmsg = PyObject_New(CMessage, &CMessage_Type); + if (py_cmsg == NULL) { + return NULL; + } + + google::protobuf::Message* message = self->message; + const google::protobuf::Reflection* reflection = message->GetReflection(); + google::protobuf::Message* mutable_message = + reflection->MutableMessage(message, cfield_descriptor->descriptor, + global_message_factory); + + py_cmsg->full_name = mutable_message->GetDescriptor()->full_name().c_str(); + py_cmsg->message = mutable_message; + py_cmsg->free_message = false; + py_cmsg->read_only = false; + return reinterpret_cast<PyObject*>(py_cmsg); +} + +static PyObject* CMessage_Equals(CMessage* self, PyObject* arg) { + CMessage* other_message; + if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), &CMessage_Type)) { + PyErr_SetString(PyExc_TypeError, "Must be a message"); + return NULL; + } + other_message = reinterpret_cast<CMessage*>(arg); + + if (other_message->message == self->message) { + return PyBool_FromLong(1); + } + + if (other_message->message->GetDescriptor() != + self->message->GetDescriptor()) { + return PyBool_FromLong(0); + } + + return PyBool_FromLong(1); +} + +static PyObject* CMessage_ListFields(CMessage* self, PyObject* args) { + google::protobuf::Message* message = self->message; + const google::protobuf::Reflection* reflection = message->GetReflection(); + vector<const google::protobuf::FieldDescriptor*> fields; + reflection->ListFields(*message, &fields); + + PyObject* list = PyList_New(fields.size()); + if (list == NULL) { + return NULL; + } + + for (unsigned int i = 0; i < fields.size(); ++i) { + bool is_extension = fields[i]->is_extension(); + PyObject* t = PyTuple_New(2); + if (t == NULL) { + Py_DECREF(list); + return NULL; + } + + PyObject* is_extension_object = PyBool_FromLong(is_extension ? 1 : 0); + + PyObject* field_name; + const string* s; + if (is_extension) { + s = &fields[i]->full_name(); + } else { + s = &fields[i]->name(); + } + field_name = PyString_FromStringAndSize(s->c_str(), s->length()); + if (field_name == NULL) { + Py_DECREF(list); + Py_DECREF(t); + return NULL; + } + + PyTuple_SET_ITEM(t, 0, is_extension_object); + PyTuple_SET_ITEM(t, 1, field_name); + PyList_SET_ITEM(list, i, t); + } + + return list; +} + +static PyObject* CMessage_FindInitializationErrors(CMessage* self) { + google::protobuf::Message* message = self->message; + vector<string> errors; + message->FindInitializationErrors(&errors); + + PyObject* error_list = PyList_New(errors.size()); + if (error_list == NULL) { + return NULL; + } + for (unsigned int i = 0; i < errors.size(); ++i) { + const string& error = errors[i]; + PyObject* error_string = PyString_FromStringAndSize( + error.c_str(), error.length()); + if (error_string == NULL) { + Py_DECREF(error_list); + return NULL; + } + PyList_SET_ITEM(error_list, i, error_string); + } + return error_list; +} + +// ------ Python Constructor: + +PyObject* Python_NewCMessage(PyObject* ignored, PyObject* arg) { + const char* message_type = PyString_AsString(arg); + if (message_type == NULL) { + return NULL; + } + + const google::protobuf::Message* message = CreateMessage(message_type); + if (message == NULL) { + PyErr_Format(PyExc_TypeError, "Couldn't create message of type %s!", + message_type); + return NULL; + } + + CMessage* py_cmsg = PyObject_New(CMessage, &CMessage_Type); + if (py_cmsg == NULL) { + return NULL; + } + py_cmsg->message = message->New(); + py_cmsg->free_message = true; + py_cmsg->full_name = message->GetDescriptor()->full_name().c_str(); + py_cmsg->read_only = false; + py_cmsg->parent = NULL; + py_cmsg->parent_field = NULL; + return reinterpret_cast<PyObject*>(py_cmsg); +} + +// --- Module Functions (exposed to Python): + +PyMethodDef methods[] = { + { C("NewCMessage"), (PyCFunction)Python_NewCMessage, + METH_O, + C("Creates a new C++ protocol message, given its full name.") }, + { C("NewCDescriptorPool"), (PyCFunction)Python_NewCDescriptorPool, + METH_NOARGS, + C("Creates a new C++ descriptor pool.") }, + { C("BuildFile"), (PyCFunction)Python_BuildFile, + METH_O, + C("Registers a new protocol buffer file in the global C++ descriptor " + "pool.") }, + {NULL} +}; + +// --- Exposing the C proto living inside Python proto to C code: + +extern const Message* (*GetCProtoInsidePyProtoPtr)(PyObject* msg); +extern Message* (*MutableCProtoInsidePyProtoPtr)(PyObject* msg); + +static const google::protobuf::Message* GetCProtoInsidePyProtoImpl(PyObject* msg) { + PyObject* c_msg_obj = PyObject_GetAttrString(msg, "_cmsg"); + if (c_msg_obj == NULL) { + PyErr_Clear(); + return NULL; + } + Py_DECREF(c_msg_obj); + if (!PyObject_TypeCheck(c_msg_obj, &CMessage_Type)) { + return NULL; + } + CMessage* c_msg = reinterpret_cast<CMessage*>(c_msg_obj); + return c_msg->message; +} + +static google::protobuf::Message* MutableCProtoInsidePyProtoImpl(PyObject* msg) { + PyObject* c_msg_obj = PyObject_GetAttrString(msg, "_cmsg"); + if (c_msg_obj == NULL) { + PyErr_Clear(); + return NULL; + } + Py_DECREF(c_msg_obj); + if (!PyObject_TypeCheck(c_msg_obj, &CMessage_Type)) { + return NULL; + } + CMessage* c_msg = reinterpret_cast<CMessage*>(c_msg_obj); + AssureWritable(c_msg); + return c_msg->message; +} + +// --- Module Init Function: + +static const char module_docstring[] = +"python-proto2 is a module that can be used to enhance proto2 Python API\n" +"performance.\n" +"\n" +"It provides access to the protocol buffers C++ reflection API that\n" +"implements the basic protocol buffer functions."; + +extern "C" { + void init_net_proto2___python() { + // Initialize constants. + kPythonZero = PyInt_FromLong(0); + kint32min_py = PyInt_FromLong(kint32min); + kint32max_py = PyInt_FromLong(kint32max); + kuint32max_py = PyLong_FromLongLong(kuint32max); + kint64min_py = PyLong_FromLongLong(kint64min); + kint64max_py = PyLong_FromLongLong(kint64max); + kuint64max_py = PyLong_FromUnsignedLongLong(kuint64max); + + global_message_factory = new DynamicMessageFactory(GetDescriptorPool()); + global_message_factory->SetDelegateToGeneratedFactory(true); + + // Export our functions to Python. + PyObject *m; + m = Py_InitModule3(C("_net_proto2___python"), methods, C(module_docstring)); + if (m == NULL) { + return; + } + + AddConstants(m); + + CMessage_Type.tp_new = PyType_GenericNew; + if (PyType_Ready(&CMessage_Type) < 0) { + return; + } + + if (!InitDescriptor()) { + return; + } + + // Override {Get,Mutable}CProtoInsidePyProto. + GetCProtoInsidePyProtoPtr = GetCProtoInsidePyProtoImpl; + MutableCProtoInsidePyProtoPtr = MutableCProtoInsidePyProtoImpl; + } +} + +} // namespace python +} // namespace protobuf +} // namespace google diff --git a/python/google/protobuf/pyext/python_descriptor.cc b/python/google/protobuf/pyext/python_descriptor.cc new file mode 100644 index 00000000..fb87bad1 --- /dev/null +++ b/python/google/protobuf/pyext/python_descriptor.cc @@ -0,0 +1,334 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: petar@google.com (Petar Petrov) + +#include <Python.h> + +#include <google/protobuf/pyext/python_descriptor.h> +#include <google/protobuf/descriptor.pb.h> + +#define C(str) const_cast<char*>(str) + +namespace google { +namespace protobuf { +namespace python { + +static void CFieldDescriptorDealloc(CFieldDescriptor* self); + +static google::protobuf::DescriptorPool* g_descriptor_pool = NULL; + +static PyObject* CFieldDescriptor_GetFullName( + CFieldDescriptor* self, void *closure) { + Py_XINCREF(self->full_name); + return self->full_name; +} + +static PyObject* CFieldDescriptor_GetName( + CFieldDescriptor *self, void *closure) { + Py_XINCREF(self->name); + return self->name; +} + +static PyObject* CFieldDescriptor_GetCppType( + CFieldDescriptor *self, void *closure) { + Py_XINCREF(self->cpp_type); + return self->cpp_type; +} + +static PyObject* CFieldDescriptor_GetLabel( + CFieldDescriptor *self, void *closure) { + Py_XINCREF(self->label); + return self->label; +} + +static PyObject* CFieldDescriptor_GetID( + CFieldDescriptor *self, void *closure) { + Py_XINCREF(self->id); + return self->id; +} + + +static PyGetSetDef CFieldDescriptorGetters[] = { + { C("full_name"), + (getter)CFieldDescriptor_GetFullName, NULL, "Full name", NULL}, + { C("name"), + (getter)CFieldDescriptor_GetName, NULL, "last name", NULL}, + { C("cpp_type"), + (getter)CFieldDescriptor_GetCppType, NULL, "C++ Type", NULL}, + { C("label"), + (getter)CFieldDescriptor_GetLabel, NULL, "Label", NULL}, + { C("id"), + (getter)CFieldDescriptor_GetID, NULL, "ID", NULL}, + {NULL} +}; + +PyTypeObject CFieldDescriptor_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + C("google3.net.google.protobuf.python.internal." + "_net_proto2___python." + "CFieldDescriptor"), // tp_name + sizeof(CFieldDescriptor), // tp_basicsize + 0, // tp_itemsize + (destructor)CFieldDescriptorDealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + C("A Field Descriptor"), // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + 0, // tp_methods + 0, // tp_members + CFieldDescriptorGetters, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + PyType_GenericAlloc, // tp_alloc + PyType_GenericNew, // tp_new + PyObject_Del, // tp_free +}; + +static void CFieldDescriptorDealloc(CFieldDescriptor* self) { + Py_DECREF(self->full_name); + Py_DECREF(self->name); + Py_DECREF(self->cpp_type); + Py_DECREF(self->label); + Py_DECREF(self->id); + self->ob_type->tp_free(reinterpret_cast<PyObject*>(self)); +} + +typedef struct { + PyObject_HEAD + + const google::protobuf::DescriptorPool* pool; +} CDescriptorPool; + +static void CDescriptorPoolDealloc(CDescriptorPool* self); + +static PyObject* CDescriptorPool_NewCDescriptor( + const google::protobuf::FieldDescriptor* field_descriptor) { + CFieldDescriptor* cfield_descriptor = PyObject_New( + CFieldDescriptor, &CFieldDescriptor_Type); + if (cfield_descriptor == NULL) { + return NULL; + } + cfield_descriptor->descriptor = field_descriptor; + + cfield_descriptor->full_name = PyString_FromString( + field_descriptor->full_name().c_str()); + cfield_descriptor->name = PyString_FromString( + field_descriptor->name().c_str()); + cfield_descriptor->cpp_type = PyLong_FromLong(field_descriptor->cpp_type()); + cfield_descriptor->label = PyLong_FromLong(field_descriptor->label()); + cfield_descriptor->id = PyLong_FromVoidPtr(cfield_descriptor); + return reinterpret_cast<PyObject*>(cfield_descriptor); +} + +static PyObject* CDescriptorPool_FindFieldByName( + CDescriptorPool* self, PyObject* arg) { + const char* full_field_name = PyString_AsString(arg); + if (full_field_name == NULL) { + return NULL; + } + + const google::protobuf::FieldDescriptor* field_descriptor = NULL; + + field_descriptor = self->pool->FindFieldByName(full_field_name); + if (field_descriptor == NULL) { + PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s", + full_field_name); + return NULL; + } + + return CDescriptorPool_NewCDescriptor(field_descriptor); +} + +static PyObject* CDescriptorPool_FindExtensionByName( + CDescriptorPool* self, PyObject* arg) { + const char* full_field_name = PyString_AsString(arg); + if (full_field_name == NULL) { + return NULL; + } + + const google::protobuf::FieldDescriptor* field_descriptor = + self->pool->FindExtensionByName(full_field_name); + if (field_descriptor == NULL) { + PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s", + full_field_name); + return NULL; + } + + return CDescriptorPool_NewCDescriptor(field_descriptor); +} + +static PyMethodDef CDescriptorPoolMethods[] = { + { C("FindFieldByName"), + (PyCFunction)CDescriptorPool_FindFieldByName, + METH_O, + C("Searches for a field descriptor by full name.") }, + { C("FindExtensionByName"), + (PyCFunction)CDescriptorPool_FindExtensionByName, + METH_O, + C("Searches for extension descriptor by full name.") }, + {NULL} +}; + +PyTypeObject CDescriptorPool_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + C("google3.net.google.protobuf.python.internal." + "_net_proto2___python." + "CFieldDescriptor"), // tp_name + sizeof(CDescriptorPool), // tp_basicsize + 0, // tp_itemsize + (destructor)CDescriptorPoolDealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + C("A Descriptor Pool"), // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + CDescriptorPoolMethods, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + PyType_GenericAlloc, // tp_alloc + PyType_GenericNew, // tp_new + PyObject_Del, // tp_free +}; + +static void CDescriptorPoolDealloc(CDescriptorPool* self) { + self->ob_type->tp_free(reinterpret_cast<PyObject*>(self)); +} + +google::protobuf::DescriptorPool* GetDescriptorPool() { + if (g_descriptor_pool == NULL) { + g_descriptor_pool = new google::protobuf::DescriptorPool( + google::protobuf::DescriptorPool::generated_pool()); + } + return g_descriptor_pool; +} + +PyObject* Python_NewCDescriptorPool(PyObject* ignored, PyObject* args) { + CDescriptorPool* cdescriptor_pool = PyObject_New( + CDescriptorPool, &CDescriptorPool_Type); + if (cdescriptor_pool == NULL) { + return NULL; + } + cdescriptor_pool->pool = GetDescriptorPool(); + return reinterpret_cast<PyObject*>(cdescriptor_pool); +} + +PyObject* Python_BuildFile(PyObject* ignored, PyObject* arg) { + char* message_type; + Py_ssize_t message_len; + + if (PyString_AsStringAndSize(arg, &message_type, &message_len) < 0) { + return NULL; + } + + google::protobuf::FileDescriptorProto file_proto; + if (!file_proto.ParseFromArray(message_type, message_len)) { + PyErr_SetString(PyExc_TypeError, "Couldn't parse file content!"); + return NULL; + } + + // If this file is already in the generated pool, don't add it again. + if (google::protobuf::DescriptorPool::generated_pool()->FindFileByName( + file_proto.name()) != NULL) { + Py_RETURN_NONE; + } + + const google::protobuf::FileDescriptor* descriptor = GetDescriptorPool()->BuildFile( + file_proto); + if (descriptor == NULL) { + PyErr_SetString(PyExc_TypeError, + "Couldn't build proto file into descriptor pool!"); + return NULL; + } + + Py_RETURN_NONE; +} + +bool InitDescriptor() { + CFieldDescriptor_Type.tp_new = PyType_GenericNew; + if (PyType_Ready(&CFieldDescriptor_Type) < 0) + return false; + + CDescriptorPool_Type.tp_new = PyType_GenericNew; + if (PyType_Ready(&CDescriptorPool_Type) < 0) + return false; + return true; +} + +} // namespace python +} // namespace protobuf +} // namespace google diff --git a/python/google/protobuf/pyext/python_descriptor.h b/python/google/protobuf/pyext/python_descriptor.h new file mode 100644 index 00000000..5232680f --- /dev/null +++ b/python/google/protobuf/pyext/python_descriptor.h @@ -0,0 +1,87 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: petar@google.com (Petar Petrov) + +#ifndef GOOGLE_PROTOBUF_PYTHON_DESCRIPTOR_H__ +#define GOOGLE_PROTOBUF_PYTHON_DESCRIPTOR_H__ + +#include <Python.h> +#include <structmember.h> + +#include <google/protobuf/descriptor.h> + +#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) +typedef int Py_ssize_t; +#define PY_SSIZE_T_MAX INT_MAX +#define PY_SSIZE_T_MIN INT_MIN +#endif + +namespace google { +namespace protobuf { +namespace python { + +typedef struct { + PyObject_HEAD + + // The proto2 descriptor that this object represents. + const google::protobuf::FieldDescriptor* descriptor; + + // Full name of the field (PyString). + PyObject* full_name; + + // Name of the field (PyString). + PyObject* name; + + // C++ type of the field (PyLong). + PyObject* cpp_type; + + // Name of the field (PyLong). + PyObject* label; + + // Identity of the descriptor (PyLong used as a poiner). + PyObject* id; +} CFieldDescriptor; + +extern PyTypeObject CFieldDescriptor_Type; + +extern PyTypeObject CDescriptorPool_Type; + + +PyObject* Python_NewCDescriptorPool(PyObject* ignored, PyObject* args); +PyObject* Python_BuildFile(PyObject* ignored, PyObject* args); +bool InitDescriptor(); +google::protobuf::DescriptorPool* GetDescriptorPool(); + +} // namespace python +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_PYTHON_DESCRIPTOR_H__ diff --git a/python/google/protobuf/pyext/python_protobuf.cc b/python/google/protobuf/pyext/python_protobuf.cc new file mode 100644 index 00000000..1b1ab5d1 --- /dev/null +++ b/python/google/protobuf/pyext/python_protobuf.cc @@ -0,0 +1,63 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: qrczak@google.com (Marcin Kowalczyk) + +#include <google/protobuf/pyext/python_protobuf.h> + +namespace google { +namespace protobuf { +namespace python { + +static const Message* GetCProtoInsidePyProtoStub(PyObject* msg) { + return NULL; +} +static Message* MutableCProtoInsidePyProtoStub(PyObject* msg) { + return NULL; +} + +// This is initialized with a default, stub implementation. +// If python-google.protobuf.cc is loaded, the function pointer is overridden +// with a full implementation. +const Message* (*GetCProtoInsidePyProtoPtr)(PyObject* msg) = + GetCProtoInsidePyProtoStub; +Message* (*MutableCProtoInsidePyProtoPtr)(PyObject* msg) = + MutableCProtoInsidePyProtoStub; + +const Message* GetCProtoInsidePyProto(PyObject* msg) { + return GetCProtoInsidePyProtoPtr(msg); +} +Message* MutableCProtoInsidePyProto(PyObject* msg) { + return MutableCProtoInsidePyProtoPtr(msg); +} + +} // namespace python +} // namespace protobuf +} // namespace google diff --git a/python/google/protobuf/pyext/python_protobuf.h b/python/google/protobuf/pyext/python_protobuf.h new file mode 100644 index 00000000..c5b0b1cd --- /dev/null +++ b/python/google/protobuf/pyext/python_protobuf.h @@ -0,0 +1,57 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: qrczak@google.com (Marcin Kowalczyk) +// +// This module exposes the C proto inside the given Python proto, in +// case the Python proto is implemented with a C proto. + +#ifndef GOOGLE_PROTOBUF_PYTHON_PYTHON_PROTOBUF_H__ +#define GOOGLE_PROTOBUF_PYTHON_PYTHON_PROTOBUF_H__ + +#include <Python.h> + +namespace google { +namespace protobuf { + +class Message; + +namespace python { + +// Return the pointer to the C proto inside the given Python proto, +// or NULL when this is not a Python proto implemented with a C proto. +const Message* GetCProtoInsidePyProto(PyObject* msg); +Message* MutableCProtoInsidePyProto(PyObject* msg); + +} // namespace python +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_PYTHON_PYTHON_PROTOBUF_H__ diff --git a/python/google/protobuf/reflection.py b/python/google/protobuf/reflection.py index 45e5ba88..1373c882 100755 --- a/python/google/protobuf/reflection.py +++ b/python/google/protobuf/reflection.py @@ -29,9 +29,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # This code is meant to work on Python 2.4 and above only. -# -# TODO(robinson): Helpers for verbose, common checks like seeing if a -# descriptor's cpp_type is CPPTYPE_MESSAGE. """Contains a metaclass and helper functions used to create protocol message classes from Descriptor objects at runtime. @@ -50,27 +47,22 @@ this file*. __author__ = 'robinson@google.com (Will Robinson)' -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO -import struct -import weakref -# We use "as" to avoid name collisions with variables. -from google.protobuf.internal import containers -from google.protobuf.internal import decoder -from google.protobuf.internal import encoder -from google.protobuf.internal import message_listener as message_listener_mod -from google.protobuf.internal import type_checkers -from google.protobuf.internal import wire_format +from google.protobuf.internal import api_implementation from google.protobuf import descriptor as descriptor_mod -from google.protobuf import message as message_mod -from google.protobuf import text_format - _FieldDescriptor = descriptor_mod.FieldDescriptor +if api_implementation.Type() == 'cpp': + from google.protobuf.internal import cpp_message + _NewMessage = cpp_message.NewMessage + _InitMessage = cpp_message.InitMessage +else: + from google.protobuf.internal import python_message + _NewMessage = python_message.NewMessage + _InitMessage = python_message.InitMessage + + class GeneratedProtocolMessageType(type): """Metaclass for protocol message classes created at runtime from Descriptors. @@ -120,10 +112,12 @@ class GeneratedProtocolMessageType(type): Newly-allocated class. """ descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY] - _AddSlots(descriptor, dictionary) - _AddClassAttributesForNestedExtensions(descriptor, dictionary) + _NewMessage(descriptor, dictionary) superclass = super(GeneratedProtocolMessageType, cls) - return superclass.__new__(cls, name, bases, dictionary) + + new_class = superclass.__new__(cls, name, bases, dictionary) + setattr(descriptor, '_concrete_class', new_class) + return new_class def __init__(cls, name, bases, dictionary): """Here we perform the majority of our work on the class. @@ -143,1018 +137,6 @@ class GeneratedProtocolMessageType(type): type. """ descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY] - - cls._decoders_by_tag = {} - cls._extensions_by_name = {} - cls._extensions_by_number = {} - if (descriptor.has_options and - descriptor.GetOptions().message_set_wire_format): - cls._decoders_by_tag[decoder.MESSAGE_SET_ITEM_TAG] = ( - decoder.MessageSetItemDecoder(cls._extensions_by_number)) - - # We act as a "friend" class of the descriptor, setting - # its _concrete_class attribute the first time we use a - # given descriptor to initialize a concrete protocol message - # class. We also attach stuff to each FieldDescriptor for quick - # lookup later on. - concrete_class_attr_name = '_concrete_class' - if not hasattr(descriptor, concrete_class_attr_name): - setattr(descriptor, concrete_class_attr_name, cls) - for field in descriptor.fields: - _AttachFieldHelpers(cls, field) - - _AddEnumValues(descriptor, cls) - _AddInitMethod(descriptor, cls) - _AddPropertiesForFields(descriptor, cls) - _AddPropertiesForExtensions(descriptor, cls) - _AddStaticMethods(cls) - _AddMessageMethods(descriptor, cls) - _AddPrivateHelperMethods(cls) + _InitMessage(descriptor, cls) superclass = super(GeneratedProtocolMessageType, cls) superclass.__init__(name, bases, dictionary) - - -# Stateless helpers for GeneratedProtocolMessageType below. -# Outside clients should not access these directly. -# -# I opted not to make any of these methods on the metaclass, to make it more -# clear that I'm not really using any state there and to keep clients from -# thinking that they have direct access to these construction helpers. - - -def _PropertyName(proto_field_name): - """Returns the name of the public property attribute which - clients can use to get and (in some cases) set the value - of a protocol message field. - - Args: - proto_field_name: The protocol message field name, exactly - as it appears (or would appear) in a .proto file. - """ - # TODO(robinson): Escape Python keywords (e.g., yield), and test this support. - # nnorwitz makes my day by writing: - # """ - # FYI. See the keyword module in the stdlib. This could be as simple as: - # - # if keyword.iskeyword(proto_field_name): - # return proto_field_name + "_" - # return proto_field_name - # """ - # Kenton says: The above is a BAD IDEA. People rely on being able to use - # getattr() and setattr() to reflectively manipulate field values. If we - # rename the properties, then every such user has to also make sure to apply - # the same transformation. Note that currently if you name a field "yield", - # you can still access it just fine using getattr/setattr -- it's not even - # that cumbersome to do so. - # TODO(kenton): Remove this method entirely if/when everyone agrees with my - # position. - return proto_field_name - - -def _VerifyExtensionHandle(message, extension_handle): - """Verify that the given extension handle is valid.""" - - if not isinstance(extension_handle, _FieldDescriptor): - raise KeyError('HasExtension() expects an extension handle, got: %s' % - extension_handle) - - if not extension_handle.is_extension: - raise KeyError('"%s" is not an extension.' % extension_handle.full_name) - - if extension_handle.containing_type is not message.DESCRIPTOR: - raise KeyError('Extension "%s" extends message type "%s", but this ' - 'message is of type "%s".' % - (extension_handle.full_name, - extension_handle.containing_type.full_name, - message.DESCRIPTOR.full_name)) - - -def _AddSlots(message_descriptor, dictionary): - """Adds a __slots__ entry to dictionary, containing the names of all valid - attributes for this message type. - - Args: - message_descriptor: A Descriptor instance describing this message type. - dictionary: Class dictionary to which we'll add a '__slots__' entry. - """ - dictionary['__slots__'] = ['_cached_byte_size', - '_cached_byte_size_dirty', - '_fields', - '_is_present_in_parent', - '_listener', - '_listener_for_children', - '__weakref__'] - - -def _IsMessageSetExtension(field): - return (field.is_extension and - field.containing_type.has_options and - field.containing_type.GetOptions().message_set_wire_format and - field.type == _FieldDescriptor.TYPE_MESSAGE and - field.message_type == field.extension_scope and - field.label == _FieldDescriptor.LABEL_OPTIONAL) - - -def _AttachFieldHelpers(cls, field_descriptor): - is_repeated = (field_descriptor.label == _FieldDescriptor.LABEL_REPEATED) - is_packed = (field_descriptor.has_options and - field_descriptor.GetOptions().packed) - - if _IsMessageSetExtension(field_descriptor): - field_encoder = encoder.MessageSetItemEncoder(field_descriptor.number) - sizer = encoder.MessageSetItemSizer(field_descriptor.number) - else: - field_encoder = type_checkers.TYPE_TO_ENCODER[field_descriptor.type]( - field_descriptor.number, is_repeated, is_packed) - sizer = type_checkers.TYPE_TO_SIZER[field_descriptor.type]( - field_descriptor.number, is_repeated, is_packed) - - field_descriptor._encoder = field_encoder - field_descriptor._sizer = sizer - field_descriptor._default_constructor = _DefaultValueConstructorForField( - field_descriptor) - - def AddDecoder(wiretype, is_packed): - tag_bytes = encoder.TagBytes(field_descriptor.number, wiretype) - cls._decoders_by_tag[tag_bytes] = ( - type_checkers.TYPE_TO_DECODER[field_descriptor.type]( - field_descriptor.number, is_repeated, is_packed, - field_descriptor, field_descriptor._default_constructor)) - - AddDecoder(type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type], - False) - - if is_repeated and wire_format.IsTypePackable(field_descriptor.type): - # To support wire compatibility of adding packed = true, add a decoder for - # packed values regardless of the field's options. - AddDecoder(wire_format.WIRETYPE_LENGTH_DELIMITED, True) - - -def _AddClassAttributesForNestedExtensions(descriptor, dictionary): - extension_dict = descriptor.extensions_by_name - for extension_name, extension_field in extension_dict.iteritems(): - assert extension_name not in dictionary - dictionary[extension_name] = extension_field - - -def _AddEnumValues(descriptor, cls): - """Sets class-level attributes for all enum fields defined in this message. - - Args: - descriptor: Descriptor object for this message type. - cls: Class we're constructing for this message type. - """ - for enum_type in descriptor.enum_types: - for enum_value in enum_type.values: - setattr(cls, enum_value.name, enum_value.number) - - -def _DefaultValueConstructorForField(field): - """Returns a function which returns a default value for a field. - - Args: - field: FieldDescriptor object for this field. - - The returned function has one argument: - message: Message instance containing this field, or a weakref proxy - of same. - - That function in turn returns a default value for this field. The default - value may refer back to |message| via a weak reference. - """ - - if field.label == _FieldDescriptor.LABEL_REPEATED: - if field.default_value != []: - raise ValueError('Repeated field default value not empty list: %s' % ( - field.default_value)) - if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: - # We can't look at _concrete_class yet since it might not have - # been set. (Depends on order in which we initialize the classes). - message_type = field.message_type - def MakeRepeatedMessageDefault(message): - return containers.RepeatedCompositeFieldContainer( - message._listener_for_children, field.message_type) - return MakeRepeatedMessageDefault - else: - type_checker = type_checkers.GetTypeChecker(field.cpp_type, field.type) - def MakeRepeatedScalarDefault(message): - return containers.RepeatedScalarFieldContainer( - message._listener_for_children, type_checker) - return MakeRepeatedScalarDefault - - if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: - # _concrete_class may not yet be initialized. - message_type = field.message_type - def MakeSubMessageDefault(message): - result = message_type._concrete_class() - result._SetListener(message._listener_for_children) - return result - return MakeSubMessageDefault - - def MakeScalarDefault(message): - return field.default_value - return MakeScalarDefault - - -def _AddInitMethod(message_descriptor, cls): - """Adds an __init__ method to cls.""" - fields = message_descriptor.fields - def init(self, **kwargs): - self._cached_byte_size = 0 - self._cached_byte_size_dirty = False - self._fields = {} - self._is_present_in_parent = False - self._listener = message_listener_mod.NullMessageListener() - self._listener_for_children = _Listener(self) - for field_name, field_value in kwargs.iteritems(): - field = _GetFieldByName(message_descriptor, field_name) - if field is None: - raise TypeError("%s() got an unexpected keyword argument '%s'" % - (message_descriptor.name, field_name)) - if field.label == _FieldDescriptor.LABEL_REPEATED: - copy = field._default_constructor(self) - if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: # Composite - for val in field_value: - copy.add().MergeFrom(val) - else: # Scalar - copy.extend(field_value) - self._fields[field] = copy - elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: - copy = field._default_constructor(self) - copy.MergeFrom(field_value) - self._fields[field] = copy - else: - setattr(self, field_name, field_value) - - init.__module__ = None - init.__doc__ = None - cls.__init__ = init - - -def _GetFieldByName(message_descriptor, field_name): - """Returns a field descriptor by field name. - - Args: - message_descriptor: A Descriptor describing all fields in message. - field_name: The name of the field to retrieve. - Returns: - The field descriptor associated with the field name. - """ - try: - return message_descriptor.fields_by_name[field_name] - except KeyError: - raise ValueError('Protocol message has no "%s" field.' % field_name) - - -def _AddPropertiesForFields(descriptor, cls): - """Adds properties for all fields in this protocol message type.""" - for field in descriptor.fields: - _AddPropertiesForField(field, cls) - - if descriptor.is_extendable: - # _ExtensionDict is just an adaptor with no state so we allocate a new one - # every time it is accessed. - cls.Extensions = property(lambda self: _ExtensionDict(self)) - - -def _AddPropertiesForField(field, cls): - """Adds a public property for a protocol message field. - Clients can use this property to get and (in the case - of non-repeated scalar fields) directly set the value - of a protocol message field. - - Args: - field: A FieldDescriptor for this field. - cls: The class we're constructing. - """ - # Catch it if we add other types that we should - # handle specially here. - assert _FieldDescriptor.MAX_CPPTYPE == 10 - - constant_name = field.name.upper() + "_FIELD_NUMBER" - setattr(cls, constant_name, field.number) - - if field.label == _FieldDescriptor.LABEL_REPEATED: - _AddPropertiesForRepeatedField(field, cls) - elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: - _AddPropertiesForNonRepeatedCompositeField(field, cls) - else: - _AddPropertiesForNonRepeatedScalarField(field, cls) - - -def _AddPropertiesForRepeatedField(field, cls): - """Adds a public property for a "repeated" protocol message field. Clients - can use this property to get the value of the field, which will be either a - _RepeatedScalarFieldContainer or _RepeatedCompositeFieldContainer (see - below). - - Note that when clients add values to these containers, we perform - type-checking in the case of repeated scalar fields, and we also set any - necessary "has" bits as a side-effect. - - Args: - field: A FieldDescriptor for this field. - cls: The class we're constructing. - """ - proto_field_name = field.name - property_name = _PropertyName(proto_field_name) - - def getter(self): - field_value = self._fields.get(field) - if field_value is None: - # Construct a new object to represent this field. - field_value = field._default_constructor(self) - - # Atomically check if another thread has preempted us and, if not, swap - # in the new object we just created. If someone has preempted us, we - # take that object and discard ours. - # WARNING: We are relying on setdefault() being atomic. This is true - # in CPython but we haven't investigated others. This warning appears - # in several other locations in this file. - field_value = self._fields.setdefault(field, field_value) - return field_value - getter.__module__ = None - getter.__doc__ = 'Getter for %s.' % proto_field_name - - # We define a setter just so we can throw an exception with a more - # helpful error message. - def setter(self, new_value): - raise AttributeError('Assignment not allowed to repeated field ' - '"%s" in protocol message object.' % proto_field_name) - - doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name - setattr(cls, property_name, property(getter, setter, doc=doc)) - - -def _AddPropertiesForNonRepeatedScalarField(field, cls): - """Adds a public property for a nonrepeated, scalar protocol message field. - Clients can use this property to get and directly set the value of the field. - Note that when the client sets the value of a field by using this property, - all necessary "has" bits are set as a side-effect, and we also perform - type-checking. - - Args: - field: A FieldDescriptor for this field. - cls: The class we're constructing. - """ - proto_field_name = field.name - property_name = _PropertyName(proto_field_name) - type_checker = type_checkers.GetTypeChecker(field.cpp_type, field.type) - default_value = field.default_value - - def getter(self): - return self._fields.get(field, default_value) - getter.__module__ = None - getter.__doc__ = 'Getter for %s.' % proto_field_name - def setter(self, new_value): - type_checker.CheckValue(new_value) - self._fields[field] = new_value - # Check _cached_byte_size_dirty inline to improve performance, since scalar - # setters are called frequently. - if not self._cached_byte_size_dirty: - self._Modified() - setter.__module__ = None - setter.__doc__ = 'Setter for %s.' % proto_field_name - - # Add a property to encapsulate the getter/setter. - doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name - setattr(cls, property_name, property(getter, setter, doc=doc)) - - -def _AddPropertiesForNonRepeatedCompositeField(field, cls): - """Adds a public property for a nonrepeated, composite protocol message field. - A composite field is a "group" or "message" field. - - Clients can use this property to get the value of the field, but cannot - assign to the property directly. - - Args: - field: A FieldDescriptor for this field. - cls: The class we're constructing. - """ - # TODO(robinson): Remove duplication with similar method - # for non-repeated scalars. - proto_field_name = field.name - property_name = _PropertyName(proto_field_name) - message_type = field.message_type - - def getter(self): - field_value = self._fields.get(field) - if field_value is None: - # Construct a new object to represent this field. - field_value = message_type._concrete_class() - field_value._SetListener(self._listener_for_children) - - # Atomically check if another thread has preempted us and, if not, swap - # in the new object we just created. If someone has preempted us, we - # take that object and discard ours. - # WARNING: We are relying on setdefault() being atomic. This is true - # in CPython but we haven't investigated others. This warning appears - # in several other locations in this file. - field_value = self._fields.setdefault(field, field_value) - return field_value - getter.__module__ = None - getter.__doc__ = 'Getter for %s.' % proto_field_name - - # We define a setter just so we can throw an exception with a more - # helpful error message. - def setter(self, new_value): - raise AttributeError('Assignment not allowed to composite field ' - '"%s" in protocol message object.' % proto_field_name) - - # Add a property to encapsulate the getter. - doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name - setattr(cls, property_name, property(getter, setter, doc=doc)) - - -def _AddPropertiesForExtensions(descriptor, cls): - """Adds properties for all fields in this protocol message type.""" - extension_dict = descriptor.extensions_by_name - for extension_name, extension_field in extension_dict.iteritems(): - constant_name = extension_name.upper() + "_FIELD_NUMBER" - setattr(cls, constant_name, extension_field.number) - - -def _AddStaticMethods(cls): - # TODO(robinson): This probably needs to be thread-safe(?) - def RegisterExtension(extension_handle): - extension_handle.containing_type = cls.DESCRIPTOR - _AttachFieldHelpers(cls, extension_handle) - - # Try to insert our extension, failing if an extension with the same number - # already exists. - actual_handle = cls._extensions_by_number.setdefault( - extension_handle.number, extension_handle) - if actual_handle is not extension_handle: - raise AssertionError( - 'Extensions "%s" and "%s" both try to extend message type "%s" with ' - 'field number %d.' % - (extension_handle.full_name, actual_handle.full_name, - cls.DESCRIPTOR.full_name, extension_handle.number)) - - cls._extensions_by_name[extension_handle.full_name] = extension_handle - - handle = extension_handle # avoid line wrapping - if _IsMessageSetExtension(handle): - # MessageSet extension. Also register under type name. - cls._extensions_by_name[ - extension_handle.message_type.full_name] = extension_handle - - cls.RegisterExtension = staticmethod(RegisterExtension) - - def FromString(s): - message = cls() - message.MergeFromString(s) - return message - cls.FromString = staticmethod(FromString) - - -def _IsPresent(item): - """Given a (FieldDescriptor, value) tuple from _fields, return true if the - value should be included in the list returned by ListFields().""" - - if item[0].label == _FieldDescriptor.LABEL_REPEATED: - return bool(item[1]) - elif item[0].cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: - return item[1]._is_present_in_parent - else: - return True - - -def _AddListFieldsMethod(message_descriptor, cls): - """Helper for _AddMessageMethods().""" - - def ListFields(self): - all_fields = [item for item in self._fields.iteritems() if _IsPresent(item)] - all_fields.sort(key = lambda item: item[0].number) - return all_fields - - cls.ListFields = ListFields - - -def _AddHasFieldMethod(message_descriptor, cls): - """Helper for _AddMessageMethods().""" - - singular_fields = {} - for field in message_descriptor.fields: - if field.label != _FieldDescriptor.LABEL_REPEATED: - singular_fields[field.name] = field - - def HasField(self, field_name): - try: - field = singular_fields[field_name] - except KeyError: - raise ValueError( - 'Protocol message has no singular "%s" field.' % field_name) - - if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: - value = self._fields.get(field) - return value is not None and value._is_present_in_parent - else: - return field in self._fields - cls.HasField = HasField - - -def _AddClearFieldMethod(message_descriptor, cls): - """Helper for _AddMessageMethods().""" - def ClearField(self, field_name): - try: - field = message_descriptor.fields_by_name[field_name] - except KeyError: - raise ValueError('Protocol message has no "%s" field.' % field_name) - - if field in self._fields: - # Note: If the field is a sub-message, its listener will still point - # at us. That's fine, because the worst than can happen is that it - # will call _Modified() and invalidate our byte size. Big deal. - del self._fields[field] - - # Always call _Modified() -- even if nothing was changed, this is - # a mutating method, and thus calling it should cause the field to become - # present in the parent message. - self._Modified() - - cls.ClearField = ClearField - - -def _AddClearExtensionMethod(cls): - """Helper for _AddMessageMethods().""" - def ClearExtension(self, extension_handle): - _VerifyExtensionHandle(self, extension_handle) - - # Similar to ClearField(), above. - if extension_handle in self._fields: - del self._fields[extension_handle] - self._Modified() - cls.ClearExtension = ClearExtension - - -def _AddClearMethod(message_descriptor, cls): - """Helper for _AddMessageMethods().""" - def Clear(self): - # Clear fields. - self._fields = {} - self._Modified() - cls.Clear = Clear - - -def _AddHasExtensionMethod(cls): - """Helper for _AddMessageMethods().""" - def HasExtension(self, extension_handle): - _VerifyExtensionHandle(self, extension_handle) - if extension_handle.label == _FieldDescriptor.LABEL_REPEATED: - raise KeyError('"%s" is repeated.' % extension_handle.full_name) - - if extension_handle.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: - value = self._fields.get(extension_handle) - return value is not None and value._is_present_in_parent - else: - return extension_handle in self._fields - cls.HasExtension = HasExtension - - -def _AddEqualsMethod(message_descriptor, cls): - """Helper for _AddMessageMethods().""" - def __eq__(self, other): - if (not isinstance(other, message_mod.Message) or - other.DESCRIPTOR != self.DESCRIPTOR): - return False - - if self is other: - return True - - return self.ListFields() == other.ListFields() - - cls.__eq__ = __eq__ - - -def _AddStrMethod(message_descriptor, cls): - """Helper for _AddMessageMethods().""" - def __str__(self): - return text_format.MessageToString(self) - cls.__str__ = __str__ - - -def _AddSetListenerMethod(cls): - """Helper for _AddMessageMethods().""" - def SetListener(self, listener): - if listener is None: - self._listener = message_listener_mod.NullMessageListener() - else: - self._listener = listener - cls._SetListener = SetListener - - -def _BytesForNonRepeatedElement(value, field_number, field_type): - """Returns the number of bytes needed to serialize a non-repeated element. - The returned byte count includes space for tag information and any - other additional space associated with serializing value. - - Args: - value: Value we're serializing. - field_number: Field number of this value. (Since the field number - is stored as part of a varint-encoded tag, this has an impact - on the total bytes required to serialize the value). - field_type: The type of the field. One of the TYPE_* constants - within FieldDescriptor. - """ - try: - fn = type_checkers.TYPE_TO_BYTE_SIZE_FN[field_type] - return fn(field_number, value) - except KeyError: - raise message_mod.EncodeError('Unrecognized field type: %d' % field_type) - - -def _AddByteSizeMethod(message_descriptor, cls): - """Helper for _AddMessageMethods().""" - - def ByteSize(self): - if not self._cached_byte_size_dirty: - return self._cached_byte_size - - size = 0 - for field_descriptor, field_value in self.ListFields(): - size += field_descriptor._sizer(field_value) - - self._cached_byte_size = size - self._cached_byte_size_dirty = False - self._listener_for_children.dirty = False - return size - - cls.ByteSize = ByteSize - - -def _AddSerializeToStringMethod(message_descriptor, cls): - """Helper for _AddMessageMethods().""" - - def SerializeToString(self): - # Check if the message has all of its required fields set. - errors = [] - if not self.IsInitialized(): - raise message_mod.EncodeError( - 'Message is missing required fields: ' + - ','.join(self.FindInitializationErrors())) - return self.SerializePartialToString() - cls.SerializeToString = SerializeToString - - -def _AddSerializePartialToStringMethod(message_descriptor, cls): - """Helper for _AddMessageMethods().""" - - def SerializePartialToString(self): - out = StringIO() - self._InternalSerialize(out.write) - return out.getvalue() - cls.SerializePartialToString = SerializePartialToString - - def InternalSerialize(self, write_bytes): - for field_descriptor, field_value in self.ListFields(): - field_descriptor._encoder(write_bytes, field_value) - cls._InternalSerialize = InternalSerialize - - -def _AddMergeFromStringMethod(message_descriptor, cls): - """Helper for _AddMessageMethods().""" - def MergeFromString(self, serialized): - length = len(serialized) - try: - if self._InternalParse(serialized, 0, length) != length: - # The only reason _InternalParse would return early is if it - # encountered an end-group tag. - raise message_mod.DecodeError('Unexpected end-group tag.') - except IndexError: - raise message_mod.DecodeError('Truncated message.') - except struct.error, e: - raise message_mod.DecodeError(e) - return length # Return this for legacy reasons. - cls.MergeFromString = MergeFromString - - local_ReadTag = decoder.ReadTag - local_SkipField = decoder.SkipField - decoders_by_tag = cls._decoders_by_tag - - def InternalParse(self, buffer, pos, end): - self._Modified() - field_dict = self._fields - while pos != end: - (tag_bytes, new_pos) = local_ReadTag(buffer, pos) - field_decoder = decoders_by_tag.get(tag_bytes) - if field_decoder is None: - new_pos = local_SkipField(buffer, new_pos, end, tag_bytes) - if new_pos == -1: - return pos - pos = new_pos - else: - pos = field_decoder(buffer, new_pos, end, self, field_dict) - return pos - cls._InternalParse = InternalParse - - -def _AddIsInitializedMethod(message_descriptor, cls): - """Adds the IsInitialized and FindInitializationError methods to the - protocol message class.""" - - required_fields = [field for field in message_descriptor.fields - if field.label == _FieldDescriptor.LABEL_REQUIRED] - - def IsInitialized(self, errors=None): - """Checks if all required fields of a message are set. - - Args: - errors: A list which, if provided, will be populated with the field - paths of all missing required fields. - - Returns: - True iff the specified message has all required fields set. - """ - - # Performance is critical so we avoid HasField() and ListFields(). - - for field in required_fields: - if (field not in self._fields or - (field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE and - not self._fields[field]._is_present_in_parent)): - if errors is not None: - errors.extend(self.FindInitializationErrors()) - return False - - for field, value in self._fields.iteritems(): - if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: - if field.label == _FieldDescriptor.LABEL_REPEATED: - for element in value: - if not element.IsInitialized(): - if errors is not None: - errors.extend(self.FindInitializationErrors()) - return False - elif value._is_present_in_parent and not value.IsInitialized(): - if errors is not None: - errors.extend(self.FindInitializationErrors()) - return False - - return True - - cls.IsInitialized = IsInitialized - - def FindInitializationErrors(self): - """Finds required fields which are not initialized. - - Returns: - A list of strings. Each string is a path to an uninitialized field from - the top-level message, e.g. "foo.bar[5].baz". - """ - - errors = [] # simplify things - - for field in required_fields: - if not self.HasField(field.name): - errors.append(field.name) - - for field, value in self.ListFields(): - if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: - if field.is_extension: - name = "(%s)" % field.full_name - else: - name = field.name - - if field.label == _FieldDescriptor.LABEL_REPEATED: - for i in xrange(len(value)): - element = value[i] - prefix = "%s[%d]." % (name, i) - sub_errors = element.FindInitializationErrors() - errors += [ prefix + error for error in sub_errors ] - else: - prefix = name + "." - sub_errors = value.FindInitializationErrors() - errors += [ prefix + error for error in sub_errors ] - - return errors - - cls.FindInitializationErrors = FindInitializationErrors - - -def _AddMergeFromMethod(cls): - LABEL_REPEATED = _FieldDescriptor.LABEL_REPEATED - CPPTYPE_MESSAGE = _FieldDescriptor.CPPTYPE_MESSAGE - - def MergeFrom(self, msg): - if not isinstance(msg, cls): - raise TypeError( - "Parameter to MergeFrom() must be instance of same class.") - - assert msg is not self - self._Modified() - - fields = self._fields - - for field, value in msg._fields.iteritems(): - if field.label == LABEL_REPEATED: - field_value = fields.get(field) - if field_value is None: - # Construct a new object to represent this field. - field_value = field._default_constructor(self) - fields[field] = field_value - field_value.MergeFrom(value) - elif field.cpp_type == CPPTYPE_MESSAGE: - if value._is_present_in_parent: - field_value = fields.get(field) - if field_value is None: - # Construct a new object to represent this field. - field_value = field._default_constructor(self) - fields[field] = field_value - field_value.MergeFrom(value) - else: - self._fields[field] = value - cls.MergeFrom = MergeFrom - - -def _AddMessageMethods(message_descriptor, cls): - """Adds implementations of all Message methods to cls.""" - _AddListFieldsMethod(message_descriptor, cls) - _AddHasFieldMethod(message_descriptor, cls) - _AddClearFieldMethod(message_descriptor, cls) - if message_descriptor.is_extendable: - _AddClearExtensionMethod(cls) - _AddHasExtensionMethod(cls) - _AddClearMethod(message_descriptor, cls) - _AddEqualsMethod(message_descriptor, cls) - _AddStrMethod(message_descriptor, cls) - _AddSetListenerMethod(cls) - _AddByteSizeMethod(message_descriptor, cls) - _AddSerializeToStringMethod(message_descriptor, cls) - _AddSerializePartialToStringMethod(message_descriptor, cls) - _AddMergeFromStringMethod(message_descriptor, cls) - _AddIsInitializedMethod(message_descriptor, cls) - _AddMergeFromMethod(cls) - - -def _AddPrivateHelperMethods(cls): - """Adds implementation of private helper methods to cls.""" - - def Modified(self): - """Sets the _cached_byte_size_dirty bit to true, - and propagates this to our listener iff this was a state change. - """ - - # Note: Some callers check _cached_byte_size_dirty before calling - # _Modified() as an extra optimization. So, if this method is ever - # changed such that it does stuff even when _cached_byte_size_dirty is - # already true, the callers need to be updated. - if not self._cached_byte_size_dirty: - self._cached_byte_size_dirty = True - self._listener_for_children.dirty = True - self._is_present_in_parent = True - self._listener.Modified() - - cls._Modified = Modified - cls.SetInParent = Modified - - -class _Listener(object): - - """MessageListener implementation that a parent message registers with its - child message. - - In order to support semantics like: - - foo.bar.baz.qux = 23 - assert foo.HasField('bar') - - ...child objects must have back references to their parents. - This helper class is at the heart of this support. - """ - - def __init__(self, parent_message): - """Args: - parent_message: The message whose _Modified() method we should call when - we receive Modified() messages. - """ - # This listener establishes a back reference from a child (contained) object - # to its parent (containing) object. We make this a weak reference to avoid - # creating cyclic garbage when the client finishes with the 'parent' object - # in the tree. - if isinstance(parent_message, weakref.ProxyType): - self._parent_message_weakref = parent_message - else: - self._parent_message_weakref = weakref.proxy(parent_message) - - # As an optimization, we also indicate directly on the listener whether - # or not the parent message is dirty. This way we can avoid traversing - # up the tree in the common case. - self.dirty = False - - def Modified(self): - if self.dirty: - return - try: - # Propagate the signal to our parents iff this is the first field set. - self._parent_message_weakref._Modified() - except ReferenceError: - # We can get here if a client has kept a reference to a child object, - # and is now setting a field on it, but the child's parent has been - # garbage-collected. This is not an error. - pass - - -# TODO(robinson): Move elsewhere? This file is getting pretty ridiculous... -# TODO(robinson): Unify error handling of "unknown extension" crap. -# TODO(robinson): Support iteritems()-style iteration over all -# extensions with the "has" bits turned on? -class _ExtensionDict(object): - - """Dict-like container for supporting an indexable "Extensions" - field on proto instances. - - Note that in all cases we expect extension handles to be - FieldDescriptors. - """ - - def __init__(self, extended_message): - """extended_message: Message instance for which we are the Extensions dict. - """ - - self._extended_message = extended_message - - def __getitem__(self, extension_handle): - """Returns the current value of the given extension handle.""" - - _VerifyExtensionHandle(self._extended_message, extension_handle) - - result = self._extended_message._fields.get(extension_handle) - if result is not None: - return result - - if extension_handle.label == _FieldDescriptor.LABEL_REPEATED: - result = extension_handle._default_constructor(self._extended_message) - elif extension_handle.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: - result = extension_handle.message_type._concrete_class() - try: - result._SetListener(self._extended_message._listener_for_children) - except ReferenceError: - pass - else: - # Singular scalar -- just return the default without inserting into the - # dict. - return extension_handle.default_value - - # Atomically check if another thread has preempted us and, if not, swap - # in the new object we just created. If someone has preempted us, we - # take that object and discard ours. - # WARNING: We are relying on setdefault() being atomic. This is true - # in CPython but we haven't investigated others. This warning appears - # in several other locations in this file. - result = self._extended_message._fields.setdefault( - extension_handle, result) - - return result - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return False - - my_fields = self._extended_message.ListFields() - other_fields = other._extended_message.ListFields() - - # Get rid of non-extension fields. - my_fields = [ field for field in my_fields if field.is_extension ] - other_fields = [ field for field in other_fields if field.is_extension ] - - return my_fields == other_fields - - def __ne__(self, other): - return not self == other - - # Note that this is only meaningful for non-repeated, scalar extension - # fields. Note also that we may have to call _Modified() when we do - # successfully set a field this way, to set any necssary "has" bits in the - # ancestors of the extended message. - def __setitem__(self, extension_handle, value): - """If extension_handle specifies a non-repeated, scalar extension - field, sets the value of that field. - """ - - _VerifyExtensionHandle(self._extended_message, extension_handle) - - if (extension_handle.label == _FieldDescriptor.LABEL_REPEATED or - extension_handle.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE): - raise TypeError( - 'Cannot assign to extension "%s" because it is a repeated or ' - 'composite type.' % extension_handle.full_name) - - # It's slightly wasteful to lookup the type checker each time, - # but we expect this to be a vanishingly uncommon case anyway. - type_checker = type_checkers.GetTypeChecker( - extension_handle.cpp_type, extension_handle.type) - type_checker.CheckValue(value) - self._extended_message._fields[extension_handle] = value - self._extended_message._Modified() - - def _FindExtensionByName(self, name): - """Tries to find a known extension with the specified name. - - Args: - name: Extension full name. - - Returns: - Extension field descriptor. - """ - return self._extended_message._extensions_by_name.get(name, None) diff --git a/python/google/protobuf/text_format.py b/python/google/protobuf/text_format.py index cc6ac902..6d77b543 100755 --- a/python/google/protobuf/text_format.py +++ b/python/google/protobuf/text_format.py @@ -53,24 +53,26 @@ class ParseError(Exception): """Thrown in case of ASCII parsing error.""" -def MessageToString(message): +def MessageToString(message, as_utf8=False, as_one_line=False): out = cStringIO.StringIO() - PrintMessage(message, out) + PrintMessage(message, out, as_utf8=as_utf8, as_one_line=as_one_line) result = out.getvalue() out.close() + if as_one_line: + return result.rstrip() return result -def PrintMessage(message, out, indent = 0): +def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False): for field, value in message.ListFields(): if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: for element in value: - PrintField(field, element, out, indent) + PrintField(field, element, out, indent, as_utf8, as_one_line) else: - PrintField(field, value, out, indent) + PrintField(field, value, out, indent, as_utf8, as_one_line) -def PrintField(field, value, out, indent = 0): +def PrintField(field, value, out, indent=0, as_utf8=False, as_one_line=False): """Print a single field name/value pair. For repeated fields, the value should be a single element.""" @@ -96,23 +98,35 @@ def PrintField(field, value, out, indent = 0): # don't include it. out.write(': ') - PrintFieldValue(field, value, out, indent) - out.write('\n') + PrintFieldValue(field, value, out, indent, as_utf8, as_one_line) + if as_one_line: + out.write(' ') + else: + out.write('\n') -def PrintFieldValue(field, value, out, indent = 0): +def PrintFieldValue(field, value, out, indent=0, + as_utf8=False, as_one_line=False): """Print a single field value (not including name). For repeated fields, the value should be a single element.""" if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: - out.write(' {\n') - PrintMessage(value, out, indent + 2) - out.write(' ' * indent + '}') + if as_one_line: + out.write(' { ') + PrintMessage(value, out, indent, as_utf8, as_one_line) + out.write('}') + else: + out.write(' {\n') + PrintMessage(value, out, indent + 2, as_utf8, as_one_line) + out.write(' ' * indent + '}') elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: out.write(field.enum_type.values_by_number[value].name) elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: out.write('\"') - out.write(_CEscape(value)) + if type(value) is unicode: + out.write(_CEscape(value.encode('utf-8'), as_utf8)) + else: + out.write(_CEscape(value, as_utf8)) out.write('\"') elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: if value: @@ -334,10 +348,10 @@ class _Tokenizer(object): Returns: True iff the end was reached. """ - return not self._lines and not self._current_line + return self.token == '' def _PopLine(self): - while not self._current_line: + while len(self._current_line) <= self._column: if not self._lines: self._current_line = '' return @@ -348,11 +362,10 @@ class _Tokenizer(object): def _SkipWhitespace(self): while True: self._PopLine() - match = re.match(self._WHITESPACE, self._current_line) + match = self._WHITESPACE.match(self._current_line, self._column) if not match: break length = len(match.group(0)) - self._current_line = self._current_line[length:] self._column += length def TryConsume(self, token): @@ -402,7 +415,7 @@ class _Tokenizer(object): ParseError: If an identifier couldn't be consumed. """ result = self.token - if not re.match(self._IDENTIFIER, result): + if not self._IDENTIFIER.match(result): raise self._ParseError('Expected identifier.') self.NextToken() return result @@ -481,13 +494,13 @@ class _Tokenizer(object): ParseError: If a floating point number couldn't be consumed. """ text = self.token - if re.match(self._FLOAT_INFINITY, text): + if self._FLOAT_INFINITY.match(text): self.NextToken() if text.startswith('-'): return -_INFINITY return _INFINITY - if re.match(self._FLOAT_NAN, text): + if self._FLOAT_NAN.match(text): self.NextToken() return _NAN @@ -507,10 +520,10 @@ class _Tokenizer(object): Raises: ParseError: If a boolean value couldn't be consumed. """ - if self.token == 'true': + if self.token in ('true', 't', '1'): self.NextToken() return True - elif self.token == 'false': + elif self.token in ('false', 'f', '0'): self.NextToken() return False else: @@ -525,7 +538,11 @@ class _Tokenizer(object): Raises: ParseError: If a string value couldn't be consumed. """ - return unicode(self.ConsumeByteString(), 'utf-8') + bytes = self.ConsumeByteString() + try: + return unicode(bytes, 'utf-8') + except UnicodeDecodeError, e: + raise self._StringParseError(e) def ConsumeByteString(self): """Consumes a byte array value. @@ -609,7 +626,7 @@ class _Tokenizer(object): def _ParseError(self, message): """Creates and *returns* a ParseError for the current token.""" return ParseError('%d:%d : %s' % ( - self._line + 1, self._column + 1, message)) + self._line + 1, self._column - len(self.token) + 1, message)) def _IntegerParseError(self, e): return self._ParseError('Couldn\'t parse integer: ' + str(e)) @@ -617,27 +634,27 @@ class _Tokenizer(object): def _FloatParseError(self, e): return self._ParseError('Couldn\'t parse number: ' + str(e)) + def _StringParseError(self, e): + return self._ParseError('Couldn\'t parse string: ' + str(e)) + def NextToken(self): """Reads the next meaningful token.""" self._previous_line = self._line self._previous_column = self._column - if self.AtEnd(): - self.token = '' - return + self._column += len(self.token) + self._SkipWhitespace() - # Make sure there is data to work on. - self._PopLine() + if not self._lines and len(self._current_line) <= self._column: + self.token = '' + return - match = re.match(self._TOKEN, self._current_line) + match = self._TOKEN.match(self._current_line, self._column) if match: token = match.group(0) - self._current_line = self._current_line[len(token):] self.token = token else: - self.token = self._current_line[0] - self._current_line = self._current_line[1:] - self._SkipWhitespace() + self.token = self._current_line[self._column] # text.encode('string_escape') does not seem to satisfy our needs as it @@ -645,7 +662,7 @@ class _Tokenizer(object): # C++ unescaping function allows hex escapes to be any length. So, # "\0011".encode('string_escape') ends up being "\\x011", which will be # decoded in C++ as a single-character string with char code 0x11. -def _CEscape(text): +def _CEscape(text, as_utf8): def escape(c): o = ord(c) if o == 10: return r"\n" # optional escape @@ -656,12 +673,13 @@ def _CEscape(text): if o == 34: return r'\"' # necessary escape if o == 92: return r"\\" # necessary escape - if o >= 127 or o < 32: return "\\%03o" % o # necessary escapes + # necessary escapes + if not as_utf8 and (o >= 127 or o < 32): return "\\%03o" % o return c return "".join([escape(c) for c in text]) -_CUNESCAPE_HEX = re.compile('\\\\x([0-9a-fA-F]{2}|[0-9a-f-A-F])') +_CUNESCAPE_HEX = re.compile('\\\\x([0-9a-fA-F]{2}|[0-9a-fA-F])') def _CUnescape(text): diff --git a/python/setup.py b/python/setup.py index 81887300..47646a81 100755 --- a/python/setup.py +++ b/python/setup.py @@ -7,7 +7,7 @@ from ez_setup import use_setuptools use_setuptools() -from setuptools import setup +from setuptools import setup, Extension from distutils.spawn import find_executable import sys import os @@ -60,6 +60,7 @@ def MakeTestSuite(): del sys.modules['google'] generate_proto("../src/google/protobuf/unittest.proto") + generate_proto("../src/google/protobuf/unittest_custom_options.proto") generate_proto("../src/google/protobuf/unittest_import.proto") generate_proto("../src/google/protobuf/unittest_mset.proto") generate_proto("../src/google/protobuf/unittest_no_generic_services.proto") @@ -94,24 +95,39 @@ if __name__ == '__main__': for (dirpath, dirnames, filenames) in os.walk("."): for filename in filenames: filepath = os.path.join(dirpath, filename) - if filepath.endswith("_pb2.py") or filepath.endswith(".pyc"): + if filepath.endswith("_pb2.py") or filepath.endswith(".pyc") or \ + filepath.endswith(".so") or filepath.endswith(".o"): os.remove(filepath) else: # Generate necessary .proto file if it doesn't exist. # TODO(kenton): Maybe we should hook this into a distutils command? generate_proto("../src/google/protobuf/descriptor.proto") + python_c_extension = Extension("google.protobuf.internal._net_proto2___python", + [ "google/protobuf/pyext/python_descriptor.cc", + "google/protobuf/pyext/python_protobuf.cc", + "google/protobuf/pyext/python-proto2.cc", + ], + include_dirs = [ "../src", ".", ], + libraries = [ "protobuf" ], + runtime_library_dirs = [ "../src/.libs" ], + library_dirs = [ "../src/.libs" ], + ) + setup(name = 'protobuf', - version = '2.3.1-pre', + version = '2.4.0-pre', packages = [ 'google' ], namespace_packages = [ 'google' ], test_suite = 'setup.MakeTestSuite', # Must list modules explicitly so that we don't install tests. py_modules = [ + 'google.protobuf.internal.api_implementation', 'google.protobuf.internal.containers', + 'google.protobuf.internal.cpp_message', 'google.protobuf.internal.decoder', 'google.protobuf.internal.encoder', 'google.protobuf.internal.message_listener', + 'google.protobuf.internal.python_message', 'google.protobuf.internal.type_checkers', 'google.protobuf.internal.wire_format', 'google.protobuf.descriptor', @@ -121,6 +137,7 @@ if __name__ == '__main__': 'google.protobuf.service', 'google.protobuf.service_reflection', 'google.protobuf.text_format' ], + ext_modules = [ python_c_extension ], url = 'http://code.google.com/p/protobuf/', maintainer = maintainer_email, maintainer_email = 'protobuf@googlegroups.com', diff --git a/src/Makefile.am b/src/Makefile.am index 35f5c0c5..92bbe8cc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -178,6 +178,8 @@ libprotoc_la_SOURCES = \ google/protobuf/compiler/java/java_primitive_field.h \ google/protobuf/compiler/java/java_service.cc \ google/protobuf/compiler/java/java_service.h \ + google/protobuf/compiler/java/java_string_field.cc \ + google/protobuf/compiler/java/java_string_field.h \ google/protobuf/compiler/python/python_generator.cc bin_PROGRAMS = protoc diff --git a/src/google/protobuf/compiler/code_generator.cc b/src/google/protobuf/compiler/code_generator.cc index 3413a36a..455c239a 100644 --- a/src/google/protobuf/compiler/code_generator.cc +++ b/src/google/protobuf/compiler/code_generator.cc @@ -42,14 +42,19 @@ namespace protobuf { namespace compiler { CodeGenerator::~CodeGenerator() {} -OutputDirectory::~OutputDirectory() {} +GeneratorContext::~GeneratorContext() {} -io::ZeroCopyOutputStream* OutputDirectory::OpenForInsert( +io::ZeroCopyOutputStream* GeneratorContext::OpenForInsert( const string& filename, const string& insertion_point) { - GOOGLE_LOG(FATAL) << "This OutputDirectory does not support insertion."; + GOOGLE_LOG(FATAL) << "This GeneratorContext does not support insertion."; return NULL; // make compiler happy } +void GeneratorContext::ListParsedFiles( + vector<const FileDescriptor*>* output) { + GOOGLE_LOG(FATAL) << "This GeneratorContext does not support ListParsedFiles"; +} + // Parses a set of comma-delimited name/value pairs. void ParseGeneratorParameter(const string& text, vector<pair<string, string> >* output) { diff --git a/src/google/protobuf/compiler/code_generator.h b/src/google/protobuf/compiler/code_generator.h index ea094cdd..252f68d1 100644 --- a/src/google/protobuf/compiler/code_generator.h +++ b/src/google/protobuf/compiler/code_generator.h @@ -53,7 +53,7 @@ namespace compiler { // Defined in this file. class CodeGenerator; -class OutputDirectory; +class GeneratorContext; // The abstract interface to a class which generates code implementing a // particular proto file in a particular language. A number of these may @@ -76,7 +76,7 @@ class LIBPROTOC_EXPORT CodeGenerator { // the problem (e.g. "invalid parameter") and returns false. virtual bool Generate(const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* generator_context, string* error) const = 0; private: @@ -85,11 +85,12 @@ class LIBPROTOC_EXPORT CodeGenerator { // CodeGenerators generate one or more files in a given directory. This // abstract interface represents the directory to which the CodeGenerator is -// to write. -class LIBPROTOC_EXPORT OutputDirectory { +// to write and other information about the context in which the Generator +// runs. +class LIBPROTOC_EXPORT GeneratorContext { public: - inline OutputDirectory() {} - virtual ~OutputDirectory(); + inline GeneratorContext() {} + virtual ~GeneratorContext(); // Opens the given file, truncating it if it exists, and returns a // ZeroCopyOutputStream that writes to the file. The caller takes ownership @@ -112,10 +113,19 @@ class LIBPROTOC_EXPORT OutputDirectory { virtual io::ZeroCopyOutputStream* OpenForInsert( const string& filename, const string& insertion_point); + // Returns a vector of FileDescriptors for all the files being compiled + // in this run. Useful for languages, such as Go, that treat files + // differently when compiled as a set rather than individually. + virtual void ListParsedFiles(vector<const FileDescriptor*>* output); + private: - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OutputDirectory); + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GeneratorContext); }; +// The type GeneratorContext was once called OutputDirectory. This typedef +// provides backward compatibility. +typedef GeneratorContext OutputDirectory; + // Several code generators treat the parameter argument as holding a // list of options separated by commas. This helper function parses // a set of comma-delimited name/value pairs: e.g., diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index 02c3e0f9..9e9849cf 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -48,6 +48,9 @@ #include <iostream> #include <ctype.h> +#include <google/protobuf/stubs/hash.h> + +#include <google/protobuf/stubs/common.h> #include <google/protobuf/compiler/importer.h> #include <google/protobuf/compiler/code_generator.h> #include <google/protobuf/compiler/plugin.pb.h> @@ -58,12 +61,10 @@ #include <google/protobuf/dynamic_message.h> #include <google/protobuf/io/zero_copy_stream_impl.h> #include <google/protobuf/io/printer.h> -#include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/strutil.h> #include <google/protobuf/stubs/substitute.h> #include <google/protobuf/stubs/map-util.h> #include <google/protobuf/stubs/stl_util-inl.h> -#include <google/protobuf/stubs/hash.h> namespace google { @@ -182,7 +183,7 @@ bool TryCreateParentDirectory(const string& prefix, const string& filename) { class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector, public io::ErrorCollector { public: - ErrorPrinter(ErrorFormat format, DiskSourceTree *tree = NULL) + ErrorPrinter(ErrorFormat format, DiskSourceTree *tree = NULL) : format_(format), tree_(tree) {} ~ErrorPrinter() {} @@ -191,8 +192,8 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector, const string& message) { // Print full path when running under MSVS - std::string dfile; - if (format_ == CommandLineInterface::ERROR_FORMAT_MSVS && + string dfile; + if (format_ == CommandLineInterface::ERROR_FORMAT_MSVS && tree_ != NULL && tree_->VirtualFileToDiskFile(filename, &dfile)) { cerr << dfile; @@ -229,12 +230,12 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector, // ------------------------------------------------------------------- -// An OutputDirectory implementation that buffers files in memory, then dumps +// A GeneratorContext implementation that buffers files in memory, then dumps // them all to disk on demand. -class CommandLineInterface::MemoryOutputDirectory : public OutputDirectory { +class CommandLineInterface::GeneratorContextImpl : public GeneratorContext { public: - MemoryOutputDirectory(); - ~MemoryOutputDirectory(); + GeneratorContextImpl(const vector<const FileDescriptor*>& parsed_files); + ~GeneratorContextImpl(); // Write all files in the directory to disk at the given output location, // which must end in a '/'. @@ -248,10 +249,13 @@ class CommandLineInterface::MemoryOutputDirectory : public OutputDirectory { // format, unless one has already been written. void AddJarManifest(); - // implements OutputDirectory -------------------------------------- + // implements GeneratorContext -------------------------------------- io::ZeroCopyOutputStream* Open(const string& filename); io::ZeroCopyOutputStream* OpenForInsert( const string& filename, const string& insertion_point); + void ListParsedFiles(vector<const FileDescriptor*>* output) { + *output = parsed_files_; + } private: friend class MemoryOutputStream; @@ -259,14 +263,15 @@ class CommandLineInterface::MemoryOutputDirectory : public OutputDirectory { // map instead of hash_map so that files are written in order (good when // writing zips). map<string, string*> files_; + const vector<const FileDescriptor*>& parsed_files_; bool had_error_; }; class CommandLineInterface::MemoryOutputStream : public io::ZeroCopyOutputStream { public: - MemoryOutputStream(MemoryOutputDirectory* directory, const string& filename); - MemoryOutputStream(MemoryOutputDirectory* directory, const string& filename, + MemoryOutputStream(GeneratorContextImpl* directory, const string& filename); + MemoryOutputStream(GeneratorContextImpl* directory, const string& filename, const string& insertion_point); virtual ~MemoryOutputStream(); @@ -277,7 +282,7 @@ class CommandLineInterface::MemoryOutputStream private: // Where to insert the string when it's done. - MemoryOutputDirectory* directory_; + GeneratorContextImpl* directory_; string filename_; string insertion_point_; @@ -290,14 +295,17 @@ class CommandLineInterface::MemoryOutputStream // ------------------------------------------------------------------- -CommandLineInterface::MemoryOutputDirectory::MemoryOutputDirectory() - : had_error_(false) {} +CommandLineInterface::GeneratorContextImpl::GeneratorContextImpl( + const vector<const FileDescriptor*>& parsed_files) + : parsed_files_(parsed_files), + had_error_(false) { +} -CommandLineInterface::MemoryOutputDirectory::~MemoryOutputDirectory() { +CommandLineInterface::GeneratorContextImpl::~GeneratorContextImpl() { STLDeleteValues(&files_); } -bool CommandLineInterface::MemoryOutputDirectory::WriteAllToDisk( +bool CommandLineInterface::GeneratorContextImpl::WriteAllToDisk( const string& prefix) { if (had_error_) { return false; @@ -372,7 +380,7 @@ bool CommandLineInterface::MemoryOutputDirectory::WriteAllToDisk( return true; } -bool CommandLineInterface::MemoryOutputDirectory::WriteAllToZip( +bool CommandLineInterface::GeneratorContextImpl::WriteAllToZip( const string& filename) { if (had_error_) { return false; @@ -413,7 +421,7 @@ bool CommandLineInterface::MemoryOutputDirectory::WriteAllToZip( return true; } -void CommandLineInterface::MemoryOutputDirectory::AddJarManifest() { +void CommandLineInterface::GeneratorContextImpl::AddJarManifest() { string** map_slot = &files_["META-INF/MANIFEST.MF"]; if (*map_slot == NULL) { *map_slot = new string( @@ -423,13 +431,13 @@ void CommandLineInterface::MemoryOutputDirectory::AddJarManifest() { } } -io::ZeroCopyOutputStream* CommandLineInterface::MemoryOutputDirectory::Open( +io::ZeroCopyOutputStream* CommandLineInterface::GeneratorContextImpl::Open( const string& filename) { return new MemoryOutputStream(this, filename); } io::ZeroCopyOutputStream* -CommandLineInterface::MemoryOutputDirectory::OpenForInsert( +CommandLineInterface::GeneratorContextImpl::OpenForInsert( const string& filename, const string& insertion_point) { return new MemoryOutputStream(this, filename, insertion_point); } @@ -437,14 +445,14 @@ CommandLineInterface::MemoryOutputDirectory::OpenForInsert( // ------------------------------------------------------------------- CommandLineInterface::MemoryOutputStream::MemoryOutputStream( - MemoryOutputDirectory* directory, const string& filename) + GeneratorContextImpl* directory, const string& filename) : directory_(directory), filename_(filename), inner_(new io::StringOutputStream(&data_)) { } CommandLineInterface::MemoryOutputStream::MemoryOutputStream( - MemoryOutputDirectory* directory, const string& filename, + GeneratorContextImpl* directory, const string& filename, const string& insertion_point) : directory_(directory), filename_(filename), @@ -613,11 +621,11 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) { } } - // We construct a separate OutputDirectory for each output location. Note + // We construct a separate GeneratorContext for each output location. Note // that two code generators may output to the same location, in which case - // they should share a single OutputDirectory (so that OpenForInsert() works). - typedef hash_map<string, MemoryOutputDirectory*> OutputDirectoryMap; - OutputDirectoryMap output_directories; + // they should share a single GeneratorContext so that OpenForInsert() works. + typedef hash_map<string, GeneratorContextImpl*> GeneratorContextMap; + GeneratorContextMap output_directories; // Generate output. if (mode_ == MODE_COMPILE) { @@ -627,11 +635,11 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) { !HasSuffixString(output_location, ".jar")) { AddTrailingSlash(&output_location); } - MemoryOutputDirectory** map_slot = &output_directories[output_location]; + GeneratorContextImpl** map_slot = &output_directories[output_location]; if (*map_slot == NULL) { // First time we've seen this output location. - *map_slot = new MemoryOutputDirectory; + *map_slot = new GeneratorContextImpl(parsed_files); } if (!GenerateOutput(parsed_files, output_directives_[i], *map_slot)) { @@ -642,10 +650,10 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) { } // Write all output to disk. - for (OutputDirectoryMap::iterator iter = output_directories.begin(); + for (GeneratorContextMap::iterator iter = output_directories.begin(); iter != output_directories.end(); ++iter) { const string& location = iter->first; - MemoryOutputDirectory* directory = iter->second; + GeneratorContextImpl* directory = iter->second; if (HasSuffixString(location, "/")) { if (!directory->WriteAllToDisk(location)) { STLDeleteValues(&output_directories); @@ -1107,7 +1115,7 @@ void CommandLineInterface::PrintHelpText() { bool CommandLineInterface::GenerateOutput( const vector<const FileDescriptor*>& parsed_files, const OutputDirective& output_directive, - OutputDirectory* output_directory) { + GeneratorContext* generator_context) { // Call the generator. string error; if (output_directive.generator == NULL) { @@ -1122,7 +1130,7 @@ bool CommandLineInterface::GenerateOutput( if (!GeneratePluginOutput(parsed_files, plugin_name, output_directive.parameter, - output_directory, &error)) { + generator_context, &error)) { cerr << output_directive.name << ": " << error << endl; return false; } @@ -1131,7 +1139,7 @@ bool CommandLineInterface::GenerateOutput( for (int i = 0; i < parsed_files.size(); i++) { if (!output_directive.generator->Generate( parsed_files[i], output_directive.parameter, - output_directory, &error)) { + generator_context, &error)) { // Generator returned an error. cerr << output_directive.name << ": " << parsed_files[i]->name() << ": " << error << endl; @@ -1147,7 +1155,7 @@ bool CommandLineInterface::GeneratePluginOutput( const vector<const FileDescriptor*>& parsed_files, const string& plugin_name, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* generator_context, string* error) { CodeGeneratorRequest request; CodeGeneratorResponse response; @@ -1190,14 +1198,14 @@ bool CommandLineInterface::GeneratePluginOutput( // We reset current_output to NULL first so that the old file is closed // before the new one is opened. current_output.reset(); - current_output.reset(output_directory->OpenForInsert( + current_output.reset(generator_context->OpenForInsert( output_file.name(), output_file.insertion_point())); } else if (!output_file.name().empty()) { // Starting a new file. Open it. // We reset current_output to NULL first so that the old file is closed // before the new one is opened. current_output.reset(); - current_output.reset(output_directory->Open(output_file.name())); + current_output.reset(generator_context->Open(output_file.name())); } else if (current_output == NULL) { *error = strings::Substitute( "$0: First file chunk returned by plugin did not specify a file name.", diff --git a/src/google/protobuf/compiler/command_line_interface.h b/src/google/protobuf/compiler/command_line_interface.h index d25a50e6..0b507d80 100644 --- a/src/google/protobuf/compiler/command_line_interface.h +++ b/src/google/protobuf/compiler/command_line_interface.h @@ -56,7 +56,7 @@ template<typename T> class RepeatedPtrField; // repeated_field.h namespace compiler { class CodeGenerator; // code_generator.h -class OutputDirectory; // code_generator.h +class GeneratorContext; // code_generator.h class DiskSourceTree; // importer.h // This class implements the command-line interface to the protocol compiler. @@ -174,7 +174,7 @@ class LIBPROTOC_EXPORT CommandLineInterface { // ----------------------------------------------------------------- class ErrorPrinter; - class MemoryOutputDirectory; + class GeneratorContextImpl; class MemoryOutputStream; // Clear state from previous Run(). @@ -212,11 +212,11 @@ class LIBPROTOC_EXPORT CommandLineInterface { struct OutputDirective; // see below bool GenerateOutput(const vector<const FileDescriptor*>& parsed_files, const OutputDirective& output_directive, - OutputDirectory* output_directory); + GeneratorContext* generator_context); bool GeneratePluginOutput(const vector<const FileDescriptor*>& parsed_files, const string& plugin_name, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* generator_context, string* error); // Implements --encode and --decode. diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index bdf37ad1..6634b676 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -143,6 +143,10 @@ class CommandLineInterfaceTest : public testing::Test { const string& proto_name, const string& message_name, const string& output_directory); + void ExpectGeneratedWithMultipleInputs(const string& generator_name, + const string& all_proto_names, + const string& proto_name, + const string& message_name); void ExpectGeneratedWithInsertions(const string& generator_name, const string& parameter, const string& insertions, @@ -190,7 +194,7 @@ class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator { // implements CodeGenerator ---------------------------------------- bool Generate(const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* context, string* error) const { called_ = true; parameter_ = parameter; @@ -251,7 +255,6 @@ void CommandLineInterfaceTest::Run(const string& command) { if (!disallow_plugins_) { cli_.AllowPlugins("prefix-"); - const char* possible_paths[] = { // When building with shared libraries, libtool hides the real executable // in .libs and puts a fake wrapper in the current directory. @@ -353,7 +356,8 @@ void CommandLineInterfaceTest::ExpectGenerated( const string& proto_name, const string& message_name) { MockCodeGenerator::ExpectGenerated( - generator_name, parameter, "", proto_name, message_name, temp_directory_); + generator_name, parameter, "", proto_name, message_name, proto_name, + temp_directory_); } void CommandLineInterfaceTest::ExpectGenerated( @@ -363,10 +367,21 @@ void CommandLineInterfaceTest::ExpectGenerated( const string& message_name, const string& output_directory) { MockCodeGenerator::ExpectGenerated( - generator_name, parameter, "", proto_name, message_name, + generator_name, parameter, "", proto_name, message_name, proto_name, temp_directory_ + "/" + output_directory); } +void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs( + const string& generator_name, + const string& all_proto_names, + const string& proto_name, + const string& message_name) { + MockCodeGenerator::ExpectGenerated( + generator_name, "", "", proto_name, message_name, + all_proto_names, + temp_directory_); +} + void CommandLineInterfaceTest::ExpectGeneratedWithInsertions( const string& generator_name, const string& parameter, @@ -375,7 +390,7 @@ void CommandLineInterfaceTest::ExpectGeneratedWithInsertions( const string& message_name) { MockCodeGenerator::ExpectGenerated( generator_name, parameter, insertions, proto_name, message_name, - temp_directory_); + proto_name, temp_directory_); } void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled( @@ -455,8 +470,44 @@ TEST_F(CommandLineInterfaceTest, MultipleInputs) { "--proto_path=$tmpdir foo.proto bar.proto"); ExpectNoErrors(); - ExpectGenerated("test_generator", "", "foo.proto", "Foo"); - ExpectGenerated("test_generator", "", "bar.proto", "Bar"); + ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto", + "foo.proto", "Foo"); + ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto", + "bar.proto", "Bar"); + ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto", + "foo.proto", "Foo"); + ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto", + "bar.proto", "Bar"); +} + +TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) { + // Test parsing multiple input files with an import of a separate file. + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + CreateTempFile("bar.proto", + "syntax = \"proto2\";\n" + "import \"baz.proto\";\n" + "message Bar {\n" + " optional Baz a = 1;\n" + "}\n"); + CreateTempFile("baz.proto", + "syntax = \"proto2\";\n" + "message Baz {}\n"); + + Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir " + "--proto_path=$tmpdir foo.proto bar.proto"); + + ExpectNoErrors(); + ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto", + "foo.proto", "Foo"); + ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto", + "bar.proto", "Bar"); + ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto", + "foo.proto", "Foo"); + ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto", + "bar.proto", "Bar"); } TEST_F(CommandLineInterfaceTest, CreateDirectory) { diff --git a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc index 30b1d2bf..bcfa5020 100644 --- a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc @@ -79,10 +79,10 @@ class MockErrorCollector : public MultiFileErrorCollector { } }; -class MockOutputDirectory : public OutputDirectory { +class MockGeneratorContext : public GeneratorContext { public: - MockOutputDirectory() {} - ~MockOutputDirectory() { + MockGeneratorContext() {} + ~MockGeneratorContext() { STLDeleteValues(&files_); } @@ -102,7 +102,7 @@ class MockOutputDirectory : public OutputDirectory { "to your CL."; } - // implements OutputDirectory -------------------------------------- + // implements GeneratorContext -------------------------------------- virtual io::ZeroCopyOutputStream* Open(const string& filename) { string** map_slot = &files_[filename]; @@ -130,24 +130,24 @@ TEST(BootstrapTest, GeneratedDescriptorMatches) { ASSERT_TRUE(plugin_proto_file != NULL); CppGenerator generator; - MockOutputDirectory output_directory; + MockGeneratorContext context; string error; string parameter; parameter = "dllexport_decl=LIBPROTOBUF_EXPORT"; ASSERT_TRUE(generator.Generate(proto_file, parameter, - &output_directory, &error)); + &context, &error)); parameter = "dllexport_decl=LIBPROTOC_EXPORT"; ASSERT_TRUE(generator.Generate(plugin_proto_file, parameter, - &output_directory, &error)); - - output_directory.ExpectFileMatches("google/protobuf/descriptor.pb.h", - "google/protobuf/descriptor.pb.h"); - output_directory.ExpectFileMatches("google/protobuf/descriptor.pb.cc", - "google/protobuf/descriptor.pb.cc"); - output_directory.ExpectFileMatches("google/protobuf/compiler/plugin.pb.h", - "google/protobuf/compiler/plugin.pb.h"); - output_directory.ExpectFileMatches("google/protobuf/compiler/plugin.pb.cc", - "google/protobuf/compiler/plugin.pb.cc"); + &context, &error)); + + context.ExpectFileMatches("google/protobuf/descriptor.pb.h", + "google/protobuf/descriptor.pb.h"); + context.ExpectFileMatches("google/protobuf/descriptor.pb.cc", + "google/protobuf/descriptor.pb.cc"); + context.ExpectFileMatches("google/protobuf/compiler/plugin.pb.h", + "google/protobuf/compiler/plugin.pb.h"); + context.ExpectFileMatches("google/protobuf/compiler/plugin.pb.cc", + "google/protobuf/compiler/plugin.pb.cc"); } } // namespace diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc index 91ce493a..a369f417 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -85,7 +85,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { "}\n" "inline void $classname$::set_$name$($type$ value) {\n" " GOOGLE_DCHECK($type$_IsValid(value));\n" - " _set_bit($index$);\n" + " set_has_$name$();\n" " $name$_ = value;\n" "}\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index e31cb425..22d6fdba 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -283,6 +283,7 @@ void FileGenerator::GenerateSource(io::Printer* printer) { printer->Print( "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" "\n" + // The generated code calls accessors that might be deprecated. We don't // want the compiler to warn in generated code. "#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.cc b/src/google/protobuf/compiler/cpp/cpp_generator.cc index d67d3504..bb84e2ab 100644 --- a/src/google/protobuf/compiler/cpp/cpp_generator.cc +++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc @@ -53,7 +53,7 @@ CppGenerator::~CppGenerator() {} bool CppGenerator::Generate(const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* generator_context, string* error) const { vector<pair<string, string> > options; ParseGeneratorParameter(parameter, &options); @@ -100,7 +100,7 @@ bool CppGenerator::Generate(const FileDescriptor* file, // Generate header. { scoped_ptr<io::ZeroCopyOutputStream> output( - output_directory->Open(basename + ".h")); + generator_context->Open(basename + ".h")); io::Printer printer(output.get(), '$'); file_generator.GenerateHeader(&printer); } @@ -108,7 +108,7 @@ bool CppGenerator::Generate(const FileDescriptor* file, // Generate cc file. { scoped_ptr<io::ZeroCopyOutputStream> output( - output_directory->Open(basename + ".cc")); + generator_context->Open(basename + ".cc")); io::Printer printer(output.get(), '$'); file_generator.GenerateSource(&printer); } diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.h b/src/google/protobuf/compiler/cpp/cpp_generator.h index f52e886a..a90e84d7 100644 --- a/src/google/protobuf/compiler/cpp/cpp_generator.h +++ b/src/google/protobuf/compiler/cpp/cpp_generator.h @@ -57,7 +57,7 @@ class LIBPROTOC_EXPORT CppGenerator : public CodeGenerator { // implements CodeGenerator ---------------------------------------- bool Generate(const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* generator_context, string* error) const; private: diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index cbdcce8f..c4e6fb2c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -35,6 +35,7 @@ #include <algorithm> #include <google/protobuf/stubs/hash.h> #include <map> +#include <utility> #include <vector> #include <google/protobuf/compiler/cpp/cpp_message.h> #include <google/protobuf/compiler/cpp/cpp_field.h> @@ -143,6 +144,137 @@ static bool HasRequiredFields(const Descriptor* type) { return HasRequiredFields(type, &already_seen); } +// This returns an estimate of the compiler's alignment for the field. This +// can't guarantee to be correct because the generated code could be compiled on +// different systems with different alignment rules. The estimates below assume +// 64-bit pointers. +int EstimateAlignmentSize(const FieldDescriptor* field) { + if (field == NULL) return 0; + if (field->is_repeated()) return 8; + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_BOOL: + return 1; + + case FieldDescriptor::CPPTYPE_INT32: + case FieldDescriptor::CPPTYPE_UINT32: + case FieldDescriptor::CPPTYPE_ENUM: + case FieldDescriptor::CPPTYPE_FLOAT: + return 4; + + case FieldDescriptor::CPPTYPE_INT64: + case FieldDescriptor::CPPTYPE_UINT64: + case FieldDescriptor::CPPTYPE_DOUBLE: + case FieldDescriptor::CPPTYPE_STRING: + case FieldDescriptor::CPPTYPE_MESSAGE: + return 8; + } + GOOGLE_LOG(FATAL) << "Can't get here."; + return -1; // Make compiler happy. +} + +// FieldGroup is just a helper for OptimizePadding below. It holds a vector of +// fields that are grouped together because they have compatible alignment, and +// a preferred location in the final field ordering. +class FieldGroup { + public: + FieldGroup() + : preferred_location_(0) {} + + // A group with a single field. + FieldGroup(float preferred_location, const FieldDescriptor* field) + : preferred_location_(preferred_location), + fields_(1, field) {} + + // Append the fields in 'other' to this group. + void Append(const FieldGroup& other) { + if (other.fields_.empty()) { + return; + } + // Preferred location is the average among all the fields, so we weight by + // the number of fields on each FieldGroup object. + preferred_location_ = + (preferred_location_ * fields_.size() + + (other.preferred_location_ * other.fields_.size())) / + (fields_.size() + other.fields_.size()); + fields_.insert(fields_.end(), other.fields_.begin(), other.fields_.end()); + } + + void SetPreferredLocation(float location) { preferred_location_ = location; } + const vector<const FieldDescriptor*>& fields() const { return fields_; } + + // FieldGroup objects sort by their preferred location. + bool operator<(const FieldGroup& other) const { + return preferred_location_ < other.preferred_location_; + } + + private: + // "preferred_location_" is an estimate of where this group should go in the + // final list of fields. We compute this by taking the average index of each + // field in this group in the original ordering of fields. This is very + // approximate, but should put this group close to where its member fields + // originally went. + float preferred_location_; + vector<const FieldDescriptor*> fields_; + // We rely on the default copy constructor and operator= so this type can be + // used in a vector. +}; + +// Reorder 'fields' so that if the fields are output into a c++ class in the new +// order, the alignment padding is minimized. We try to do this while keeping +// each field as close as possible to its original position so that we don't +// reduce cache locality much for function that access each field in order. +void OptimizePadding(vector<const FieldDescriptor*>* fields) { + // First divide fields into those that align to 1 byte, 4 bytes or 8 bytes. + vector<FieldGroup> aligned_to_1, aligned_to_4, aligned_to_8; + for (int i = 0; i < fields->size(); ++i) { + switch (EstimateAlignmentSize((*fields)[i])) { + case 1: aligned_to_1.push_back(FieldGroup(i, (*fields)[i])); break; + case 4: aligned_to_4.push_back(FieldGroup(i, (*fields)[i])); break; + case 8: aligned_to_8.push_back(FieldGroup(i, (*fields)[i])); break; + default: + GOOGLE_LOG(FATAL) << "Unknown alignment size."; + } + } + + // Now group fields aligned to 1 byte into sets of 4, and treat those like a + // single field aligned to 4 bytes. + for (int i = 0; i < aligned_to_1.size(); i += 4) { + FieldGroup field_group; + for (int j = i; j < aligned_to_1.size() && j < i + 4; ++j) { + field_group.Append(aligned_to_1[j]); + } + aligned_to_4.push_back(field_group); + } + // Sort by preferred location to keep fields as close to their original + // location as possible. + sort(aligned_to_4.begin(), aligned_to_4.end()); + + // Now group fields aligned to 4 bytes (or the 4-field groups created above) + // into pairs, and treat those like a single field aligned to 8 bytes. + for (int i = 0; i < aligned_to_4.size(); i += 2) { + FieldGroup field_group; + for (int j = i; j < aligned_to_4.size() && j < i + 2; ++j) { + field_group.Append(aligned_to_4[j]); + } + if (i == aligned_to_4.size() - 1) { + // Move incomplete 4-byte block to the end. + field_group.SetPreferredLocation(fields->size() + 1); + } + aligned_to_8.push_back(field_group); + } + // Sort by preferred location to keep fields as close to their original + // location as possible. + sort(aligned_to_8.begin(), aligned_to_8.end()); + + // Now pull out all the FieldDescriptors in order. + fields->clear(); + for (int i = 0; i < aligned_to_8.size(); ++i) { + fields->insert(fields->end(), + aligned_to_8[i].fields().begin(), + aligned_to_8[i].fields().end()); + } +} + } // =================================================================== @@ -264,10 +396,20 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) { "}\n"); } else { // Singular field. + char buffer[kFastToBufferSize]; + vars["has_array_index"] = SimpleItoa(field->index() / 32); + vars["has_mask"] = FastHex32ToBuffer(1u << (field->index() % 32), buffer); printer->Print(vars, "inline bool $classname$::has_$name$() const {\n" - " return _has_bit($index$);\n" - "}\n"); + " return (_has_bits_[$has_array_index$] & 0x$has_mask$u) != 0;\n" + "}\n" + "inline void $classname$::set_has_$name$() {\n" + " _has_bits_[$has_array_index$] |= 0x$has_mask$u;\n" + "}\n" + "inline void $classname$::clear_has_$name$() {\n" + " _has_bits_[$has_array_index$] &= ~0x$has_mask$u;\n" + "}\n" + ); } // Generate clear_$name$() @@ -279,7 +421,8 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) { printer->Outdent(); if (!field->is_repeated()) { - printer->Print(vars, " _clear_bit($index$);\n"); + printer->Print(vars, + " clear_has_$name$();\n"); } printer->Print("}\n"); @@ -444,28 +587,74 @@ GenerateClassDefinition(io::Printer* printer) { "// @@protoc_insertion_point(class_scope:$full_name$)\n", "full_name", descriptor_->full_name()); - // Generate private members for fields. + // Generate private members. printer->Outdent(); printer->Print(" private:\n"); printer->Indent(); + for (int i = 0; i < descriptor_->field_count(); i++) { + if (!descriptor_->field(i)->is_repeated()) { + printer->Print( + "inline void set_has_$name$();\n", + "name", FieldName(descriptor_->field(i))); + printer->Print( + "inline void clear_has_$name$();\n", + "name", FieldName(descriptor_->field(i))); + } + } + printer->Print("\n"); + + // To minimize padding, data members are divided into three sections: + // (1) members assumed to align to 8 bytes + // (2) members corresponding to message fields, re-ordered to optimize + // alignment. + // (3) members assumed to align to 4 bytes. + + // Members assumed to align to 8 bytes: + if (descriptor_->extension_range_count() > 0) { printer->Print( - "::google::protobuf::internal::ExtensionSet _extensions_;\n"); + "::google::protobuf::internal::ExtensionSet _extensions_;\n" + "\n"); } if (HasUnknownFields(descriptor_->file())) { printer->Print( - "::google::protobuf::UnknownFieldSet _unknown_fields_;\n"); + "::google::protobuf::UnknownFieldSet _unknown_fields_;\n" + "\n"); + } + + // Field members: + + vector<const FieldDescriptor*> fields; + for (int i = 0; i < descriptor_->field_count(); i++) { + fields.push_back(descriptor_->field(i)); + } + OptimizePadding(&fields); + for (int i = 0; i < fields.size(); ++i) { + field_generators_.get(fields[i]).GeneratePrivateMembers(printer); } + // Members assumed to align to 4 bytes: + // TODO(kenton): Make _cached_size_ an atomic<int> when C++ supports it. printer->Print( - "mutable int _cached_size_;\n" - "\n"); - for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(descriptor_->field(i)) - .GeneratePrivateMembers(printer); + "\n" + "mutable int _cached_size_;\n"); + + // Generate _has_bits_. + if (descriptor_->field_count() > 0) { + printer->Print(vars, + "::google::protobuf::uint32 _has_bits_[($field_count$ + 31) / 32];\n" + "\n"); + } else { + // Zero-size arrays aren't technically allowed, and MSVC in particular + // doesn't like them. We still need to declare these arrays to make + // other code compile. Since this is an uncommon case, we'll just declare + // them with size 1 and waste some space. Oh well. + printer->Print( + "::google::protobuf::uint32 _has_bits_[1];\n" + "\n"); } // Declare AddDescriptors(), BuildDescriptors(), and ShutdownFile() as @@ -484,32 +673,7 @@ GenerateClassDefinition(io::Printer* printer) { GlobalAssignDescriptorsName(descriptor_->file()->name()), "shutdownfilename", GlobalShutdownFileName(descriptor_->file()->name())); - // Generate offsets and _has_bits_ boilerplate. - if (descriptor_->field_count() > 0) { - printer->Print(vars, - "::google::protobuf::uint32 _has_bits_[($field_count$ + 31) / 32];\n"); - } else { - // Zero-size arrays aren't technically allowed, and MSVC in particular - // doesn't like them. We still need to declare these arrays to make - // other code compile. Since this is an uncommon case, we'll just declare - // them with size 1 and waste some space. Oh well. - printer->Print( - "::google::protobuf::uint32 _has_bits_[1];\n"); - } - printer->Print( - "\n" - "// WHY DOES & HAVE LOWER PRECEDENCE THAN != !?\n" - "inline bool _has_bit(int index) const {\n" - " return (_has_bits_[index / 32] & (1u << (index % 32))) != 0;\n" - "}\n" - "inline void _set_bit(int index) {\n" - " _has_bits_[index / 32] |= (1u << (index % 32));\n" - "}\n" - "inline void _clear_bit(int index) {\n" - " _has_bits_[index / 32] &= ~(1u << (index % 32));\n" - "}\n" - "\n" "void InitAsDefaultInstance();\n" "static $classname$* default_instance_;\n", "classname", classname_); @@ -961,9 +1125,6 @@ GenerateClear(io::Printer* printer) { const FieldDescriptor* field = descriptor_->field(i); if (!field->is_repeated()) { - map<string, string> vars; - vars["index"] = SimpleItoa(field->index()); - // We can use the fact that _has_bits_ is a giant bitfield to our // advantage: We can check up to 32 bits at a time for equality to // zero, and skip the whole range if so. This can improve the speed @@ -975,8 +1136,9 @@ GenerateClear(io::Printer* printer) { printer->Outdent(); printer->Print("}\n"); } - printer->Print(vars, - "if (_has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n"); + printer->Print( + "if (_has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n", + "index", SimpleItoa(field->index())); printer->Indent(); } last_index = i; @@ -989,7 +1151,9 @@ GenerateClear(io::Printer* printer) { field->cpp_type() == FieldDescriptor::CPPTYPE_STRING; if (should_check_bit) { - printer->Print(vars, "if (_has_bit($index$)) {\n"); + printer->Print( + "if (has_$name$()) {\n", + "name", FieldName(field)); printer->Indent(); } @@ -1129,24 +1293,23 @@ GenerateMergeFrom(io::Printer* printer) { const FieldDescriptor* field = descriptor_->field(i); if (!field->is_repeated()) { - map<string, string> vars; - vars["index"] = SimpleItoa(field->index()); - // See above in GenerateClear for an explanation of this. if (i / 8 != last_index / 8 || last_index < 0) { if (last_index >= 0) { printer->Outdent(); printer->Print("}\n"); } - printer->Print(vars, - "if (from._has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n"); + printer->Print( + "if (from._has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n", + "index", SimpleItoa(field->index())); printer->Indent(); } last_index = i; - printer->Print(vars, - "if (from._has_bit($index$)) {\n"); + printer->Print( + "if (from.has_$name$()) {\n", + "name", FieldName(field)); printer->Indent(); field_generators_.get(field).GenerateMergingCode(printer); @@ -1423,8 +1586,8 @@ void MessageGenerator::GenerateSerializeOneField( if (!field->is_repeated()) { printer->Print( - "if (_has_bit($index$)) {\n", - "index", SimpleItoa(field->index())); + "if (has_$name$()) {\n", + "name", FieldName(field)); printer->Indent(); } diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc index c04bdc66..23e75b87 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -75,7 +75,8 @@ void MessageFieldGenerator:: GenerateAccessorDeclarations(io::Printer* printer) const { printer->Print(variables_, "inline const $type$& $name$() const$deprecation$;\n" - "inline $type$* mutable_$name$()$deprecation$;\n"); + "inline $type$* mutable_$name$()$deprecation$;\n" + "inline $type$* release_$name$()$deprecation$;\n"); } void MessageFieldGenerator:: @@ -85,9 +86,15 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " return $name$_ != NULL ? *$name$_ : *default_instance_->$name$_;\n" "}\n" "inline $type$* $classname$::mutable_$name$() {\n" - " _set_bit($index$);\n" + " set_has_$name$();\n" " if ($name$_ == NULL) $name$_ = new $type$;\n" " return $name$_;\n" + "}\n" + "inline $type$* $classname$::release_$name$() {\n" + " clear_has_$name$();\n" + " $type$* temp = $name$_;\n" + " $name$_ = NULL;\n" + " return temp;\n" "}\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc index 440b7166..5c4aa4fb 100644 --- a/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc @@ -56,24 +56,24 @@ class TestGenerator : public CodeGenerator { virtual bool Generate(const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* context, string* error) const { - TryInsert("test.pb.h", "includes", output_directory); - TryInsert("test.pb.h", "namespace_scope", output_directory); - TryInsert("test.pb.h", "global_scope", output_directory); - TryInsert("test.pb.h", "class_scope:foo.Bar", output_directory); - TryInsert("test.pb.h", "class_scope:foo.Bar.Baz", output_directory); + TryInsert("test.pb.h", "includes", context); + TryInsert("test.pb.h", "namespace_scope", context); + TryInsert("test.pb.h", "global_scope", context); + TryInsert("test.pb.h", "class_scope:foo.Bar", context); + TryInsert("test.pb.h", "class_scope:foo.Bar.Baz", context); - TryInsert("test.pb.cc", "includes", output_directory); - TryInsert("test.pb.cc", "namespace_scope", output_directory); - TryInsert("test.pb.cc", "global_scope", output_directory); + TryInsert("test.pb.cc", "includes", context); + TryInsert("test.pb.cc", "namespace_scope", context); + TryInsert("test.pb.cc", "global_scope", context); return true; } void TryInsert(const string& filename, const string& insertion_point, - OutputDirectory* output_directory) const { + GeneratorContext* context) const { scoped_ptr<io::ZeroCopyOutputStream> output( - output_directory->OpenForInsert(filename, insertion_point)); + context->OpenForInsert(filename, insertion_point)); io::Printer printer(output.get(), '$'); printer.Print("// inserted $name$\n", "name", insertion_point); } diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc index a69c48b5..5e8df0f4 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc @@ -125,7 +125,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " return $name$_;\n" "}\n" "inline void $classname$::set_$name$($type$ value) {\n" - " _set_bit($index$);\n" + " set_has_$name$();\n" " $name$_ = value;\n" "}\n"); } @@ -156,7 +156,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { "DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<\n" " $type$, $wire_format_field_type$>(\n" " input, &$name$_)));\n" - "_set_bit($index$);\n"); + "set_has_$name$();\n"); } void PrimitiveFieldGenerator:: diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc index ea6809a9..67d7eae6 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -50,6 +50,9 @@ void SetStringVariables(const FieldDescriptor* descriptor, SetCommonFieldVariables(descriptor, variables); (*variables)["default"] = "\"" + CEscape(descriptor->default_value_string()) + "\""; + (*variables)["default_variable"] = descriptor->default_value_string().empty() + ? "::google::protobuf::internal::kEmptyString" + : "_default_" + FieldName(descriptor) + "_"; (*variables)["pointer_type"] = descriptor->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char"; } @@ -68,9 +71,10 @@ StringFieldGenerator::~StringFieldGenerator() {} void StringFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { - printer->Print(variables_, - "::std::string* $name$_;\n" - "static const ::std::string _default_$name$_;\n"); + printer->Print(variables_, "::std::string* $name$_;\n"); + if (!descriptor_->default_value_string().empty()) { + printer->Print(variables_, "static const ::std::string $default_variable$;\n"); + } } void StringFieldGenerator:: @@ -105,7 +109,8 @@ GenerateAccessorDeclarations(io::Printer* printer) const { "inline void set_$name$(const char* value)$deprecation$;\n" "inline void set_$name$(const $pointer_type$* value, size_t size)" "$deprecation$;\n" - "inline ::std::string* mutable_$name$()$deprecation$;\n"); + "inline ::std::string* mutable_$name$()$deprecation$;\n" + "inline ::std::string* release_$name$()$deprecation$;\n"); if (descriptor_->options().ctype() != FieldOptions::STRING) { printer->Outdent(); @@ -121,51 +126,58 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " return *$name$_;\n" "}\n" "inline void $classname$::set_$name$(const ::std::string& value) {\n" - " _set_bit($index$);\n" - " if ($name$_ == &_default_$name$_) {\n" + " set_has_$name$();\n" + " if ($name$_ == &$default_variable$) {\n" " $name$_ = new ::std::string;\n" " }\n" " $name$_->assign(value);\n" "}\n" "inline void $classname$::set_$name$(const char* value) {\n" - " _set_bit($index$);\n" - " if ($name$_ == &_default_$name$_) {\n" + " set_has_$name$();\n" + " if ($name$_ == &$default_variable$) {\n" " $name$_ = new ::std::string;\n" " }\n" " $name$_->assign(value);\n" "}\n" "inline " "void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n" - " _set_bit($index$);\n" - " if ($name$_ == &_default_$name$_) {\n" + " set_has_$name$();\n" + " if ($name$_ == &$default_variable$) {\n" " $name$_ = new ::std::string;\n" " }\n" " $name$_->assign(reinterpret_cast<const char*>(value), size);\n" "}\n" "inline ::std::string* $classname$::mutable_$name$() {\n" - " _set_bit($index$);\n" - " if ($name$_ == &_default_$name$_) {\n"); + " set_has_$name$();\n" + " if ($name$_ == &$default_variable$) {\n"); if (descriptor_->default_value_string().empty()) { printer->Print(variables_, " $name$_ = new ::std::string;\n"); } else { printer->Print(variables_, - " $name$_ = new ::std::string(_default_$name$_);\n"); + " $name$_ = new ::std::string($default_variable$);\n"); } printer->Print(variables_, " }\n" " return $name$_;\n" + "}\n" + "inline ::std::string* $classname$::release_$name$() {\n" + " clear_has_$name$();\n" + " if ($name$_ == &$default_variable$) {\n" + " return NULL;\n" + " } else {\n" + " ::std::string* temp = $name$_;\n" + " $name$_ = const_cast< ::std::string*>(&$default_variable$);\n" + " return temp;\n" + " }\n" "}\n"); } void StringFieldGenerator:: GenerateNonInlineAccessorDefinitions(io::Printer* printer) const { - if (descriptor_->default_value_string().empty()) { - printer->Print(variables_, - "const ::std::string $classname$::_default_$name$_;\n"); - } else { + if (!descriptor_->default_value_string().empty()) { printer->Print(variables_, - "const ::std::string $classname$::_default_$name$_($default$);\n"); + "const ::std::string $classname$::$default_variable$($default$);\n"); } } @@ -173,13 +185,13 @@ void StringFieldGenerator:: GenerateClearingCode(io::Printer* printer) const { if (descriptor_->default_value_string().empty()) { printer->Print(variables_, - "if ($name$_ != &_default_$name$_) {\n" + "if ($name$_ != &$default_variable$) {\n" " $name$_->clear();\n" "}\n"); } else { printer->Print(variables_, - "if ($name$_ != &_default_$name$_) {\n" - " $name$_->assign(_default_$name$_);\n" + "if ($name$_ != &$default_variable$) {\n" + " $name$_->assign($default_variable$);\n" "}\n"); } } @@ -197,13 +209,13 @@ GenerateSwappingCode(io::Printer* printer) const { void StringFieldGenerator:: GenerateConstructorCode(io::Printer* printer) const { printer->Print(variables_, - "$name$_ = const_cast< ::std::string*>(&_default_$name$_);\n"); + "$name$_ = const_cast< ::std::string*>(&$default_variable$);\n"); } void StringFieldGenerator:: GenerateDestructorCode(io::Printer* printer) const { printer->Print(variables_, - "if ($name$_ != &_default_$name$_) {\n" + "if ($name$_ != &$default_variable$) {\n" " delete $name$_;\n" "}\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto index 79971a95..54d830fc 100644 --- a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto +++ b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto @@ -36,6 +36,10 @@ // though the same identifiers are used internally by the C++ code generator. +// Some generic_services option(s) added automatically. +// See: http://go/proto2-generic-services-default +option cc_generic_services = true; // auto-added + // We don't put this in a package within proto2 because we need to make sure // that the generated code doesn't depend on being in the proto2 namespace. package protobuf_unittest; diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc index a7e852de..41ba5e43 100644 --- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -195,6 +195,48 @@ TEST(GeneratedMessageTest, MutableStringDefault) { EXPECT_EQ("hello", *message.mutable_default_string()); } +TEST(GeneratedMessageTest, ReleaseString) { + // Check that release_foo() starts out NULL, and gives us a value + // that we can delete after it's been set. + unittest::TestAllTypes message; + + EXPECT_EQ(NULL, message.release_default_string()); + EXPECT_FALSE(message.has_default_string()); + EXPECT_EQ("hello", message.default_string()); + + message.set_default_string("blah"); + EXPECT_TRUE(message.has_default_string()); + string* str = message.release_default_string(); + EXPECT_FALSE(message.has_default_string()); + ASSERT_TRUE(str != NULL); + EXPECT_EQ("blah", *str); + delete str; + + EXPECT_EQ(NULL, message.release_default_string()); + EXPECT_FALSE(message.has_default_string()); + EXPECT_EQ("hello", message.default_string()); +} + +TEST(GeneratedMessageTest, ReleaseMessage) { + // Check that release_foo() starts out NULL, and gives us a value + // that we can delete after it's been set. + unittest::TestAllTypes message; + + EXPECT_EQ(NULL, message.release_optional_nested_message()); + EXPECT_FALSE(message.has_optional_nested_message()); + + message.mutable_optional_nested_message()->set_bb(1); + unittest::TestAllTypes::NestedMessage* nest = + message.release_optional_nested_message(); + EXPECT_FALSE(message.has_optional_nested_message()); + ASSERT_TRUE(nest != NULL); + EXPECT_EQ(1, nest->bb()); + delete nest; + + EXPECT_EQ(NULL, message.release_optional_nested_message()); + EXPECT_FALSE(message.has_optional_nested_message()); +} + TEST(GeneratedMessageTest, Clear) { // Set every field to a unique value, clear the message, then check that // it is cleared. @@ -282,6 +324,7 @@ TEST(GeneratedMessageTest, CopyFrom) { TestUtil::ExpectAllFieldsSet(message2); } + TEST(GeneratedMessageTest, SwapWithEmpty) { unittest::TestAllTypes message1, message2; TestUtil::SetAllFields(&message1); @@ -376,7 +419,7 @@ TEST(GeneratedMessageTest, CopyAssignmentOperator) { TestUtil::ExpectAllFieldsSet(message2); // Make sure that self-assignment does something sane. - message2 = message2; + message2.operator=(message2); TestUtil::ExpectAllFieldsSet(message2); } @@ -718,6 +761,7 @@ TEST(GeneratedMessageTest, TestSpaceUsed) { #endif // !PROTOBUF_TEST_NO_DESCRIPTORS + TEST(GeneratedMessageTest, FieldConstantValues) { unittest::TestRequired message; EXPECT_EQ(unittest::TestAllTypes_NestedMessage::kBbFieldNumber, 1); diff --git a/src/google/protobuf/compiler/java/java_enum.cc b/src/google/protobuf/compiler/java/java_enum.cc index e796587f..9d7bcab6 100644 --- a/src/google/protobuf/compiler/java/java_enum.cc +++ b/src/google/protobuf/compiler/java/java_enum.cc @@ -104,6 +104,15 @@ void EnumGenerator::Generate(io::Printer* printer) { "public static final $classname$ $name$ = $canonical_name$;\n"); } + for (int i = 0; i < descriptor_->value_count(); i++) { + map<string, string> vars; + vars["name"] = descriptor_->value(i)->name(); + vars["number"] = SimpleItoa(descriptor_->value(i)->number()); + printer->Print(vars, + "public static final int $name$_VALUE = $number$;\n"); + } + printer->Print("\n"); + // ----------------------------------------------------------------- printer->Print( @@ -219,17 +228,6 @@ void EnumGenerator::Generate(io::Printer* printer) { " this.value = value;\n" "}\n"); - if (HasDescriptorMethods(descriptor_)) { - // Force the static initialization code for the file to run, since it may - // initialize static variables declared in this class. - printer->Print( - "\n" - "static {\n" - " $file$.getDescriptor();\n" - "}\n", - "file", ClassName(descriptor_->file())); - } - printer->Print( "\n" "// @@protoc_insertion_point(enum_scope:$full_name$)\n", diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc index af6b1cd2..72caa10b 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.cc +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -52,17 +52,44 @@ namespace { // TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of // repeat code between this and the other field types. void SetEnumVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, map<string, string>* variables) { (*variables)["name"] = UnderscoresToCamelCase(descriptor); (*variables)["capitalized_name"] = UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["constant_name"] = FieldConstantName(descriptor); (*variables)["number"] = SimpleItoa(descriptor->number()); (*variables)["type"] = ClassName(descriptor->enum_type()); (*variables)["default"] = DefaultValue(descriptor); (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); (*variables)["tag_size"] = SimpleItoa( internal::WireFormat::TagSize(descriptor->number(), GetType(descriptor))); + // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported + // by the proto compiler + (*variables)["deprecation"] = descriptor->options().deprecated() + ? "@java.lang.Deprecated " : ""; + (*variables)["on_changed"] = + HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + + // For singular messages and builders, one bit is used for the hasField bit. + (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); + + (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); + (*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex); + (*variables)["clear_has_field_bit_builder"] = + GenerateClearBit(builderBitIndex); + + // For repated builders, one bit is used for whether the array is immutable. + (*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex); + (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); + (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); + + (*variables)["get_has_field_bit_from_local"] = + GenerateGetBitFromLocal(builderBitIndex); + (*variables)["set_has_field_bit_to_local"] = + GenerateSetBitToLocal(messageBitIndex); } } // namespace @@ -70,52 +97,88 @@ void SetEnumVariables(const FieldDescriptor* descriptor, // =================================================================== EnumFieldGenerator:: -EnumFieldGenerator(const FieldDescriptor* descriptor) - : descriptor_(descriptor) { - SetEnumVariables(descriptor, &variables_); +EnumFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex) { + SetEnumVariables(descriptor, messageBitIndex, builderBitIndex, &variables_); } EnumFieldGenerator::~EnumFieldGenerator() {} +int EnumFieldGenerator::GetNumBitsForMessage() const { + return 1; +} + +int EnumFieldGenerator::GetNumBitsForBuilder() const { + return 1; +} + +void EnumFieldGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n" + "$deprecation$$type$ get$capitalized_name$();\n"); +} + void EnumFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private boolean has$capitalized_name$;\n" "private $type$ $name$_;\n" - "public boolean has$capitalized_name$() { return has$capitalized_name$; }\n" - "public $type$ get$capitalized_name$() { return $name$_; }\n"); + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n" + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return $name$_;\n" + "}\n"); } void EnumFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, - "public boolean has$capitalized_name$() {\n" - " return result.has$capitalized_name$();\n" + "private $type$ $name$_ = $default$;\n" + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_builder$;\n" "}\n" - "public $type$ get$capitalized_name$() {\n" - " return result.get$capitalized_name$();\n" + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return $name$_;\n" "}\n" - "public Builder set$capitalized_name$($type$ value) {\n" + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" " if (value == null) {\n" " throw new NullPointerException();\n" " }\n" - " result.has$capitalized_name$ = true;\n" - " result.$name$_ = value;\n" + " $set_has_field_bit_builder$;\n" + " $name$_ = value;\n" + " $on_changed$\n" " return this;\n" "}\n" - "public Builder clear$capitalized_name$() {\n" - " result.has$capitalized_name$ = false;\n" - " result.$name$_ = $default$;\n" + "$deprecation$public Builder clear$capitalized_name$() {\n" + " $clear_has_field_bit_builder$;\n" + " $name$_ = $default$;\n" + " $on_changed$\n" " return this;\n" "}\n"); } void EnumFieldGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for enums +} + +void EnumFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = $default$;\n"); } void EnumFieldGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_ = $default$;\n" + "$clear_has_field_bit_builder$;\n"); +} + +void EnumFieldGenerator:: GenerateMergingCode(io::Printer* printer) const { printer->Print(variables_, "if (other.has$capitalized_name$()) {\n" @@ -125,7 +188,11 @@ GenerateMergingCode(io::Printer* printer) const { void EnumFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { - // Nothing to do here for enum types. + printer->Print(variables_, + "if ($get_has_field_bit_from_local$) {\n" + " $set_has_field_bit_to_local$;\n" + "}\n" + "result.$name$_ = $name$_;\n"); } void EnumFieldGenerator:: @@ -143,27 +210,42 @@ GenerateParsingCode(io::Printer* printer) const { "if (value != null) {\n"); } printer->Print(variables_, - " set$capitalized_name$(value);\n" + " $set_has_field_bit_builder$;\n" + " $name$_ = value;\n" "}\n"); } void EnumFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, - "if (has$capitalized_name$()) {\n" - " output.writeEnum($number$, get$capitalized_name$().getNumber());\n" + "if ($get_has_field_bit_message$) {\n" + " output.writeEnum($number$, $name$_.getNumber());\n" "}\n"); } void EnumFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, - "if (has$capitalized_name$()) {\n" + "if ($get_has_field_bit_message$) {\n" " size += com.google.protobuf.CodedOutputStream\n" - " .computeEnumSize($number$, get$capitalized_name$().getNumber());\n" + " .computeEnumSize($number$, $name$_.getNumber());\n" "}\n"); } +void EnumFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result &&\n" + " (get$capitalized_name$() == other.get$capitalized_name$());\n"); +} + +void EnumFieldGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n" + "hash = (53 * hash) + hashEnum(get$capitalized_name$());\n"); +} + string EnumFieldGenerator::GetBoxedType() const { return ClassName(descriptor_->enum_type()); } @@ -171,23 +253,43 @@ string EnumFieldGenerator::GetBoxedType() const { // =================================================================== RepeatedEnumFieldGenerator:: -RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor) - : descriptor_(descriptor) { - SetEnumVariables(descriptor, &variables_); +RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex) { + SetEnumVariables(descriptor, messageBitIndex, builderBitIndex, &variables_); } RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {} +int RepeatedEnumFieldGenerator::GetNumBitsForMessage() const { + return 0; +} + +int RepeatedEnumFieldGenerator::GetNumBitsForBuilder() const { + return 1; +} + +void RepeatedEnumFieldGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + printer->Print(variables_, + "$deprecation$java.util.List<$type$> get$capitalized_name$List();\n" + "$deprecation$int get$capitalized_name$Count();\n" + "$deprecation$$type$ get$capitalized_name$(int index);\n"); +} + void RepeatedEnumFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private java.util.List<$type$> $name$_ =\n" - " java.util.Collections.emptyList();\n" - "public java.util.List<$type$> get$capitalized_name$List() {\n" + "private java.util.List<$type$> $name$_;\n" + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" " return $name$_;\n" // note: unmodifiable list "}\n" - "public int get$capitalized_name$Count() { return $name$_.size(); }\n" - "public $type$ get$capitalized_name$(int index) {\n" + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n" + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" " return $name$_.get(index);\n" "}\n"); @@ -201,73 +303,119 @@ GenerateMembers(io::Printer* printer) const { void RepeatedEnumFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, + // One field is the list and the other field keeps track of whether the + // list is immutable. If it's immutable, the invariant is that it must + // either an instance of Collections.emptyList() or it's an ArrayList + // wrapped in a Collections.unmodifiableList() wrapper and nobody else has + // a refererence to the underlying ArrayList. This invariant allows us to + // share instances of lists between protocol buffers avoiding expensive + // memory allocations. Note, immutable is a strong guarantee here -- not + // just that the list cannot be modified via the reference but that the + // list can never be modified. + "private java.util.List<$type$> $name$_ =\n" + " java.util.Collections.emptyList();\n" + + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$get_mutable_bit_builder$) {\n" + " $name$_ = new java.util.ArrayList<$type$>($name$_);\n" + " $set_mutable_bit_builder$;\n" + " }\n" + "}\n" + // Note: We return an unmodifiable list because otherwise the caller // could hold on to the returned list and modify it after the message // has been built, thus mutating the message which is supposed to be // immutable. - "public java.util.List<$type$> get$capitalized_name$List() {\n" - " return java.util.Collections.unmodifiableList(result.$name$_);\n" + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList($name$_);\n" "}\n" - "public int get$capitalized_name$Count() {\n" - " return result.get$capitalized_name$Count();\n" + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" "}\n" - "public $type$ get$capitalized_name$(int index) {\n" - " return result.get$capitalized_name$(index);\n" + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" "}\n" - "public Builder set$capitalized_name$(int index, $type$ value) {\n" + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$ value) {\n" " if (value == null) {\n" " throw new NullPointerException();\n" " }\n" - " result.$name$_.set(index, value);\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, value);\n" + " $on_changed$\n" " return this;\n" "}\n" - "public Builder add$capitalized_name$($type$ value) {\n" + "$deprecation$public Builder add$capitalized_name$($type$ value) {\n" " if (value == null) {\n" " throw new NullPointerException();\n" " }\n" - " if (result.$name$_.isEmpty()) {\n" - " result.$name$_ = new java.util.ArrayList<$type$>();\n" - " }\n" - " result.$name$_.add(value);\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value);\n" + " $on_changed$\n" " return this;\n" "}\n" - "public Builder addAll$capitalized_name$(\n" + "$deprecation$public Builder addAll$capitalized_name$(\n" " java.lang.Iterable<? extends $type$> values) {\n" - " if (result.$name$_.isEmpty()) {\n" - " result.$name$_ = new java.util.ArrayList<$type$>();\n" - " }\n" - " super.addAll(values, result.$name$_);\n" + " ensure$capitalized_name$IsMutable();\n" + " super.addAll(values, $name$_);\n" + " $on_changed$\n" " return this;\n" "}\n" - "public Builder clear$capitalized_name$() {\n" - " result.$name$_ = java.util.Collections.emptyList();\n" + "$deprecation$public Builder clear$capitalized_name$() {\n" + " $name$_ = java.util.Collections.emptyList();\n" + " $clear_mutable_bit_builder$;\n" + " $on_changed$\n" " return this;\n" "}\n"); } void RepeatedEnumFieldGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for enums +} + +void RepeatedEnumFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { - // Initialized inline. + printer->Print(variables_, "$name$_ = java.util.Collections.emptyList();\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_ = java.util.Collections.emptyList();\n" + "$clear_mutable_bit_builder$;\n"); } void RepeatedEnumFieldGenerator:: GenerateMergingCode(io::Printer* printer) const { + // The code below does two optimizations: + // 1. If the other list is empty, there's nothing to do. This ensures we + // don't allocate a new array if we already have an immutable one. + // 2. If the other list is non-empty and our current list is empty, we can + // reuse the other list which is guaranteed to be immutable. printer->Print(variables_, "if (!other.$name$_.isEmpty()) {\n" - " if (result.$name$_.isEmpty()) {\n" - " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " if ($name$_.isEmpty()) {\n" + " $name$_ = other.$name$_;\n" + " $clear_mutable_bit_builder$;\n" + " } else {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.addAll(other.$name$_);\n" " }\n" - " result.$name$_.addAll(other.$name$_);\n" + " $on_changed$\n" "}\n"); } void RepeatedEnumFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { + // The code below ensures that the result has an immutable list. If our + // list is immutable, we can just reuse it. If not, we make it immutable. printer->Print(variables_, - "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n" - " result.$name$_ =\n" - " java.util.Collections.unmodifiableList(result.$name$_);\n" - "}\n"); + "if ($get_mutable_bit_builder$) {\n" + " $name$_ = java.util.Collections.unmodifiableList($name$_);\n" + " $clear_mutable_bit_builder$;\n" + "}\n" + "result.$name$_ = $name$_;\n"); } void RepeatedEnumFieldGenerator:: @@ -316,13 +464,13 @@ GenerateSerializationCode(io::Printer* printer) const { " output.writeRawVarint32($tag$);\n" " output.writeRawVarint32($name$MemoizedSerializedSize);\n" "}\n" - "for ($type$ element : get$capitalized_name$List()) {\n" - " output.writeEnumNoTag(element.getNumber());\n" + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.writeEnumNoTag($name$_.get(i).getNumber());\n" "}\n"); } else { printer->Print(variables_, - "for ($type$ element : get$capitalized_name$List()) {\n" - " output.writeEnum($number$, element.getNumber());\n" + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.writeEnum($number$, $name$_.get(i).getNumber());\n" "}\n"); } } @@ -335,9 +483,9 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Indent(); printer->Print(variables_, - "for ($type$ element : get$capitalized_name$List()) {\n" + "for (int i = 0; i < $name$_.size(); i++) {\n" " dataSize += com.google.protobuf.CodedOutputStream\n" - " .computeEnumSizeNoTag(element.getNumber());\n" + " .computeEnumSizeNoTag($name$_.get(i).getNumber());\n" "}\n"); printer->Print( "size += dataSize;\n"); @@ -350,7 +498,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}"); } else { printer->Print(variables_, - "size += $tag_size$ * get$capitalized_name$List().size();\n"); + "size += $tag_size$ * $name$_.size();\n"); } // cache the data size for packed fields. @@ -363,6 +511,22 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print("}\n"); } +void RepeatedEnumFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$List()\n" + " .equals(other.get$capitalized_name$List());\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "if (get$capitalized_name$Count() > 0) {\n" + " hash = (37 * hash) + $constant_name$;\n" + " hash = (53 * hash) + hashEnumList(get$capitalized_name$List());\n" + "}\n"); +} + string RepeatedEnumFieldGenerator::GetBoxedType() const { return ClassName(descriptor_->enum_type()); } diff --git a/src/google/protobuf/compiler/java/java_enum_field.h b/src/google/protobuf/compiler/java/java_enum_field.h index c54a0faf..0cad6be0 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.h +++ b/src/google/protobuf/compiler/java/java_enum_field.h @@ -46,49 +46,69 @@ namespace java { class EnumFieldGenerator : public FieldGenerator { public: - explicit EnumFieldGenerator(const FieldDescriptor* descriptor); + explicit EnumFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, int builderBitIndex); ~EnumFieldGenerator(); // implements FieldGenerator --------------------------------------- + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; + void GenerateBuilderClearCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; string GetBoxedType() const; private: const FieldDescriptor* descriptor_; map<string, string> variables_; + const int messageBitIndex_; + const int builderBitIndex_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator); }; class RepeatedEnumFieldGenerator : public FieldGenerator { public: - explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor); + explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, int builderBitIndex); ~RepeatedEnumFieldGenerator(); // implements FieldGenerator --------------------------------------- + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; + void GenerateBuilderClearCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateParsingCodeFromPacked(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; string GetBoxedType() const; private: const FieldDescriptor* descriptor_; map<string, string> variables_; + const int messageBitIndex_; + const int builderBitIndex_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator); }; diff --git a/src/google/protobuf/compiler/java/java_extension.cc b/src/google/protobuf/compiler/java/java_extension.cc index 903b0a9b..9b147c77 100644 --- a/src/google/protobuf/compiler/java/java_extension.cc +++ b/src/google/protobuf/compiler/java/java_extension.cc @@ -86,108 +86,123 @@ ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor) ExtensionGenerator::~ExtensionGenerator() {} -void ExtensionGenerator::Generate(io::Printer* printer) { - map<string, string> vars; - vars["name"] = UnderscoresToCamelCase(descriptor_); - vars["containing_type"] = ClassName(descriptor_->containing_type()); - vars["number"] = SimpleItoa(descriptor_->number()); - vars["constant_name"] = FieldConstantName(descriptor_); - vars["lite"] = HasDescriptorMethods(descriptor_->file()) ? "" : "Lite"; +// Initializes the vars referenced in the generated code templates. +void InitTemplateVars(const FieldDescriptor* descriptor, + const string& scope, + map<string, string>* vars_pointer) { + map<string, string> &vars = *vars_pointer; + vars["scope"] = scope; + vars["name"] = UnderscoresToCamelCase(descriptor); + vars["containing_type"] = ClassName(descriptor->containing_type()); + vars["number"] = SimpleItoa(descriptor->number()); + vars["constant_name"] = FieldConstantName(descriptor); + vars["index"] = SimpleItoa(descriptor->index()); + vars["default"] = + descriptor->is_repeated() ? "" : DefaultValue(descriptor); + vars["type_constant"] = TypeName(GetType(descriptor)); + vars["packed"] = descriptor->options().packed() ? "true" : "false"; + vars["enum_map"] = "null"; + vars["prototype"] = "null"; - JavaType java_type = GetJavaType(descriptor_); + JavaType java_type = GetJavaType(descriptor); string singular_type; switch (java_type) { case JAVATYPE_MESSAGE: - vars["type"] = ClassName(descriptor_->message_type()); + singular_type = ClassName(descriptor->message_type()); + vars["prototype"] = singular_type + ".getDefaultInstance()"; break; case JAVATYPE_ENUM: - vars["type"] = ClassName(descriptor_->enum_type()); + singular_type = ClassName(descriptor->enum_type()); + vars["enum_map"] = singular_type + ".internalGetValueMap()"; break; default: - vars["type"] = BoxedPrimitiveTypeName(java_type); + singular_type = BoxedPrimitiveTypeName(java_type); break; } - - printer->Print(vars, - "public static final int $constant_name$ = $number$;\n"); - if (descriptor_->is_repeated()) { - printer->Print(vars, - "public static final\n" - " com.google.protobuf.GeneratedMessage$lite$.GeneratedExtension<\n" - " $containing_type$,\n" - " java.util.List<$type$>> $name$ =\n" - " com.google.protobuf.GeneratedMessage$lite$\n" - " .newGeneratedExtension();\n"); - } else { - printer->Print(vars, - "public static final\n" - " com.google.protobuf.GeneratedMessage$lite$.GeneratedExtension<\n" - " $containing_type$,\n" - " $type$> $name$ =\n" - " com.google.protobuf.GeneratedMessage$lite$\n" - " .newGeneratedExtension();\n"); - } + vars["type"] = descriptor->is_repeated() ? + "java.util.List<" + singular_type + ">" : singular_type; + vars["singular_type"] = singular_type; } -void ExtensionGenerator::GenerateInitializationCode(io::Printer* printer) { +void ExtensionGenerator::Generate(io::Printer* printer) { map<string, string> vars; - vars["name"] = UnderscoresToCamelCase(descriptor_); - vars["scope"] = scope_; - vars["index"] = SimpleItoa(descriptor_->index()); - vars["extendee"] = ClassName(descriptor_->containing_type()); - vars["default"] = descriptor_->is_repeated() ? "" : DefaultValue(descriptor_); - vars["number"] = SimpleItoa(descriptor_->number()); - vars["type_constant"] = TypeName(GetType(descriptor_)); - vars["packed"] = descriptor_->options().packed() ? "true" : "false"; - vars["enum_map"] = "null"; - vars["prototype"] = "null"; - - JavaType java_type = GetJavaType(descriptor_); - string singular_type; - switch (java_type) { - case JAVATYPE_MESSAGE: - vars["type"] = ClassName(descriptor_->message_type()); - vars["prototype"] = ClassName(descriptor_->message_type()) + - ".getDefaultInstance()"; - break; - case JAVATYPE_ENUM: - vars["type"] = ClassName(descriptor_->enum_type()); - vars["enum_map"] = ClassName(descriptor_->enum_type()) + - ".internalGetValueMap()"; - break; - default: - vars["type"] = BoxedPrimitiveTypeName(java_type); - break; - } + InitTemplateVars(descriptor_, scope_, &vars); + printer->Print(vars, + "public static final int $constant_name$ = $number$;\n"); if (HasDescriptorMethods(descriptor_->file())) { - printer->Print(vars, - "$scope$.$name$.internalInit(\n" - " $scope$.getDescriptor().getExtensions().get($index$),\n" - " $type$.class);\n"); + // Non-lite extensions + if (descriptor_->extension_scope() == NULL) { + // Non-nested + printer->Print( + vars, + "public static final\n" + " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ = com.google.protobuf.GeneratedMessage\n" + " .newFileScopedGeneratedExtension(\n" + " $singular_type$.class,\n" + " $prototype$);\n"); + } else { + // Nested + printer->Print( + vars, + "public static final\n" + " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ = com.google.protobuf.GeneratedMessage\n" + " .newMessageScopedGeneratedExtension(\n" + " $scope$.getDefaultInstance(),\n" + " $index$,\n" + " $singular_type$.class,\n" + " $prototype$);\n"); + } } else { + // Lite extensions if (descriptor_->is_repeated()) { - printer->Print(vars, - "$scope$.$name$.internalInitRepeated(\n" - " $extendee$.getDefaultInstance(),\n" - " $prototype$,\n" - " $enum_map$,\n" - " $number$,\n" - " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n" - " $packed$);\n"); + printer->Print( + vars, + "public static final\n" + " com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n" + " .newRepeatedGeneratedExtension(\n" + " $containing_type$.getDefaultInstance(),\n" + " $prototype$,\n" + " $enum_map$,\n" + " $number$,\n" + " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n" + " $packed$);\n"); } else { - printer->Print(vars, - "$scope$.$name$.internalInitSingular(\n" - " $extendee$.getDefaultInstance(),\n" - " $default$,\n" - " $prototype$,\n" - " $enum_map$,\n" - " $number$,\n" - " com.google.protobuf.WireFormat.FieldType.$type_constant$);\n"); + printer->Print( + vars, + "public static final\n" + " com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n" + " .newSingularGeneratedExtension(\n" + " $containing_type$.getDefaultInstance(),\n" + " $default$,\n" + " $prototype$,\n" + " $enum_map$,\n" + " $number$,\n" + " com.google.protobuf.WireFormat.FieldType.$type_constant$);\n"); } } } +void ExtensionGenerator::GenerateNonNestedInitializationCode( + io::Printer* printer) { + if (descriptor_->extension_scope() == NULL && + HasDescriptorMethods(descriptor_->file())) { + // Only applies to non-nested, non-lite extensions. + printer->Print( + "$name$.internalInit(descriptor.getExtensions().get($index$));\n", + "name", UnderscoresToCamelCase(descriptor_), + "index", SimpleItoa(descriptor_->index())); + } +} + void ExtensionGenerator::GenerateRegistrationCode(io::Printer* printer) { printer->Print( "registry.add($scope$.$name$);\n", diff --git a/src/google/protobuf/compiler/java/java_extension.h b/src/google/protobuf/compiler/java/java_extension.h index 1e423048..009ed9ff 100644 --- a/src/google/protobuf/compiler/java/java_extension.h +++ b/src/google/protobuf/compiler/java/java_extension.h @@ -60,7 +60,7 @@ class ExtensionGenerator { ~ExtensionGenerator(); void Generate(io::Printer* printer); - void GenerateInitializationCode(io::Printer* printer); + void GenerateNonNestedInitializationCode(io::Printer* printer); void GenerateRegistrationCode(io::Printer* printer); private: diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc index 978c8f33..c7d433c8 100644 --- a/src/google/protobuf/compiler/java/java_field.cc +++ b/src/google/protobuf/compiler/java/java_field.cc @@ -37,6 +37,7 @@ #include <google/protobuf/compiler/java/java_primitive_field.h> #include <google/protobuf/compiler/java/java_enum_field.h> #include <google/protobuf/compiler/java/java_message_field.h> +#include <google/protobuf/compiler/java/java_string_field.h> #include <google/protobuf/stubs/common.h> namespace google { @@ -63,33 +64,57 @@ FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor) extension_generators_( new scoped_ptr<FieldGenerator>[descriptor->extension_count()]) { - // Construct all the FieldGenerators. + // Construct all the FieldGenerators and assign them bit indices for their + // bit fields. + int messageBitIndex = 0; + int builderBitIndex = 0; for (int i = 0; i < descriptor->field_count(); i++) { - field_generators_[i].reset(MakeGenerator(descriptor->field(i))); + FieldGenerator* generator = MakeGenerator(descriptor->field(i), + messageBitIndex, builderBitIndex); + field_generators_[i].reset(generator); + messageBitIndex += generator->GetNumBitsForMessage(); + builderBitIndex += generator->GetNumBitsForBuilder(); } for (int i = 0; i < descriptor->extension_count(); i++) { - extension_generators_[i].reset(MakeGenerator(descriptor->extension(i))); + FieldGenerator* generator = MakeGenerator(descriptor->extension(i), + messageBitIndex, builderBitIndex); + extension_generators_[i].reset(generator); + messageBitIndex += generator->GetNumBitsForMessage(); + builderBitIndex += generator->GetNumBitsForBuilder(); } } -FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field) { +FieldGenerator* FieldGeneratorMap::MakeGenerator( + const FieldDescriptor* field, int messageBitIndex, int builderBitIndex) { if (field->is_repeated()) { switch (GetJavaType(field)) { case JAVATYPE_MESSAGE: - return new RepeatedMessageFieldGenerator(field); + return new RepeatedMessageFieldGenerator( + field, messageBitIndex, builderBitIndex); case JAVATYPE_ENUM: - return new RepeatedEnumFieldGenerator(field); + return new RepeatedEnumFieldGenerator( + field, messageBitIndex, builderBitIndex); + case JAVATYPE_STRING: + return new RepeatedStringFieldGenerator( + field, messageBitIndex, builderBitIndex); default: - return new RepeatedPrimitiveFieldGenerator(field); + return new RepeatedPrimitiveFieldGenerator( + field, messageBitIndex, builderBitIndex); } } else { switch (GetJavaType(field)) { case JAVATYPE_MESSAGE: - return new MessageFieldGenerator(field); + return new MessageFieldGenerator( + field, messageBitIndex, builderBitIndex); case JAVATYPE_ENUM: - return new EnumFieldGenerator(field); + return new EnumFieldGenerator( + field, messageBitIndex, builderBitIndex); + case JAVATYPE_STRING: + return new StringFieldGenerator( + field, messageBitIndex, builderBitIndex); default: - return new PrimitiveFieldGenerator(field); + return new PrimitiveFieldGenerator( + field, messageBitIndex, builderBitIndex); } } } diff --git a/src/google/protobuf/compiler/java/java_field.h b/src/google/protobuf/compiler/java/java_field.h index f5bef7ab..6097f357 100644 --- a/src/google/protobuf/compiler/java/java_field.h +++ b/src/google/protobuf/compiler/java/java_field.h @@ -55,15 +55,24 @@ class FieldGenerator { FieldGenerator() {} virtual ~FieldGenerator(); + virtual int GetNumBitsForMessage() const = 0; + virtual int GetNumBitsForBuilder() const = 0; + virtual void GenerateInterfaceMembers(io::Printer* printer) const = 0; virtual void GenerateMembers(io::Printer* printer) const = 0; virtual void GenerateBuilderMembers(io::Printer* printer) const = 0; virtual void GenerateInitializationCode(io::Printer* printer) const = 0; + virtual void GenerateBuilderClearCode(io::Printer* printer) const = 0; virtual void GenerateMergingCode(io::Printer* printer) const = 0; virtual void GenerateBuildingCode(io::Printer* printer) const = 0; virtual void GenerateParsingCode(io::Printer* printer) const = 0; virtual void GenerateParsingCodeFromPacked(io::Printer* printer) const; virtual void GenerateSerializationCode(io::Printer* printer) const = 0; virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; + virtual void GenerateFieldBuilderInitializationCode(io::Printer* printer) + const = 0; + + virtual void GenerateEqualsCode(io::Printer* printer) const = 0; + virtual void GenerateHashCode(io::Printer* printer) const = 0; virtual string GetBoxedType() const = 0; @@ -85,7 +94,8 @@ class FieldGeneratorMap { scoped_array<scoped_ptr<FieldGenerator> > field_generators_; scoped_array<scoped_ptr<FieldGenerator> > extension_generators_; - static FieldGenerator* MakeGenerator(const FieldDescriptor* field); + static FieldGenerator* MakeGenerator(const FieldDescriptor* field, + int messageBitIndex, int builderBitIndex); GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap); }; diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc index 7ea127c0..8968069f 100644 --- a/src/google/protobuf/compiler/java/java_file.cc +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -88,7 +88,8 @@ bool UsesExtensions(const Message& message) { FileGenerator::FileGenerator(const FileDescriptor* file) : file_(file), java_package_(FileJavaPackage(file)), - classname_(FileClassName(file)) {} + classname_(FileClassName(file)) { +} FileGenerator::~FileGenerator() {} @@ -179,7 +180,9 @@ void FileGenerator::Generate(io::Printer* printer) { EnumGenerator(file_->enum_type(i)).Generate(printer); } for (int i = 0; i < file_->message_type_count(); i++) { - MessageGenerator(file_->message_type(i)).Generate(printer); + MessageGenerator messageGenerator(file_->message_type(i)); + messageGenerator.GenerateInterface(printer); + messageGenerator.Generate(printer); } if (HasGenericServices(file_)) { for (int i = 0; i < file_->service_count(); i++) { @@ -215,24 +218,11 @@ void FileGenerator::Generate(io::Printer* printer) { .GenerateStaticVariableInitializers(printer); } - for (int i = 0; i < file_->extension_count(); i++) { - // TODO(kenton): Reuse ExtensionGenerator objects? - ExtensionGenerator(file_->extension(i)) - .GenerateInitializationCode(printer); - } - printer->Outdent(); printer->Print( "}\n"); } - // Dummy function we can use to force the static initialization block to - // run. Needed by inner classes. Cannot be private due to - // java_multiple_files option. - printer->Print( - "\n" - "public static void internalForceInit() {}\n"); - printer->Print( "\n" "// @@protoc_insertion_point(outer_class_scope)\n"); @@ -310,11 +300,10 @@ void FileGenerator::GenerateEmbeddedDescriptor(io::Printer* printer) { MessageGenerator(file_->message_type(i)) .GenerateStaticVariableInitializers(printer); } - for (int i = 0; i < file_->extension_count(); i++) { // TODO(kenton): Reuse ExtensionGenerator objects? ExtensionGenerator(file_->extension(i)) - .GenerateInitializationCode(printer); + .GenerateNonNestedInitializationCode(printer); } if (UsesExtensions(file_proto)) { @@ -325,9 +314,11 @@ void FileGenerator::GenerateEmbeddedDescriptor(io::Printer* printer) { " com.google.protobuf.ExtensionRegistry.newInstance();\n" "registerAllExtensions(registry);\n"); for (int i = 0; i < file_->dependency_count(); i++) { - printer->Print( - "$dependency$.registerAllExtensions(registry);\n", - "dependency", ClassName(file_->dependency(i))); + if (ShouldIncludeDependency(file_->dependency(i))) { + printer->Print( + "$dependency$.registerAllExtensions(registry);\n", + "dependency", ClassName(file_->dependency(i))); + } } printer->Print( "return registry;\n"); @@ -372,13 +363,14 @@ template<typename GeneratorClass, typename DescriptorClass> static void GenerateSibling(const string& package_dir, const string& java_package, const DescriptorClass* descriptor, - OutputDirectory* output_directory, - vector<string>* file_list) { - string filename = package_dir + descriptor->name() + ".java"; + GeneratorContext* context, + vector<string>* file_list, + const string& name_suffix, + void (GeneratorClass::*pfn)(io::Printer* printer)) { + string filename = package_dir + descriptor->name() + name_suffix + ".java"; file_list->push_back(filename); - scoped_ptr<io::ZeroCopyOutputStream> output( - output_directory->Open(filename)); + scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename)); io::Printer printer(output.get(), '$'); printer.Print( @@ -391,28 +383,36 @@ static void GenerateSibling(const string& package_dir, "package", java_package); } - GeneratorClass(descriptor).Generate(&printer); + GeneratorClass generator(descriptor); + (generator.*pfn)(&printer); } void FileGenerator::GenerateSiblings(const string& package_dir, - OutputDirectory* output_directory, + GeneratorContext* context, vector<string>* file_list) { if (file_->options().java_multiple_files()) { for (int i = 0; i < file_->enum_type_count(); i++) { GenerateSibling<EnumGenerator>(package_dir, java_package_, file_->enum_type(i), - output_directory, file_list); + context, file_list, "", + &EnumGenerator::Generate); } for (int i = 0; i < file_->message_type_count(); i++) { GenerateSibling<MessageGenerator>(package_dir, java_package_, file_->message_type(i), - output_directory, file_list); + context, file_list, "OrBuilder", + &MessageGenerator::GenerateInterface); + GenerateSibling<MessageGenerator>(package_dir, java_package_, + file_->message_type(i), + context, file_list, "", + &MessageGenerator::Generate); } if (HasGenericServices(file_)) { for (int i = 0; i < file_->service_count(); i++) { GenerateSibling<ServiceGenerator>(package_dir, java_package_, file_->service(i), - output_directory, file_list); + context, file_list, "", + &ServiceGenerator::Generate); } } } diff --git a/src/google/protobuf/compiler/java/java_file.h b/src/google/protobuf/compiler/java/java_file.h index 9e35d330..59911462 100644 --- a/src/google/protobuf/compiler/java/java_file.h +++ b/src/google/protobuf/compiler/java/java_file.h @@ -46,7 +46,7 @@ namespace protobuf { class Printer; // printer.h } namespace compiler { - class OutputDirectory; // code_generator.h + class GeneratorContext; // code_generator.h } } @@ -70,12 +70,13 @@ class FileGenerator { // files other than the outer file (i.e. one for each message, enum, and // service type). void GenerateSiblings(const string& package_dir, - OutputDirectory* output_directory, + GeneratorContext* generator_context, vector<string>* file_list); const string& java_package() { return java_package_; } const string& classname() { return classname_; } + private: // Returns whether the dependency should be included in the output file. // Always returns true for opensource, but used internally at Google to help @@ -86,6 +87,7 @@ class FileGenerator { string java_package_; string classname_; + void GenerateEmbeddedDescriptor(io::Printer* printer); GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator); diff --git a/src/google/protobuf/compiler/java/java_generator.cc b/src/google/protobuf/compiler/java/java_generator.cc index 745b55ae..e6c79abc 100644 --- a/src/google/protobuf/compiler/java/java_generator.cc +++ b/src/google/protobuf/compiler/java/java_generator.cc @@ -51,11 +51,8 @@ JavaGenerator::~JavaGenerator() {} bool JavaGenerator::Generate(const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* context, string* error) const { - vector<pair<string, string> > options; - ParseGeneratorParameter(parameter, &options); - // ----------------------------------------------------------------- // parse generator options @@ -63,6 +60,10 @@ bool JavaGenerator::Generate(const FileDescriptor* file, // per line. string output_list_file; + + vector<pair<string, string> > options; + ParseGeneratorParameter(parameter, &options); + for (int i = 0; i < options.size(); i++) { if (options[i].first == "output_list_file") { output_list_file = options[i].second; @@ -72,18 +73,23 @@ bool JavaGenerator::Generate(const FileDescriptor* file, } } - // ----------------------------------------------------------------- + if (file->options().optimize_for() == FileOptions::LITE_RUNTIME && + file->options().java_generate_equals_and_hash()) { + *error = "The \"java_generate_equals_and_hash\" option is incompatible " + "with \"optimize_for = LITE_RUNTIME\". You must optimize for " + "SPEED or CODE_SIZE if you want to use this option."; + return false; + } + FileGenerator file_generator(file); if (!file_generator.Validate(error)) { return false; } - string package_dir = - StringReplace(file_generator.java_package(), ".", "/", true); - if (!package_dir.empty()) package_dir += "/"; + string package_dir = JavaPackageToDir(file_generator.java_package()); vector<string> all_files; @@ -94,19 +100,19 @@ bool JavaGenerator::Generate(const FileDescriptor* file, // Generate main java file. scoped_ptr<io::ZeroCopyOutputStream> output( - output_directory->Open(java_filename)); + context->Open(java_filename)); io::Printer printer(output.get(), '$'); file_generator.Generate(&printer); // Generate sibling files. - file_generator.GenerateSiblings(package_dir, output_directory, &all_files); + file_generator.GenerateSiblings(package_dir, context, &all_files); // Generate output list if requested. if (!output_list_file.empty()) { // Generate output list. This is just a simple text file placed in a // deterministic location which lists the .java files being generated. scoped_ptr<io::ZeroCopyOutputStream> srclist_raw_output( - output_directory->Open(output_list_file)); + context->Open(output_list_file)); io::Printer srclist_printer(srclist_raw_output.get(), '$'); for (int i = 0; i < all_files.size(); i++) { srclist_printer.Print("$filename$\n", "filename", all_files[i]); diff --git a/src/google/protobuf/compiler/java/java_generator.h b/src/google/protobuf/compiler/java/java_generator.h index c91c9053..888b8d85 100644 --- a/src/google/protobuf/compiler/java/java_generator.h +++ b/src/google/protobuf/compiler/java/java_generator.h @@ -57,7 +57,7 @@ class LIBPROTOC_EXPORT JavaGenerator : public CodeGenerator { // implements CodeGenerator ---------------------------------------- bool Generate(const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* context, string* error) const; private: diff --git a/src/google/protobuf/compiler/java/java_helpers.cc b/src/google/protobuf/compiler/java/java_helpers.cc index 7ed0c3cc..1b6f1653 100644 --- a/src/google/protobuf/compiler/java/java_helpers.cc +++ b/src/google/protobuf/compiler/java/java_helpers.cc @@ -134,16 +134,27 @@ string FileClassName(const FileDescriptor* file) { } string FileJavaPackage(const FileDescriptor* file) { + string result; + if (file->options().has_java_package()) { - return file->options().java_package(); + result = file->options().java_package(); } else { - string result = kDefaultPackage; + result = kDefaultPackage; if (!file->package().empty()) { if (!result.empty()) result += '.'; result += file->package(); } - return result; } + + + return result; +} + +string JavaPackageToDir(string package_name) { + string package_dir = + StringReplace(package_name, ".", "/", true); + if (!package_dir.empty()) package_dir += "/"; + return package_dir; } string ToJavaName(const string& full_name, const FileDescriptor* file) { @@ -335,6 +346,132 @@ string DefaultValue(const FieldDescriptor* field) { return ""; } +bool IsDefaultValueJavaDefault(const FieldDescriptor* field) { + // Switch on CppType since we need to know which default_value_* method + // of FieldDescriptor to call. + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return field->default_value_int32() == 0; + case FieldDescriptor::CPPTYPE_UINT32: + return field->default_value_uint32() == 0; + case FieldDescriptor::CPPTYPE_INT64: + return field->default_value_int64() == 0L; + case FieldDescriptor::CPPTYPE_UINT64: + return field->default_value_uint64() == 0L; + case FieldDescriptor::CPPTYPE_DOUBLE: + return field->default_value_double() == 0.0; + case FieldDescriptor::CPPTYPE_FLOAT: + return field->default_value_float() == 0.0; + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool() == false; + + case FieldDescriptor::CPPTYPE_STRING: + case FieldDescriptor::CPPTYPE_ENUM: + case FieldDescriptor::CPPTYPE_MESSAGE: + return false; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return false; +} + +const char* bit_masks[] = { + "0x00000001", + "0x00000002", + "0x00000004", + "0x00000008", + "0x00000010", + "0x00000020", + "0x00000040", + "0x00000080", + + "0x00000100", + "0x00000200", + "0x00000400", + "0x00000800", + "0x00001000", + "0x00002000", + "0x00004000", + "0x00008000", + + "0x00010000", + "0x00020000", + "0x00040000", + "0x00080000", + "0x00100000", + "0x00200000", + "0x00400000", + "0x00800000", + + "0x01000000", + "0x02000000", + "0x04000000", + "0x08000000", + "0x10000000", + "0x20000000", + "0x40000000", + "0x80000000", +}; + +string GetBitFieldName(int index) { + string varName = "bitField"; + varName += SimpleItoa(index); + varName += "_"; + return varName; +} + +string GetBitFieldNameForBit(int bitIndex) { + return GetBitFieldName(bitIndex / 32); +} + +string GenerateGetBit(int bitIndex) { + string varName = GetBitFieldNameForBit(bitIndex); + int bitInVarIndex = bitIndex % 32; + + string mask = bit_masks[bitInVarIndex]; + string result = "((" + varName + " & " + mask + ") == " + mask + ")"; + return result; +} + +string GenerateSetBit(int bitIndex) { + string varName = GetBitFieldNameForBit(bitIndex); + int bitInVarIndex = bitIndex % 32; + + string mask = bit_masks[bitInVarIndex]; + string result = varName + " |= " + mask; + return result; +} + +string GenerateClearBit(int bitIndex) { + string varName = GetBitFieldNameForBit(bitIndex); + int bitInVarIndex = bitIndex % 32; + + string mask = bit_masks[bitInVarIndex]; + string result = varName + " = (" + varName + " & ~" + mask + ")"; + return result; +} + +string GenerateGetBitFromLocal(int bitIndex) { + string varName = "from_" + GetBitFieldNameForBit(bitIndex); + int bitInVarIndex = bitIndex % 32; + + string mask = bit_masks[bitInVarIndex]; + string result = "((" + varName + " & " + mask + ") == " + mask + ")"; + return result; +} + +string GenerateSetBitToLocal(int bitIndex) { + string varName = "to_" + GetBitFieldNameForBit(bitIndex); + int bitInVarIndex = bitIndex % 32; + + string mask = bit_masks[bitInVarIndex]; + string result = varName + " |= " + mask; + return result; +} + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h index 3c8974c9..4ae07f15 100644 --- a/src/google/protobuf/compiler/java/java_helpers.h +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -68,6 +68,9 @@ string FileClassName(const FileDescriptor* file); // Returns the file's Java package name. string FileJavaPackage(const FileDescriptor* file); +// Returns output directory for the given package name. +string JavaPackageToDir(string package_name); + // Converts the given fully-qualified name in the proto namespace to its // fully-qualified name in the Java namespace, given that it is in the given // file. @@ -118,6 +121,7 @@ JavaType GetJavaType(const FieldDescriptor* field); const char* BoxedPrimitiveTypeName(JavaType type); string DefaultValue(const FieldDescriptor* field); +bool IsDefaultValueJavaDefault(const FieldDescriptor* field); // Does this message class keep track of unknown fields? inline bool HasUnknownFields(const Descriptor* descriptor) { @@ -132,6 +136,11 @@ inline bool HasGeneratedMethods(const Descriptor* descriptor) { FileOptions::CODE_SIZE; } +// Does this message have specialized equals() and hashCode() methods? +inline bool HasEqualsAndHashCode(const Descriptor* descriptor) { + return descriptor->file()->options().java_generate_equals_and_hash(); +} + // Does this message class have descriptor and reflection methods? inline bool HasDescriptorMethods(const Descriptor* descriptor) { return descriptor->file()->options().optimize_for() != @@ -146,6 +155,12 @@ inline bool HasDescriptorMethods(const FileDescriptor* descriptor) { FileOptions::LITE_RUNTIME; } +inline bool HasNestedBuilders(const Descriptor* descriptor) { + // The proto-lite version doesn't support nested builders. + return descriptor->file()->options().optimize_for() != + FileOptions::LITE_RUNTIME; +} + // Should we generate generic services for this file? inline bool HasGenericServices(const FileDescriptor *file) { return file->service_count() > 0 && @@ -153,6 +168,43 @@ inline bool HasGenericServices(const FileDescriptor *file) { file->options().java_generic_services(); } + +// Methods for shared bitfields. + +// Gets the name of the shared bitfield for the given index. +string GetBitFieldName(int index); + +// Gets the name of the shared bitfield for the given bit index. +// Effectively, GetBitFieldName(bitIndex / 32) +string GetBitFieldNameForBit(int bitIndex); + +// Generates the java code for the expression that returns the boolean value +// of the bit of the shared bitfields for the given bit index. +// Example: "((bitField1_ & 0x04) == 0x04)" +string GenerateGetBit(int bitIndex); + +// Generates the java code for the expression that sets the bit of the shared +// bitfields for the given bit index. +// Example: "bitField1_ = (bitField1_ | 0x04)" +string GenerateSetBit(int bitIndex); + +// Generates the java code for the expression that clears the bit of the shared +// bitfields for the given bit index. +// Example: "bitField1_ = (bitField1_ & ~0x04)" +string GenerateClearBit(int bitIndex); + +// Does the same as GenerateGetBit but operates on the bit field on a local +// variable. This is used by the builder to copy the value in the builder to +// the message. +// Example: "((from_bitField1_ & 0x04) == 0x04)" +string GenerateGetBitFromLocal(int bitIndex); + +// Does the same as GenerateSetBit but operates on the bit field on a local +// variable. This is used by the builder to copy the value in the builder to +// the message. +// Example: "to_bitField1_ = (to_bitField1_ | 0x04)" +string GenerateSetBitToLocal(int bitIndex); + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc index 8b91193f..47ee84cf 100644 --- a/src/google/protobuf/compiler/java/java_message.cc +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -245,14 +245,54 @@ void MessageGenerator::GenerateStaticVariableInitializers( MessageGenerator(descriptor_->nested_type(i)) .GenerateStaticVariableInitializers(printer); } +} - for (int i = 0; i < descriptor_->extension_count(); i++) { - // TODO(kenton): Reuse ExtensionGenerator objects? - ExtensionGenerator(descriptor_->extension(i)) - .GenerateInitializationCode(printer); +// =================================================================== + +void MessageGenerator::GenerateInterface(io::Printer* printer) { + + if (descriptor_->extension_range_count() > 0) { + if (HasDescriptorMethods(descriptor_)) { + printer->Print( + "public interface $classname$OrBuilder extends\n" + " com.google.protobuf.GeneratedMessage.\n" + " ExtendableMessageOrBuilder<$classname$> {\n", + "classname", descriptor_->name()); + } else { + printer->Print( + "public interface $classname$OrBuilder extends \n" + " com.google.protobuf.GeneratedMessageLite.\n" + " ExtendableMessageOrBuilder<$classname$> {\n", + "classname", descriptor_->name()); + } + } else { + if (HasDescriptorMethods(descriptor_)) { + printer->Print( + "public interface $classname$OrBuilder\n" + " extends com.google.protobuf.MessageOrBuilder {\n", + "classname", descriptor_->name()); + } else { + printer->Print( + "public interface $classname$OrBuilder\n" + " extends com.google.protobuf.MessageLiteOrBuilder {\n", + "classname", descriptor_->name()); + } } + + printer->Indent(); + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print("\n"); + PrintFieldComment(printer, descriptor_->field(i)); + field_generators_.get(descriptor_->field(i)) + .GenerateInterfaceMembers(printer); + } + printer->Outdent(); + + printer->Print("}\n"); } +// =================================================================== + void MessageGenerator::Generate(io::Printer* printer) { bool is_own_file = descriptor_->containing_type() == NULL && @@ -263,14 +303,14 @@ void MessageGenerator::Generate(io::Printer* printer) { printer->Print( "public $static$ final class $classname$ extends\n" " com.google.protobuf.GeneratedMessage.ExtendableMessage<\n" - " $classname$> {\n", + " $classname$> implements $classname$OrBuilder {\n", "static", is_own_file ? "" : "static", "classname", descriptor_->name()); } else { printer->Print( "public $static$ final class $classname$ extends\n" " com.google.protobuf.GeneratedMessageLite.ExtendableMessage<\n" - " $classname$> {\n", + " $classname$> implements $classname$OrBuilder {\n", "static", is_own_file ? "" : "static", "classname", descriptor_->name()); } @@ -278,13 +318,15 @@ void MessageGenerator::Generate(io::Printer* printer) { if (HasDescriptorMethods(descriptor_)) { printer->Print( "public $static$ final class $classname$ extends\n" - " com.google.protobuf.GeneratedMessage {\n", + " com.google.protobuf.GeneratedMessage\n" + " implements $classname$OrBuilder {\n", "static", is_own_file ? "" : "static", "classname", descriptor_->name()); } else { printer->Print( "public $static$ final class $classname$ extends\n" - " com.google.protobuf.GeneratedMessageLite {\n", + " com.google.protobuf.GeneratedMessageLite\n" + " implements $classname$OrBuilder {\n", "static", is_own_file ? "" : "static", "classname", descriptor_->name()); } @@ -292,8 +334,8 @@ void MessageGenerator::Generate(io::Printer* printer) { printer->Indent(); printer->Print( "// Use $classname$.newBuilder() to construct.\n" - "private $classname$() {\n" - " initFields();\n" + "private $classname$(Builder builder) {\n" + " super(builder);\n" "}\n" // Used when constructing the default instance, which cannot be initialized // immediately because it may cyclically refer to other default instances. @@ -310,33 +352,29 @@ void MessageGenerator::Generate(io::Printer* printer) { "\n", "classname", descriptor_->name()); - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public static final com.google.protobuf.Descriptors.Descriptor\n" - " getDescriptor() {\n" - " return $fileclass$.internal_$identifier$_descriptor;\n" - "}\n" - "\n" - "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" - " internalGetFieldAccessorTable() {\n" - " return $fileclass$.internal_$identifier$_fieldAccessorTable;\n" - "}\n" - "\n", - "fileclass", ClassName(descriptor_->file()), - "identifier", UniqueFileScopeIdentifier(descriptor_)); - } + GenerateDescriptorMethods(printer); - // Nested types and extensions + // Nested types for (int i = 0; i < descriptor_->enum_type_count(); i++) { EnumGenerator(descriptor_->enum_type(i)).Generate(printer); } for (int i = 0; i < descriptor_->nested_type_count(); i++) { - MessageGenerator(descriptor_->nested_type(i)).Generate(printer); + MessageGenerator messageGenerator(descriptor_->nested_type(i)); + messageGenerator.GenerateInterface(printer); + messageGenerator.Generate(printer); } - for (int i = 0; i < descriptor_->extension_count(); i++) { - ExtensionGenerator(descriptor_->extension(i)).Generate(printer); + // Integers for bit fields. + int totalBits = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + totalBits += field_generators_.get(descriptor_->field(i)) + .GetNumBitsForMessage(); + } + int totalInts = (totalBits + 31) / 32; + for (int i = 0; i < totalInts; i++) { + printer->Print("private int $bit_field_name$;\n", + "bit_field_name", GetBitFieldName(i)); } // Fields @@ -361,35 +399,42 @@ void MessageGenerator::Generate(io::Printer* printer) { printer->Print("}\n"); if (HasGeneratedMethods(descriptor_)) { - GenerateIsInitialized(printer); + GenerateIsInitialized(printer, MEMOIZE); GenerateMessageSerializationMethods(printer); } + if (HasEqualsAndHashCode(descriptor_)) { + GenerateEqualsAndHashCode(printer); + } + GenerateParseFromMethods(printer); GenerateBuilder(printer); - // Force initialization of outer class. Otherwise, nested extensions may - // not be initialized. Also carefully initialize the default instance in - // such a way that it doesn't conflict with other initialization. + // Carefully initialize the default instance in such a way that it doesn't + // conflict with other initialization. printer->Print( "\n" "static {\n" " defaultInstance = new $classname$(true);\n" - " $file$.internalForceInit();\n" " defaultInstance.initFields();\n" - "}\n", - "file", ClassName(descriptor_->file()), - "classname", descriptor_->name()); - - printer->Print( + "}\n" "\n" "// @@protoc_insertion_point(class_scope:$full_name$)\n", + "classname", descriptor_->name(), "full_name", descriptor_->full_name()); + // Extensions must be declared after the defaultInstance is initialized + // because the defaultInstance is used by the extension to lazily retrieve + // the outer class's FileDescriptor. + for (int i = 0; i < descriptor_->extension_count(); i++) { + ExtensionGenerator(descriptor_->extension(i)).Generate(printer); + } + printer->Outdent(); printer->Print("}\n\n"); } + // =================================================================== void MessageGenerator:: @@ -502,6 +547,13 @@ GenerateMessageSerializationMethods(io::Printer* printer) { " return size;\n" "}\n" "\n"); + + printer->Print( + "@java.lang.Override\n" + "protected Object writeReplace() throws java.io.ObjectStreamException {\n" + " return super.writeReplace();\n" + "}\n" + "\n"); } void MessageGenerator:: @@ -605,42 +657,68 @@ void MessageGenerator::GenerateBuilder(io::Printer* printer) { "\n", "classname", ClassName(descriptor_)); + if (HasNestedBuilders(descriptor_)) { + printer->Print( + "@java.lang.Override\n" + "protected Builder newBuilderForType(\n" + " com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n" + " Builder builder = new Builder(parent);\n" + " return builder;\n" + "}\n"); + } + if (descriptor_->extension_range_count() > 0) { if (HasDescriptorMethods(descriptor_)) { printer->Print( "public static final class Builder extends\n" " com.google.protobuf.GeneratedMessage.ExtendableBuilder<\n" - " $classname$, Builder> {\n", + " $classname$, Builder> implements $classname$OrBuilder {\n", "classname", ClassName(descriptor_)); } else { printer->Print( "public static final class Builder extends\n" " com.google.protobuf.GeneratedMessageLite.ExtendableBuilder<\n" - " $classname$, Builder> {\n", + " $classname$, Builder> implements $classname$OrBuilder {\n", "classname", ClassName(descriptor_)); } } else { if (HasDescriptorMethods(descriptor_)) { printer->Print( "public static final class Builder extends\n" - " com.google.protobuf.GeneratedMessage.Builder<Builder> {\n", + " com.google.protobuf.GeneratedMessage.Builder<Builder>\n" + " implements $classname$OrBuilder {\n", "classname", ClassName(descriptor_)); } else { printer->Print( "public static final class Builder extends\n" " com.google.protobuf.GeneratedMessageLite.Builder<\n" - " $classname$, Builder> {\n", + " $classname$, Builder>\n" + " implements $classname$OrBuilder {\n", "classname", ClassName(descriptor_)); } } printer->Indent(); + GenerateDescriptorMethods(printer); GenerateCommonBuilderMethods(printer); if (HasGeneratedMethods(descriptor_)) { + GenerateIsInitialized(printer, DONT_MEMOIZE); GenerateBuilderParsingMethods(printer); } + // Integers for bit fields. + int totalBits = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + totalBits += field_generators_.get(descriptor_->field(i)) + .GetNumBitsForBuilder(); + } + int totalInts = (totalBits + 31) / 32; + for (int i = 0; i < totalInts; i++) { + printer->Print("private int $bit_field_name$;\n", + "bit_field_name", GetBitFieldName(i)); + } + for (int i = 0; i < descriptor_->field_count(); i++) { printer->Print("\n"); PrintFieldComment(printer, descriptor_->field(i)); @@ -657,36 +735,92 @@ void MessageGenerator::GenerateBuilder(io::Printer* printer) { printer->Print("}\n"); } +void MessageGenerator::GenerateDescriptorMethods(io::Printer* printer) { + if (HasDescriptorMethods(descriptor_)) { + printer->Print( + "public static final com.google.protobuf.Descriptors.Descriptor\n" + " getDescriptor() {\n" + " return $fileclass$.internal_$identifier$_descriptor;\n" + "}\n" + "\n" + "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" + " internalGetFieldAccessorTable() {\n" + " return $fileclass$.internal_$identifier$_fieldAccessorTable;\n" + "}\n" + "\n", + "fileclass", ClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); + } +} + // =================================================================== void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { printer->Print( - "private $classname$ result;\n" - "\n" "// Construct using $classname$.newBuilder()\n" - "private Builder() {}\n" - "\n" - "private static Builder create() {\n" - " Builder builder = new Builder();\n" - " builder.result = new $classname$();\n" - " return builder;\n" + "private Builder() {\n" + " maybeForceBuilderInitialization();\n" "}\n" - "\n" - "protected $classname$ internalGetResult() {\n" - " return result;\n" + "\n", + "classname", ClassName(descriptor_)); + + if (HasDescriptorMethods(descriptor_)) { + printer->Print( + "private Builder(BuilderParent parent) {\n" + " super(parent);\n" + " maybeForceBuilderInitialization();\n" + "}\n", + "classname", ClassName(descriptor_)); + } + + + if (HasNestedBuilders(descriptor_)) { + printer->Print( + "private void maybeForceBuilderInitialization() {\n" + " if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {\n"); + + printer->Indent(); + printer->Indent(); + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateFieldBuilderInitializationCode(printer); + } + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\n" + "}\n"); + } else { + printer->Print( + "private void maybeForceBuilderInitialization() {\n" + "}\n"); + } + + printer->Print( + "private static Builder create() {\n" + " return new Builder();\n" "}\n" "\n" "public Builder clear() {\n" - " if (result == null) {\n" - " throw new IllegalStateException(\n" - " \"Cannot call clear() after build().\");\n" - " }\n" - " result = new $classname$();\n" + " super.clear();\n", + "classname", ClassName(descriptor_)); + + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateBuilderClearCode(printer); + } + + printer->Outdent(); + + printer->Print( " return this;\n" "}\n" "\n" "public Builder clone() {\n" - " return create().mergeFrom(result);\n" + " return create().mergeFrom(buildPartial());\n" "}\n" "\n", "classname", ClassName(descriptor_)); @@ -703,49 +837,78 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { "public $classname$ getDefaultInstanceForType() {\n" " return $classname$.getDefaultInstance();\n" "}\n" - "\n" - "public boolean isInitialized() {\n" - " return result.isInitialized();\n" - "}\n", + "\n", "classname", ClassName(descriptor_)); // ----------------------------------------------------------------- printer->Print( "public $classname$ build() {\n" - // If result == null, we'll throw an appropriate exception later. - " if (result != null && !isInitialized()) {\n" + " $classname$ result = buildPartial();\n" + " if (!result.isInitialized()) {\n" " throw newUninitializedMessageException(result);\n" " }\n" - " return buildPartial();\n" + " return result;\n" "}\n" "\n" "private $classname$ buildParsed()\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " if (!isInitialized()) {\n" + " $classname$ result = buildPartial();\n" + " if (!result.isInitialized()) {\n" " throw newUninitializedMessageException(\n" " result).asInvalidProtocolBufferException();\n" " }\n" - " return buildPartial();\n" + " return result;\n" "}\n" "\n" "public $classname$ buildPartial() {\n" - " if (result == null) {\n" - " throw new IllegalStateException(\n" - " \"build() has already been called on this Builder.\");\n" - " }\n", + " $classname$ result = new $classname$(this);\n", "classname", ClassName(descriptor_)); + printer->Indent(); + // Local vars for from and to bit fields to avoid accessing the builder and + // message over and over for these fields. Seems to provide a slight + // perforamance improvement in micro benchmark and this is also what proto1 + // code does. + int totalBuilderBits = 0; + int totalMessageBits = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldGenerator& field = field_generators_.get(descriptor_->field(i)); + totalBuilderBits += field.GetNumBitsForBuilder(); + totalMessageBits += field.GetNumBitsForMessage(); + } + int totalBuilderInts = (totalBuilderBits + 31) / 32; + int totalMessageInts = (totalMessageBits + 31) / 32; + for (int i = 0; i < totalBuilderInts; i++) { + printer->Print("int from_$bit_field_name$ = $bit_field_name$;\n", + "bit_field_name", GetBitFieldName(i)); + } + for (int i = 0; i < totalMessageInts; i++) { + printer->Print("int to_$bit_field_name$ = 0;\n", + "bit_field_name", GetBitFieldName(i)); + } + + // Output generation code for each field. for (int i = 0; i < descriptor_->field_count(); i++) { field_generators_.get(descriptor_->field(i)).GenerateBuildingCode(printer); } + // Copy the bit field results to the generated message + for (int i = 0; i < totalMessageInts; i++) { + printer->Print("result.$bit_field_name$ = to_$bit_field_name$;\n", + "bit_field_name", GetBitFieldName(i)); + } + printer->Outdent(); + + if (HasDescriptorMethods(descriptor_)) { + printer->Print( + " onBuilt();\n"); + } + printer->Print( - " $classname$ returnMe = result;\n" - " result = null;\n" - " return returnMe;\n" + " return result;\n" "}\n" "\n", "classname", ClassName(descriptor_)); @@ -834,25 +997,31 @@ void MessageGenerator::GenerateBuilderParsingMethods(io::Printer* printer) { printer->Print( "case 0:\n" // zero signals EOF / limit reached " this.setUnknownFields(unknownFields.build());\n" + " $on_changed$\n" " return this;\n" "default: {\n" " if (!parseUnknownField(input, unknownFields,\n" " extensionRegistry, tag)) {\n" " this.setUnknownFields(unknownFields.build());\n" + " $on_changed$\n" " return this;\n" // it's an endgroup tag " }\n" " break;\n" - "}\n"); + "}\n", + "on_changed", HasDescriptorMethods(descriptor_) ? "onChanged();" : ""); } else { printer->Print( "case 0:\n" // zero signals EOF / limit reached + " $on_changed$\n" " return this;\n" "default: {\n" " if (!parseUnknownField(input, extensionRegistry, tag)) {\n" + " $on_changed$\n" " return this;\n" // it's an endgroup tag " }\n" " break;\n" - "}\n"); + "}\n", + "on_changed", HasDescriptorMethods(descriptor_) ? "onChanged();" : ""); } for (int i = 0; i < descriptor_->field_count(); i++) { @@ -898,16 +1067,33 @@ void MessageGenerator::GenerateBuilderParsingMethods(io::Printer* printer) { " }\n" // switch (tag) " }\n" // while (true) "}\n" + "\n"); } // =================================================================== -void MessageGenerator::GenerateIsInitialized(io::Printer* printer) { +void MessageGenerator::GenerateIsInitialized( + io::Printer* printer, UseMemoization useMemoization) { + bool memoization = useMemoization == MEMOIZE; + if (memoization) { + // Memoizes whether the protocol buffer is fully initialized (has all + // required fields). -1 means not yet computed. 0 means false and 1 means + // true. + printer->Print( + "private byte memoizedIsInitialized = -1;\n"); + } printer->Print( "public final boolean isInitialized() {\n"); printer->Indent(); + if (memoization) { + printer->Print( + "byte isInitialized = memoizedIsInitialized;\n" + "if (isInitialized != -1) return isInitialized == 1;\n" + "\n"); + } + // Check that all required fields in this message are set. // TODO(kenton): We can optimize this when we switch to putting all the // "has" fields into a single bitfield. @@ -916,8 +1102,12 @@ void MessageGenerator::GenerateIsInitialized(io::Printer* printer) { if (field->is_required()) { printer->Print( - "if (!has$name$) return false;\n", - "name", UnderscoresToCapitalizedCamelCase(field)); + "if (!has$name$()) {\n" + " $memoize$\n" + " return false;\n" + "}\n", + "name", UnderscoresToCapitalizedCamelCase(field), + "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); } } @@ -929,25 +1119,37 @@ void MessageGenerator::GenerateIsInitialized(io::Printer* printer) { switch (field->label()) { case FieldDescriptor::LABEL_REQUIRED: printer->Print( - "if (!get$name$().isInitialized()) return false;\n", + "if (!get$name$().isInitialized()) {\n" + " $memoize$\n" + " return false;\n" + "}\n", "type", ClassName(field->message_type()), - "name", UnderscoresToCapitalizedCamelCase(field)); + "name", UnderscoresToCapitalizedCamelCase(field), + "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); break; case FieldDescriptor::LABEL_OPTIONAL: printer->Print( "if (has$name$()) {\n" - " if (!get$name$().isInitialized()) return false;\n" + " if (!get$name$().isInitialized()) {\n" + " $memoize$\n" + " return false;\n" + " }\n" "}\n", "type", ClassName(field->message_type()), - "name", UnderscoresToCapitalizedCamelCase(field)); + "name", UnderscoresToCapitalizedCamelCase(field), + "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); break; case FieldDescriptor::LABEL_REPEATED: printer->Print( - "for ($type$ element : get$name$List()) {\n" - " if (!element.isInitialized()) return false;\n" + "for (int i = 0; i < get$name$Count(); i++) {\n" + " if (!get$name$(i).isInitialized()) {\n" + " $memoize$\n" + " return false;\n" + " }\n" "}\n", "type", ClassName(field->message_type()), - "name", UnderscoresToCapitalizedCamelCase(field)); + "name", UnderscoresToCapitalizedCamelCase(field), + "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); break; } } @@ -955,10 +1157,20 @@ void MessageGenerator::GenerateIsInitialized(io::Printer* printer) { if (descriptor_->extension_range_count() > 0) { printer->Print( - "if (!extensionsAreInitialized()) return false;\n"); + "if (!extensionsAreInitialized()) {\n" + " $memoize$\n" + " return false;\n" + "}\n", + "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); } printer->Outdent(); + + if (memoization) { + printer->Print( + " memoizedIsInitialized = 1;\n"); + } + printer->Print( " return true;\n" "}\n" @@ -967,6 +1179,94 @@ void MessageGenerator::GenerateIsInitialized(io::Printer* printer) { // =================================================================== +void MessageGenerator::GenerateEqualsAndHashCode(io::Printer* printer) { + printer->Print( + "@java.lang.Override\n" + "public boolean equals(final Object obj) {\n"); + printer->Indent(); + printer->Print( + "if (obj == this) {\n" + " return true;\n" + "}\n" + "if (!(obj instanceof $classname$)) {\n" + " return super.equals(obj);\n" + "}\n" + "$classname$ other = ($classname$) obj;\n" + "\n", + "classname", ClassName(descriptor_)); + + printer->Print("boolean result = true;\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (!field->is_repeated()) { + printer->Print( + "result = result && (has$name$() == other.has$name$());\n" + "if (has$name$()) {\n", + "name", UnderscoresToCapitalizedCamelCase(field)); + printer->Indent(); + } + field_generators_.get(field).GenerateEqualsCode(printer); + if (!field->is_repeated()) { + printer->Outdent(); + printer->Print( + "}\n"); + } + } + if (HasDescriptorMethods(descriptor_)) { + printer->Print( + "result = result &&\n" + " getUnknownFields().equals(other.getUnknownFields());\n"); + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "result = result &&\n" + " getExtensionFields().equals(other.getExtensionFields());\n"); + } + } + printer->Print( + "return result;\n"); + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + + printer->Print( + "@java.lang.Override\n" + "public int hashCode() {\n"); + printer->Indent(); + printer->Print( + "int hash = 41;\n" + "hash = (19 * hash) + getDescriptorForType().hashCode();\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (!field->is_repeated()) { + printer->Print( + "if (has$name$()) {\n", + "name", UnderscoresToCapitalizedCamelCase(field)); + printer->Indent(); + } + field_generators_.get(field).GenerateHashCode(printer); + if (!field->is_repeated()) { + printer->Outdent(); + printer->Print("}\n"); + } + } + if (HasDescriptorMethods(descriptor_)) { + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "hash = hashFields(hash, getExtensionFields());\n"); + } + } + printer->Print( + "hash = (29 * hash) + getUnknownFields().hashCode();\n" + "return hash;\n"); + printer->Outdent(); + printer->Print( + "}\n" + "\n"); +} + +// =================================================================== + void MessageGenerator::GenerateExtensionRegistrationCode(io::Printer* printer) { for (int i = 0; i < descriptor_->extension_count(); i++) { ExtensionGenerator(descriptor_->extension(i)) diff --git a/src/google/protobuf/compiler/java/java_message.h b/src/google/protobuf/compiler/java/java_message.h index 50ffae08..4c6fbbe5 100644 --- a/src/google/protobuf/compiler/java/java_message.h +++ b/src/google/protobuf/compiler/java/java_message.h @@ -67,11 +67,19 @@ class MessageGenerator { // Generate the class itself. void Generate(io::Printer* printer); + // Generates the base interface that both the class and its builder implement + void GenerateInterface(io::Printer* printer); + // Generate code to register all contained extensions with an // ExtensionRegistry. void GenerateExtensionRegistrationCode(io::Printer* printer); private: + enum UseMemoization { + MEMOIZE, + DONT_MEMOIZE + }; + void GenerateMessageSerializationMethods(io::Printer* printer); void GenerateParseFromMethods(io::Printer* printer); void GenerateSerializeOneField(io::Printer* printer, @@ -81,8 +89,11 @@ class MessageGenerator { void GenerateBuilder(io::Printer* printer); void GenerateCommonBuilderMethods(io::Printer* printer); + void GenerateDescriptorMethods(io::Printer* printer); void GenerateBuilderParsingMethods(io::Printer* printer); - void GenerateIsInitialized(io::Printer* printer); + void GenerateIsInitialized(io::Printer* printer, + UseMemoization useMemoization); + void GenerateEqualsAndHashCode(io::Printer* printer); const Descriptor* descriptor_; FieldGeneratorMap field_generators_; diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc index 71edc024..251945af 100644 --- a/src/google/protobuf/compiler/java/java_message_field.cc +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -51,16 +51,43 @@ namespace { // TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of // repeat code between this and the other field types. void SetMessageVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, map<string, string>* variables) { (*variables)["name"] = UnderscoresToCamelCase(descriptor); (*variables)["capitalized_name"] = UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["constant_name"] = FieldConstantName(descriptor); (*variables)["number"] = SimpleItoa(descriptor->number()); (*variables)["type"] = ClassName(descriptor->message_type()); (*variables)["group_or_message"] = (GetType(descriptor) == FieldDescriptor::TYPE_GROUP) ? "Group" : "Message"; + // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported + // by the proto compiler + (*variables)["deprecation"] = descriptor->options().deprecated() + ? "@java.lang.Deprecated " : ""; + (*variables)["on_changed"] = + HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + + // For singular messages and builders, one bit is used for the hasField bit. + (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); + + (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); + (*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex); + (*variables)["clear_has_field_bit_builder"] = + GenerateClearBit(builderBitIndex); + + // For repated builders, one bit is used for whether the array is immutable. + (*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex); + (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); + (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); + + (*variables)["get_has_field_bit_from_local"] = + GenerateGetBitFromLocal(builderBitIndex); + (*variables)["set_has_field_bit_to_local"] = + GenerateSetBitToLocal(messageBitIndex); } } // namespace @@ -68,68 +95,244 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // =================================================================== MessageFieldGenerator:: -MessageFieldGenerator(const FieldDescriptor* descriptor) - : descriptor_(descriptor) { - SetMessageVariables(descriptor, &variables_); +MessageFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex) { + SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, + &variables_); } MessageFieldGenerator::~MessageFieldGenerator() {} +int MessageFieldGenerator::GetNumBitsForMessage() const { + return 1; +} + +int MessageFieldGenerator::GetNumBitsForBuilder() const { + return 1; +} + +void MessageFieldGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + // TODO(jonp): In the future, consider having a method specific to the + // interface so that builders can choose dynamically to either return a + // message or a nested builder, so that asking for the interface doesn't + // cause a message to ever be built. + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n" + "$deprecation$$type$ get$capitalized_name$();\n"); + + if (HasNestedBuilders(descriptor_->containing_type())) { + printer->Print(variables_, + "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder();\n"); + } +} + void MessageFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private boolean has$capitalized_name$;\n" "private $type$ $name$_;\n" - "public boolean has$capitalized_name$() { return has$capitalized_name$; }\n" - "public $type$ get$capitalized_name$() { return $name$_; }\n"); + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n" + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return $name$_;\n" + "}\n"); + + if (HasNestedBuilders(descriptor_->containing_type())) { + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + " return $name$_;\n" + "}\n"); + } +} + +void MessageFieldGenerator::PrintNestedBuilderCondition( + io::Printer* printer, + const char* regular_case, + const char* nested_builder_case) const { + if (HasNestedBuilders(descriptor_->containing_type())) { + printer->Print(variables_, "if ($name$Builder_ == null) {\n"); + printer->Indent(); + printer->Print(variables_, regular_case); + printer->Outdent(); + printer->Print("} else {\n"); + printer->Indent(); + printer->Print(variables_, nested_builder_case); + printer->Outdent(); + printer->Print("}\n"); + } else { + printer->Print(variables_, regular_case); + } +} + +void MessageFieldGenerator::PrintNestedBuilderFunction( + io::Printer* printer, + const char* method_prototype, + const char* regular_case, + const char* nested_builder_case, + const char* trailing_code) const { + printer->Print(variables_, method_prototype); + printer->Print(" {\n"); + printer->Indent(); + PrintNestedBuilderCondition(printer, regular_case, nested_builder_case); + if (trailing_code != NULL) { + printer->Print(variables_, trailing_code); + } + printer->Outdent(); + printer->Print("}\n"); } void MessageFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { + // When using nested-builders, the code initially works just like the + // non-nested builder case. It only creates a nested builder lazily on + // demand and then forever delegates to it after creation. + printer->Print(variables_, - "public boolean has$capitalized_name$() {\n" - " return result.has$capitalized_name$();\n" - "}\n" - "public $type$ get$capitalized_name$() {\n" - " return result.get$capitalized_name$();\n" - "}\n" - "public Builder set$capitalized_name$($type$ value) {\n" - " if (value == null) {\n" - " throw new NullPointerException();\n" - " }\n" - " result.has$capitalized_name$ = true;\n" - " result.$name$_ = value;\n" - " return this;\n" - "}\n" - "public Builder set$capitalized_name$($type$.Builder builderForValue) {\n" - " result.has$capitalized_name$ = true;\n" - " result.$name$_ = builderForValue.build();\n" - " return this;\n" + // Used when the builder is null. + "private $type$ $name$_ = $type$.getDefaultInstance();\n"); + + if (HasNestedBuilders(descriptor_->containing_type())) { + printer->Print(variables_, + // If this builder is non-null, it is used and the other fields are + // ignored. + "private com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;" + "\n"); + } + + // The comments above the methods below are based on a hypothetical + // field of type "Field" called "Field". + + // boolean hasField() + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_builder$;\n" + "}\n"); + + // Field getField() + PrintNestedBuilderFunction(printer, + "$deprecation$public $type$ get$capitalized_name$()", + + "return $name$_;\n", + + "return $name$Builder_.getMessage();\n", + + NULL); + + // Field.Builder setField(Field value) + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder set$capitalized_name$($type$ value)", + + "if (value == null) {\n" + " throw new NullPointerException();\n" "}\n" - "public Builder merge$capitalized_name$($type$ value) {\n" - " if (result.has$capitalized_name$() &&\n" - " result.$name$_ != $type$.getDefaultInstance()) {\n" - " result.$name$_ =\n" - " $type$.newBuilder(result.$name$_).mergeFrom(value).buildPartial();\n" - " } else {\n" - " result.$name$_ = value;\n" - " }\n" - " result.has$capitalized_name$ = true;\n" - " return this;\n" + "$name$_ = value;\n" + "$on_changed$\n", + + "$name$Builder_.setMessage(value);\n", + + "$set_has_field_bit_builder$;\n" + "return this;\n"); + + // Field.Builder setField(Field.Builder builderForValue) + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder set$capitalized_name$(\n" + " $type$.Builder builderForValue)", + + "$name$_ = builderForValue.build();\n" + "$on_changed$\n", + + "$name$Builder_.setMessage(builderForValue.build());\n", + + "$set_has_field_bit_builder$;\n" + "return this;\n"); + + // Field.Builder mergeField(Field value) + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder merge$capitalized_name$($type$ value)", + + "if ($get_has_field_bit_builder$ &&\n" + " $name$_ != $type$.getDefaultInstance()) {\n" + " $name$_ =\n" + " $type$.newBuilder($name$_).mergeFrom(value).buildPartial();\n" + "} else {\n" + " $name$_ = value;\n" "}\n" - "public Builder clear$capitalized_name$() {\n" - " result.has$capitalized_name$ = false;\n" - " result.$name$_ = $type$.getDefaultInstance();\n" - " return this;\n" - "}\n"); + "$on_changed$\n", + + "$name$Builder_.mergeFrom(value);\n", + + "$set_has_field_bit_builder$;\n" + "return this;\n"); + + // Field.Builder clearField() + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder clear$capitalized_name$()", + + "$name$_ = $type$.getDefaultInstance();\n" + "$on_changed$\n", + + "$name$Builder_.clear();\n", + + "$clear_has_field_bit_builder$;\n" + "return this;\n"); + + if (HasNestedBuilders(descriptor_->containing_type())) { + printer->Print(variables_, + "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n" + " $set_has_field_bit_builder$;\n" + " $on_changed$\n" + " return get$capitalized_name$FieldBuilder().getBuilder();\n" + "}\n" + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + " if ($name$Builder_ != null) {\n" + " return $name$Builder_.getMessageOrBuilder();\n" + " } else {\n" + " return $name$_;\n" + " }\n" + "}\n" + "private com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> \n" + " get$capitalized_name$FieldBuilder() {\n" + " if ($name$Builder_ == null) {\n" + " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder>(\n" + " $name$_,\n" + " getParentForChildren(),\n" + " isClean());\n" + " $name$_ = null;\n" + " }\n" + " return $name$Builder_;\n" + "}\n"); + } } void MessageFieldGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + printer->Print(variables_, + "get$capitalized_name$FieldBuilder();\n"); +} + + +void MessageFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = $type$.getDefaultInstance();\n"); } void MessageFieldGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + PrintNestedBuilderCondition(printer, + "$name$_ = $type$.getDefaultInstance();\n", + + "$name$Builder_.clear();\n"); + printer->Print(variables_, "$clear_has_field_bit_builder$;\n"); +} + +void MessageFieldGenerator:: GenerateMergingCode(io::Printer* printer) const { printer->Print(variables_, "if (other.has$capitalized_name$()) {\n" @@ -139,7 +342,16 @@ GenerateMergingCode(io::Printer* printer) const { void MessageFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { - // Nothing to do for singular fields. + + printer->Print(variables_, + "if ($get_has_field_bit_from_local$) {\n" + " $set_has_field_bit_to_local$;\n" + "}\n"); + + PrintNestedBuilderCondition(printer, + "result.$name$_ = $name$_;\n", + + "result.$name$_ = $name$Builder_.build();\n"); } void MessageFieldGenerator:: @@ -165,20 +377,34 @@ GenerateParsingCode(io::Printer* printer) const { void MessageFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, - "if (has$capitalized_name$()) {\n" - " output.write$group_or_message$($number$, get$capitalized_name$());\n" + "if ($get_has_field_bit_message$) {\n" + " output.write$group_or_message$($number$, $name$_);\n" "}\n"); } void MessageFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, - "if (has$capitalized_name$()) {\n" + "if ($get_has_field_bit_message$) {\n" " size += com.google.protobuf.CodedOutputStream\n" - " .compute$group_or_message$Size($number$, get$capitalized_name$());\n" + " .compute$group_or_message$Size($number$, $name$_);\n" "}\n"); } +void MessageFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$()\n" + " .equals(other.get$capitalized_name$());\n"); +} + +void MessageFieldGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n" + "hash = (53 * hash) + get$capitalized_name$().hashCode();\n"); +} + string MessageFieldGenerator::GetBoxedType() const { return ClassName(descriptor_->message_type()); } @@ -186,109 +412,416 @@ string MessageFieldGenerator::GetBoxedType() const { // =================================================================== RepeatedMessageFieldGenerator:: -RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor) - : descriptor_(descriptor) { - SetMessageVariables(descriptor, &variables_); +RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex) { + SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, + &variables_); } RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {} +int RepeatedMessageFieldGenerator::GetNumBitsForMessage() const { + return 0; +} + +int RepeatedMessageFieldGenerator::GetNumBitsForBuilder() const { + return 1; +} + +void RepeatedMessageFieldGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + // TODO(jonp): In the future, consider having methods specific to the + // interface so that builders can choose dynamically to either return a + // message or a nested builder, so that asking for the interface doesn't + // cause a message to ever be built. + printer->Print(variables_, + "$deprecation$java.util.List<$type$> \n" + " get$capitalized_name$List();\n" + "$deprecation$$type$ get$capitalized_name$(int index);\n" + "$deprecation$int get$capitalized_name$Count();\n"); + if (HasNestedBuilders(descriptor_->containing_type())) { + printer->Print(variables_, + "$deprecation$java.util.List<? extends $type$OrBuilder> \n" + " get$capitalized_name$OrBuilderList();\n" + "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder(\n" + " int index);\n"); + } +} + void RepeatedMessageFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private java.util.List<$type$> $name$_ =\n" - " java.util.Collections.emptyList();\n" - "public java.util.List<$type$> get$capitalized_name$List() {\n" + "private java.util.List<$type$> $name$_;\n" + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" " return $name$_;\n" // note: unmodifiable list "}\n" - "public int get$capitalized_name$Count() { return $name$_.size(); }\n" - "public $type$ get$capitalized_name$(int index) {\n" + "$deprecation$public java.util.List<? extends $type$OrBuilder> \n" + " get$capitalized_name$OrBuilderList() {\n" + " return $name$_;\n" + "}\n" + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n" + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n" + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n" + " int index) {\n" " return $name$_.get(index);\n" "}\n"); + +} + +void RepeatedMessageFieldGenerator::PrintNestedBuilderCondition( + io::Printer* printer, + const char* regular_case, + const char* nested_builder_case) const { + if (HasNestedBuilders(descriptor_->containing_type())) { + printer->Print(variables_, "if ($name$Builder_ == null) {\n"); + printer->Indent(); + printer->Print(variables_, regular_case); + printer->Outdent(); + printer->Print("} else {\n"); + printer->Indent(); + printer->Print(variables_, nested_builder_case); + printer->Outdent(); + printer->Print("}\n"); + } else { + printer->Print(variables_, regular_case); + } +} + +void RepeatedMessageFieldGenerator::PrintNestedBuilderFunction( + io::Printer* printer, + const char* method_prototype, + const char* regular_case, + const char* nested_builder_case, + const char* trailing_code) const { + printer->Print(variables_, method_prototype); + printer->Print(" {\n"); + printer->Indent(); + PrintNestedBuilderCondition(printer, regular_case, nested_builder_case); + if (trailing_code != NULL) { + printer->Print(variables_, trailing_code); + } + printer->Outdent(); + printer->Print("}\n"); } void RepeatedMessageFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { + // When using nested-builders, the code initially works just like the + // non-nested builder case. It only creates a nested builder lazily on + // demand and then forever delegates to it after creation. + printer->Print(variables_, - // Note: We return an unmodifiable list because otherwise the caller - // could hold on to the returned list and modify it after the message - // has been built, thus mutating the message which is supposed to be - // immutable. - "public java.util.List<$type$> get$capitalized_name$List() {\n" - " return java.util.Collections.unmodifiableList(result.$name$_);\n" - "}\n" - "public int get$capitalized_name$Count() {\n" - " return result.get$capitalized_name$Count();\n" - "}\n" - "public $type$ get$capitalized_name$(int index) {\n" - " return result.get$capitalized_name$(index);\n" - "}\n" - "public Builder set$capitalized_name$(int index, $type$ value) {\n" - " if (value == null) {\n" - " throw new NullPointerException();\n" - " }\n" - " result.$name$_.set(index, value);\n" - " return this;\n" - "}\n" - "public Builder set$capitalized_name$(int index, " - "$type$.Builder builderForValue) {\n" - " result.$name$_.set(index, builderForValue.build());\n" - " return this;\n" + // Used when the builder is null. + // One field is the list and the other field keeps track of whether the + // list is immutable. If it's immutable, the invariant is that it must + // either an instance of Collections.emptyList() or it's an ArrayList + // wrapped in a Collections.unmodifiableList() wrapper and nobody else has + // a refererence to the underlying ArrayList. This invariant allows us to + // share instances of lists between protocol buffers avoiding expensive + // memory allocations. Note, immutable is a strong guarantee here -- not + // just that the list cannot be modified via the reference but that the + // list can never be modified. + "private java.util.List<$type$> $name$_ =\n" + " java.util.Collections.emptyList();\n" + + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$get_mutable_bit_builder$) {\n" + " $name$_ = new java.util.ArrayList<$type$>($name$_);\n" + " $set_mutable_bit_builder$;\n" + " }\n" "}\n" - "public Builder add$capitalized_name$($type$ value) {\n" - " if (value == null) {\n" - " throw new NullPointerException();\n" - " }\n" - " if (result.$name$_.isEmpty()) {\n" - " result.$name$_ = new java.util.ArrayList<$type$>();\n" - " }\n" - " result.$name$_.add(value);\n" - " return this;\n" + "\n"); + + if (HasNestedBuilders(descriptor_->containing_type())) { + printer->Print(variables_, + // If this builder is non-null, it is used and the other fields are + // ignored. + "private com.google.protobuf.RepeatedFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;\n" + "\n"); + } + + // The comments above the methods below are based on a hypothetical + // repeated field of type "Field" called "RepeatedField". + + // List<Field> getRepeatedFieldList() + PrintNestedBuilderFunction(printer, + "$deprecation$public java.util.List<$type$> get$capitalized_name$List()", + + "return java.util.Collections.unmodifiableList($name$_);\n", + "return $name$Builder_.getMessageList();\n", + + NULL); + + // int getRepeatedFieldCount() + PrintNestedBuilderFunction(printer, + "$deprecation$public int get$capitalized_name$Count()", + + "return $name$_.size();\n", + "return $name$Builder_.getCount();\n", + + NULL); + + // Field getRepeatedField(int index) + PrintNestedBuilderFunction(printer, + "$deprecation$public $type$ get$capitalized_name$(int index)", + + "return $name$_.get(index);\n", + + "return $name$Builder_.getMessage(index);\n", + + NULL); + + // Builder setRepeatedField(int index, Field value) + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$ value)", + "if (value == null) {\n" + " throw new NullPointerException();\n" "}\n" - "public Builder add$capitalized_name$($type$.Builder builderForValue) {\n" - " if (result.$name$_.isEmpty()) {\n" - " result.$name$_ = new java.util.ArrayList<$type$>();\n" - " }\n" - " result.$name$_.add(builderForValue.build());\n" - " return this;\n" + "ensure$capitalized_name$IsMutable();\n" + "$name$_.set(index, value);\n" + "$on_changed$\n", + "$name$Builder_.setMessage(index, value);\n", + "return this;\n"); + + // Builder setRepeatedField(int index, Field.Builder builderForValue) + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$.Builder builderForValue)", + + "ensure$capitalized_name$IsMutable();\n" + "$name$_.set(index, builderForValue.build());\n" + "$on_changed$\n", + + "$name$Builder_.setMessage(index, builderForValue.build());\n", + + "return this;\n"); + + // Builder addRepeatedField(Field value) + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder add$capitalized_name$($type$ value)", + + "if (value == null) {\n" + " throw new NullPointerException();\n" "}\n" - "public Builder addAll$capitalized_name$(\n" - " java.lang.Iterable<? extends $type$> values) {\n" - " if (result.$name$_.isEmpty()) {\n" - " result.$name$_ = new java.util.ArrayList<$type$>();\n" - " }\n" - " super.addAll(values, result.$name$_);\n" - " return this;\n" + "ensure$capitalized_name$IsMutable();\n" + "$name$_.add(value);\n" + + "$on_changed$\n", + + "$name$Builder_.addMessage(value);\n", + + "return this;\n"); + + // Builder addRepeatedField(int index, Field value) + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder add$capitalized_name$(\n" + " int index, $type$ value)", + + "if (value == null) {\n" + " throw new NullPointerException();\n" "}\n" - "public Builder clear$capitalized_name$() {\n" - " result.$name$_ = java.util.Collections.emptyList();\n" - " return this;\n" - "}\n"); + "ensure$capitalized_name$IsMutable();\n" + "$name$_.add(index, value);\n" + "$on_changed$\n", + + "$name$Builder_.addMessage(index, value);\n", + + "return this;\n"); + + // Builder addRepeatedField(Field.Builder builderForValue) + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder add$capitalized_name$(\n" + " $type$.Builder builderForValue)", + + "ensure$capitalized_name$IsMutable();\n" + "$name$_.add(builderForValue.build());\n" + "$on_changed$\n", + + "$name$Builder_.addMessage(builderForValue.build());\n", + + "return this;\n"); + + // Builder addRepeatedField(int index, Field.Builder builderForValue) + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder add$capitalized_name$(\n" + " int index, $type$.Builder builderForValue)", + + "ensure$capitalized_name$IsMutable();\n" + "$name$_.add(index, builderForValue.build());\n" + "$on_changed$\n", + + "$name$Builder_.addMessage(index, builderForValue.build());\n", + + "return this;\n"); + + // Builder addAllRepeatedField(Iterable<Field> values) + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable<? extends $type$> values)", + + "ensure$capitalized_name$IsMutable();\n" + "super.addAll(values, $name$_);\n" + "$on_changed$\n", + + "$name$Builder_.addAllMessages(values);\n", + + "return this;\n"); + + // Builder clearAllRepeatedField() + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder clear$capitalized_name$()", + + "$name$_ = java.util.Collections.emptyList();\n" + "$clear_mutable_bit_builder$;\n" + "$on_changed$\n", + + "$name$Builder_.clear();\n", + + "return this;\n"); + + // Builder removeRepeatedField(int index) + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder remove$capitalized_name$(int index)", + + "ensure$capitalized_name$IsMutable();\n" + "$name$_.remove(index);\n" + "$on_changed$\n", + + "$name$Builder_.remove(index);\n", + + "return this;\n"); + + if (HasNestedBuilders(descriptor_->containing_type())) { + printer->Print(variables_, + "$deprecation$public $type$.Builder get$capitalized_name$Builder(\n" + " int index) {\n" + " return get$capitalized_name$FieldBuilder().getBuilder(index);\n" + "}\n" + + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n" + " int index) {\n" + " if ($name$Builder_ == null) {\n" + " return $name$_.get(index);" + " } else {\n" + " return $name$Builder_.getMessageOrBuilder(index);\n" + " }\n" + "}\n" + + "$deprecation$public java.util.List<? extends $type$OrBuilder> \n" + " get$capitalized_name$OrBuilderList() {\n" + " if ($name$Builder_ != null) {\n" + " return $name$Builder_.getMessageOrBuilderList();\n" + " } else {\n" + " return java.util.Collections.unmodifiableList($name$_);\n" + " }\n" + "}\n" + + "$deprecation$public $type$.Builder add$capitalized_name$Builder() {\n" + " return get$capitalized_name$FieldBuilder().addBuilder(\n" + " $type$.getDefaultInstance());\n" + "}\n" + "$deprecation$public $type$.Builder add$capitalized_name$Builder(\n" + " int index) {\n" + " return get$capitalized_name$FieldBuilder().addBuilder(\n" + " index, $type$.getDefaultInstance());\n" + "}\n" + "$deprecation$public java.util.List<$type$.Builder> \n" + " get$capitalized_name$BuilderList() {\n" + " return get$capitalized_name$FieldBuilder().getBuilderList();\n" + "}\n" + "private com.google.protobuf.RepeatedFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> \n" + " get$capitalized_name$FieldBuilder() {\n" + " if ($name$Builder_ == null) {\n" + " $name$Builder_ = new com.google.protobuf.RepeatedFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder>(\n" + " $name$_,\n" + " $get_mutable_bit_builder$,\n" + " getParentForChildren(),\n" + " isClean());\n" + " $name$_ = null;\n" + " }\n" + " return $name$Builder_;\n" + "}\n"); + } +} + +void RepeatedMessageFieldGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + printer->Print(variables_, + "get$capitalized_name$FieldBuilder();\n"); } void RepeatedMessageFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { - // Initialized inline. + printer->Print(variables_, "$name$_ = java.util.Collections.emptyList();\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + PrintNestedBuilderCondition(printer, + "$name$_ = java.util.Collections.emptyList();\n" + "$clear_mutable_bit_builder$;\n", + + "$name$Builder_.clear();\n"); } void RepeatedMessageFieldGenerator:: GenerateMergingCode(io::Printer* printer) const { - printer->Print(variables_, + // The code below does two optimizations (non-nested builder case): + // 1. If the other list is empty, there's nothing to do. This ensures we + // don't allocate a new array if we already have an immutable one. + // 2. If the other list is non-empty and our current list is empty, we can + // reuse the other list which is guaranteed to be immutable. + PrintNestedBuilderCondition(printer, "if (!other.$name$_.isEmpty()) {\n" - " if (result.$name$_.isEmpty()) {\n" - " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " if ($name$_.isEmpty()) {\n" + " $name$_ = other.$name$_;\n" + " $clear_mutable_bit_builder$;\n" + " } else {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.addAll(other.$name$_);\n" + " }\n" + " $on_changed$\n" + "}\n", + + "if (!other.$name$_.isEmpty()) {\n" + " if ($name$Builder_.isEmpty()) {\n" + " $name$Builder_.dispose();\n" + " $name$Builder_ = null;\n" + " $name$_ = other.$name$_;\n" + " $clear_mutable_bit_builder$;\n" + " $name$Builder_ = \n" + " com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?\n" + " get$capitalized_name$FieldBuilder() : null;\n" + " } else {\n" + " $name$Builder_.addAllMessages(other.$name$_);\n" " }\n" - " result.$name$_.addAll(other.$name$_);\n" "}\n"); } void RepeatedMessageFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { - printer->Print(variables_, - "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n" - " result.$name$_ =\n" - " java.util.Collections.unmodifiableList(result.$name$_);\n" - "}\n"); + // The code below (non-nested builder case) ensures that the result has an + // immutable list. If our list is immutable, we can just reuse it. If not, + // we make it immutable. + PrintNestedBuilderCondition(printer, + "if ($get_mutable_bit_builder$) {\n" + " $name$_ = java.util.Collections.unmodifiableList($name$_);\n" + " $clear_mutable_bit_builder$;\n" + "}\n" + "result.$name$_ = $name$_;\n", + + "result.$name$_ = $name$Builder_.build();\n"); } void RepeatedMessageFieldGenerator:: @@ -311,17 +844,33 @@ GenerateParsingCode(io::Printer* printer) const { void RepeatedMessageFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, - "for ($type$ element : get$capitalized_name$List()) {\n" - " output.write$group_or_message$($number$, element);\n" + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.write$group_or_message$($number$, $name$_.get(i));\n" "}\n"); } void RepeatedMessageFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, - "for ($type$ element : get$capitalized_name$List()) {\n" + "for (int i = 0; i < $name$_.size(); i++) {\n" " size += com.google.protobuf.CodedOutputStream\n" - " .compute$group_or_message$Size($number$, element);\n" + " .compute$group_or_message$Size($number$, $name$_.get(i));\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$List()\n" + " .equals(other.get$capitalized_name$List());\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "if (get$capitalized_name$Count() > 0) {\n" + " hash = (37 * hash) + $constant_name$;\n" + " hash = (53 * hash) + get$capitalized_name$List().hashCode();\n" "}\n"); } diff --git a/src/google/protobuf/compiler/java/java_message_field.h b/src/google/protobuf/compiler/java/java_message_field.h index 66bdd884..2efbcd97 100644 --- a/src/google/protobuf/compiler/java/java_message_field.h +++ b/src/google/protobuf/compiler/java/java_message_field.h @@ -46,50 +46,84 @@ namespace java { class MessageFieldGenerator : public FieldGenerator { public: - explicit MessageFieldGenerator(const FieldDescriptor* descriptor); + explicit MessageFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, int builderBitIndex); ~MessageFieldGenerator(); // implements FieldGenerator --------------------------------------- + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; + void GenerateBuilderClearCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; string GetBoxedType() const; private: const FieldDescriptor* descriptor_; map<string, string> variables_; + const int messageBitIndex_; + const int builderBitIndex_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator); + + void PrintNestedBuilderCondition(io::Printer* printer, + const char* regular_case, const char* nested_builder_case) const; + void PrintNestedBuilderFunction(io::Printer* printer, + const char* method_prototype, const char* regular_case, + const char* nested_builder_case, + const char* trailing_code) const; }; class RepeatedMessageFieldGenerator : public FieldGenerator { public: - explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor); + explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, int builderBitIndex); ~RepeatedMessageFieldGenerator(); // implements FieldGenerator --------------------------------------- + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; + void GenerateBuilderClearCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; string GetBoxedType() const; private: const FieldDescriptor* descriptor_; map<string, string> variables_; + const int messageBitIndex_; + const int builderBitIndex_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator); + + void PrintNestedBuilderCondition(io::Printer* printer, + const char* regular_case, const char* nested_builder_case) const; + void PrintNestedBuilderFunction(io::Printer* printer, + const char* method_prototype, const char* regular_case, + const char* nested_builder_case, + const char* trailing_code) const; }; } // namespace java diff --git a/src/google/protobuf/compiler/java/java_plugin_unittest.cc b/src/google/protobuf/compiler/java/java_plugin_unittest.cc index cfe01885..ccc94c9d 100644 --- a/src/google/protobuf/compiler/java/java_plugin_unittest.cc +++ b/src/google/protobuf/compiler/java/java_plugin_unittest.cc @@ -56,21 +56,22 @@ class TestGenerator : public CodeGenerator { virtual bool Generate(const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* context, string* error) const { - TryInsert("Test.java", "outer_class_scope", output_directory); - TryInsert("Test.java", "class_scope:foo.Bar", output_directory); - TryInsert("Test.java", "class_scope:foo.Bar.Baz", output_directory); - TryInsert("Test.java", "builder_scope:foo.Bar", output_directory); - TryInsert("Test.java", "builder_scope:foo.Bar.Baz", output_directory); - TryInsert("Test.java", "enum_scope:foo.Qux", output_directory); + string filename = "Test.java"; + TryInsert(filename, "outer_class_scope", context); + TryInsert(filename, "class_scope:foo.Bar", context); + TryInsert(filename, "class_scope:foo.Bar.Baz", context); + TryInsert(filename, "builder_scope:foo.Bar", context); + TryInsert(filename, "builder_scope:foo.Bar.Baz", context); + TryInsert(filename, "enum_scope:foo.Qux", context); return true; } void TryInsert(const string& filename, const string& insertion_point, - OutputDirectory* output_directory) const { + GeneratorContext* context) const { scoped_ptr<io::ZeroCopyOutputStream> output( - output_directory->OpenForInsert(filename, insertion_point)); + context->OpenForInsert(filename, insertion_point)); io::Printer printer(output.get(), '$'); printer.Print("// inserted $name$\n", "name", insertion_point); } diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc index f6179bfa..addb8819 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -154,15 +154,24 @@ int FixedSize(FieldDescriptor::Type type) { } void SetPrimitiveVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, map<string, string>* variables) { (*variables)["name"] = UnderscoresToCamelCase(descriptor); (*variables)["capitalized_name"] = UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["constant_name"] = FieldConstantName(descriptor); (*variables)["number"] = SimpleItoa(descriptor->number()); (*variables)["type"] = PrimitiveTypeName(GetJavaType(descriptor)); (*variables)["boxed_type"] = BoxedPrimitiveTypeName(GetJavaType(descriptor)); + (*variables)["field_type"] = (*variables)["type"]; + (*variables)["field_list_type"] = "java.util.List<" + + (*variables)["boxed_type"] + ">"; + (*variables)["empty_list"] = "java.util.Collections.emptyList();"; (*variables)["default"] = DefaultValue(descriptor); + (*variables)["default_init"] = IsDefaultValueJavaDefault(descriptor) ? + "" : ("= " + DefaultValue(descriptor)); (*variables)["capitalized_type"] = GetCapitalizedType(descriptor); (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); (*variables)["tag_size"] = SimpleItoa( @@ -175,67 +184,135 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, } else { (*variables)["null_check"] = ""; } + // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported + // by the proto compiler + (*variables)["deprecation"] = descriptor->options().deprecated() + ? "@java.lang.Deprecated " : ""; int fixed_size = FixedSize(GetType(descriptor)); if (fixed_size != -1) { (*variables)["fixed_size"] = SimpleItoa(fixed_size); } + (*variables)["on_changed"] = + HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + + // For singular messages and builders, one bit is used for the hasField bit. + (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); + + (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); + (*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex); + (*variables)["clear_has_field_bit_builder"] = + GenerateClearBit(builderBitIndex); + + // For repated builders, one bit is used for whether the array is immutable. + (*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex); + (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); + (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); + + (*variables)["get_has_field_bit_from_local"] = + GenerateGetBitFromLocal(builderBitIndex); + (*variables)["set_has_field_bit_to_local"] = + GenerateSetBitToLocal(messageBitIndex); } + } // namespace // =================================================================== PrimitiveFieldGenerator:: -PrimitiveFieldGenerator(const FieldDescriptor* descriptor) - : descriptor_(descriptor) { - SetPrimitiveVariables(descriptor, &variables_); +PrimitiveFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex) { + SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, + &variables_); } PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {} +int PrimitiveFieldGenerator::GetNumBitsForMessage() const { + return 1; +} + +int PrimitiveFieldGenerator::GetNumBitsForBuilder() const { + return 1; +} + +void PrimitiveFieldGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n" + "$deprecation$$type$ get$capitalized_name$();\n"); +} + void PrimitiveFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private boolean has$capitalized_name$;\n" - "private $type$ $name$_ = $default$;\n" - "public boolean has$capitalized_name$() { return has$capitalized_name$; }\n" - "public $type$ get$capitalized_name$() { return $name$_; }\n"); + "private $field_type$ $name$_;\n" + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n"); + + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return $name$_;\n" + "}\n"); } void PrimitiveFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, - "public boolean has$capitalized_name$() {\n" - " return result.has$capitalized_name$();\n" - "}\n" - "public $type$ get$capitalized_name$() {\n" - " return result.get$capitalized_name$();\n" - "}\n" - "public Builder set$capitalized_name$($type$ value) {\n" + "private $field_type$ $name$_ $default_init$;\n" + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_builder$;\n" + "}\n"); + + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return $name$_;\n" + "}\n"); + + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" "$null_check$" - " result.has$capitalized_name$ = true;\n" - " result.$name$_ = value;\n" + " $set_has_field_bit_builder$;\n" + " $name$_ = value;\n" + " $on_changed$\n" " return this;\n" "}\n" - "public Builder clear$capitalized_name$() {\n" - " result.has$capitalized_name$ = false;\n"); + "$deprecation$public Builder clear$capitalized_name$() {\n" + " $clear_has_field_bit_builder$;\n"); JavaType type = GetJavaType(descriptor_); if (type == JAVATYPE_STRING || type == JAVATYPE_BYTES) { // The default value is not a simple literal so we want to avoid executing // it multiple times. Instead, get the default out of the default instance. printer->Print(variables_, - " result.$name$_ = getDefaultInstance().get$capitalized_name$();\n"); + " $name$_ = getDefaultInstance().get$capitalized_name$();\n"); } else { printer->Print(variables_, - " result.$name$_ = $default$;\n"); + " $name$_ = $default$;\n"); } printer->Print(variables_, + " $on_changed$\n" " return this;\n" "}\n"); } void PrimitiveFieldGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for primitives +} + +void PrimitiveFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { - // Initialized inline. + printer->Print(variables_, "$name$_ = $default$;\n"); +} + +void PrimitiveFieldGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_ = $default$;\n" + "$clear_has_field_bit_builder$;\n"); } void PrimitiveFieldGenerator:: @@ -248,32 +325,121 @@ GenerateMergingCode(io::Printer* printer) const { void PrimitiveFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { - // Nothing to do here for primitive types. + printer->Print(variables_, + "if ($get_has_field_bit_from_local$) {\n" + " $set_has_field_bit_to_local$;\n" + "}\n" + "result.$name$_ = $name$_;\n"); } void PrimitiveFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, - "set$capitalized_name$(input.read$capitalized_type$());\n"); + "$set_has_field_bit_builder$;\n" + "$name$_ = input.read$capitalized_type$();\n"); } void PrimitiveFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, - "if (has$capitalized_name$()) {\n" - " output.write$capitalized_type$($number$, get$capitalized_name$());\n" + "if ($get_has_field_bit_message$) {\n" + " output.write$capitalized_type$($number$, $name$_);\n" "}\n"); } void PrimitiveFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, - "if (has$capitalized_name$()) {\n" + "if ($get_has_field_bit_message$) {\n" " size += com.google.protobuf.CodedOutputStream\n" - " .compute$capitalized_type$Size($number$, get$capitalized_name$());\n" + " .compute$capitalized_type$Size($number$, $name$_);\n" "}\n"); } +void PrimitiveFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + switch (GetJavaType(descriptor_)) { + case JAVATYPE_INT: + case JAVATYPE_LONG: + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "result = result && (get$capitalized_name$()\n" + " == other.get$capitalized_name$());\n"); + break; + + case JAVATYPE_FLOAT: + printer->Print(variables_, + "result = result && (Float.floatToIntBits(get$capitalized_name$())" + " == Float.floatToIntBits(other.get$capitalized_name$()));\n"); + break; + + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "result = result && (Double.doubleToLongBits(get$capitalized_name$())" + " == Double.doubleToLongBits(other.get$capitalized_name$()));\n"); + break; + + case JAVATYPE_STRING: + case JAVATYPE_BYTES: + printer->Print(variables_, + "result = result && get$capitalized_name$()\n" + " .equals(other.get$capitalized_name$());\n"); + break; + + case JAVATYPE_ENUM: + case JAVATYPE_MESSAGE: + default: + GOOGLE_LOG(FATAL) << "Can't get here."; + break; + } +} + +void PrimitiveFieldGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n"); + switch (GetJavaType(descriptor_)) { + case JAVATYPE_INT: + printer->Print(variables_, + "hash = (53 * hash) + get$capitalized_name$();\n"); + break; + + case JAVATYPE_LONG: + printer->Print(variables_, + "hash = (53 * hash) + hashLong(get$capitalized_name$());\n"); + break; + + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "hash = (53 * hash) + hashBoolean(get$capitalized_name$());\n"); + break; + + case JAVATYPE_FLOAT: + printer->Print(variables_, + "hash = (53 * hash) + Float.floatToIntBits(\n" + " get$capitalized_name$());\n"); + break; + + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "hash = (53 * hash) + hashLong(\n" + " Double.doubleToLongBits(get$capitalized_name$()));\n"); + break; + + case JAVATYPE_STRING: + case JAVATYPE_BYTES: + printer->Print(variables_, + "hash = (53 * hash) + get$capitalized_name$().hashCode();\n"); + break; + + case JAVATYPE_ENUM: + case JAVATYPE_MESSAGE: + default: + GOOGLE_LOG(FATAL) << "Can't get here."; + break; + } +} + string PrimitiveFieldGenerator::GetBoxedType() const { return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); } @@ -281,23 +447,46 @@ string PrimitiveFieldGenerator::GetBoxedType() const { // =================================================================== RepeatedPrimitiveFieldGenerator:: -RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor) - : descriptor_(descriptor) { - SetPrimitiveVariables(descriptor, &variables_); +RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex) { + SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, + &variables_); } RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {} +int RepeatedPrimitiveFieldGenerator::GetNumBitsForMessage() const { + return 0; +} + +int RepeatedPrimitiveFieldGenerator::GetNumBitsForBuilder() const { + return 1; +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + printer->Print(variables_, + "$deprecation$java.util.List<$boxed_type$> get$capitalized_name$List();\n" + "$deprecation$int get$capitalized_name$Count();\n" + "$deprecation$$type$ get$capitalized_name$(int index);\n"); +} + + void RepeatedPrimitiveFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private java.util.List<$boxed_type$> $name$_ =\n" - " java.util.Collections.emptyList();\n" - "public java.util.List<$boxed_type$> get$capitalized_name$List() {\n" + "private $field_list_type$ $name$_;\n" + "$deprecation$public java.util.List<$boxed_type$>\n" + " get$capitalized_name$List() {\n" " return $name$_;\n" // note: unmodifiable list "}\n" - "public int get$capitalized_name$Count() { return $name$_.size(); }\n" - "public $type$ get$capitalized_name$(int index) {\n" + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n" + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" " return $name$_.get(index);\n" "}\n"); @@ -310,76 +499,125 @@ GenerateMembers(io::Printer* printer) const { void RepeatedPrimitiveFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { + // One field is the list and the bit field keeps track of whether the + // list is immutable. If it's immutable, the invariant is that it must + // either an instance of Collections.emptyList() or it's an ArrayList + // wrapped in a Collections.unmodifiableList() wrapper and nobody else has + // a refererence to the underlying ArrayList. This invariant allows us to + // share instances of lists between protocol buffers avoiding expensive + // memory allocations. Note, immutable is a strong guarantee here -- not + // just that the list cannot be modified via the reference but that the + // list can never be modified. + printer->Print(variables_, + "private $field_list_type$ $name$_ = $empty_list$;\n"); + printer->Print(variables_, + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$get_mutable_bit_builder$) {\n" + " $name$_ = new java.util.ArrayList<$boxed_type$>($name$_);\n" + " $set_mutable_bit_builder$;\n" + " }\n" + "}\n"); + // Note: We return an unmodifiable list because otherwise the caller // could hold on to the returned list and modify it after the message // has been built, thus mutating the message which is supposed to be // immutable. - "public java.util.List<$boxed_type$> get$capitalized_name$List() {\n" - " return java.util.Collections.unmodifiableList(result.$name$_);\n" + printer->Print(variables_, + "$deprecation$public java.util.List<$boxed_type$>\n" + " get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList($name$_);\n" "}\n" - "public int get$capitalized_name$Count() {\n" - " return result.get$capitalized_name$Count();\n" + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" "}\n" - "public $type$ get$capitalized_name$(int index) {\n" - " return result.get$capitalized_name$(index);\n" + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" "}\n" - "public Builder set$capitalized_name$(int index, $type$ value) {\n" + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$ value) {\n" "$null_check$" - " result.$name$_.set(index, value);\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, value);\n" + " $on_changed$\n" " return this;\n" "}\n" - "public Builder add$capitalized_name$($type$ value) {\n" + "$deprecation$public Builder add$capitalized_name$($type$ value) {\n" "$null_check$" - " if (result.$name$_.isEmpty()) {\n" - " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n" - " }\n" - " result.$name$_.add(value);\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value);\n" + " $on_changed$\n" " return this;\n" "}\n" - "public Builder addAll$capitalized_name$(\n" + "$deprecation$public Builder addAll$capitalized_name$(\n" " java.lang.Iterable<? extends $boxed_type$> values) {\n" - " if (result.$name$_.isEmpty()) {\n" - " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n" - " }\n" - " super.addAll(values, result.$name$_);\n" + " ensure$capitalized_name$IsMutable();\n" + " super.addAll(values, $name$_);\n" + " $on_changed$\n" " return this;\n" "}\n" - "public Builder clear$capitalized_name$() {\n" - " result.$name$_ = java.util.Collections.emptyList();\n" + "$deprecation$public Builder clear$capitalized_name$() {\n" + " $name$_ = $empty_list$;\n" + " $clear_mutable_bit_builder$;\n" + " $on_changed$\n" " return this;\n" "}\n"); } void RepeatedPrimitiveFieldGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for primitives +} + +void RepeatedPrimitiveFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { - // Initialized inline. + printer->Print(variables_, "$name$_ = $empty_list$;\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_ = $empty_list$;\n" + "$clear_mutable_bit_builder$;\n"); } void RepeatedPrimitiveFieldGenerator:: GenerateMergingCode(io::Printer* printer) const { + // The code below does two optimizations: + // 1. If the other list is empty, there's nothing to do. This ensures we + // don't allocate a new array if we already have an immutable one. + // 2. If the other list is non-empty and our current list is empty, we can + // reuse the other list which is guaranteed to be immutable. printer->Print(variables_, "if (!other.$name$_.isEmpty()) {\n" - " if (result.$name$_.isEmpty()) {\n" - " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n" + " if ($name$_.isEmpty()) {\n" + " $name$_ = other.$name$_;\n" + " $clear_mutable_bit_builder$;\n" + " } else {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.addAll(other.$name$_);\n" " }\n" - " result.$name$_.addAll(other.$name$_);\n" + " $on_changed$\n" "}\n"); } void RepeatedPrimitiveFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { + // The code below ensures that the result has an immutable list. If our + // list is immutable, we can just reuse it. If not, we make it immutable. printer->Print(variables_, - "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n" - " result.$name$_ =\n" - " java.util.Collections.unmodifiableList(result.$name$_);\n" - "}\n"); + "if ($get_mutable_bit_builder$) {\n" + " $name$_ = java.util.Collections.unmodifiableList($name$_);\n" + " $clear_mutable_bit_builder$;\n" + "}\n" + "result.$name$_ = $name$_;\n"); } void RepeatedPrimitiveFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, - "add$capitalized_name$(input.read$capitalized_type$());\n"); + "ensure$capitalized_name$IsMutable();\n" + "$name$_.add(input.read$capitalized_type$());\n"); } void RepeatedPrimitiveFieldGenerator:: @@ -401,13 +639,13 @@ GenerateSerializationCode(io::Printer* printer) const { " output.writeRawVarint32($tag$);\n" " output.writeRawVarint32($name$MemoizedSerializedSize);\n" "}\n" - "for ($type$ element : get$capitalized_name$List()) {\n" - " output.write$capitalized_type$NoTag(element);\n" + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.write$capitalized_type$NoTag($name$_.get(i));\n" "}\n"); } else { printer->Print(variables_, - "for ($type$ element : get$capitalized_name$List()) {\n" - " output.write$capitalized_type$($number$, element);\n" + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.write$capitalized_type$($number$, $name$_.get(i));\n" "}\n"); } } @@ -421,9 +659,9 @@ GenerateSerializedSizeCode(io::Printer* printer) const { if (FixedSize(GetType(descriptor_)) == -1) { printer->Print(variables_, - "for ($type$ element : get$capitalized_name$List()) {\n" + "for (int i = 0; i < $name$_.size(); i++) {\n" " dataSize += com.google.protobuf.CodedOutputStream\n" - " .compute$capitalized_type$SizeNoTag(element);\n" + " .compute$capitalized_type$SizeNoTag($name$_.get(i));\n" "}\n"); } else { printer->Print(variables_, @@ -455,6 +693,22 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print("}\n"); } +void RepeatedPrimitiveFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$List()\n" + " .equals(other.get$capitalized_name$List());\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "if (get$capitalized_name$Count() > 0) {\n" + " hash = (37 * hash) + $constant_name$;\n" + " hash = (53 * hash) + get$capitalized_name$List().hashCode();\n" + "}\n"); +} + string RepeatedPrimitiveFieldGenerator::GetBoxedType() const { return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); } diff --git a/src/google/protobuf/compiler/java/java_primitive_field.h b/src/google/protobuf/compiler/java/java_primitive_field.h index 4e482a05..7900fac5 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.h +++ b/src/google/protobuf/compiler/java/java_primitive_field.h @@ -46,49 +46,69 @@ namespace java { class PrimitiveFieldGenerator : public FieldGenerator { public: - explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor); + explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, int builderBitIndex); ~PrimitiveFieldGenerator(); // implements FieldGenerator --------------------------------------- + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; + void GenerateBuilderClearCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; string GetBoxedType() const; private: const FieldDescriptor* descriptor_; map<string, string> variables_; + const int messageBitIndex_; + const int builderBitIndex_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator); }; class RepeatedPrimitiveFieldGenerator : public FieldGenerator { public: - explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor); + explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, int builderBitIndex); ~RepeatedPrimitiveFieldGenerator(); // implements FieldGenerator --------------------------------------- + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; + void GenerateBuilderClearCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateParsingCodeFromPacked(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; string GetBoxedType() const; private: const FieldDescriptor* descriptor_; map<string, string> variables_; + const int messageBitIndex_; + const int builderBitIndex_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPrimitiveFieldGenerator); }; diff --git a/src/google/protobuf/compiler/java/java_service.cc b/src/google/protobuf/compiler/java/java_service.cc index 5545bf7f..1ae4f461 100644 --- a/src/google/protobuf/compiler/java/java_service.cc +++ b/src/google/protobuf/compiler/java/java_service.cc @@ -118,7 +118,7 @@ void ServiceGenerator::GenerateNewReflectiveServiceMethod( for (int i = 0; i < descriptor_->method_count(); i++) { const MethodDescriptor* method = descriptor_->method(i); - printer->Print("@Override\n"); + printer->Print("@java.lang.Override\n"); GenerateMethodSignature(printer, method, IS_CONCRETE); printer->Print( " {\n" diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc new file mode 100644 index 00000000..a93ff434 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_string_field.cc @@ -0,0 +1,605 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Author: jonp@google.com (Jon Perlow) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include <map> +#include <string> + +#include <google/protobuf/compiler/java/java_string_field.h> +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/wire_format.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { + +void SetPrimitiveVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + map<string, string>* variables) { + (*variables)["name"] = + UnderscoresToCamelCase(descriptor); + (*variables)["capitalized_name"] = + UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["constant_name"] = FieldConstantName(descriptor); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["empty_list"] = "com.google.protobuf.LazyStringArrayList.EMPTY"; + + (*variables)["default"] = DefaultValue(descriptor); + (*variables)["default_init"] = ("= " + DefaultValue(descriptor)); + (*variables)["capitalized_type"] = "String"; + (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), GetType(descriptor))); + (*variables)["null_check"] = + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n"; + + // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported + // by the proto compiler + (*variables)["deprecation"] = descriptor->options().deprecated() + ? "@java.lang.Deprecated " : ""; + (*variables)["on_changed"] = + HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + + // For singular messages and builders, one bit is used for the hasField bit. + (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); + + (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); + (*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex); + (*variables)["clear_has_field_bit_builder"] = + GenerateClearBit(builderBitIndex); + + // For repated builders, one bit is used for whether the array is immutable. + (*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex); + (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); + (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); + + (*variables)["get_has_field_bit_from_local"] = + GenerateGetBitFromLocal(builderBitIndex); + (*variables)["set_has_field_bit_to_local"] = + GenerateSetBitToLocal(messageBitIndex); +} + +} // namespace + +// =================================================================== + +StringFieldGenerator:: +StringFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex) { + SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, + &variables_); +} + +StringFieldGenerator::~StringFieldGenerator() {} + +int StringFieldGenerator::GetNumBitsForMessage() const { + return 1; +} + +int StringFieldGenerator::GetNumBitsForBuilder() const { + return 1; +} + +// A note about how strings are handled. This code used to just store a String +// in the Message. This had two issues: +// +// 1. It wouldn't roundtrip byte arrays that were not vaid UTF-8 encoded +// strings, but rather fields that were raw bytes incorrectly marked +// as strings in the proto file. This is common because in the proto1 +// syntax, string was the way to indicate bytes and C++ engineers can +// easily make this mistake without affecting the C++ API. By converting to +// strings immediately, some java code might corrupt these byte arrays as +// it passes through a java server even if the field was never accessed by +// application code. +// +// 2. There's a performance hit to converting between bytes and strings and +// it many cases, the field is never even read by the application code. This +// avoids unnecessary conversions in the common use cases. +// +// So now, the field for String is maintained as an Object reference which can +// either store a String or a ByteString. The code uses an instanceof check +// to see which one it has and converts to the other one if needed. It remembers +// the last value requested (in a thread safe manner) as this is most likely +// the one needed next. The thread safety is such that if two threads both +// convert the field because the changes made by each thread were not visible to +// the other, they may cause a conversion to happen more times than would +// otherwise be necessary. This was deemed better than adding synchronization +// overhead. It will not cause any corruption issues or affect the behavior of +// the API. The instanceof check is also highly optimized in the JVM and we +// decided it was better to reduce the memory overhead by not having two +// separate fields but rather use dynamic type checking. +// +// For single fields, the logic for this is done inside the generated code. For +// repeated fields, the logic is done in LazyStringArrayList and +// UnmodifiableLazyStringList. +void StringFieldGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n" + "$deprecation$String get$capitalized_name$();\n"); +} + +void StringFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private Object $name$_;\n" + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n"); + + printer->Print(variables_, + "$deprecation$public String get$capitalized_name$() {\n" + " Object ref = $name$_;\n" + " if (ref instanceof String) {\n" + " return (String) ref;\n" + " } else {\n" + " com.google.protobuf.ByteString bs = \n" + " (com.google.protobuf.ByteString) ref;\n" + " String s = bs.toStringUtf8();\n" + " if (com.google.protobuf.Internal.isValidUtf8(bs)) {\n" + " $name$_ = s;\n" + " }\n" + " return s;\n" + " }\n" + "}\n" + "private com.google.protobuf.ByteString get$capitalized_name$Bytes() {\n" + " Object ref = $name$_;\n" + " if (ref instanceof String) {\n" + " com.google.protobuf.ByteString b = \n" + " com.google.protobuf.ByteString.copyFromUtf8((String) ref);\n" + " $name$_ = b;\n" + " return b;\n" + " } else {\n" + " return (com.google.protobuf.ByteString) ref;\n" + " }\n" + "}\n"); +} + +void StringFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + "private Object $name$_ $default_init$;\n" + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_builder$;\n" + "}\n"); + + printer->Print(variables_, + "$deprecation$public String get$capitalized_name$() {\n" + " Object ref = $name$_;\n" + " if (!(ref instanceof String)) {\n" + " String s = ((com.google.protobuf.ByteString) ref).toStringUtf8();\n" + " $name$_ = s;\n" + " return s;\n" + " } else {\n" + " return (String) ref;\n" + " }\n" + "}\n"); + + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(String value) {\n" + "$null_check$" + " $set_has_field_bit_builder$;\n" + " $name$_ = value;\n" + " $on_changed$\n" + " return this;\n" + "}\n" + "$deprecation$public Builder clear$capitalized_name$() {\n" + " $clear_has_field_bit_builder$;\n"); + // The default value is not a simple literal so we want to avoid executing + // it multiple times. Instead, get the default out of the default instance. + printer->Print(variables_, + " $name$_ = getDefaultInstance().get$capitalized_name$();\n"); + printer->Print(variables_, + " $on_changed$\n" + " return this;\n" + "}\n"); + + printer->Print(variables_, + "void set$capitalized_name$(com.google.protobuf.ByteString value) {\n" + " $set_has_field_bit_builder$;\n" + " $name$_ = value;\n" + " $on_changed$\n" + "}\n"); +} + +void StringFieldGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for primitives +} + +void StringFieldGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default$;\n"); +} + +void StringFieldGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_ = $default$;\n" + "$clear_has_field_bit_builder$;\n"); +} + +void StringFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " set$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); +} + +void StringFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_has_field_bit_from_local$) {\n" + " $set_has_field_bit_to_local$;\n" + "}\n" + "result.$name$_ = $name$_;\n"); +} + +void StringFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$set_has_field_bit_builder$;\n" + "$name$_ = input.readBytes();\n"); +} + +void StringFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_has_field_bit_message$) {\n" + " output.writeBytes($number$, get$capitalized_name$Bytes());\n" + "}\n"); +} + +void StringFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_has_field_bit_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeBytesSize($number$, get$capitalized_name$Bytes());\n" + "}\n"); +} + +void StringFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$()\n" + " .equals(other.get$capitalized_name$());\n"); +} + +void StringFieldGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n"); + printer->Print(variables_, + "hash = (53 * hash) + get$capitalized_name$().hashCode();\n"); +} + +string StringFieldGenerator::GetBoxedType() const { + return "String"; +} + + +// =================================================================== + +RepeatedStringFieldGenerator:: +RepeatedStringFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex) { + SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, + &variables_); +} + +RepeatedStringFieldGenerator::~RepeatedStringFieldGenerator() {} + +int RepeatedStringFieldGenerator::GetNumBitsForMessage() const { + return 0; +} + +int RepeatedStringFieldGenerator::GetNumBitsForBuilder() const { + return 1; +} + +void RepeatedStringFieldGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + printer->Print(variables_, + "$deprecation$java.util.List<String> get$capitalized_name$List();\n" + "$deprecation$int get$capitalized_name$Count();\n" + "$deprecation$String get$capitalized_name$(int index);\n"); +} + + +void RepeatedStringFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private com.google.protobuf.LazyStringList $name$_;\n" + "$deprecation$public java.util.List<String>\n" + " get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n" + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n" + "$deprecation$public String get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); + + if (descriptor_->options().packed() && + HasGeneratedMethods(descriptor_->containing_type())) { + printer->Print(variables_, + "private int $name$MemoizedSerializedSize = -1;\n"); + } +} + +void RepeatedStringFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // One field is the list and the bit field keeps track of whether the + // list is immutable. If it's immutable, the invariant is that it must + // either an instance of Collections.emptyList() or it's an ArrayList + // wrapped in a Collections.unmodifiableList() wrapper and nobody else has + // a refererence to the underlying ArrayList. This invariant allows us to + // share instances of lists between protocol buffers avoiding expensive + // memory allocations. Note, immutable is a strong guarantee here -- not + // just that the list cannot be modified via the reference but that the + // list can never be modified. + printer->Print(variables_, + "private com.google.protobuf.LazyStringList $name$_ = $empty_list$;\n"); + + printer->Print(variables_, + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$get_mutable_bit_builder$) {\n" + " $name$_ = new com.google.protobuf.LazyStringArrayList($name$_);\n" + " $set_mutable_bit_builder$;\n" + " }\n" + "}\n"); + + // Note: We return an unmodifiable list because otherwise the caller + // could hold on to the returned list and modify it after the message + // has been built, thus mutating the message which is supposed to be + // immutable. + printer->Print(variables_, + "$deprecation$public java.util.List<String>\n" + " get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList($name$_);\n" + "}\n" + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n" + "$deprecation$public String get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n" + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, String value) {\n" + "$null_check$" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, value);\n" + " $on_changed$\n" + " return this;\n" + "}\n" + "$deprecation$public Builder add$capitalized_name$(String value) {\n" + "$null_check$" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value);\n" + " $on_changed$\n" + " return this;\n" + "}\n" + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable<String> values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " super.addAll(values, $name$_);\n" + " $on_changed$\n" + " return this;\n" + "}\n" + "$deprecation$public Builder clear$capitalized_name$() {\n" + " $name$_ = $empty_list$;\n" + " $clear_mutable_bit_builder$;\n" + " $on_changed$\n" + " return this;\n" + "}\n"); + + printer->Print(variables_, + "void add$capitalized_name$(com.google.protobuf.ByteString value) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value);\n" + " $on_changed$\n" + "}\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for primitives +} + +void RepeatedStringFieldGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $empty_list$;\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_ = $empty_list$;\n" + "$clear_mutable_bit_builder$;\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + // The code below does two optimizations: + // 1. If the other list is empty, there's nothing to do. This ensures we + // don't allocate a new array if we already have an immutable one. + // 2. If the other list is non-empty and our current list is empty, we can + // reuse the other list which is guaranteed to be immutable. + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\n" + " if ($name$_.isEmpty()) {\n" + " $name$_ = other.$name$_;\n" + " $clear_mutable_bit_builder$;\n" + " } else {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.addAll(other.$name$_);\n" + " }\n" + " $on_changed$\n" + "}\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // The code below ensures that the result has an immutable list. If our + // list is immutable, we can just reuse it. If not, we make it immutable. + + printer->Print(variables_, + "if ($get_mutable_bit_builder$) {\n" + " $name$_ = new com.google.protobuf.UnmodifiableLazyStringList(\n" + " $name$_);\n" + " $clear_mutable_bit_builder$;\n" + "}\n" + "result.$name$_ = $name$_;\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "ensure$capitalized_name$IsMutable();\n" + "$name$_.add(input.readBytes());\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateParsingCodeFromPacked(io::Printer* printer) const { + printer->Print(variables_, + "int length = input.readRawVarint32();\n" + "int limit = input.pushLimit(length);\n" + "while (input.getBytesUntilLimit() > 0) {\n" + " add$capitalized_name$(input.read$capitalized_type$());\n" + "}\n" + "input.popLimit(limit);\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (get$capitalized_name$List().size() > 0) {\n" + " output.writeRawVarint32($tag$);\n" + " output.writeRawVarint32($name$MemoizedSerializedSize);\n" + "}\n" + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.write$capitalized_type$NoTag($name$_.get(i));\n" + "}\n"); + } else { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.writeBytes($number$, $name$_.getByteString(i));\n" + "}\n"); + } +} + +void RepeatedStringFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "{\n" + " int dataSize = 0;\n"); + printer->Indent(); + + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " dataSize += com.google.protobuf.CodedOutputStream\n" + " .computeBytesSizeNoTag($name$_.getByteString(i));\n" + "}\n"); + + printer->Print( + "size += dataSize;\n"); + + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (!get$capitalized_name$List().isEmpty()) {\n" + " size += $tag_size$;\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeInt32SizeNoTag(dataSize);\n" + "}\n"); + } else { + printer->Print(variables_, + "size += $tag_size$ * get$capitalized_name$List().size();\n"); + } + + // cache the data size for packed fields. + if (descriptor_->options().packed()) { + printer->Print(variables_, + "$name$MemoizedSerializedSize = dataSize;\n"); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$List()\n" + " .equals(other.get$capitalized_name$List());\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "if (get$capitalized_name$Count() > 0) {\n" + " hash = (37 * hash) + $constant_name$;\n" + " hash = (53 * hash) + get$capitalized_name$List().hashCode();\n" + "}\n"); +} + +string RepeatedStringFieldGenerator::GetBoxedType() const { + return "String"; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_string_field.h b/src/google/protobuf/compiler/java/java_string_field.h new file mode 100644 index 00000000..8cb41469 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_string_field.h @@ -0,0 +1,120 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Author: jonp@google.com (Jon Perlow) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_STRING_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_STRING_FIELD_H__ + +#include <map> +#include <string> +#include <google/protobuf/compiler/java/java_field.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +class StringFieldGenerator : public FieldGenerator { + public: + explicit StringFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, int builderBitIndex); + ~StringFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateBuilderClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map<string, string> variables_; + const int messageBitIndex_; + const int builderBitIndex_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringFieldGenerator); +}; + +class RepeatedStringFieldGenerator : public FieldGenerator { + public: + explicit RepeatedStringFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, int builderBitIndex); + ~RepeatedStringFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateBuilderClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingCodeFromPacked(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map<string, string> variables_; + const int messageBitIndex_; + const int builderBitIndex_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedStringFieldGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_STRING_FIELD_H__ diff --git a/src/google/protobuf/compiler/mock_code_generator.cc b/src/google/protobuf/compiler/mock_code_generator.cc index 83d5a4e4..5b76af25 100644 --- a/src/google/protobuf/compiler/mock_code_generator.cc +++ b/src/google/protobuf/compiler/mock_code_generator.cc @@ -45,6 +45,16 @@ namespace google { namespace protobuf { namespace compiler { +// Returns the list of the names of files in all_files in the form of a +// comma-separated string. +string CommaSeparatedList(const vector<const FileDescriptor*> all_files) { + vector<string> names; + for (int i = 0; i < all_files.size(); i++) { + names.push_back(all_files[i]->name()); + } + return JoinStrings(names, ","); +} + static const char* kFirstInsertionPointName = "first_mock_insertion_point"; static const char* kSecondInsertionPointName = "second_mock_insertion_point"; static const char* kFirstInsertionPoint = @@ -63,6 +73,7 @@ void MockCodeGenerator::ExpectGenerated( const string& insertions, const string& file, const string& first_message_name, + const string& first_parsed_file_name, const string& output_directory) { string content; ASSERT_TRUE(File::ReadFileToString( @@ -84,7 +95,8 @@ void MockCodeGenerator::ExpectGenerated( } ASSERT_EQ(lines.size(), 3 + insertion_list.size() * 2); - EXPECT_EQ(GetOutputFileContent(name, parameter, file, first_message_name), + EXPECT_EQ(GetOutputFileContent(name, parameter, file, + first_parsed_file_name, first_message_name), lines[0]); EXPECT_EQ(kFirstInsertionPoint, lines[1 + insertion_list.size()]); @@ -92,12 +104,12 @@ void MockCodeGenerator::ExpectGenerated( for (int i = 0; i < insertion_list.size(); i++) { EXPECT_EQ(GetOutputFileContent(insertion_list[i], "first_insert", - file, first_message_name), + file, file, first_message_name), lines[1 + i]); // Second insertion point is indented, so the inserted text should // automatically be indented too. EXPECT_EQ(" " + GetOutputFileContent(insertion_list[i], "second_insert", - file, first_message_name), + file, file, first_message_name), lines[2 + insertion_list.size() + i]); } } @@ -105,7 +117,7 @@ void MockCodeGenerator::ExpectGenerated( bool MockCodeGenerator::Generate( const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* context, string* error) const { for (int i = 0; i < file->message_type_count(); i++) { if (HasPrefixString(file->message_type(i)->name(), "MockCodeGenerator_")) { @@ -134,11 +146,12 @@ bool MockCodeGenerator::Generate( for (int i = 0; i < insert_into.size(); i++) { { scoped_ptr<io::ZeroCopyOutputStream> output( - output_directory->OpenForInsert( + context->OpenForInsert( GetOutputFileName(insert_into[i], file), kFirstInsertionPointName)); io::Printer printer(output.get(), '$'); - printer.PrintRaw(GetOutputFileContent(name_, "first_insert", file)); + printer.PrintRaw(GetOutputFileContent(name_, "first_insert", + file, context)); if (printer.failed()) { *error = "MockCodeGenerator detected write error."; return false; @@ -147,11 +160,12 @@ bool MockCodeGenerator::Generate( { scoped_ptr<io::ZeroCopyOutputStream> output( - output_directory->OpenForInsert( + context->OpenForInsert( GetOutputFileName(insert_into[i], file), kSecondInsertionPointName)); io::Printer printer(output.get(), '$'); - printer.PrintRaw(GetOutputFileContent(name_, "second_insert", file)); + printer.PrintRaw(GetOutputFileContent(name_, "second_insert", + file, context)); if (printer.failed()) { *error = "MockCodeGenerator detected write error."; return false; @@ -160,10 +174,11 @@ bool MockCodeGenerator::Generate( } } else { scoped_ptr<io::ZeroCopyOutputStream> output( - output_directory->Open(GetOutputFileName(name_, file))); + context->Open(GetOutputFileName(name_, file))); io::Printer printer(output.get(), '$'); - printer.PrintRaw(GetOutputFileContent(name_, parameter, file)); + printer.PrintRaw(GetOutputFileContent(name_, parameter, + file, context)); printer.PrintRaw(kFirstInsertionPoint); printer.PrintRaw(kSecondInsertionPoint); @@ -186,11 +201,16 @@ string MockCodeGenerator::GetOutputFileName(const string& generator_name, return file + ".MockCodeGenerator." + generator_name; } -string MockCodeGenerator::GetOutputFileContent(const string& generator_name, - const string& parameter, - const FileDescriptor* file) { +string MockCodeGenerator::GetOutputFileContent( + const string& generator_name, + const string& parameter, + const FileDescriptor* file, + GeneratorContext *context) { + vector<const FileDescriptor*> all_files; + context->ListParsedFiles(&all_files); return GetOutputFileContent( generator_name, parameter, file->name(), + CommaSeparatedList(all_files), file->message_type_count() > 0 ? file->message_type(0)->name() : "(none)"); } @@ -199,9 +219,11 @@ string MockCodeGenerator::GetOutputFileContent( const string& generator_name, const string& parameter, const string& file, + const string& parsed_file_list, const string& first_message_name) { - return strings::Substitute("$0: $1, $2, $3\n", - generator_name, parameter, file, first_message_name); + return strings::Substitute("$0: $1, $2, $3, $4\n", + generator_name, parameter, file, + first_message_name, parsed_file_list); } } // namespace compiler diff --git a/src/google/protobuf/compiler/mock_code_generator.h b/src/google/protobuf/compiler/mock_code_generator.h index 01d69dd2..5c7942bd 100644 --- a/src/google/protobuf/compiler/mock_code_generator.h +++ b/src/google/protobuf/compiler/mock_code_generator.h @@ -69,11 +69,14 @@ class MockCodeGenerator : public CodeGenerator { // // |insertions| is a comma-separated list of names of MockCodeGenerators which // should have inserted lines into this file. + // |parsed_file_list| is a comma-separated list of names of the files + // that are being compiled together in this run. static void ExpectGenerated(const string& name, const string& parameter, const string& insertions, const string& file, const string& first_message_name, + const string& parsed_file_list, const string& output_directory); // Get the name of the file which would be written by the given generator. @@ -86,7 +89,7 @@ class MockCodeGenerator : public CodeGenerator { virtual bool Generate(const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* context, string* error) const; private: @@ -94,10 +97,12 @@ class MockCodeGenerator : public CodeGenerator { static string GetOutputFileContent(const string& generator_name, const string& parameter, - const FileDescriptor* file); + const FileDescriptor* file, + GeneratorContext *context); static string GetOutputFileContent(const string& generator_name, const string& parameter, const string& file, + const string& parsed_file_list, const string& first_message_name); }; diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc index 9fcb1314..34317b1f 100644 --- a/src/google/protobuf/compiler/parser.cc +++ b/src/google/protobuf/compiler/parser.cc @@ -250,20 +250,69 @@ void Parser::AddError(const string& error) { AddError(input_->current().line, input_->current().column, error); } -void Parser::RecordLocation( - const Message* descriptor, - DescriptorPool::ErrorCollector::ErrorLocation location, - int line, int column) { - if (source_location_table_ != NULL) { - source_location_table_->Add(descriptor, location, line, column); +// ------------------------------------------------------------------- + +Parser::LocationRecorder::LocationRecorder(Parser* parser) + : parser_(parser), + location_(parser_->source_code_info_->add_location()) { + location_->add_span(parser_->input_->current().line); + location_->add_span(parser_->input_->current().column); +} + +Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent) { + Init(parent); +} + +Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent, + int path1) { + Init(parent); + AddPath(path1); +} + +Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent, + int path1, int path2) { + Init(parent); + AddPath(path1); + AddPath(path2); +} + +void Parser::LocationRecorder::Init(const LocationRecorder& parent) { + parser_ = parent.parser_; + location_ = parser_->source_code_info_->add_location(); + location_->mutable_path()->CopyFrom(parent.location_->path()); + + location_->add_span(parser_->input_->current().line); + location_->add_span(parser_->input_->current().column); +} + +Parser::LocationRecorder::~LocationRecorder() { + if (location_->span_size() <= 2) { + EndAt(parser_->input_->previous()); } } -void Parser::RecordLocation( - const Message* descriptor, +void Parser::LocationRecorder::AddPath(int path_component) { + location_->add_path(path_component); +} + +void Parser::LocationRecorder::StartAt(const io::Tokenizer::Token& token) { + location_->set_span(0, token.line); + location_->set_span(1, token.column); +} + +void Parser::LocationRecorder::EndAt(const io::Tokenizer::Token& token) { + if (token.line != location_->span(0)) { + location_->add_span(token.line); + } + location_->add_span(token.end_column); +} + +void Parser::LocationRecorder::RecordLegacyLocation(const Message* descriptor, DescriptorPool::ErrorCollector::ErrorLocation location) { - RecordLocation(descriptor, location, - input_->current().line, input_->current().column); + if (parser_->source_location_table_ != NULL) { + parser_->source_location_table_->Add( + descriptor, location, location_->span(0), location_->span(1)); + } } // ------------------------------------------------------------------- @@ -308,38 +357,51 @@ bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) { had_errors_ = false; syntax_identifier_.clear(); + // Note that |file| could be NULL at this point if + // stop_after_syntax_identifier_ is true. So, we conservatively allocate + // SourceCodeInfo on the stack, then swap it into the FileDescriptorProto + // later on. + SourceCodeInfo source_code_info; + source_code_info_ = &source_code_info; + if (LookingAtType(io::Tokenizer::TYPE_START)) { // Advance to first token. input_->Next(); } - if (require_syntax_identifier_ || LookingAt("syntax")) { - if (!ParseSyntaxIdentifier()) { - // Don't attempt to parse the file if we didn't recognize the syntax - // identifier. - return false; + { + LocationRecorder root_location(this); + + if (require_syntax_identifier_ || LookingAt("syntax")) { + if (!ParseSyntaxIdentifier()) { + // Don't attempt to parse the file if we didn't recognize the syntax + // identifier. + return false; + } + } else if (!stop_after_syntax_identifier_) { + syntax_identifier_ = "proto2"; } - } else if (!stop_after_syntax_identifier_) { - syntax_identifier_ = "proto2"; - } - if (stop_after_syntax_identifier_) return !had_errors_; + if (stop_after_syntax_identifier_) return !had_errors_; - // Repeatedly parse statements until we reach the end of the file. - while (!AtEnd()) { - if (!ParseTopLevelStatement(file)) { - // This statement failed to parse. Skip it, but keep looping to parse - // other statements. - SkipStatement(); + // Repeatedly parse statements until we reach the end of the file. + while (!AtEnd()) { + if (!ParseTopLevelStatement(file, root_location)) { + // This statement failed to parse. Skip it, but keep looping to parse + // other statements. + SkipStatement(); - if (LookingAt("}")) { - AddError("Unmatched \"}\"."); - input_->Next(); + if (LookingAt("}")) { + AddError("Unmatched \"}\"."); + input_->Next(); + } } } } input_ = NULL; + source_code_info_ = NULL; + source_code_info.Swap(file->mutable_source_code_info()); return !had_errors_; } @@ -363,25 +425,40 @@ bool Parser::ParseSyntaxIdentifier() { return true; } -bool Parser::ParseTopLevelStatement(FileDescriptorProto* file) { +bool Parser::ParseTopLevelStatement(FileDescriptorProto* file, + const LocationRecorder& root_location) { if (TryConsume(";")) { // empty statement; ignore return true; } else if (LookingAt("message")) { - return ParseMessageDefinition(file->add_message_type()); + LocationRecorder location(root_location, + FileDescriptorProto::kMessageTypeFieldNumber, file->message_type_size()); + return ParseMessageDefinition(file->add_message_type(), location); } else if (LookingAt("enum")) { - return ParseEnumDefinition(file->add_enum_type()); + LocationRecorder location(root_location, + FileDescriptorProto::kEnumTypeFieldNumber, file->enum_type_size()); + return ParseEnumDefinition(file->add_enum_type(), location); } else if (LookingAt("service")) { - return ParseServiceDefinition(file->add_service()); + LocationRecorder location(root_location, + FileDescriptorProto::kServiceFieldNumber, file->service_size()); + return ParseServiceDefinition(file->add_service(), location); } else if (LookingAt("extend")) { + LocationRecorder location(root_location, + FileDescriptorProto::kExtensionFieldNumber); return ParseExtend(file->mutable_extension(), - file->mutable_message_type()); + file->mutable_message_type(), + root_location, + FileDescriptorProto::kMessageTypeFieldNumber, + location); } else if (LookingAt("import")) { - return ParseImport(file->add_dependency()); + int index = file->dependency_size(); + return ParseImport(file->add_dependency(), root_location, index); } else if (LookingAt("package")) { - return ParsePackage(file); + return ParsePackage(file, root_location); } else if (LookingAt("option")) { - return ParseOption(file->mutable_options()); + LocationRecorder location(root_location, + FileDescriptorProto::kOptionsFieldNumber); + return ParseOption(file->mutable_options(), location); } else { AddError("Expected top-level statement (e.g. \"message\")."); return false; @@ -391,15 +468,22 @@ bool Parser::ParseTopLevelStatement(FileDescriptorProto* file) { // ------------------------------------------------------------------- // Messages -bool Parser::ParseMessageDefinition(DescriptorProto* message) { +bool Parser::ParseMessageDefinition(DescriptorProto* message, + const LocationRecorder& message_location) { DO(Consume("message")); - RecordLocation(message, DescriptorPool::ErrorCollector::NAME); - DO(ConsumeIdentifier(message->mutable_name(), "Expected message name.")); - DO(ParseMessageBlock(message)); + { + LocationRecorder location(message_location, + DescriptorProto::kNameFieldNumber); + location.RecordLegacyLocation( + message, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(message->mutable_name(), "Expected message name.")); + } + DO(ParseMessageBlock(message, message_location)); return true; } -bool Parser::ParseMessageBlock(DescriptorProto* message) { +bool Parser::ParseMessageBlock(DescriptorProto* message, + const LocationRecorder& message_location) { DO(Consume("{")); while (!TryConsume("}")) { @@ -408,7 +492,7 @@ bool Parser::ParseMessageBlock(DescriptorProto* message) { return false; } - if (!ParseMessageStatement(message)) { + if (!ParseMessageStatement(message, message_location)) { // This statement failed to parse. Skip it, but keep looping to parse // other statements. SkipStatement(); @@ -418,66 +502,133 @@ bool Parser::ParseMessageBlock(DescriptorProto* message) { return true; } -bool Parser::ParseMessageStatement(DescriptorProto* message) { +bool Parser::ParseMessageStatement(DescriptorProto* message, + const LocationRecorder& message_location) { if (TryConsume(";")) { // empty statement; ignore return true; } else if (LookingAt("message")) { - return ParseMessageDefinition(message->add_nested_type()); + LocationRecorder location(message_location, + DescriptorProto::kNestedTypeFieldNumber, + message->nested_type_size()); + return ParseMessageDefinition(message->add_nested_type(), location); } else if (LookingAt("enum")) { - return ParseEnumDefinition(message->add_enum_type()); + LocationRecorder location(message_location, + DescriptorProto::kEnumTypeFieldNumber, + message->enum_type_size()); + return ParseEnumDefinition(message->add_enum_type(), location); } else if (LookingAt("extensions")) { - return ParseExtensions(message); + LocationRecorder location(message_location, + DescriptorProto::kExtensionRangeFieldNumber); + return ParseExtensions(message, location); } else if (LookingAt("extend")) { + LocationRecorder location(message_location, + DescriptorProto::kExtensionFieldNumber); return ParseExtend(message->mutable_extension(), - message->mutable_nested_type()); + message->mutable_nested_type(), + message_location, + DescriptorProto::kNestedTypeFieldNumber, + location); } else if (LookingAt("option")) { - return ParseOption(message->mutable_options()); + LocationRecorder location(message_location, + DescriptorProto::kOptionsFieldNumber); + return ParseOption(message->mutable_options(), location); } else { + LocationRecorder location(message_location, + DescriptorProto::kFieldFieldNumber, + message->field_size()); return ParseMessageField(message->add_field(), - message->mutable_nested_type()); + message->mutable_nested_type(), + message_location, + DescriptorProto::kNestedTypeFieldNumber, + location); } } bool Parser::ParseMessageField(FieldDescriptorProto* field, - RepeatedPtrField<DescriptorProto>* messages) { + RepeatedPtrField<DescriptorProto>* messages, + const LocationRecorder& parent_location, + int location_field_number_for_nested_type, + const LocationRecorder& field_location) { // Parse label and type. - FieldDescriptorProto::Label label; - DO(ParseLabel(&label)); - field->set_label(label); - - RecordLocation(field, DescriptorPool::ErrorCollector::TYPE); - FieldDescriptorProto::Type type = FieldDescriptorProto::TYPE_INT32; - string type_name; - DO(ParseType(&type, &type_name)); - if (type_name.empty()) { - field->set_type(type); - } else { - field->set_type_name(type_name); + io::Tokenizer::Token label_token = input_->current(); + { + LocationRecorder location(field_location, + FieldDescriptorProto::kLabelFieldNumber); + FieldDescriptorProto::Label label; + DO(ParseLabel(&label)); + field->set_label(label); + } + + { + LocationRecorder location(field_location); // add path later + location.RecordLegacyLocation(field, DescriptorPool::ErrorCollector::TYPE); + + FieldDescriptorProto::Type type = FieldDescriptorProto::TYPE_INT32; + string type_name; + DO(ParseType(&type, &type_name)); + if (type_name.empty()) { + location.AddPath(FieldDescriptorProto::kTypeFieldNumber); + field->set_type(type); + } else { + location.AddPath(FieldDescriptorProto::kTypeNameFieldNumber); + field->set_type_name(type_name); + } } // Parse name and '='. - RecordLocation(field, DescriptorPool::ErrorCollector::NAME); io::Tokenizer::Token name_token = input_->current(); - DO(ConsumeIdentifier(field->mutable_name(), "Expected field name.")); + { + LocationRecorder location(field_location, + FieldDescriptorProto::kNameFieldNumber); + location.RecordLegacyLocation(field, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(field->mutable_name(), "Expected field name.")); + } DO(Consume("=", "Missing field number.")); // Parse field number. - RecordLocation(field, DescriptorPool::ErrorCollector::NUMBER); - int number; - DO(ConsumeInteger(&number, "Expected field number.")); - field->set_number(number); + { + LocationRecorder location(field_location, + FieldDescriptorProto::kNumberFieldNumber); + location.RecordLegacyLocation( + field, DescriptorPool::ErrorCollector::NUMBER); + int number; + DO(ConsumeInteger(&number, "Expected field number.")); + field->set_number(number); + } // Parse options. - DO(ParseFieldOptions(field)); + DO(ParseFieldOptions(field, field_location)); // Deal with groups. - if (type_name.empty() && type == FieldDescriptorProto::TYPE_GROUP) { + if (field->has_type() && field->type() == FieldDescriptorProto::TYPE_GROUP) { + // Awkward: Since a group declares both a message type and a field, we + // have to create overlapping locations. + LocationRecorder group_location(parent_location); + group_location.StartAt(label_token); + group_location.AddPath(location_field_number_for_nested_type); + group_location.AddPath(messages->size()); + DescriptorProto* group = messages->Add(); group->set_name(field->name()); + // Record name location to match the field name's location. - RecordLocation(group, DescriptorPool::ErrorCollector::NAME, - name_token.line, name_token.column); + { + LocationRecorder location(group_location, + DescriptorProto::kNameFieldNumber); + location.StartAt(name_token); + location.EndAt(name_token); + location.RecordLegacyLocation( + group, DescriptorPool::ErrorCollector::NAME); + } + + // The field's type_name also comes from the name. Confusing! + { + LocationRecorder location(field_location, + FieldDescriptorProto::kTypeNameFieldNumber); + location.StartAt(name_token); + location.EndAt(name_token); + } // As a hack for backwards-compatibility, we force the group name to start // with a capital letter and lower-case the field name. New code should @@ -490,7 +641,7 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field, field->set_type_name(group->name()); if (LookingAt("{")) { - DO(ParseMessageBlock(group)); + DO(ParseMessageBlock(group, group_location)); } else { AddError("Missing group body."); return false; @@ -502,15 +653,23 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field, return true; } -bool Parser::ParseFieldOptions(FieldDescriptorProto* field) { - if (!TryConsume("[")) return true; +bool Parser::ParseFieldOptions(FieldDescriptorProto* field, + const LocationRecorder& field_location) { + if (!LookingAt("[")) return true; + + LocationRecorder location(field_location, + FieldDescriptorProto::kOptionsFieldNumber); + + DO(Consume("[")); // Parse field options. do { if (LookingAt("default")) { - DO(ParseDefaultAssignment(field)); + // We intentionally pass field_location rather than location here, since + // the default value is not actually an option. + DO(ParseDefaultAssignment(field, field_location)); } else { - DO(ParseOptionAssignment(field->mutable_options())); + DO(ParseOptionAssignment(field->mutable_options(), location)); } } while (TryConsume(",")); @@ -518,7 +677,8 @@ bool Parser::ParseFieldOptions(FieldDescriptorProto* field) { return true; } -bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field) { +bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field, + const LocationRecorder& field_location) { if (field->has_default_value()) { AddError("Already set option \"default\"."); field->clear_default_value(); @@ -527,7 +687,10 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field) { DO(Consume("default")); DO(Consume("=")); - RecordLocation(field, DescriptorPool::ErrorCollector::DEFAULT_VALUE); + LocationRecorder location(field_location, + FieldDescriptorProto::kDefaultValueFieldNumber); + location.RecordLegacyLocation( + field, DescriptorPool::ErrorCollector::DEFAULT_VALUE); string* default_value = field->mutable_default_value(); if (!field->has_type()) { @@ -634,26 +797,35 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field) { return true; } -bool Parser::ParseOptionNamePart(UninterpretedOption* uninterpreted_option) { +bool Parser::ParseOptionNamePart(UninterpretedOption* uninterpreted_option, + const LocationRecorder& part_location) { UninterpretedOption::NamePart* name = uninterpreted_option->add_name(); string identifier; // We parse identifiers into this string. if (LookingAt("(")) { // This is an extension. DO(Consume("(")); - // An extension name consists of dot-separated identifiers, and may begin - // with a dot. - if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) { - DO(ConsumeIdentifier(&identifier, "Expected identifier.")); - name->mutable_name_part()->append(identifier); - } - while (LookingAt(".")) { - DO(Consume(".")); - name->mutable_name_part()->append("."); - DO(ConsumeIdentifier(&identifier, "Expected identifier.")); - name->mutable_name_part()->append(identifier); + + { + LocationRecorder location( + part_location, UninterpretedOption::NamePart::kNamePartFieldNumber); + // An extension name consists of dot-separated identifiers, and may begin + // with a dot. + if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) { + DO(ConsumeIdentifier(&identifier, "Expected identifier.")); + name->mutable_name_part()->append(identifier); + } + while (LookingAt(".")) { + DO(Consume(".")); + name->mutable_name_part()->append("."); + DO(ConsumeIdentifier(&identifier, "Expected identifier.")); + name->mutable_name_part()->append(identifier); + } } + DO(Consume(")")); name->set_is_extension(true); } else { // This is a regular field. + LocationRecorder location( + part_location, UninterpretedOption::NamePart::kNamePartFieldNumber); DO(ConsumeIdentifier(&identifier, "Expected identifier.")); name->mutable_name_part()->append(identifier); name->set_is_extension(false); @@ -661,34 +833,75 @@ bool Parser::ParseOptionNamePart(UninterpretedOption* uninterpreted_option) { return true; } +bool Parser::ParseUninterpretedBlock(string* value) { + // Note that enclosing braces are not added to *value. + DO(Consume("{")); + int brace_depth = 1; + while (!AtEnd()) { + if (LookingAt("{")) { + brace_depth++; + } else if (LookingAt("}")) { + brace_depth--; + if (brace_depth == 0) { + input_->Next(); + return true; + } + } + // TODO(sanjay): Interpret line/column numbers to preserve formatting + if (!value->empty()) value->push_back(' '); + value->append(input_->current().text); + input_->Next(); + } + AddError("Unexpected end of stream while parsing aggregate value."); + return false; +} + // We don't interpret the option here. Instead we store it in an // UninterpretedOption, to be interpreted later. -bool Parser::ParseOptionAssignment(Message* options) { +bool Parser::ParseOptionAssignment(Message* options, + const LocationRecorder& options_location) { // Create an entry in the uninterpreted_option field. const FieldDescriptor* uninterpreted_option_field = options->GetDescriptor()-> FindFieldByName("uninterpreted_option"); GOOGLE_CHECK(uninterpreted_option_field != NULL) << "No field named \"uninterpreted_option\" in the Options proto."; + const Reflection* reflection = options->GetReflection(); + + LocationRecorder location( + options_location, uninterpreted_option_field->number(), + reflection->FieldSize(*options, uninterpreted_option_field)); + UninterpretedOption* uninterpreted_option = down_cast<UninterpretedOption*>( options->GetReflection()->AddMessage(options, uninterpreted_option_field)); // Parse dot-separated name. - RecordLocation(uninterpreted_option, - DescriptorPool::ErrorCollector::OPTION_NAME); - - DO(ParseOptionNamePart(uninterpreted_option)); + { + LocationRecorder name_location(location, + UninterpretedOption::kNameFieldNumber); + name_location.RecordLegacyLocation( + uninterpreted_option, DescriptorPool::ErrorCollector::OPTION_NAME); + + { + LocationRecorder part_location(name_location, + uninterpreted_option->name_size()); + DO(ParseOptionNamePart(uninterpreted_option, part_location)); + } - while (LookingAt(".")) { - DO(Consume(".")); - DO(ParseOptionNamePart(uninterpreted_option)); + while (LookingAt(".")) { + DO(Consume(".")); + LocationRecorder part_location(name_location, + uninterpreted_option->name_size()); + DO(ParseOptionNamePart(uninterpreted_option, part_location)); + } } DO(Consume("=")); - RecordLocation(uninterpreted_option, - DescriptorPool::ErrorCollector::OPTION_VALUE); + LocationRecorder value_location(location); + value_location.RecordLegacyLocation( + uninterpreted_option, DescriptorPool::ErrorCollector::OPTION_VALUE); // All values are a single token, except for negative numbers, which consist // of a single '-' symbol, followed by a positive number. @@ -704,6 +917,7 @@ bool Parser::ParseOptionAssignment(Message* options) { return false; case io::Tokenizer::TYPE_IDENTIFIER: { + value_location.AddPath(UninterpretedOption::kIdentifierValueFieldNumber); if (is_negative) { AddError("Invalid '-' symbol before identifier."); return false; @@ -720,15 +934,19 @@ bool Parser::ParseOptionAssignment(Message* options) { is_negative ? static_cast<uint64>(kint64max) + 1 : kuint64max; DO(ConsumeInteger64(max_value, &value, "Expected integer.")); if (is_negative) { - uninterpreted_option->set_negative_int_value( - -static_cast<int64>(value)); + value_location.AddPath( + UninterpretedOption::kNegativeIntValueFieldNumber); + uninterpreted_option->set_negative_int_value(-static_cast<int64>(value)); } else { + value_location.AddPath( + UninterpretedOption::kPositiveIntValueFieldNumber); uninterpreted_option->set_positive_int_value(value); } break; } case io::Tokenizer::TYPE_FLOAT: { + value_location.AddPath(UninterpretedOption::kDoubleValueFieldNumber); double value; DO(ConsumeNumber(&value, "Expected number.")); uninterpreted_option->set_double_value(is_negative ? -value : value); @@ -736,6 +954,7 @@ bool Parser::ParseOptionAssignment(Message* options) { } case io::Tokenizer::TYPE_STRING: { + value_location.AddPath(UninterpretedOption::kStringValueFieldNumber); if (is_negative) { AddError("Invalid '-' symbol before string."); return false; @@ -747,31 +966,57 @@ bool Parser::ParseOptionAssignment(Message* options) { } case io::Tokenizer::TYPE_SYMBOL: - AddError("Expected option value."); - return false; + if (LookingAt("{")) { + value_location.AddPath(UninterpretedOption::kAggregateValueFieldNumber); + DO(ParseUninterpretedBlock( + uninterpreted_option->mutable_aggregate_value())); + } else { + AddError("Expected option value."); + return false; + } + break; } return true; } -bool Parser::ParseExtensions(DescriptorProto* message) { +bool Parser::ParseExtensions(DescriptorProto* message, + const LocationRecorder& extensions_location) { // Parse the declaration. DO(Consume("extensions")); do { + // Note that kExtensionRangeFieldNumber was already pushed by the parent. + LocationRecorder location(extensions_location, + message->extension_range_size()); + DescriptorProto::ExtensionRange* range = message->add_extension_range(); - RecordLocation(range, DescriptorPool::ErrorCollector::NUMBER); + location.RecordLegacyLocation( + range, DescriptorPool::ErrorCollector::NUMBER); int start, end; - DO(ConsumeInteger(&start, "Expected field number range.")); + io::Tokenizer::Token start_token; + + { + LocationRecorder start_location( + location, DescriptorProto::ExtensionRange::kStartFieldNumber); + start_token = input_->current(); + DO(ConsumeInteger(&start, "Expected field number range.")); + } if (TryConsume("to")) { + LocationRecorder end_location( + location, DescriptorProto::ExtensionRange::kEndFieldNumber); if (TryConsume("max")) { end = FieldDescriptor::kMaxNumber; } else { DO(ConsumeInteger(&end, "Expected integer.")); } } else { + LocationRecorder end_location( + location, DescriptorProto::ExtensionRange::kEndFieldNumber); + end_location.StartAt(start_token); + end_location.EndAt(start_token); end = start; } @@ -788,16 +1033,17 @@ bool Parser::ParseExtensions(DescriptorProto* message) { } bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions, - RepeatedPtrField<DescriptorProto>* messages) { + RepeatedPtrField<DescriptorProto>* messages, + const LocationRecorder& parent_location, + int location_field_number_for_nested_type, + const LocationRecorder& extend_location) { DO(Consume("extend")); - // We expect to see at least one extension field defined in the extend block. - // We need to create it now so we can record the extendee's location. - FieldDescriptorProto* first_field = extensions->Add(); - // Parse the extendee type. - RecordLocation(first_field, DescriptorPool::ErrorCollector::EXTENDEE); - DO(ParseUserDefinedType(first_field->mutable_extendee())); + io::Tokenizer::Token extendee_start = input_->current(); + string extendee; + DO(ParseUserDefinedType(&extendee)); + io::Tokenizer::Token extendee_end = input_->previous(); // Parse the block. DO(Consume("{")); @@ -810,16 +1056,29 @@ bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions, return false; } - FieldDescriptorProto* field; - if (is_first) { - field = first_field; - is_first = false; - } else { - field = extensions->Add(); - field->set_extendee(first_field->extendee()); + // Note that kExtensionFieldNumber was already pushed by the parent. + LocationRecorder location(extend_location, extensions->size()); + + FieldDescriptorProto* field = extensions->Add(); + + { + LocationRecorder extendee_location( + location, FieldDescriptorProto::kExtendeeFieldNumber); + extendee_location.StartAt(extendee_start); + extendee_location.EndAt(extendee_end); + + if (is_first) { + extendee_location.RecordLegacyLocation( + field, DescriptorPool::ErrorCollector::EXTENDEE); + is_first = false; + } } - if (!ParseMessageField(field, messages)) { + field->set_extendee(extendee); + + if (!ParseMessageField(field, messages, parent_location, + location_field_number_for_nested_type, + location)) { // This statement failed to parse. Skip it, but keep looping to parse // other statements. SkipStatement(); @@ -832,15 +1091,24 @@ bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions, // ------------------------------------------------------------------- // Enums -bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type) { +bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type, + const LocationRecorder& enum_location) { DO(Consume("enum")); - RecordLocation(enum_type, DescriptorPool::ErrorCollector::NAME); - DO(ConsumeIdentifier(enum_type->mutable_name(), "Expected enum name.")); - DO(ParseEnumBlock(enum_type)); + + { + LocationRecorder location(enum_location, + EnumDescriptorProto::kNameFieldNumber); + location.RecordLegacyLocation( + enum_type, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(enum_type->mutable_name(), "Expected enum name.")); + } + + DO(ParseEnumBlock(enum_type, enum_location)); return true; } -bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type) { +bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type, + const LocationRecorder& enum_location) { DO(Consume("{")); while (!TryConsume("}")) { @@ -849,7 +1117,7 @@ bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type) { return false; } - if (!ParseEnumStatement(enum_type)) { + if (!ParseEnumStatement(enum_type, enum_location)) { // This statement failed to parse. Skip it, but keep looping to parse // other statements. SkipStatement(); @@ -859,41 +1127,69 @@ bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type) { return true; } -bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type) { +bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type, + const LocationRecorder& enum_location) { if (TryConsume(";")) { // empty statement; ignore return true; } else if (LookingAt("option")) { - return ParseOption(enum_type->mutable_options()); + LocationRecorder location(enum_location, + EnumDescriptorProto::kOptionsFieldNumber); + return ParseOption(enum_type->mutable_options(), location); } else { - return ParseEnumConstant(enum_type->add_value()); + LocationRecorder location(enum_location, + EnumDescriptorProto::kValueFieldNumber, enum_type->value_size()); + return ParseEnumConstant(enum_type->add_value(), location); } } -bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value) { - RecordLocation(enum_value, DescriptorPool::ErrorCollector::NAME); - DO(ConsumeIdentifier(enum_value->mutable_name(), - "Expected enum constant name.")); +bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value, + const LocationRecorder& enum_value_location) { + // Parse name. + { + LocationRecorder location(enum_value_location, + EnumValueDescriptorProto::kNameFieldNumber); + location.RecordLegacyLocation( + enum_value, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(enum_value->mutable_name(), + "Expected enum constant name.")); + } + DO(Consume("=", "Missing numeric value for enum constant.")); - bool is_negative = TryConsume("-"); - int number; - DO(ConsumeInteger(&number, "Expected integer.")); - if (is_negative) number *= -1; - enum_value->set_number(number); + // Parse value. + { + LocationRecorder location( + enum_value_location, EnumValueDescriptorProto::kNumberFieldNumber); + location.RecordLegacyLocation( + enum_value, DescriptorPool::ErrorCollector::NUMBER); + + bool is_negative = TryConsume("-"); + int number; + DO(ConsumeInteger(&number, "Expected integer.")); + if (is_negative) number *= -1; + enum_value->set_number(number); + } - DO(ParseEnumConstantOptions(enum_value)); + DO(ParseEnumConstantOptions(enum_value, enum_value_location)); DO(Consume(";")); return true; } -bool Parser::ParseEnumConstantOptions(EnumValueDescriptorProto* value) { - if (!TryConsume("[")) return true; +bool Parser::ParseEnumConstantOptions( + EnumValueDescriptorProto* value, + const LocationRecorder& enum_value_location) { + if (!LookingAt("[")) return true; + + LocationRecorder location( + enum_value_location, EnumValueDescriptorProto::kOptionsFieldNumber); + + DO(Consume("[")); do { - DO(ParseOptionAssignment(value->mutable_options())); + DO(ParseOptionAssignment(value->mutable_options(), location)); } while (TryConsume(",")); DO(Consume("]")); @@ -903,15 +1199,24 @@ bool Parser::ParseEnumConstantOptions(EnumValueDescriptorProto* value) { // ------------------------------------------------------------------- // Services -bool Parser::ParseServiceDefinition(ServiceDescriptorProto* service) { +bool Parser::ParseServiceDefinition(ServiceDescriptorProto* service, + const LocationRecorder& service_location) { DO(Consume("service")); - RecordLocation(service, DescriptorPool::ErrorCollector::NAME); - DO(ConsumeIdentifier(service->mutable_name(), "Expected service name.")); - DO(ParseServiceBlock(service)); + + { + LocationRecorder location(service_location, + ServiceDescriptorProto::kNameFieldNumber); + location.RecordLegacyLocation( + service, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(service->mutable_name(), "Expected service name.")); + } + + DO(ParseServiceBlock(service, service_location)); return true; } -bool Parser::ParseServiceBlock(ServiceDescriptorProto* service) { +bool Parser::ParseServiceBlock(ServiceDescriptorProto* service, + const LocationRecorder& service_location) { DO(Consume("{")); while (!TryConsume("}")) { @@ -920,7 +1225,7 @@ bool Parser::ParseServiceBlock(ServiceDescriptorProto* service) { return false; } - if (!ParseServiceStatement(service)) { + if (!ParseServiceStatement(service, service_location)) { // This statement failed to parse. Skip it, but keep looping to parse // other statements. SkipStatement(); @@ -930,33 +1235,55 @@ bool Parser::ParseServiceBlock(ServiceDescriptorProto* service) { return true; } -bool Parser::ParseServiceStatement(ServiceDescriptorProto* service) { +bool Parser::ParseServiceStatement(ServiceDescriptorProto* service, + const LocationRecorder& service_location) { if (TryConsume(";")) { // empty statement; ignore return true; } else if (LookingAt("option")) { - return ParseOption(service->mutable_options()); + LocationRecorder location( + service_location, ServiceDescriptorProto::kOptionsFieldNumber); + return ParseOption(service->mutable_options(), location); } else { - return ParseServiceMethod(service->add_method()); + LocationRecorder location(service_location, + ServiceDescriptorProto::kMethodFieldNumber, service->method_size()); + return ParseServiceMethod(service->add_method(), location); } } -bool Parser::ParseServiceMethod(MethodDescriptorProto* method) { +bool Parser::ParseServiceMethod(MethodDescriptorProto* method, + const LocationRecorder& method_location) { DO(Consume("rpc")); - RecordLocation(method, DescriptorPool::ErrorCollector::NAME); - DO(ConsumeIdentifier(method->mutable_name(), "Expected method name.")); + + { + LocationRecorder location(method_location, + MethodDescriptorProto::kNameFieldNumber); + location.RecordLegacyLocation( + method, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(method->mutable_name(), "Expected method name.")); + } // Parse input type. DO(Consume("(")); - RecordLocation(method, DescriptorPool::ErrorCollector::INPUT_TYPE); - DO(ParseUserDefinedType(method->mutable_input_type())); + { + LocationRecorder location(method_location, + MethodDescriptorProto::kInputTypeFieldNumber); + location.RecordLegacyLocation( + method, DescriptorPool::ErrorCollector::INPUT_TYPE); + DO(ParseUserDefinedType(method->mutable_input_type())); + } DO(Consume(")")); // Parse output type. DO(Consume("returns")); DO(Consume("(")); - RecordLocation(method, DescriptorPool::ErrorCollector::OUTPUT_TYPE); - DO(ParseUserDefinedType(method->mutable_output_type())); + { + LocationRecorder location(method_location, + MethodDescriptorProto::kOutputTypeFieldNumber); + location.RecordLegacyLocation( + method, DescriptorPool::ErrorCollector::OUTPUT_TYPE); + DO(ParseUserDefinedType(method->mutable_output_type())); + } DO(Consume(")")); if (TryConsume("{")) { @@ -970,7 +1297,9 @@ bool Parser::ParseServiceMethod(MethodDescriptorProto* method) { if (TryConsume(";")) { // empty statement; ignore } else { - if (!ParseOption(method->mutable_options())) { + LocationRecorder location(method_location, + MethodDescriptorProto::kOptionsFieldNumber); + if (!ParseOption(method->mutable_options(), location)) { // This statement failed to parse. Skip it, but keep looping to // parse other statements. SkipStatement(); @@ -1054,7 +1383,8 @@ bool Parser::ParseUserDefinedType(string* type_name) { // =================================================================== -bool Parser::ParsePackage(FileDescriptorProto* file) { +bool Parser::ParsePackage(FileDescriptorProto* file, + const LocationRecorder& root_location) { if (file->has_package()) { AddError("Multiple package definitions."); // Don't append the new package to the old one. Just replace it. Not @@ -1064,31 +1394,43 @@ bool Parser::ParsePackage(FileDescriptorProto* file) { DO(Consume("package")); - RecordLocation(file, DescriptorPool::ErrorCollector::NAME); + { + LocationRecorder location(root_location, + FileDescriptorProto::kPackageFieldNumber); + location.RecordLegacyLocation(file, DescriptorPool::ErrorCollector::NAME); - while (true) { - string identifier; - DO(ConsumeIdentifier(&identifier, "Expected identifier.")); - file->mutable_package()->append(identifier); - if (!TryConsume(".")) break; - file->mutable_package()->append("."); + while (true) { + string identifier; + DO(ConsumeIdentifier(&identifier, "Expected identifier.")); + file->mutable_package()->append(identifier); + if (!TryConsume(".")) break; + file->mutable_package()->append("."); + } } DO(Consume(";")); return true; } -bool Parser::ParseImport(string* import_filename) { +bool Parser::ParseImport(string* import_filename, + const LocationRecorder& root_location, + int index) { DO(Consume("import")); - DO(ConsumeString(import_filename, - "Expected a string naming the file to import.")); + { + LocationRecorder location(root_location, + FileDescriptorProto::kDependencyFieldNumber, + index); + DO(ConsumeString(import_filename, + "Expected a string naming the file to import.")); + } DO(Consume(";")); return true; } -bool Parser::ParseOption(Message* options) { +bool Parser::ParseOption(Message* options, + const LocationRecorder& options_location) { DO(Consume("option")); - DO(ParseOptionAssignment(options)); + DO(ParseOptionAssignment(options, options_location)); DO(Consume(";")); return true; } diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h index 72c96d04..4cc90a29 100644 --- a/src/google/protobuf/compiler/parser.h +++ b/src/google/protobuf/compiler/parser.h @@ -74,6 +74,9 @@ class LIBPROTOBUF_EXPORT Parser { // Optional fetaures: + // DEPRECATED: New code should use the SourceCodeInfo embedded in the + // FileDescriptorProto. + // // Requests that locations of certain definitions be recorded to the given // SourceLocationTable while parsing. This can be used to look up exact line // and column numbers for errors reported by DescriptorPool during validation. @@ -82,7 +85,7 @@ class LIBPROTOBUF_EXPORT Parser { source_location_table_ = location_table; } - // Requsets that errors be recorded to the given ErrorCollector while + // Requests that errors be recorded to the given ErrorCollector while // parsing. Set to NULL (the default) to discard error messages. void RecordErrorsTo(io::ErrorCollector* error_collector) { error_collector_ = error_collector; @@ -180,16 +183,56 @@ class LIBPROTOBUF_EXPORT Parser { // of the current token. void AddError(const string& error); - // Record the given line and column and associate it with this descriptor - // in the SourceLocationTable. - void RecordLocation(const Message* descriptor, - DescriptorPool::ErrorCollector::ErrorLocation location, - int line, int column); - - // Record the current line and column and associate it with this descriptor - // in the SourceLocationTable. - void RecordLocation(const Message* descriptor, - DescriptorPool::ErrorCollector::ErrorLocation location); + // Records a location in the SourceCodeInfo.location table (see + // descriptor.proto). We use RAII to ensure that the start and end locations + // are recorded -- the constructor records the start location and the + // destructor records the end location. Since the parser is + // recursive-descent, this works out beautifully. + class LIBPROTOBUF_EXPORT LocationRecorder { + public: + // Construct the file's "root" location. + LocationRecorder(Parser* parser); + + // Construct a location that represents a declaration nested within the + // given parent. E.g. a field's location is nested within the location + // for a message type. The parent's path will be copied, so you should + // call AddPath() only to add the path components leading from the parent + // to the child (as opposed to leading from the root to the child). + LocationRecorder(const LocationRecorder& parent); + + // Convenience constructors that call AddPath() one or two times. + LocationRecorder(const LocationRecorder& parent, int path1); + LocationRecorder(const LocationRecorder& parent, int path1, int path2); + + ~LocationRecorder(); + + // Add a path component. See SourceCodeInfo.Location.path in + // descriptor.proto. + void AddPath(int path_component); + + // By default the location is considered to start at the current token at + // the time the LocationRecorder is created. StartAt() sets the start + // location to the given token instead. + void StartAt(const io::Tokenizer::Token& token); + + // By default the location is considered to end at the previous token at + // the time the LocationRecorder is destroyed. EndAt() sets the end + // location to the given token instead. + void EndAt(const io::Tokenizer::Token& token); + + // Records the start point of this location to the SourceLocationTable that + // was passed to RecordSourceLocationsTo(), if any. SourceLocationTable + // is an older way of keeping track of source locations which is still + // used in some places. + void RecordLegacyLocation(const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location); + + private: + Parser* parser_; + SourceCodeInfo::Location* location_; + + void Init(const LocationRecorder& parent); + }; // ================================================================= // Parsers for various language constructs @@ -210,50 +253,81 @@ class LIBPROTOBUF_EXPORT Parser { // makes logic much simpler for the caller. // Parse a top-level message, enum, service, etc. - bool ParseTopLevelStatement(FileDescriptorProto* file); + bool ParseTopLevelStatement(FileDescriptorProto* file, + const LocationRecorder& root_location); // Parse various language high-level language construrcts. - bool ParseMessageDefinition(DescriptorProto* message); - bool ParseEnumDefinition(EnumDescriptorProto* enum_type); - bool ParseServiceDefinition(ServiceDescriptorProto* service); - bool ParsePackage(FileDescriptorProto* file); - bool ParseImport(string* import_filename); - bool ParseOption(Message* options); + bool ParseMessageDefinition(DescriptorProto* message, + const LocationRecorder& message_location); + bool ParseEnumDefinition(EnumDescriptorProto* enum_type, + const LocationRecorder& enum_location); + bool ParseServiceDefinition(ServiceDescriptorProto* service, + const LocationRecorder& service_location); + bool ParsePackage(FileDescriptorProto* file, + const LocationRecorder& root_location); + bool ParseImport(string* import_filename, + const LocationRecorder& root_location, + int index); + bool ParseOption(Message* options, + const LocationRecorder& options_location); // These methods parse the contents of a message, enum, or service type and // add them to the given object. They consume the entire block including // the beginning and ending brace. - bool ParseMessageBlock(DescriptorProto* message); - bool ParseEnumBlock(EnumDescriptorProto* enum_type); - bool ParseServiceBlock(ServiceDescriptorProto* service); + bool ParseMessageBlock(DescriptorProto* message, + const LocationRecorder& message_location); + bool ParseEnumBlock(EnumDescriptorProto* enum_type, + const LocationRecorder& enum_location); + bool ParseServiceBlock(ServiceDescriptorProto* service, + const LocationRecorder& service_location); // Parse one statement within a message, enum, or service block, inclunding // final semicolon. - bool ParseMessageStatement(DescriptorProto* message); - bool ParseEnumStatement(EnumDescriptorProto* message); - bool ParseServiceStatement(ServiceDescriptorProto* message); + bool ParseMessageStatement(DescriptorProto* message, + const LocationRecorder& message_location); + bool ParseEnumStatement(EnumDescriptorProto* message, + const LocationRecorder& enum_location); + bool ParseServiceStatement(ServiceDescriptorProto* message, + const LocationRecorder& service_location); // Parse a field of a message. If the field is a group, its type will be // added to "messages". + // + // parent_location and location_field_number_for_nested_type are needed when + // parsing groups -- we need to generate a nested message type within the + // parent and record its location accordingly. Since the parent could be + // either a FileDescriptorProto or a DescriptorProto, we must pass in the + // correct field number to use. bool ParseMessageField(FieldDescriptorProto* field, - RepeatedPtrField<DescriptorProto>* messages); + RepeatedPtrField<DescriptorProto>* messages, + const LocationRecorder& parent_location, + int location_field_number_for_nested_type, + const LocationRecorder& field_location); // Parse an "extensions" declaration. - bool ParseExtensions(DescriptorProto* message); + bool ParseExtensions(DescriptorProto* message, + const LocationRecorder& extensions_location); - // Parse an "extend" declaration. + // Parse an "extend" declaration. (See also comments for + // ParseMessageField().) bool ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions, - RepeatedPtrField<DescriptorProto>* messages); + RepeatedPtrField<DescriptorProto>* messages, + const LocationRecorder& parent_location, + int location_field_number_for_nested_type, + const LocationRecorder& extend_location); // Parse a single enum value within an enum block. - bool ParseEnumConstant(EnumValueDescriptorProto* enum_value); + bool ParseEnumConstant(EnumValueDescriptorProto* enum_value, + const LocationRecorder& enum_value_location); // Parse enum constant options, i.e. the list in square brackets at the end // of the enum constant value definition. - bool ParseEnumConstantOptions(EnumValueDescriptorProto* value); + bool ParseEnumConstantOptions(EnumValueDescriptorProto* value, + const LocationRecorder& enum_value_location); // Parse a single method within a service definition. - bool ParseServiceMethod(MethodDescriptorProto* method); + bool ParseServiceMethod(MethodDescriptorProto* method, + const LocationRecorder& method_location); // Parse "required", "optional", or "repeated" and fill in "label" // with the value. @@ -269,28 +343,45 @@ class LIBPROTOBUF_EXPORT Parser { // Parses field options, i.e. the stuff in square brackets at the end // of a field definition. Also parses default value. - bool ParseFieldOptions(FieldDescriptorProto* field); + bool ParseFieldOptions(FieldDescriptorProto* field, + const LocationRecorder& field_location); // Parse the "default" option. This needs special handling because its // type is the field's type. - bool ParseDefaultAssignment(FieldDescriptorProto* field); + bool ParseDefaultAssignment(FieldDescriptorProto* field, + const LocationRecorder& field_location); // Parse a single option name/value pair, e.g. "ctype = CORD". The name // identifies a field of the given Message, and the value of that field // is set to the parsed value. - bool ParseOptionAssignment(Message* options); + bool ParseOptionAssignment(Message* options, + const LocationRecorder& options_location); // Parses a single part of a multipart option name. A multipart name consists // of names separated by dots. Each name is either an identifier or a series // of identifiers separated by dots and enclosed in parentheses. E.g., // "foo.(bar.baz).qux". - bool ParseOptionNamePart(UninterpretedOption* uninterpreted_option); + bool ParseOptionNamePart(UninterpretedOption* uninterpreted_option, + const LocationRecorder& part_location); + + // Parses a string surrounded by balanced braces. Strips off the outer + // braces and stores the enclosed string in *value. + // E.g., + // { foo } *value gets 'foo' + // { foo { bar: box } } *value gets 'foo { bar: box }' + // {} *value gets '' + // + // REQUIRES: LookingAt("{") + // When finished successfully, we are looking at the first token past + // the ending brace. + bool ParseUninterpretedBlock(string* value); // ================================================================= io::Tokenizer* input_; io::ErrorCollector* error_collector_; - SourceLocationTable* source_location_table_; + SourceCodeInfo* source_code_info_; + SourceLocationTable* source_location_table_; // legacy bool had_errors_; bool require_syntax_identifier_; bool stop_after_syntax_identifier_; @@ -302,6 +393,11 @@ class LIBPROTOBUF_EXPORT Parser { // A table mapping (descriptor, ErrorLocation) pairs -- as reported by // DescriptorPool when validating descriptors -- to line and column numbers // within the original source code. +// +// This is semi-obsolete: FileDescriptorProto.source_code_info now contains +// far more complete information about source locations. However, as of this +// writing you still need to use SourceLocationTable when integrating with +// DescriptorPool. class LIBPROTOBUF_EXPORT SourceLocationTable { public: SourceLocationTable(); diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc index e2262b8b..156c0dc3 100644 --- a/src/google/protobuf/compiler/parser_unittest.cc +++ b/src/google/protobuf/compiler/parser_unittest.cc @@ -34,6 +34,7 @@ #include <vector> #include <algorithm> +#include <map> #include <google/protobuf/compiler/parser.h> @@ -45,6 +46,7 @@ #include <google/protobuf/unittest.pb.h> #include <google/protobuf/stubs/strutil.h> #include <google/protobuf/stubs/substitute.h> +#include <google/protobuf/stubs/map-util.h> #include <google/protobuf/testing/googletest.h> #include <gtest/gtest.h> @@ -118,6 +120,9 @@ class ParserTest : public testing::Test { EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); ASSERT_EQ("", error_collector_.text_); + // We don't cover SourceCodeInfo in these tests. + actual.clear_source_code_info(); + // Parse the ASCII representation in order to canonicalize it. We could // just compare directly to actual.DebugString(), but that would require // that the caller precisely match the formatting that DebugString() @@ -930,6 +935,12 @@ TEST_F(ParseErrorTest, MultipleParseErrors) { "3:25: Expected \";\".\n"); } +TEST_F(ParseErrorTest, EofInAggregateValue) { + ExpectHasErrors( + "option (fileopt) = { i:100\n", + "1:0: Unexpected end of stream while parsing aggregate value.\n"); +} + // ------------------------------------------------------------------- // Enum errors @@ -1248,6 +1259,861 @@ TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) { } // =================================================================== +// SourceCodeInfo tests. + +// Follows a path -- as defined by SourceCodeInfo.Location.path -- from a +// message to a particular sub-field. +// * If the target is itself a message, sets *output_message to point at it, +// *output_field to NULL, and *output_index to -1. +// * Otherwise, if the target is an element of a repeated field, sets +// *output_message to the containing message, *output_field to the descriptor +// of the field, and *output_index to the index of the element. +// * Otherwise, the target is a field (possibly a repeated field, but not any +// one element). Sets *output_message to the containing message, +// *output_field to the descriptor of the field, and *output_index to -1. +// Returns true if the path was valid, false otherwise. A gTest failure is +// recorded before returning false. +bool FollowPath(const Message& root, + const int* path_begin, const int* path_end, + const Message** output_message, + const FieldDescriptor** output_field, + int* output_index) { + if (path_begin == path_end) { + // Path refers to this whole message. + *output_message = &root; + *output_field = NULL; + *output_index = -1; + return true; + } + + const Descriptor* descriptor = root.GetDescriptor(); + const Reflection* reflection = root.GetReflection(); + + const FieldDescriptor* field = descriptor->FindFieldByNumber(*path_begin); + + if (field == NULL) { + ADD_FAILURE() << descriptor->name() << " has no field number: " + << *path_begin; + return false; + } + + ++path_begin; + + if (field->is_repeated()) { + if (path_begin == path_end) { + // Path refers to the whole repeated field. + *output_message = &root; + *output_field = field; + *output_index = -1; + return true; + } + + int index = *path_begin++; + int size = reflection->FieldSize(root, field); + + if (index >= size) { + ADD_FAILURE() << descriptor->name() << "." << field->name() + << " has size " << size << ", but path contained index: " + << index; + return false; + } + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + // Descend into child message. + const Message& child = reflection->GetRepeatedMessage(root, field, index); + return FollowPath(child, path_begin, path_end, + output_message, output_field, output_index); + } else if (path_begin == path_end) { + // Path refers to this element. + *output_message = &root; + *output_field = field; + *output_index = index; + return true; + } else { + ADD_FAILURE() << descriptor->name() << "." << field->name() + << " is not a message; cannot descend into it."; + return false; + } + } else { + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + const Message& child = reflection->GetMessage(root, field); + return FollowPath(child, path_begin, path_end, + output_message, output_field, output_index); + } else if (path_begin == path_end) { + // Path refers to this field. + *output_message = &root; + *output_field = field; + *output_index = -1; + return true; + } else { + ADD_FAILURE() << descriptor->name() << "." << field->name() + << " is not a message; cannot descend into it."; + return false; + } + } +} + +// Split some text on line breaks. The line breaks are retained in the output, +// so each line (except the last) ends with a '\n', and the lines can be +// concatenated to produce the original text. +// +// I couldn't find the proper string utility function for this. Our +// split-on-delimiter functions don't include the delimiter in the output. +void SplitLines(const string& text, vector<string>* lines) { + string::size_type pos = 0; + + while (pos != string::npos) { + string::size_type last_pos = pos; + pos = text.find_first_of('\n', pos); + if (pos != string::npos) ++pos; + lines->push_back(text.substr(last_pos, pos - last_pos)); + } +} + +// Look for the given tags in the given text and construct a span (as defined +// by SourceCodeInfo.Location.span) from them. E.g. for text like: +// /*a*/message /*b*/Foo/*c*/ {}/*d*/ +// There are four tags: "a", "b", "c", and "d". The constructed span starts +// immediately after the start tag's trailing '/' and ends immediately before +// the end tags leading '/'. +void MakeExpectedSpan(const vector<string>& lines, + const string& start_tag, const string& end_tag, + RepeatedField<int>* output) { + string start_comment = "/*" + start_tag + "*/"; + string end_comment = "/*" + end_tag + "*/"; + + int start_line = -1; + int start_column = -1; + for (int i = 0; i < lines.size(); i++) { + string::size_type pos = lines[i].find(start_comment); + if (pos != string::npos) { + start_line = i; + start_column = pos + start_comment.size(); + break; + } + } + ASSERT_NE(start_line, -1) + << "Tag \"" << start_comment << "\" not found in text."; + + int end_line = -1; + int end_column = -1; + for (int i = start_line; i < lines.size(); i++) { + string::size_type pos = lines[i].find(end_comment); + if (pos != string::npos) { + end_line = i; + end_column = pos; + break; + } + } + ASSERT_NE(end_line, -1) + << "Tag \"" << end_comment << "\" not found in text."; + + output->Add(start_line); + output->Add(start_column); + if (end_line != start_line) output->Add(end_line); + output->Add(end_column); +} + +// Check if two spans are equal. +bool CompareSpans(const RepeatedField<int>& span1, + const RepeatedField<int>& span2) { + if (span1.size() != span2.size()) return false; + for (int i = 0; i < span1.size(); i++) { + if (span1.Get(i) != span2.Get(i)) return false; + } + return true; +} + +// Test fixture for source info tests, which check that source locations are +// recorded correctly in FileDescriptorProto.source_code_info.location. +class SourceInfoTest : public ParserTest { + protected: + // The parsed file (initialized by Parse()). + FileDescriptorProto file_; + + // Parse the given text as a .proto file and populate the spans_ map with + // all the source location spans in its SourceCodeInfo table. + bool Parse(const char* text) { + SetupParser(text); + SplitLines(text, &lines_); + if (!parser_->Parse(input_.get(), &file_)) { + return false; + } + + const SourceCodeInfo& source_info = file_.source_code_info(); + for (int i = 0; i < source_info.location_size(); i++) { + const SourceCodeInfo::Location& location = source_info.location(i); + const Message* descriptor_proto = NULL; + const FieldDescriptor* field = NULL; + int index = 0; + if (!FollowPath(file_, location.path().begin(), location.path().end(), + &descriptor_proto, &field, &index)) { + return false; + } + + spans_.insert(make_pair(SpanKey(*descriptor_proto, field, index), + &location)); + } + + return true; + } + + virtual void TearDown() { + EXPECT_TRUE(spans_.empty()) + << "Forgot to call HasSpan() for:\n" + << spans_.begin()->second->DebugString(); + } + + // ----------------------------------------------------------------- + // HasSpan() checks that the span of source code delimited by the given + // tags (comments) correspond via the SourceCodeInfo table to the given + // part of the FileDescriptorProto. (If unclear, look at the actual tests; + // it should quickly become obvious.) + + bool HasSpan(const char* start_tag, const char* end_tag, + const Message& descriptor_proto) { + return HasSpan(start_tag, end_tag, descriptor_proto, NULL, -1); + } + + bool HasSpan(const char* start_tag, const char* end_tag, + const Message& descriptor_proto, const string& field_name) { + return HasSpan(start_tag, end_tag, descriptor_proto, field_name, -1); + } + + bool HasSpan(const char* start_tag, const char* end_tag, + const Message& descriptor_proto, const string& field_name, + int index) { + const FieldDescriptor* field = + descriptor_proto.GetDescriptor()->FindFieldByName(field_name); + if (field == NULL) { + ADD_FAILURE() << descriptor_proto.GetDescriptor()->name() + << " has no such field: " << field_name; + return false; + } + + return HasSpan(start_tag, end_tag, descriptor_proto, field, index); + } + + bool HasSpan(const Message& descriptor_proto) { + return HasSpan(NULL, NULL, descriptor_proto, NULL, -1); + } + + bool HasSpan(const Message& descriptor_proto, const string& field_name) { + return HasSpan(NULL, NULL, descriptor_proto, field_name, -1); + } + + bool HasSpan(const Message& descriptor_proto, const string& field_name, + int index) { + return HasSpan(NULL, NULL, descriptor_proto, field_name, index); + } + + bool HasSpan(const char* start_tag, const char* end_tag, + const Message& descriptor_proto, const FieldDescriptor* field, + int index) { + pair<SpanMap::iterator, SpanMap::iterator> range = + spans_.equal_range(SpanKey(descriptor_proto, field, index)); + + if (start_tag == NULL) { + if (range.first == range.second) { + return false; + } else { + spans_.erase(range.first); + return true; + } + } else { + RepeatedField<int> expected_span; + MakeExpectedSpan(lines_, start_tag, end_tag, &expected_span); + + for (SpanMap::iterator iter = range.first; iter != range.second; ++iter) { + if (CompareSpans(expected_span, iter->second->span())) { + spans_.erase(iter); + return true; + } + } + + return false; + } + } + + private: + struct SpanKey { + const Message* descriptor_proto; + const FieldDescriptor* field; + int index; + + inline SpanKey() {} + inline SpanKey(const Message& descriptor_proto, + const FieldDescriptor* field, + int index) + : descriptor_proto(&descriptor_proto), field(field), index(index) {} + + inline bool operator<(const SpanKey& other) const { + if (descriptor_proto < other.descriptor_proto) return true; + if (descriptor_proto > other.descriptor_proto) return false; + if (field < other.field) return true; + if (field > other.field) return false; + return index < other.index; + } + }; + + typedef multimap<SpanKey, const SourceCodeInfo::Location*> SpanMap; + SpanMap spans_; + vector<string> lines_; +}; + +TEST_F(SourceInfoTest, BasicFileDecls) { + EXPECT_TRUE(Parse( + "/*a*/syntax = \"proto2\";\n" + "package /*b*/foo.bar/*c*/;\n" + "import /*d*/\"baz.proto\"/*e*/;\n" + "import /*f*/\"qux.proto\"/*g*/;/*h*/\n" + "// comment ignored\n")); + + EXPECT_TRUE(HasSpan("a", "h", file_)); + EXPECT_TRUE(HasSpan("b", "c", file_, "package")); + EXPECT_TRUE(HasSpan("d", "e", file_, "dependency", 0)); + EXPECT_TRUE(HasSpan("f", "g", file_, "dependency", 1)); +} + +TEST_F(SourceInfoTest, Messages) { + EXPECT_TRUE(Parse( + "/*a*/message /*b*/Foo/*c*/ {}/*d*/\n" + "/*e*/message /*f*/Bar/*g*/ {}/*h*/\n")); + + EXPECT_TRUE(HasSpan("a", "d", file_.message_type(0))); + EXPECT_TRUE(HasSpan("b", "c", file_.message_type(0), "name")); + EXPECT_TRUE(HasSpan("e", "h", file_.message_type(1))); + EXPECT_TRUE(HasSpan("f", "g", file_.message_type(1), "name")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); +} + +TEST_F(SourceInfoTest, Fields) { + EXPECT_TRUE(Parse( + "message Foo {\n" + " /*a*/optional/*b*/ /*c*/int32/*d*/ /*e*/bar/*f*/ = /*g*/1/*h*/;/*i*/\n" + " /*j*/repeated/*k*/ /*l*/X.Y/*m*/ /*n*/baz/*o*/ = /*p*/2/*q*/;/*r*/\n" + "}\n")); + + const FieldDescriptorProto& field1 = file_.message_type(0).field(0); + const FieldDescriptorProto& field2 = file_.message_type(0).field(1); + + EXPECT_TRUE(HasSpan("a", "i", field1)); + EXPECT_TRUE(HasSpan("a", "b", field1, "label")); + EXPECT_TRUE(HasSpan("c", "d", field1, "type")); + EXPECT_TRUE(HasSpan("e", "f", field1, "name")); + EXPECT_TRUE(HasSpan("g", "h", field1, "number")); + + EXPECT_TRUE(HasSpan("j", "r", field2)); + EXPECT_TRUE(HasSpan("j", "k", field2, "label")); + EXPECT_TRUE(HasSpan("l", "m", field2, "type_name")); + EXPECT_TRUE(HasSpan("n", "o", field2, "name")); + EXPECT_TRUE(HasSpan("p", "q", field2, "number")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(file_.message_type(0))); + EXPECT_TRUE(HasSpan(file_.message_type(0), "name")); +} + +TEST_F(SourceInfoTest, Extensions) { + EXPECT_TRUE(Parse( + "/*a*/extend /*b*/Foo/*c*/ {\n" + " /*d*/optional/*e*/ int32 bar = 1;/*f*/\n" + " /*g*/repeated/*h*/ X.Y baz = 2;/*i*/\n" + "}/*j*/\n" + "/*k*/extend /*l*/Bar/*m*/ {\n" + " /*n*/optional int32 qux = 1;/*o*/\n" + "}/*p*/\n")); + + const FieldDescriptorProto& field1 = file_.extension(0); + const FieldDescriptorProto& field2 = file_.extension(1); + const FieldDescriptorProto& field3 = file_.extension(2); + + EXPECT_TRUE(HasSpan("a", "j", file_, "extension")); + EXPECT_TRUE(HasSpan("k", "p", file_, "extension")); + + EXPECT_TRUE(HasSpan("d", "f", field1)); + EXPECT_TRUE(HasSpan("d", "e", field1, "label")); + EXPECT_TRUE(HasSpan("b", "c", field1, "extendee")); + + EXPECT_TRUE(HasSpan("g", "i", field2)); + EXPECT_TRUE(HasSpan("g", "h", field2, "label")); + EXPECT_TRUE(HasSpan("b", "c", field2, "extendee")); + + EXPECT_TRUE(HasSpan("n", "o", field3)); + EXPECT_TRUE(HasSpan("l", "m", field3, "extendee")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(field1, "type")); + EXPECT_TRUE(HasSpan(field1, "name")); + EXPECT_TRUE(HasSpan(field1, "number")); + EXPECT_TRUE(HasSpan(field2, "type_name")); + EXPECT_TRUE(HasSpan(field2, "name")); + EXPECT_TRUE(HasSpan(field2, "number")); + EXPECT_TRUE(HasSpan(field3, "label")); + EXPECT_TRUE(HasSpan(field3, "type")); + EXPECT_TRUE(HasSpan(field3, "name")); + EXPECT_TRUE(HasSpan(field3, "number")); +} + +TEST_F(SourceInfoTest, NestedExtensions) { + EXPECT_TRUE(Parse( + "message Message {\n" + " /*a*/extend /*b*/Foo/*c*/ {\n" + " /*d*/optional/*e*/ int32 bar = 1;/*f*/\n" + " /*g*/repeated/*h*/ X.Y baz = 2;/*i*/\n" + " }/*j*/\n" + " /*k*/extend /*l*/Bar/*m*/ {\n" + " /*n*/optional int32 qux = 1;/*o*/\n" + " }/*p*/\n" + "}\n")); + + const FieldDescriptorProto& field1 = file_.message_type(0).extension(0); + const FieldDescriptorProto& field2 = file_.message_type(0).extension(1); + const FieldDescriptorProto& field3 = file_.message_type(0).extension(2); + + EXPECT_TRUE(HasSpan("a", "j", file_.message_type(0), "extension")); + EXPECT_TRUE(HasSpan("k", "p", file_.message_type(0), "extension")); + + EXPECT_TRUE(HasSpan("d", "f", field1)); + EXPECT_TRUE(HasSpan("d", "e", field1, "label")); + EXPECT_TRUE(HasSpan("b", "c", field1, "extendee")); + + EXPECT_TRUE(HasSpan("g", "i", field2)); + EXPECT_TRUE(HasSpan("g", "h", field2, "label")); + EXPECT_TRUE(HasSpan("b", "c", field2, "extendee")); + + EXPECT_TRUE(HasSpan("n", "o", field3)); + EXPECT_TRUE(HasSpan("l", "m", field3, "extendee")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(file_.message_type(0))); + EXPECT_TRUE(HasSpan(file_.message_type(0), "name")); + EXPECT_TRUE(HasSpan(field1, "type")); + EXPECT_TRUE(HasSpan(field1, "name")); + EXPECT_TRUE(HasSpan(field1, "number")); + EXPECT_TRUE(HasSpan(field2, "type_name")); + EXPECT_TRUE(HasSpan(field2, "name")); + EXPECT_TRUE(HasSpan(field2, "number")); + EXPECT_TRUE(HasSpan(field3, "label")); + EXPECT_TRUE(HasSpan(field3, "type")); + EXPECT_TRUE(HasSpan(field3, "name")); + EXPECT_TRUE(HasSpan(field3, "number")); +} + +TEST_F(SourceInfoTest, ExtensionRanges) { + EXPECT_TRUE(Parse( + "message Message {\n" + " /*a*/extensions /*b*/1/*c*/ to /*d*/4/*e*/, /*f*/6/*g*/;/*h*/\n" + " /*i*/extensions /*j*/8/*k*/ to /*l*/max/*m*/;/*n*/\n" + "}\n")); + + const DescriptorProto::ExtensionRange& range1 = + file_.message_type(0).extension_range(0); + const DescriptorProto::ExtensionRange& range2 = + file_.message_type(0).extension_range(1); + const DescriptorProto::ExtensionRange& range3 = + file_.message_type(0).extension_range(2); + + EXPECT_TRUE(HasSpan("a", "h", file_.message_type(0), "extension_range")); + EXPECT_TRUE(HasSpan("i", "n", file_.message_type(0), "extension_range")); + + EXPECT_TRUE(HasSpan("b", "e", range1)); + EXPECT_TRUE(HasSpan("b", "c", range1, "start")); + EXPECT_TRUE(HasSpan("d", "e", range1, "end")); + + EXPECT_TRUE(HasSpan("f", "g", range2)); + EXPECT_TRUE(HasSpan("f", "g", range2, "start")); + EXPECT_TRUE(HasSpan("f", "g", range2, "end")); + + EXPECT_TRUE(HasSpan("j", "m", range3)); + EXPECT_TRUE(HasSpan("j", "k", range3, "start")); + EXPECT_TRUE(HasSpan("l", "m", range3, "end")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(file_.message_type(0))); + EXPECT_TRUE(HasSpan(file_.message_type(0), "name")); +} + +TEST_F(SourceInfoTest, NestedMessages) { + EXPECT_TRUE(Parse( + "message Foo {\n" + " /*a*/message /*b*/Bar/*c*/ {\n" + " /*d*/message /*e*/Baz/*f*/ {}/*g*/\n" + " }/*h*/\n" + " /*i*/message /*j*/Qux/*k*/ {}/*l*/\n" + "}\n")); + + const DescriptorProto& bar = file_.message_type(0).nested_type(0); + const DescriptorProto& baz = bar.nested_type(0); + const DescriptorProto& qux = file_.message_type(0).nested_type(1); + + EXPECT_TRUE(HasSpan("a", "h", bar)); + EXPECT_TRUE(HasSpan("b", "c", bar, "name")); + EXPECT_TRUE(HasSpan("d", "g", baz)); + EXPECT_TRUE(HasSpan("e", "f", baz, "name")); + EXPECT_TRUE(HasSpan("i", "l", qux)); + EXPECT_TRUE(HasSpan("j", "k", qux, "name")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(file_.message_type(0))); + EXPECT_TRUE(HasSpan(file_.message_type(0), "name")); +} + +TEST_F(SourceInfoTest, Groups) { + EXPECT_TRUE(Parse( + "message Foo {\n" + " message Bar {}\n" + " /*a*/optional/*b*/ /*c*/group/*d*/ /*e*/Baz/*f*/ = /*g*/1/*h*/ {\n" + " /*i*/message Qux {}/*j*/\n" + " }/*k*/\n" + "}\n")); + + const DescriptorProto& bar = file_.message_type(0).nested_type(0); + const DescriptorProto& baz = file_.message_type(0).nested_type(1); + const DescriptorProto& qux = baz.nested_type(0); + const FieldDescriptorProto& field = file_.message_type(0).field(0); + + EXPECT_TRUE(HasSpan("a", "k", field)); + EXPECT_TRUE(HasSpan("a", "b", field, "label")); + EXPECT_TRUE(HasSpan("c", "d", field, "type")); + EXPECT_TRUE(HasSpan("e", "f", field, "name")); + EXPECT_TRUE(HasSpan("e", "f", field, "type_name")); + EXPECT_TRUE(HasSpan("g", "h", field, "number")); + + EXPECT_TRUE(HasSpan("a", "k", baz)); + EXPECT_TRUE(HasSpan("e", "f", baz, "name")); + EXPECT_TRUE(HasSpan("i", "j", qux)); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(file_.message_type(0))); + EXPECT_TRUE(HasSpan(file_.message_type(0), "name")); + EXPECT_TRUE(HasSpan(bar)); + EXPECT_TRUE(HasSpan(bar, "name")); + EXPECT_TRUE(HasSpan(qux, "name")); +} + +TEST_F(SourceInfoTest, Enums) { + EXPECT_TRUE(Parse( + "/*a*/enum /*b*/Foo/*c*/ {}/*d*/\n" + "/*e*/enum /*f*/Bar/*g*/ {}/*h*/\n")); + + EXPECT_TRUE(HasSpan("a", "d", file_.enum_type(0))); + EXPECT_TRUE(HasSpan("b", "c", file_.enum_type(0), "name")); + EXPECT_TRUE(HasSpan("e", "h", file_.enum_type(1))); + EXPECT_TRUE(HasSpan("f", "g", file_.enum_type(1), "name")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); +} + +TEST_F(SourceInfoTest, EnumValues) { + EXPECT_TRUE(Parse( + "enum Foo {\n" + " /*a*/BAR/*b*/ = /*c*/1/*d*/;/*e*/\n" + " /*f*/BAZ/*g*/ = /*h*/2/*i*/;/*j*/\n" + "}")); + + const EnumValueDescriptorProto& bar = file_.enum_type(0).value(0); + const EnumValueDescriptorProto& baz = file_.enum_type(0).value(1); + + EXPECT_TRUE(HasSpan("a", "e", bar)); + EXPECT_TRUE(HasSpan("a", "b", bar, "name")); + EXPECT_TRUE(HasSpan("c", "d", bar, "number")); + EXPECT_TRUE(HasSpan("f", "j", baz)); + EXPECT_TRUE(HasSpan("f", "g", baz, "name")); + EXPECT_TRUE(HasSpan("h", "i", baz, "number")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(file_.enum_type(0))); + EXPECT_TRUE(HasSpan(file_.enum_type(0), "name")); +} + +TEST_F(SourceInfoTest, NestedEnums) { + EXPECT_TRUE(Parse( + "message Foo {\n" + " /*a*/enum /*b*/Bar/*c*/ {}/*d*/\n" + " /*e*/enum /*f*/Baz/*g*/ {}/*h*/\n" + "}\n")); + + const EnumDescriptorProto& bar = file_.message_type(0).enum_type(0); + const EnumDescriptorProto& baz = file_.message_type(0).enum_type(1); + + EXPECT_TRUE(HasSpan("a", "d", bar)); + EXPECT_TRUE(HasSpan("b", "c", bar, "name")); + EXPECT_TRUE(HasSpan("e", "h", baz)); + EXPECT_TRUE(HasSpan("f", "g", baz, "name")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(file_.message_type(0))); + EXPECT_TRUE(HasSpan(file_.message_type(0), "name")); +} + +TEST_F(SourceInfoTest, Services) { + EXPECT_TRUE(Parse( + "/*a*/service /*b*/Foo/*c*/ {}/*d*/\n" + "/*e*/service /*f*/Bar/*g*/ {}/*h*/\n")); + + EXPECT_TRUE(HasSpan("a", "d", file_.service(0))); + EXPECT_TRUE(HasSpan("b", "c", file_.service(0), "name")); + EXPECT_TRUE(HasSpan("e", "h", file_.service(1))); + EXPECT_TRUE(HasSpan("f", "g", file_.service(1), "name")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); +} + +TEST_F(SourceInfoTest, Methods) { + EXPECT_TRUE(Parse( + "service Foo {\n" + " /*a*/rpc /*b*/Bar/*c*/(/*d*/X/*e*/) returns(/*f*/Y/*g*/);/*h*/" + " /*i*/rpc /*j*/Baz/*k*/(/*l*/Z/*m*/) returns(/*n*/W/*o*/);/*p*/" + "}")); + + const MethodDescriptorProto& bar = file_.service(0).method(0); + const MethodDescriptorProto& baz = file_.service(0).method(1); + + EXPECT_TRUE(HasSpan("a", "h", bar)); + EXPECT_TRUE(HasSpan("b", "c", bar, "name")); + EXPECT_TRUE(HasSpan("d", "e", bar, "input_type")); + EXPECT_TRUE(HasSpan("f", "g", bar, "output_type")); + + EXPECT_TRUE(HasSpan("i", "p", baz)); + EXPECT_TRUE(HasSpan("j", "k", baz, "name")); + EXPECT_TRUE(HasSpan("l", "m", baz, "input_type")); + EXPECT_TRUE(HasSpan("n", "o", baz, "output_type")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(file_.service(0))); + EXPECT_TRUE(HasSpan(file_.service(0), "name")); +} + +TEST_F(SourceInfoTest, Options) { + EXPECT_TRUE(Parse( + "/*a*/option /*b*/foo/*c*/./*d*/(/*e*/bar.baz/*f*/)/*g*/ = " + "/*h*/123/*i*/;/*j*/\n" + "/*k*/option qux = /*l*/-123/*m*/;/*n*/\n" + "/*o*/option corge = /*p*/abc/*q*/;/*r*/\n" + "/*s*/option grault = /*t*/'blah'/*u*/;/*v*/\n" + "/*w*/option garply = /*x*/{ yadda yadda }/*y*/;/*z*/\n" + "/*0*/option waldo = /*1*/123.0/*2*/;/*3*/\n" + )); + + const UninterpretedOption& option1 = file_.options().uninterpreted_option(0); + const UninterpretedOption& option2 = file_.options().uninterpreted_option(1); + const UninterpretedOption& option3 = file_.options().uninterpreted_option(2); + const UninterpretedOption& option4 = file_.options().uninterpreted_option(3); + const UninterpretedOption& option5 = file_.options().uninterpreted_option(4); + const UninterpretedOption& option6 = file_.options().uninterpreted_option(5); + + EXPECT_TRUE(HasSpan("a", "j", file_.options())); + EXPECT_TRUE(HasSpan("b", "i", option1)); + EXPECT_TRUE(HasSpan("b", "g", option1, "name")); + EXPECT_TRUE(HasSpan("b", "c", option1.name(0))); + EXPECT_TRUE(HasSpan("b", "c", option1.name(0), "name_part")); + EXPECT_TRUE(HasSpan("d", "g", option1.name(1))); + EXPECT_TRUE(HasSpan("e", "f", option1.name(1), "name_part")); + EXPECT_TRUE(HasSpan("h", "i", option1, "positive_int_value")); + + EXPECT_TRUE(HasSpan("k", "n", file_.options())); + EXPECT_TRUE(HasSpan("l", "m", option2, "negative_int_value")); + + EXPECT_TRUE(HasSpan("o", "r", file_.options())); + EXPECT_TRUE(HasSpan("p", "q", option3, "identifier_value")); + + EXPECT_TRUE(HasSpan("s", "v", file_.options())); + EXPECT_TRUE(HasSpan("t", "u", option4, "string_value")); + + EXPECT_TRUE(HasSpan("w", "z", file_.options())); + EXPECT_TRUE(HasSpan("x", "y", option5, "aggregate_value")); + + EXPECT_TRUE(HasSpan("0", "3", file_.options())); + EXPECT_TRUE(HasSpan("1", "2", option6, "double_value")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(option2)); + EXPECT_TRUE(HasSpan(option3)); + EXPECT_TRUE(HasSpan(option4)); + EXPECT_TRUE(HasSpan(option5)); + EXPECT_TRUE(HasSpan(option6)); + EXPECT_TRUE(HasSpan(option2, "name")); + EXPECT_TRUE(HasSpan(option3, "name")); + EXPECT_TRUE(HasSpan(option4, "name")); + EXPECT_TRUE(HasSpan(option5, "name")); + EXPECT_TRUE(HasSpan(option6, "name")); + EXPECT_TRUE(HasSpan(option2.name(0))); + EXPECT_TRUE(HasSpan(option3.name(0))); + EXPECT_TRUE(HasSpan(option4.name(0))); + EXPECT_TRUE(HasSpan(option5.name(0))); + EXPECT_TRUE(HasSpan(option6.name(0))); + EXPECT_TRUE(HasSpan(option2.name(0), "name_part")); + EXPECT_TRUE(HasSpan(option3.name(0), "name_part")); + EXPECT_TRUE(HasSpan(option4.name(0), "name_part")); + EXPECT_TRUE(HasSpan(option5.name(0), "name_part")); + EXPECT_TRUE(HasSpan(option6.name(0), "name_part")); +} + +TEST_F(SourceInfoTest, ScopedOptions) { + EXPECT_TRUE(Parse( + "message Foo {\n" + " /*a*/option mopt = 1;/*b*/\n" + "}\n" + "enum Bar {\n" + " /*c*/option eopt = 1;/*d*/\n" + "}\n" + "service Baz {\n" + " /*e*/option sopt = 1;/*f*/\n" + " rpc M(X) returns(Y) {\n" + " /*g*/option mopt = 1;/*h*/\n" + " }\n" + "}\n")); + + EXPECT_TRUE(HasSpan("a", "b", file_.message_type(0).options())); + EXPECT_TRUE(HasSpan("c", "d", file_.enum_type(0).options())); + EXPECT_TRUE(HasSpan("e", "f", file_.service(0).options())); + EXPECT_TRUE(HasSpan("g", "h", file_.service(0).method(0).options())); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(file_.message_type(0))); + EXPECT_TRUE(HasSpan(file_.message_type(0), "name")); + EXPECT_TRUE(HasSpan(file_.message_type(0).options() + .uninterpreted_option(0))); + EXPECT_TRUE(HasSpan(file_.message_type(0).options() + .uninterpreted_option(0), "name")); + EXPECT_TRUE(HasSpan(file_.message_type(0).options() + .uninterpreted_option(0).name(0))); + EXPECT_TRUE(HasSpan(file_.message_type(0).options() + .uninterpreted_option(0).name(0), "name_part")); + EXPECT_TRUE(HasSpan(file_.message_type(0).options() + .uninterpreted_option(0), "positive_int_value")); + EXPECT_TRUE(HasSpan(file_.enum_type(0))); + EXPECT_TRUE(HasSpan(file_.enum_type(0), "name")); + EXPECT_TRUE(HasSpan(file_.enum_type(0).options() + .uninterpreted_option(0))); + EXPECT_TRUE(HasSpan(file_.enum_type(0).options() + .uninterpreted_option(0), "name")); + EXPECT_TRUE(HasSpan(file_.enum_type(0).options() + .uninterpreted_option(0).name(0))); + EXPECT_TRUE(HasSpan(file_.enum_type(0).options() + .uninterpreted_option(0).name(0), "name_part")); + EXPECT_TRUE(HasSpan(file_.enum_type(0).options() + .uninterpreted_option(0), "positive_int_value")); + EXPECT_TRUE(HasSpan(file_.service(0))); + EXPECT_TRUE(HasSpan(file_.service(0), "name")); + EXPECT_TRUE(HasSpan(file_.service(0).method(0))); + EXPECT_TRUE(HasSpan(file_.service(0).options() + .uninterpreted_option(0))); + EXPECT_TRUE(HasSpan(file_.service(0).options() + .uninterpreted_option(0), "name")); + EXPECT_TRUE(HasSpan(file_.service(0).options() + .uninterpreted_option(0).name(0))); + EXPECT_TRUE(HasSpan(file_.service(0).options() + .uninterpreted_option(0).name(0), "name_part")); + EXPECT_TRUE(HasSpan(file_.service(0).options() + .uninterpreted_option(0), "positive_int_value")); + EXPECT_TRUE(HasSpan(file_.service(0).method(0), "name")); + EXPECT_TRUE(HasSpan(file_.service(0).method(0), "input_type")); + EXPECT_TRUE(HasSpan(file_.service(0).method(0), "output_type")); + EXPECT_TRUE(HasSpan(file_.service(0).method(0).options() + .uninterpreted_option(0))); + EXPECT_TRUE(HasSpan(file_.service(0).method(0).options() + .uninterpreted_option(0), "name")); + EXPECT_TRUE(HasSpan(file_.service(0).method(0).options() + .uninterpreted_option(0).name(0))); + EXPECT_TRUE(HasSpan(file_.service(0).method(0).options() + .uninterpreted_option(0).name(0), "name_part")); + EXPECT_TRUE(HasSpan(file_.service(0).method(0).options() + .uninterpreted_option(0), "positive_int_value")); +} + +TEST_F(SourceInfoTest, FieldOptions) { + // The actual "name = value" pairs are parsed by the same code as for + // top-level options so we won't re-test that -- just make sure that the + // syntax used for field options is understood. + EXPECT_TRUE(Parse( + "message Foo {" + " optional int32 bar = 1 " + "/*a*/[default=/*b*/123/*c*/,/*d*/opt1=123/*e*/," + "/*f*/opt2='hi'/*g*/]/*h*/;" + "}\n" + )); + + const FieldDescriptorProto& field = file_.message_type(0).field(0); + const UninterpretedOption& option1 = field.options().uninterpreted_option(0); + const UninterpretedOption& option2 = field.options().uninterpreted_option(1); + + EXPECT_TRUE(HasSpan("a", "h", field.options())); + EXPECT_TRUE(HasSpan("b", "c", field, "default_value")); + EXPECT_TRUE(HasSpan("d", "e", option1)); + EXPECT_TRUE(HasSpan("f", "g", option2)); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(file_.message_type(0))); + EXPECT_TRUE(HasSpan(file_.message_type(0), "name")); + EXPECT_TRUE(HasSpan(field)); + EXPECT_TRUE(HasSpan(field, "label")); + EXPECT_TRUE(HasSpan(field, "type")); + EXPECT_TRUE(HasSpan(field, "name")); + EXPECT_TRUE(HasSpan(field, "number")); + EXPECT_TRUE(HasSpan(option1, "name")); + EXPECT_TRUE(HasSpan(option2, "name")); + EXPECT_TRUE(HasSpan(option1.name(0))); + EXPECT_TRUE(HasSpan(option2.name(0))); + EXPECT_TRUE(HasSpan(option1.name(0), "name_part")); + EXPECT_TRUE(HasSpan(option2.name(0), "name_part")); + EXPECT_TRUE(HasSpan(option1, "positive_int_value")); + EXPECT_TRUE(HasSpan(option2, "string_value")); +} + +TEST_F(SourceInfoTest, EnumValueOptions) { + // The actual "name = value" pairs are parsed by the same code as for + // top-level options so we won't re-test that -- just make sure that the + // syntax used for enum options is understood. + EXPECT_TRUE(Parse( + "enum Foo {" + " BAR = 1 /*a*/[/*b*/opt1=123/*c*/,/*d*/opt2='hi'/*e*/]/*f*/;" + "}\n" + )); + + const EnumValueDescriptorProto& value = file_.enum_type(0).value(0); + const UninterpretedOption& option1 = value.options().uninterpreted_option(0); + const UninterpretedOption& option2 = value.options().uninterpreted_option(1); + + EXPECT_TRUE(HasSpan("a", "f", value.options())); + EXPECT_TRUE(HasSpan("b", "c", option1)); + EXPECT_TRUE(HasSpan("d", "e", option2)); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(file_.enum_type(0))); + EXPECT_TRUE(HasSpan(file_.enum_type(0), "name")); + EXPECT_TRUE(HasSpan(value)); + EXPECT_TRUE(HasSpan(value, "name")); + EXPECT_TRUE(HasSpan(value, "number")); + EXPECT_TRUE(HasSpan(option1, "name")); + EXPECT_TRUE(HasSpan(option2, "name")); + EXPECT_TRUE(HasSpan(option1.name(0))); + EXPECT_TRUE(HasSpan(option2.name(0))); + EXPECT_TRUE(HasSpan(option1.name(0), "name_part")); + EXPECT_TRUE(HasSpan(option2.name(0), "name_part")); + EXPECT_TRUE(HasSpan(option1, "positive_int_value")); + EXPECT_TRUE(HasSpan(option2, "string_value")); +} + +// =================================================================== } // anonymous namespace diff --git a/src/google/protobuf/compiler/plugin.cc b/src/google/protobuf/compiler/plugin.cc index a4aedafb..727f9421 100644 --- a/src/google/protobuf/compiler/plugin.cc +++ b/src/google/protobuf/compiler/plugin.cc @@ -59,13 +59,15 @@ namespace google { namespace protobuf { namespace compiler { -class GeneratorResponseOutputDirectory : public OutputDirectory { +class GeneratorResponseContext : public GeneratorContext { public: - GeneratorResponseOutputDirectory(CodeGeneratorResponse* response) - : response_(response) {} - virtual ~GeneratorResponseOutputDirectory() {} + GeneratorResponseContext(CodeGeneratorResponse* response, + const vector<const FileDescriptor*>& parsed_files) + : response_(response), + parsed_files_(parsed_files) {} + virtual ~GeneratorResponseContext() {} - // implements OutputDirectory -------------------------------------- + // implements GeneratorContext -------------------------------------- virtual io::ZeroCopyOutputStream* Open(const string& filename) { CodeGeneratorResponse::File* file = response_->add_file(); @@ -81,8 +83,13 @@ class GeneratorResponseOutputDirectory : public OutputDirectory { return new io::StringOutputStream(file->mutable_content()); } + void ListParsedFiles(vector<const FileDescriptor*>* output) { + *output = parsed_files_; + } + private: CodeGeneratorResponse* response_; + const vector<const FileDescriptor*>& parsed_files_; }; int PluginMain(int argc, char* argv[], const CodeGenerator* generator) { @@ -112,22 +119,26 @@ int PluginMain(int argc, char* argv[], const CodeGenerator* generator) { } } - CodeGeneratorResponse response; - GeneratorResponseOutputDirectory output_directory(&response); - + vector<const FileDescriptor*> parsed_files; for (int i = 0; i < request.file_to_generate_size(); i++) { - const FileDescriptor* file = - pool.FindFileByName(request.file_to_generate(i)); - if (file == NULL) { + parsed_files.push_back(pool.FindFileByName(request.file_to_generate(i))); + if (parsed_files.back() == NULL) { cerr << argv[0] << ": protoc asked plugin to generate a file but " "did not provide a descriptor for the file: " << request.file_to_generate(i) << endl; return 1; } + } + + CodeGeneratorResponse response; + GeneratorResponseContext context(&response, parsed_files); + + for (int i = 0; i < parsed_files.size(); i++) { + const FileDescriptor* file = parsed_files[i]; string error; bool succeeded = generator->Generate( - file, request.parameter(), &output_directory, &error); + file, request.parameter(), &context, &error); if (!succeeded && error.empty()) { error = "Code generator returned false but provided no error " diff --git a/src/google/protobuf/compiler/plugin.h b/src/google/protobuf/compiler/plugin.h index 7c403332..64dfb1d2 100644 --- a/src/google/protobuf/compiler/plugin.h +++ b/src/google/protobuf/compiler/plugin.h @@ -64,7 +64,7 @@ namespace compiler { class CodeGenerator; // code_generator.h // Implements main() for a protoc plugin exposing the given code generator. -LIBPROTOC_EXPORT int PluginMain(int argc, char* argv[], const CodeGenerator* generator); +int PluginMain(int argc, char* argv[], const CodeGenerator* generator); } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/plugin.pb.cc b/src/google/protobuf/compiler/plugin.pb.cc index 13d35c68..ad4b4deb 100644 --- a/src/google/protobuf/compiler/plugin.pb.cc +++ b/src/google/protobuf/compiler/plugin.pb.cc @@ -158,7 +158,6 @@ struct StaticDescriptorInitializer_google_2fprotobuf_2fcompiler_2fplugin_2eproto // =================================================================== -const ::std::string CodeGeneratorRequest::_default_parameter_; #ifndef _MSC_VER const int CodeGeneratorRequest::kFileToGenerateFieldNumber; const int CodeGeneratorRequest::kParameterFieldNumber; @@ -181,7 +180,7 @@ CodeGeneratorRequest::CodeGeneratorRequest(const CodeGeneratorRequest& from) void CodeGeneratorRequest::SharedCtor() { _cached_size_ = 0; - parameter_ = const_cast< ::std::string*>(&_default_parameter_); + parameter_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -190,7 +189,7 @@ CodeGeneratorRequest::~CodeGeneratorRequest() { } void CodeGeneratorRequest::SharedDtor() { - if (parameter_ != &_default_parameter_) { + if (parameter_ != &::google::protobuf::internal::kEmptyString) { delete parameter_; } if (this != default_instance_) { @@ -219,8 +218,8 @@ CodeGeneratorRequest* CodeGeneratorRequest::New() const { void CodeGeneratorRequest::Clear() { if (_has_bits_[1 / 32] & (0xffu << (1 % 32))) { - if (_has_bit(1)) { - if (parameter_ != &_default_parameter_) { + if (has_parameter()) { + if (parameter_ != &::google::protobuf::internal::kEmptyString) { parameter_->clear(); } } @@ -315,7 +314,7 @@ void CodeGeneratorRequest::SerializeWithCachedSizes( } // optional string parameter = 2; - if (_has_bit(1)) { + if (has_parameter()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->parameter().data(), this->parameter().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -347,7 +346,7 @@ void CodeGeneratorRequest::SerializeWithCachedSizes( } // optional string parameter = 2; - if (_has_bit(1)) { + if (has_parameter()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->parameter().data(), this->parameter().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -425,7 +424,7 @@ void CodeGeneratorRequest::MergeFrom(const CodeGeneratorRequest& from) { file_to_generate_.MergeFrom(from.file_to_generate_); proto_file_.MergeFrom(from.proto_file_); if (from._has_bits_[1 / 32] & (0xffu << (1 % 32))) { - if (from._has_bit(1)) { + if (from.has_parameter()) { set_parameter(from.parameter()); } } @@ -474,9 +473,6 @@ void CodeGeneratorRequest::Swap(CodeGeneratorRequest* other) { // =================================================================== -const ::std::string CodeGeneratorResponse_File::_default_name_; -const ::std::string CodeGeneratorResponse_File::_default_insertion_point_; -const ::std::string CodeGeneratorResponse_File::_default_content_; #ifndef _MSC_VER const int CodeGeneratorResponse_File::kNameFieldNumber; const int CodeGeneratorResponse_File::kInsertionPointFieldNumber; @@ -499,9 +495,9 @@ CodeGeneratorResponse_File::CodeGeneratorResponse_File(const CodeGeneratorRespon void CodeGeneratorResponse_File::SharedCtor() { _cached_size_ = 0; - name_ = const_cast< ::std::string*>(&_default_name_); - insertion_point_ = const_cast< ::std::string*>(&_default_insertion_point_); - content_ = const_cast< ::std::string*>(&_default_content_); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + insertion_point_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + content_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -510,13 +506,13 @@ CodeGeneratorResponse_File::~CodeGeneratorResponse_File() { } void CodeGeneratorResponse_File::SharedDtor() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { delete name_; } - if (insertion_point_ != &_default_insertion_point_) { + if (insertion_point_ != &::google::protobuf::internal::kEmptyString) { delete insertion_point_; } - if (content_ != &_default_content_) { + if (content_ != &::google::protobuf::internal::kEmptyString) { delete content_; } if (this != default_instance_) { @@ -545,18 +541,18 @@ CodeGeneratorResponse_File* CodeGeneratorResponse_File::New() const { void CodeGeneratorResponse_File::Clear() { if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (_has_bit(0)) { - if (name_ != &_default_name_) { + if (has_name()) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } } - if (_has_bit(1)) { - if (insertion_point_ != &_default_insertion_point_) { + if (has_insertion_point()) { + if (insertion_point_ != &::google::protobuf::internal::kEmptyString) { insertion_point_->clear(); } } - if (_has_bit(2)) { - if (content_ != &_default_content_) { + if (has_content()) { + if (content_ != &::google::protobuf::internal::kEmptyString) { content_->clear(); } } @@ -640,7 +636,7 @@ bool CodeGeneratorResponse_File::MergePartialFromCodedStream( void CodeGeneratorResponse_File::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -649,7 +645,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( } // optional string insertion_point = 2; - if (_has_bit(1)) { + if (has_insertion_point()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->insertion_point().data(), this->insertion_point().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -658,7 +654,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( } // optional string content = 15; - if (_has_bit(2)) { + if (has_content()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->content().data(), this->content().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -675,7 +671,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( ::google::protobuf::uint8* CodeGeneratorResponse_File::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -685,7 +681,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( } // optional string insertion_point = 2; - if (_has_bit(1)) { + if (has_insertion_point()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->insertion_point().data(), this->insertion_point().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -695,7 +691,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( } // optional string content = 15; - if (_has_bit(2)) { + if (has_content()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->content().data(), this->content().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -763,13 +759,13 @@ void CodeGeneratorResponse_File::MergeFrom(const ::google::protobuf::Message& fr void CodeGeneratorResponse_File::MergeFrom(const CodeGeneratorResponse_File& from) { GOOGLE_CHECK_NE(&from, this); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_name()) { set_name(from.name()); } - if (from._has_bit(1)) { + if (from.has_insertion_point()) { set_insertion_point(from.insertion_point()); } - if (from._has_bit(2)) { + if (from.has_content()) { set_content(from.content()); } } @@ -815,7 +811,6 @@ void CodeGeneratorResponse_File::Swap(CodeGeneratorResponse_File* other) { // ------------------------------------------------------------------- -const ::std::string CodeGeneratorResponse::_default_error_; #ifndef _MSC_VER const int CodeGeneratorResponse::kErrorFieldNumber; const int CodeGeneratorResponse::kFileFieldNumber; @@ -837,7 +832,7 @@ CodeGeneratorResponse::CodeGeneratorResponse(const CodeGeneratorResponse& from) void CodeGeneratorResponse::SharedCtor() { _cached_size_ = 0; - error_ = const_cast< ::std::string*>(&_default_error_); + error_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -846,7 +841,7 @@ CodeGeneratorResponse::~CodeGeneratorResponse() { } void CodeGeneratorResponse::SharedDtor() { - if (error_ != &_default_error_) { + if (error_ != &::google::protobuf::internal::kEmptyString) { delete error_; } if (this != default_instance_) { @@ -875,8 +870,8 @@ CodeGeneratorResponse* CodeGeneratorResponse::New() const { void CodeGeneratorResponse::Clear() { if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (_has_bit(0)) { - if (error_ != &_default_error_) { + if (has_error()) { + if (error_ != &::google::protobuf::internal::kEmptyString) { error_->clear(); } } @@ -942,7 +937,7 @@ bool CodeGeneratorResponse::MergePartialFromCodedStream( void CodeGeneratorResponse::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional string error = 1; - if (_has_bit(0)) { + if (has_error()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->error().data(), this->error().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -965,7 +960,7 @@ void CodeGeneratorResponse::SerializeWithCachedSizes( ::google::protobuf::uint8* CodeGeneratorResponse::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional string error = 1; - if (_has_bit(0)) { + if (has_error()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->error().data(), this->error().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -1035,7 +1030,7 @@ void CodeGeneratorResponse::MergeFrom(const CodeGeneratorResponse& from) { GOOGLE_CHECK_NE(&from, this); file_.MergeFrom(from.file_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_error()) { set_error(from.error()); } } diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h index bd6bf63c..c307bfd4 100644 --- a/src/google/protobuf/compiler/plugin.pb.h +++ b/src/google/protobuf/compiler/plugin.pb.h @@ -120,6 +120,7 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message inline void set_parameter(const char* value); inline void set_parameter(const char* value, size_t size); inline ::std::string* mutable_parameter(); + inline ::std::string* release_parameter(); // repeated .google.protobuf.FileDescriptorProto proto_file = 15; inline int proto_file_size() const; @@ -135,29 +136,21 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message // @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorRequest) private: + inline void set_has_parameter(); + inline void clear_has_parameter(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::google::protobuf::RepeatedPtrField< ::std::string> file_to_generate_; ::std::string* parameter_; - static const ::std::string _default_parameter_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto > proto_file_; - friend void LIBPROTOC_EXPORT protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOC_EXPORT protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); void InitAsDefaultInstance(); static CodeGeneratorRequest* default_instance_; @@ -227,6 +220,7 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M inline void set_name(const char* value); inline void set_name(const char* value, size_t size); inline ::std::string* mutable_name(); + inline ::std::string* release_name(); // optional string insertion_point = 2; inline bool has_insertion_point() const; @@ -237,6 +231,7 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M inline void set_insertion_point(const char* value); inline void set_insertion_point(const char* value, size_t size); inline ::std::string* mutable_insertion_point(); + inline ::std::string* release_insertion_point(); // optional string content = 15; inline bool has_content() const; @@ -247,34 +242,29 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M inline void set_content(const char* value); inline void set_content(const char* value, size_t size); inline ::std::string* mutable_content(); + inline ::std::string* release_content(); // @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorResponse.File) private: + inline void set_has_name(); + inline void clear_has_name(); + inline void set_has_insertion_point(); + inline void clear_has_insertion_point(); + inline void set_has_content(); + inline void clear_has_content(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::std::string* name_; - static const ::std::string _default_name_; ::std::string* insertion_point_; - static const ::std::string _default_insertion_point_; ::std::string* content_; - static const ::std::string _default_content_; - friend void LIBPROTOC_EXPORT protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOC_EXPORT protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); void InitAsDefaultInstance(); static CodeGeneratorResponse_File* default_instance_; @@ -346,6 +336,7 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag inline void set_error(const char* value); inline void set_error(const char* value, size_t size); inline ::std::string* mutable_error(); + inline ::std::string* release_error(); // repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15; inline int file_size() const; @@ -361,28 +352,20 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag // @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorResponse) private: + inline void set_has_error(); + inline void clear_has_error(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::std::string* error_; - static const ::std::string _default_error_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File > file_; - friend void LIBPROTOC_EXPORT protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOC_EXPORT protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); void InitAsDefaultInstance(); static CodeGeneratorResponse* default_instance_; @@ -440,45 +423,61 @@ CodeGeneratorRequest::mutable_file_to_generate() { // optional string parameter = 2; inline bool CodeGeneratorRequest::has_parameter() const { - return _has_bit(1); + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void CodeGeneratorRequest::set_has_parameter() { + _has_bits_[0] |= 0x00000002u; +} +inline void CodeGeneratorRequest::clear_has_parameter() { + _has_bits_[0] &= ~0x00000002u; } inline void CodeGeneratorRequest::clear_parameter() { - if (parameter_ != &_default_parameter_) { + if (parameter_ != &::google::protobuf::internal::kEmptyString) { parameter_->clear(); } - _clear_bit(1); + clear_has_parameter(); } inline const ::std::string& CodeGeneratorRequest::parameter() const { return *parameter_; } inline void CodeGeneratorRequest::set_parameter(const ::std::string& value) { - _set_bit(1); - if (parameter_ == &_default_parameter_) { + set_has_parameter(); + if (parameter_ == &::google::protobuf::internal::kEmptyString) { parameter_ = new ::std::string; } parameter_->assign(value); } inline void CodeGeneratorRequest::set_parameter(const char* value) { - _set_bit(1); - if (parameter_ == &_default_parameter_) { + set_has_parameter(); + if (parameter_ == &::google::protobuf::internal::kEmptyString) { parameter_ = new ::std::string; } parameter_->assign(value); } inline void CodeGeneratorRequest::set_parameter(const char* value, size_t size) { - _set_bit(1); - if (parameter_ == &_default_parameter_) { + set_has_parameter(); + if (parameter_ == &::google::protobuf::internal::kEmptyString) { parameter_ = new ::std::string; } parameter_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* CodeGeneratorRequest::mutable_parameter() { - _set_bit(1); - if (parameter_ == &_default_parameter_) { + set_has_parameter(); + if (parameter_ == &::google::protobuf::internal::kEmptyString) { parameter_ = new ::std::string; } return parameter_; } +inline ::std::string* CodeGeneratorRequest::release_parameter() { + clear_has_parameter(); + if (parameter_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = parameter_; + parameter_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // repeated .google.protobuf.FileDescriptorProto proto_file = 15; inline int CodeGeneratorRequest::proto_file_size() const { @@ -511,129 +510,177 @@ CodeGeneratorRequest::mutable_proto_file() { // optional string name = 1; inline bool CodeGeneratorResponse_File::has_name() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void CodeGeneratorResponse_File::set_has_name() { + _has_bits_[0] |= 0x00000001u; +} +inline void CodeGeneratorResponse_File::clear_has_name() { + _has_bits_[0] &= ~0x00000001u; } inline void CodeGeneratorResponse_File::clear_name() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } - _clear_bit(0); + clear_has_name(); } inline const ::std::string& CodeGeneratorResponse_File::name() const { return *name_; } inline void CodeGeneratorResponse_File::set_name(const ::std::string& value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void CodeGeneratorResponse_File::set_name(const char* value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void CodeGeneratorResponse_File::set_name(const char* value, size_t size) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* CodeGeneratorResponse_File::mutable_name() { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } return name_; } +inline ::std::string* CodeGeneratorResponse_File::release_name() { + clear_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = name_; + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional string insertion_point = 2; inline bool CodeGeneratorResponse_File::has_insertion_point() const { - return _has_bit(1); + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void CodeGeneratorResponse_File::set_has_insertion_point() { + _has_bits_[0] |= 0x00000002u; +} +inline void CodeGeneratorResponse_File::clear_has_insertion_point() { + _has_bits_[0] &= ~0x00000002u; } inline void CodeGeneratorResponse_File::clear_insertion_point() { - if (insertion_point_ != &_default_insertion_point_) { + if (insertion_point_ != &::google::protobuf::internal::kEmptyString) { insertion_point_->clear(); } - _clear_bit(1); + clear_has_insertion_point(); } inline const ::std::string& CodeGeneratorResponse_File::insertion_point() const { return *insertion_point_; } inline void CodeGeneratorResponse_File::set_insertion_point(const ::std::string& value) { - _set_bit(1); - if (insertion_point_ == &_default_insertion_point_) { + set_has_insertion_point(); + if (insertion_point_ == &::google::protobuf::internal::kEmptyString) { insertion_point_ = new ::std::string; } insertion_point_->assign(value); } inline void CodeGeneratorResponse_File::set_insertion_point(const char* value) { - _set_bit(1); - if (insertion_point_ == &_default_insertion_point_) { + set_has_insertion_point(); + if (insertion_point_ == &::google::protobuf::internal::kEmptyString) { insertion_point_ = new ::std::string; } insertion_point_->assign(value); } inline void CodeGeneratorResponse_File::set_insertion_point(const char* value, size_t size) { - _set_bit(1); - if (insertion_point_ == &_default_insertion_point_) { + set_has_insertion_point(); + if (insertion_point_ == &::google::protobuf::internal::kEmptyString) { insertion_point_ = new ::std::string; } insertion_point_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* CodeGeneratorResponse_File::mutable_insertion_point() { - _set_bit(1); - if (insertion_point_ == &_default_insertion_point_) { + set_has_insertion_point(); + if (insertion_point_ == &::google::protobuf::internal::kEmptyString) { insertion_point_ = new ::std::string; } return insertion_point_; } +inline ::std::string* CodeGeneratorResponse_File::release_insertion_point() { + clear_has_insertion_point(); + if (insertion_point_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = insertion_point_; + insertion_point_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional string content = 15; inline bool CodeGeneratorResponse_File::has_content() const { - return _has_bit(2); + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void CodeGeneratorResponse_File::set_has_content() { + _has_bits_[0] |= 0x00000004u; +} +inline void CodeGeneratorResponse_File::clear_has_content() { + _has_bits_[0] &= ~0x00000004u; } inline void CodeGeneratorResponse_File::clear_content() { - if (content_ != &_default_content_) { + if (content_ != &::google::protobuf::internal::kEmptyString) { content_->clear(); } - _clear_bit(2); + clear_has_content(); } inline const ::std::string& CodeGeneratorResponse_File::content() const { return *content_; } inline void CodeGeneratorResponse_File::set_content(const ::std::string& value) { - _set_bit(2); - if (content_ == &_default_content_) { + set_has_content(); + if (content_ == &::google::protobuf::internal::kEmptyString) { content_ = new ::std::string; } content_->assign(value); } inline void CodeGeneratorResponse_File::set_content(const char* value) { - _set_bit(2); - if (content_ == &_default_content_) { + set_has_content(); + if (content_ == &::google::protobuf::internal::kEmptyString) { content_ = new ::std::string; } content_->assign(value); } inline void CodeGeneratorResponse_File::set_content(const char* value, size_t size) { - _set_bit(2); - if (content_ == &_default_content_) { + set_has_content(); + if (content_ == &::google::protobuf::internal::kEmptyString) { content_ = new ::std::string; } content_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* CodeGeneratorResponse_File::mutable_content() { - _set_bit(2); - if (content_ == &_default_content_) { + set_has_content(); + if (content_ == &::google::protobuf::internal::kEmptyString) { content_ = new ::std::string; } return content_; } +inline ::std::string* CodeGeneratorResponse_File::release_content() { + clear_has_content(); + if (content_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = content_; + content_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // ------------------------------------------------------------------- @@ -641,45 +688,61 @@ inline ::std::string* CodeGeneratorResponse_File::mutable_content() { // optional string error = 1; inline bool CodeGeneratorResponse::has_error() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void CodeGeneratorResponse::set_has_error() { + _has_bits_[0] |= 0x00000001u; +} +inline void CodeGeneratorResponse::clear_has_error() { + _has_bits_[0] &= ~0x00000001u; } inline void CodeGeneratorResponse::clear_error() { - if (error_ != &_default_error_) { + if (error_ != &::google::protobuf::internal::kEmptyString) { error_->clear(); } - _clear_bit(0); + clear_has_error(); } inline const ::std::string& CodeGeneratorResponse::error() const { return *error_; } inline void CodeGeneratorResponse::set_error(const ::std::string& value) { - _set_bit(0); - if (error_ == &_default_error_) { + set_has_error(); + if (error_ == &::google::protobuf::internal::kEmptyString) { error_ = new ::std::string; } error_->assign(value); } inline void CodeGeneratorResponse::set_error(const char* value) { - _set_bit(0); - if (error_ == &_default_error_) { + set_has_error(); + if (error_ == &::google::protobuf::internal::kEmptyString) { error_ = new ::std::string; } error_->assign(value); } inline void CodeGeneratorResponse::set_error(const char* value, size_t size) { - _set_bit(0); - if (error_ == &_default_error_) { + set_has_error(); + if (error_ == &::google::protobuf::internal::kEmptyString) { error_ = new ::std::string; } error_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* CodeGeneratorResponse::mutable_error() { - _set_bit(0); - if (error_ == &_default_error_) { + set_has_error(); + if (error_ == &::google::protobuf::internal::kEmptyString) { error_ = new ::std::string; } return error_; } +inline ::std::string* CodeGeneratorResponse::release_error() { + clear_has_error(); + if (error_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = error_; + error_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15; inline int CodeGeneratorResponse::file_size() const { diff --git a/src/google/protobuf/compiler/plugin.proto b/src/google/protobuf/compiler/plugin.proto index 4e928b0d..651ed10c 100644 --- a/src/google/protobuf/compiler/plugin.proto +++ b/src/google/protobuf/compiler/plugin.proto @@ -131,7 +131,7 @@ message CodeGeneratorResponse { // in order to work correctly in that context. // // The code generator that generates the initial file and the one which - // inserts into it must both run as part of a single invocatino of protoc. + // inserts into it must both run as part of a single invocation of protoc. // Code generators are executed in the order in which they appear on the // command line. // diff --git a/src/google/protobuf/compiler/python/python_generator.cc b/src/google/protobuf/compiler/python/python_generator.cc index fae83a37..9b109378 100644 --- a/src/google/protobuf/compiler/python/python_generator.cc +++ b/src/google/protobuf/compiler/python/python_generator.cc @@ -230,7 +230,7 @@ Generator::~Generator() { bool Generator::Generate(const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* context, string* error) const { // Completely serialize all Generate() calls on this instance. The @@ -252,20 +252,18 @@ bool Generator::Generate(const FileDescriptor* file, fdp.SerializeToString(&file_descriptor_serialized_); - scoped_ptr<io::ZeroCopyOutputStream> output(output_directory->Open(filename)); + scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename)); GOOGLE_CHECK(output.get()); io::Printer printer(output.get(), '$'); printer_ = &printer; PrintTopBoilerplate(printer_, file_, GeneratingDescriptorProto()); + PrintImports(); PrintFileDescriptor(); PrintTopLevelEnums(); PrintTopLevelExtensions(); PrintAllNestedEnumsInFile(); PrintMessageDescriptors(); - // We have to print the imports after the descriptors, so that mutually - // recursive protos in separate files can successfully reference each other. - PrintImports(); FixForeignFieldsInDescriptors(); PrintMessages(); // We have to fix up the extensions after the message classes themselves, @@ -377,7 +375,7 @@ void Generator::PrintEnum(const EnumDescriptor& enum_descriptor) const { printer_->Print("containing_type=None,\n"); printer_->Print("options=$options_value$,\n", "options_value", - OptionsValue("EnumOptions", CEscape(options_string))); + OptionsValue("EnumOptions", options_string)); EnumDescriptorProto edp; PrintSerializedPbInterval(enum_descriptor, edp); printer_->Outdent(); @@ -674,6 +672,17 @@ void Generator::FixForeignFieldsInDescriptor( } } +void Generator::AddMessageToFileDescriptor(const Descriptor& descriptor) const { + map<string, string> m; + m["descriptor_name"] = kDescriptorKey; + m["message_name"] = descriptor.name(); + m["message_descriptor_name"] = ModuleLevelDescriptorName(descriptor); + const char file_descriptor_template[] = + "$descriptor_name$.message_types_by_name['$message_name$'] = " + "$message_descriptor_name$\n"; + printer_->Print(m, file_descriptor_template); +} + // Sets any necessary message_type and enum_type attributes // for the Python version of |field|. // @@ -752,6 +761,9 @@ void Generator::FixForeignFieldsInDescriptors() const { for (int i = 0; i < file_->message_type_count(); ++i) { FixForeignFieldsInDescriptor(*file_->message_type(i), NULL); } + for (int i = 0; i < file_->message_type_count(); ++i) { + AddMessageToFileDescriptor(*file_->message_type(i)); + } printer_->Print("\n"); } @@ -823,6 +835,8 @@ void Generator::PrintEnumValueDescriptor( " type=None)"); } +// Returns a Python expression that calls descriptor._ParseOptions using +// the given descriptor class name and serialized options protobuf string. string Generator::OptionsValue( const string& class_name, const string& serialized_options) const { if (serialized_options.length() == 0 || GeneratingDescriptorProto()) { diff --git a/src/google/protobuf/compiler/python/python_generator.h b/src/google/protobuf/compiler/python/python_generator.h index 43c20876..84eaf8ab 100644 --- a/src/google/protobuf/compiler/python/python_generator.h +++ b/src/google/protobuf/compiler/python/python_generator.h @@ -66,7 +66,7 @@ class LIBPROTOC_EXPORT Generator : public CodeGenerator { // CodeGenerator methods. virtual bool Generate(const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* generator_context, string* error) const; private: @@ -104,6 +104,7 @@ class LIBPROTOC_EXPORT Generator : public CodeGenerator { void FixForeignFieldsInField(const Descriptor* containing_type, const FieldDescriptor& field, const string& python_dict_name) const; + void AddMessageToFileDescriptor(const Descriptor& descriptor) const; string FieldReferencingExpression(const Descriptor* containing_type, const FieldDescriptor& field, const string& python_dict_name) const; diff --git a/src/google/protobuf/compiler/python/python_plugin_unittest.cc b/src/google/protobuf/compiler/python/python_plugin_unittest.cc index fde88761..da619ad3 100644 --- a/src/google/protobuf/compiler/python/python_plugin_unittest.cc +++ b/src/google/protobuf/compiler/python/python_plugin_unittest.cc @@ -56,19 +56,19 @@ class TestGenerator : public CodeGenerator { virtual bool Generate(const FileDescriptor* file, const string& parameter, - OutputDirectory* output_directory, + GeneratorContext* context, string* error) const { - TryInsert("test_pb2.py", "imports", output_directory); - TryInsert("test_pb2.py", "module_scope", output_directory); - TryInsert("test_pb2.py", "class_scope:foo.Bar", output_directory); - TryInsert("test_pb2.py", "class_scope:foo.Bar.Baz", output_directory); + TryInsert("test_pb2.py", "imports", context); + TryInsert("test_pb2.py", "module_scope", context); + TryInsert("test_pb2.py", "class_scope:foo.Bar", context); + TryInsert("test_pb2.py", "class_scope:foo.Bar.Baz", context); return true; } void TryInsert(const string& filename, const string& insertion_point, - OutputDirectory* output_directory) const { + GeneratorContext* context) const { scoped_ptr<io::ZeroCopyOutputStream> output( - output_directory->OpenForInsert(filename, insertion_point)); + context->OpenForInsert(filename, insertion_point)); io::Printer printer(output.get(), '$'); printer.Print("// inserted $name$\n", "name", insertion_point); } diff --git a/src/google/protobuf/compiler/subprocess.cc b/src/google/protobuf/compiler/subprocess.cc index dbd813e1..5fb5d5cb 100644 --- a/src/google/protobuf/compiler/subprocess.cc +++ b/src/google/protobuf/compiler/subprocess.cc @@ -32,6 +32,8 @@ #include <google/protobuf/compiler/subprocess.h> +#include <algorithm> + #ifndef _WIN32 #include <errno.h> #include <sys/select.h> @@ -39,7 +41,6 @@ #include <signal.h> #endif -#include <algorithm> #include <google/protobuf/stubs/common.h> #include <google/protobuf/message.h> #include <google/protobuf/stubs/substitute.h> diff --git a/src/google/protobuf/compiler/subprocess.h b/src/google/protobuf/compiler/subprocess.h index 7a6fa70c..de9fce9e 100644 --- a/src/google/protobuf/compiler/subprocess.h +++ b/src/google/protobuf/compiler/subprocess.h @@ -40,9 +40,10 @@ #include <sys/types.h> #include <unistd.h> #endif // !_WIN32 +#include <google/protobuf/stubs/common.h> #include <string> -#include <google/protobuf/stubs/common.h> + namespace google { namespace protobuf { diff --git a/src/google/protobuf/compiler/zip_output_unittest.sh b/src/google/protobuf/compiler/zip_output_unittest.sh index 259d5d21..3a024364 100755 --- a/src/google/protobuf/compiler/zip_output_unittest.sh +++ b/src/google/protobuf/compiler/zip_output_unittest.sh @@ -39,45 +39,51 @@ fail() { exit 1 } +TEST_TMPDIR=. +PROTOC=./protoc + echo ' + syntax = "proto2"; option java_multiple_files = true; option java_package = "test.jar"; option java_outer_classname = "Outer"; message Foo {} message Bar {} -' > testzip.proto +' > $TEST_TMPDIR/testzip.proto -./protoc --cpp_out=testzip.zip --python_out=testzip.zip --java_out=testzip.jar \ - testzip.proto || fail 'protoc failed.' +$PROTOC \ + --cpp_out=$TEST_TMPDIR/testzip.zip --python_out=$TEST_TMPDIR/testzip.zip \ + --java_out=$TEST_TMPDIR/testzip.jar -I$TEST_TMPDIR testzip.proto \ + || fail 'protoc failed.' echo "Testing output to zip..." if unzip -h > /dev/null; then - unzip -t testzip.zip > testzip.list || fail 'unzip failed.' + unzip -t $TEST_TMPDIR/testzip.zip > $TEST_TMPDIR/testzip.list || fail 'unzip failed.' - grep 'testing: testzip\.pb\.cc *OK$' testzip.list > /dev/null \ + grep 'testing: testzip\.pb\.cc *OK$' $TEST_TMPDIR/testzip.list > /dev/null \ || fail 'testzip.pb.cc not found in output zip.' - grep 'testing: testzip\.pb\.h *OK$' testzip.list > /dev/null \ + grep 'testing: testzip\.pb\.h *OK$' $TEST_TMPDIR/testzip.list > /dev/null \ || fail 'testzip.pb.h not found in output zip.' - grep 'testing: testzip_pb2\.py *OK$' testzip.list > /dev/null \ + grep 'testing: testzip_pb2\.py *OK$' $TEST_TMPDIR/testzip.list > /dev/null \ || fail 'testzip_pb2.py not found in output zip.' - grep -i 'manifest' testzip.list > /dev/null \ + grep -i 'manifest' $TEST_TMPDIR/testzip.list > /dev/null \ && fail 'Zip file contained manifest.' else echo "Warning: 'unzip' command not available. Skipping test." fi echo "Testing output to jar..." -if jar c testzip.proto > /dev/null; then - jar tf testzip.jar > testzip.list || fail 'jar failed.' +if jar c $TEST_TMPDIR/testzip.proto > /dev/null; then + jar tf $TEST_TMPDIR/testzip.jar > $TEST_TMPDIR/testzip.list || fail 'jar failed.' - grep '^test/jar/Foo\.java$' testzip.list > /dev/null \ + grep '^test/jar/Foo\.java$' $TEST_TMPDIR/testzip.list > /dev/null \ || fail 'Foo.java not found in output jar.' - grep '^test/jar/Bar\.java$' testzip.list > /dev/null \ + grep '^test/jar/Bar\.java$' $TEST_TMPDIR/testzip.list > /dev/null \ || fail 'Bar.java not found in output jar.' - grep '^test/jar/Outer\.java$' testzip.list > /dev/null \ + grep '^test/jar/Outer\.java$' $TEST_TMPDIR/testzip.list > /dev/null \ || fail 'Outer.java not found in output jar.' - grep '^META-INF/MANIFEST\.MF$' testzip.list > /dev/null \ - || fail 'Manifest not ofund in output jar.' + grep '^META-INF/MANIFEST\.MF$' $TEST_TMPDIR/testzip.list > /dev/null \ + || fail 'Manifest not found in output jar.' else echo "Warning: 'jar' command not available. Skipping test." fi diff --git a/src/google/protobuf/compiler/zip_writer.cc b/src/google/protobuf/compiler/zip_writer.cc index 53c18771..65d73527 100644 --- a/src/google/protobuf/compiler/zip_writer.cc +++ b/src/google/protobuf/compiler/zip_writer.cc @@ -28,6 +28,36 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + // Author: ambrose@google.com (Ambrose Feinstein), // kenton@google.com (Kenton Varda) // @@ -183,6 +213,6 @@ bool ZipWriter::WriteDirectory() { return output.HadError(); } -} // namespace google -} // namespace protobuf } // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/zip_writer.h b/src/google/protobuf/compiler/zip_writer.h index 42895530..be73972a 100644 --- a/src/google/protobuf/compiler/zip_writer.h +++ b/src/google/protobuf/compiler/zip_writer.h @@ -28,6 +28,36 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + // Author: kenton@google.com (Kenton Varda) #include <vector> @@ -58,6 +88,6 @@ class ZipWriter { vector<FileInfo> files_; }; -} // namespace google -} // namespace protobuf } // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index 9226dfbc..76b7219f 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -42,10 +42,12 @@ #include <google/protobuf/descriptor.h> #include <google/protobuf/descriptor_database.h> #include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/dynamic_message.h> #include <google/protobuf/text_format.h> #include <google/protobuf/unknown_field_set.h> #include <google/protobuf/wire_format.h> #include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/io/tokenizer.h> #include <google/protobuf/io/zero_copy_stream_impl.h> #include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/once.h> @@ -2056,6 +2058,8 @@ class DescriptorBuilder { // Otherwise returns true. bool InterpretOptions(OptionsToInterpret* options_to_interpret); + class AggregateOptionFinder; + private: // Interprets uninterpreted_option_ on the specified message, which // must be the mutable copy of the original options message to which @@ -2082,6 +2086,11 @@ class DescriptorBuilder { bool SetOptionValue(const FieldDescriptor* option_field, UnknownFieldSet* unknown_fields); + // Parses an aggregate value for a CPPTYPE_MESSAGE option and + // saves it into *unknown_fields. + bool SetAggregateOption(const FieldDescriptor* option_field, + UnknownFieldSet* unknown_fields); + // Convenience functions to set an int field the right way, depending on // its wire type (a single int CppType can represent multiple wire types). void SetInt32(int number, int32 value, FieldDescriptor::Type type, @@ -2128,6 +2137,10 @@ class DescriptorBuilder { // can use it to find locations recorded by the parser. const UninterpretedOption* uninterpreted_option_; + // Factory used to create the dynamic messages we need to parse + // any aggregate option values we encounter. + DynamicMessageFactory dynamic_factory_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OptionInterpreter); }; @@ -2139,6 +2152,8 @@ class DescriptorBuilder { // redundantly declare OptionInterpreter a friend just to make things extra // clear for these bad compilers. friend class OptionInterpreter; + friend class OptionInterpreter::AggregateOptionFinder; + static inline bool get_allow_unknown(const DescriptorPool* pool) { return pool->allow_unknown_; } @@ -3973,9 +3988,6 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption( intermediate_fields.push_back(field); descriptor = field->message_type(); } - } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - return AddNameError("Option field \"" + debug_msg_name + - "\" cannot be of message type."); } } @@ -4310,18 +4322,95 @@ bool DescriptorBuilder::OptionInterpreter::SetOptionValue( break; case FieldDescriptor::CPPTYPE_MESSAGE: - // We don't currently support defining a message-typed option, so we - // should never actually get here. - return AddValueError("Option \"" + option_field->full_name() + - "\" is a message. To set fields within it, use " - "syntax like \"" + option_field->name() + - ".foo = value\"."); + if (!SetAggregateOption(option_field, unknown_fields)) { + return false; + } break; } return true; } +class DescriptorBuilder::OptionInterpreter::AggregateOptionFinder + : public TextFormat::Finder { + public: + DescriptorBuilder* builder_; + + virtual const FieldDescriptor* FindExtension( + Message* message, const string& name) const { + if (builder_->pool_->mutex_ != NULL) { + builder_->pool_->mutex_->AssertHeld(); + } + Symbol result = builder_->LookupSymbolNoPlaceholder( + name, message->GetDescriptor()->full_name()); + if (result.type == Symbol::FIELD && + result.field_descriptor->is_extension()) { + return result.field_descriptor; + } else { + return NULL; + } + } +}; + +// A custom error collector to record any text-format parsing errors +namespace { +class AggregateErrorCollector : public io::ErrorCollector { + public: + string error_; + + virtual void AddError(int line, int column, const string& message) { + if (!error_.empty()) { + error_ += "; "; + } + error_ += message; + } + + virtual void AddWarning(int line, int column, const string& message) { + // Ignore warnings + } +}; +} + +// We construct a dynamic message of the type corresponding to +// option_field, parse the supplied text-format string into this +// message, and serialize the resulting message to produce the value. +bool DescriptorBuilder::OptionInterpreter::SetAggregateOption( + const FieldDescriptor* option_field, + UnknownFieldSet* unknown_fields) { + if (!uninterpreted_option_->has_aggregate_value()) { + return AddValueError("Option \"" + option_field->full_name() + + "\" is a message. To set the entire message, use " + "syntax like \"" + option_field->name() + + " = { <proto text format> }\". " + "To set fields within it, use " + "syntax like \"" + option_field->name() + + ".foo = value\"."); + } + + const Descriptor* type = option_field->message_type(); + scoped_ptr<Message> dynamic(dynamic_factory_.GetPrototype(type)->New()); + GOOGLE_CHECK(dynamic.get() != NULL) + << "Could not create an instance of " << option_field->DebugString(); + + AggregateErrorCollector collector; + AggregateOptionFinder finder; + finder.builder_ = builder_; + TextFormat::Parser parser; + parser.RecordErrorsTo(&collector); + parser.SetFinder(&finder); + if (!parser.ParseFromString(uninterpreted_option_->aggregate_value(), + dynamic.get())) { + AddValueError("Error while parsing option value for \"" + + option_field->name() + "\": " + collector.error_); + return false; + } else { + string serial; + dynamic->SerializeToString(&serial); // Never fails + unknown_fields->AddLengthDelimited(option_field->number(), serial); + return true; + } +} + void DescriptorBuilder::OptionInterpreter::SetInt32(int number, int32 value, FieldDescriptor::Type type, UnknownFieldSet* unknown_fields) { switch (type) { diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc index 922237f5..87303ef8 100644 --- a/src/google/protobuf/descriptor.pb.cc +++ b/src/google/protobuf/descriptor.pb.cc @@ -76,6 +76,12 @@ const ::google::protobuf::internal::GeneratedMessageReflection* const ::google::protobuf::Descriptor* UninterpretedOption_NamePart_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* UninterpretedOption_NamePart_reflection_ = NULL; +const ::google::protobuf::Descriptor* SourceCodeInfo_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + SourceCodeInfo_reflection_ = NULL; +const ::google::protobuf::Descriptor* SourceCodeInfo_Location_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + SourceCodeInfo_Location_reflection_ = NULL; } // namespace @@ -102,7 +108,7 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { ::google::protobuf::MessageFactory::generated_factory(), sizeof(FileDescriptorSet)); FileDescriptorProto_descriptor_ = file->message_type(1); - static const int FileDescriptorProto_offsets_[8] = { + static const int FileDescriptorProto_offsets_[9] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, name_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, package_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, dependency_), @@ -111,6 +117,7 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, service_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, extension_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, options_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, source_code_info_), }; FileDescriptorProto_reflection_ = new ::google::protobuf::internal::GeneratedMessageReflection( @@ -254,10 +261,11 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { ::google::protobuf::MessageFactory::generated_factory(), sizeof(MethodDescriptorProto)); FileOptions_descriptor_ = file->message_type(8); - static const int FileOptions_offsets_[8] = { + static const int FileOptions_offsets_[9] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_package_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_outer_classname_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_multiple_files_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_generate_equals_and_hash_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, optimize_for_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, cc_generic_services_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_generic_services_), @@ -374,13 +382,14 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { ::google::protobuf::MessageFactory::generated_factory(), sizeof(MethodOptions)); UninterpretedOption_descriptor_ = file->message_type(15); - static const int UninterpretedOption_offsets_[6] = { + static const int UninterpretedOption_offsets_[7] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption, name_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption, identifier_value_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption, positive_int_value_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption, negative_int_value_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption, double_value_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption, string_value_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption, aggregate_value_), }; UninterpretedOption_reflection_ = new ::google::protobuf::internal::GeneratedMessageReflection( @@ -409,6 +418,37 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { ::google::protobuf::DescriptorPool::generated_pool(), ::google::protobuf::MessageFactory::generated_factory(), sizeof(UninterpretedOption_NamePart)); + SourceCodeInfo_descriptor_ = file->message_type(16); + static const int SourceCodeInfo_offsets_[1] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo, location_), + }; + SourceCodeInfo_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + SourceCodeInfo_descriptor_, + SourceCodeInfo::default_instance_, + SourceCodeInfo_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(SourceCodeInfo)); + SourceCodeInfo_Location_descriptor_ = SourceCodeInfo_descriptor_->nested_type(0); + static const int SourceCodeInfo_Location_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo_Location, path_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo_Location, span_), + }; + SourceCodeInfo_Location_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + SourceCodeInfo_Location_descriptor_, + SourceCodeInfo_Location::default_instance_, + SourceCodeInfo_Location_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo_Location, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo_Location, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(SourceCodeInfo_Location)); } namespace { @@ -457,6 +497,10 @@ void protobuf_RegisterTypes(const ::std::string&) { UninterpretedOption_descriptor_, &UninterpretedOption::default_instance()); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( UninterpretedOption_NamePart_descriptor_, &UninterpretedOption_NamePart::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + SourceCodeInfo_descriptor_, &SourceCodeInfo::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + SourceCodeInfo_Location_descriptor_, &SourceCodeInfo_Location::default_instance()); } } // namespace @@ -498,6 +542,10 @@ void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto() { delete UninterpretedOption_reflection_; delete UninterpretedOption_NamePart::default_instance_; delete UninterpretedOption_NamePart_reflection_; + delete SourceCodeInfo::default_instance_; + delete SourceCodeInfo_reflection_; + delete SourceCodeInfo_Location::default_instance_; + delete SourceCodeInfo_Location_reflection_; } void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { @@ -510,7 +558,7 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { "\n google/protobuf/descriptor.proto\022\017goog" "le.protobuf\"G\n\021FileDescriptorSet\0222\n\004file" "\030\001 \003(\0132$.google.protobuf.FileDescriptorP" - "roto\"\334\002\n\023FileDescriptorProto\022\014\n\004name\030\001 \001" + "roto\"\227\003\n\023FileDescriptorProto\022\014\n\004name\030\001 \001" "(\t\022\017\n\007package\030\002 \001(\t\022\022\n\ndependency\030\003 \003(\t\022" "6\n\014message_type\030\004 \003(\0132 .google.protobuf." "DescriptorProto\0227\n\tenum_type\030\005 \003(\0132$.goo" @@ -518,88 +566,94 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { "ice\030\006 \003(\0132\'.google.protobuf.ServiceDescr" "iptorProto\0228\n\textension\030\007 \003(\0132%.google.p" "rotobuf.FieldDescriptorProto\022-\n\007options\030" - "\010 \001(\0132\034.google.protobuf.FileOptions\"\251\003\n\017" - "DescriptorProto\022\014\n\004name\030\001 \001(\t\0224\n\005field\030\002" - " \003(\0132%.google.protobuf.FieldDescriptorPr" - "oto\0228\n\textension\030\006 \003(\0132%.google.protobuf" - ".FieldDescriptorProto\0225\n\013nested_type\030\003 \003" - "(\0132 .google.protobuf.DescriptorProto\0227\n\t" - "enum_type\030\004 \003(\0132$.google.protobuf.EnumDe" - "scriptorProto\022H\n\017extension_range\030\005 \003(\0132/" - ".google.protobuf.DescriptorProto.Extensi" - "onRange\0220\n\007options\030\007 \001(\0132\037.google.protob" - "uf.MessageOptions\032,\n\016ExtensionRange\022\r\n\005s" - "tart\030\001 \001(\005\022\013\n\003end\030\002 \001(\005\"\224\005\n\024FieldDescrip" - "torProto\022\014\n\004name\030\001 \001(\t\022\016\n\006number\030\003 \001(\005\022:" - "\n\005label\030\004 \001(\0162+.google.protobuf.FieldDes" - "criptorProto.Label\0228\n\004type\030\005 \001(\0162*.googl" - "e.protobuf.FieldDescriptorProto.Type\022\021\n\t" - "type_name\030\006 \001(\t\022\020\n\010extendee\030\002 \001(\t\022\025\n\rdef" - "ault_value\030\007 \001(\t\022.\n\007options\030\010 \001(\0132\035.goog" - "le.protobuf.FieldOptions\"\266\002\n\004Type\022\017\n\013TYP" - "E_DOUBLE\020\001\022\016\n\nTYPE_FLOAT\020\002\022\016\n\nTYPE_INT64" - "\020\003\022\017\n\013TYPE_UINT64\020\004\022\016\n\nTYPE_INT32\020\005\022\020\n\014T" - "YPE_FIXED64\020\006\022\020\n\014TYPE_FIXED32\020\007\022\r\n\tTYPE_" - "BOOL\020\010\022\017\n\013TYPE_STRING\020\t\022\016\n\nTYPE_GROUP\020\n\022" - "\020\n\014TYPE_MESSAGE\020\013\022\016\n\nTYPE_BYTES\020\014\022\017\n\013TYP" - "E_UINT32\020\r\022\r\n\tTYPE_ENUM\020\016\022\021\n\rTYPE_SFIXED" - "32\020\017\022\021\n\rTYPE_SFIXED64\020\020\022\017\n\013TYPE_SINT32\020\021" - "\022\017\n\013TYPE_SINT64\020\022\"C\n\005Label\022\022\n\016LABEL_OPTI" - "ONAL\020\001\022\022\n\016LABEL_REQUIRED\020\002\022\022\n\016LABEL_REPE" - "ATED\020\003\"\214\001\n\023EnumDescriptorProto\022\014\n\004name\030\001" - " \001(\t\0228\n\005value\030\002 \003(\0132).google.protobuf.En" - "umValueDescriptorProto\022-\n\007options\030\003 \001(\0132" - "\034.google.protobuf.EnumOptions\"l\n\030EnumVal" - "ueDescriptorProto\022\014\n\004name\030\001 \001(\t\022\016\n\006numbe" - "r\030\002 \001(\005\0222\n\007options\030\003 \001(\0132!.google.protob" - "uf.EnumValueOptions\"\220\001\n\026ServiceDescripto" - "rProto\022\014\n\004name\030\001 \001(\t\0226\n\006method\030\002 \003(\0132&.g" - "oogle.protobuf.MethodDescriptorProto\0220\n\007" - "options\030\003 \001(\0132\037.google.protobuf.ServiceO" - "ptions\"\177\n\025MethodDescriptorProto\022\014\n\004name\030" - "\001 \001(\t\022\022\n\ninput_type\030\002 \001(\t\022\023\n\013output_type" - "\030\003 \001(\t\022/\n\007options\030\004 \001(\0132\036.google.protobu" - "f.MethodOptions\"\244\003\n\013FileOptions\022\024\n\014java_" - "package\030\001 \001(\t\022\034\n\024java_outer_classname\030\010 " - "\001(\t\022\"\n\023java_multiple_files\030\n \001(\010:\005false\022" - "F\n\014optimize_for\030\t \001(\0162).google.protobuf." - "FileOptions.OptimizeMode:\005SPEED\022!\n\023cc_ge" - "neric_services\030\020 \001(\010:\004true\022#\n\025java_gener" - "ic_services\030\021 \001(\010:\004true\022!\n\023py_generic_se" - "rvices\030\022 \001(\010:\004true\022C\n\024uninterpreted_opti" - "on\030\347\007 \003(\0132$.google.protobuf.Uninterprete" - "dOption\":\n\014OptimizeMode\022\t\n\005SPEED\020\001\022\r\n\tCO" - "DE_SIZE\020\002\022\020\n\014LITE_RUNTIME\020\003*\t\010\350\007\020\200\200\200\200\002\"\270" - "\001\n\016MessageOptions\022&\n\027message_set_wire_fo" - "rmat\030\001 \001(\010:\005false\022.\n\037no_standard_descrip" - "tor_accessor\030\002 \001(\010:\005false\022C\n\024uninterpret" + "\010 \001(\0132\034.google.protobuf.FileOptions\0229\n\020s" + "ource_code_info\030\t \001(\0132\037.google.protobuf." + "SourceCodeInfo\"\251\003\n\017DescriptorProto\022\014\n\004na" + "me\030\001 \001(\t\0224\n\005field\030\002 \003(\0132%.google.protobu" + "f.FieldDescriptorProto\0228\n\textension\030\006 \003(" + "\0132%.google.protobuf.FieldDescriptorProto" + "\0225\n\013nested_type\030\003 \003(\0132 .google.protobuf." + "DescriptorProto\0227\n\tenum_type\030\004 \003(\0132$.goo" + "gle.protobuf.EnumDescriptorProto\022H\n\017exte" + "nsion_range\030\005 \003(\0132/.google.protobuf.Desc" + "riptorProto.ExtensionRange\0220\n\007options\030\007 " + "\001(\0132\037.google.protobuf.MessageOptions\032,\n\016" + "ExtensionRange\022\r\n\005start\030\001 \001(\005\022\013\n\003end\030\002 \001" + "(\005\"\224\005\n\024FieldDescriptorProto\022\014\n\004name\030\001 \001(" + "\t\022\016\n\006number\030\003 \001(\005\022:\n\005label\030\004 \001(\0162+.googl" + "e.protobuf.FieldDescriptorProto.Label\0228\n" + "\004type\030\005 \001(\0162*.google.protobuf.FieldDescr" + "iptorProto.Type\022\021\n\ttype_name\030\006 \001(\t\022\020\n\010ex" + "tendee\030\002 \001(\t\022\025\n\rdefault_value\030\007 \001(\t\022.\n\007o" + "ptions\030\010 \001(\0132\035.google.protobuf.FieldOpti" + "ons\"\266\002\n\004Type\022\017\n\013TYPE_DOUBLE\020\001\022\016\n\nTYPE_FL" + "OAT\020\002\022\016\n\nTYPE_INT64\020\003\022\017\n\013TYPE_UINT64\020\004\022\016" + "\n\nTYPE_INT32\020\005\022\020\n\014TYPE_FIXED64\020\006\022\020\n\014TYPE" + "_FIXED32\020\007\022\r\n\tTYPE_BOOL\020\010\022\017\n\013TYPE_STRING" + "\020\t\022\016\n\nTYPE_GROUP\020\n\022\020\n\014TYPE_MESSAGE\020\013\022\016\n\n" + "TYPE_BYTES\020\014\022\017\n\013TYPE_UINT32\020\r\022\r\n\tTYPE_EN" + "UM\020\016\022\021\n\rTYPE_SFIXED32\020\017\022\021\n\rTYPE_SFIXED64" + "\020\020\022\017\n\013TYPE_SINT32\020\021\022\017\n\013TYPE_SINT64\020\022\"C\n\005" + "Label\022\022\n\016LABEL_OPTIONAL\020\001\022\022\n\016LABEL_REQUI" + "RED\020\002\022\022\n\016LABEL_REPEATED\020\003\"\214\001\n\023EnumDescri" + "ptorProto\022\014\n\004name\030\001 \001(\t\0228\n\005value\030\002 \003(\0132)" + ".google.protobuf.EnumValueDescriptorProt" + "o\022-\n\007options\030\003 \001(\0132\034.google.protobuf.Enu" + "mOptions\"l\n\030EnumValueDescriptorProto\022\014\n\004" + "name\030\001 \001(\t\022\016\n\006number\030\002 \001(\005\0222\n\007options\030\003 " + "\001(\0132!.google.protobuf.EnumValueOptions\"\220" + "\001\n\026ServiceDescriptorProto\022\014\n\004name\030\001 \001(\t\022" + "6\n\006method\030\002 \003(\0132&.google.protobuf.Method" + "DescriptorProto\0220\n\007options\030\003 \001(\0132\037.googl" + "e.protobuf.ServiceOptions\"\177\n\025MethodDescr" + "iptorProto\022\014\n\004name\030\001 \001(\t\022\022\n\ninput_type\030\002" + " \001(\t\022\023\n\013output_type\030\003 \001(\t\022/\n\007options\030\004 \001" + "(\0132\036.google.protobuf.MethodOptions\"\325\003\n\013F" + "ileOptions\022\024\n\014java_package\030\001 \001(\t\022\034\n\024java" + "_outer_classname\030\010 \001(\t\022\"\n\023java_multiple_" + "files\030\n \001(\010:\005false\022,\n\035java_generate_equa" + "ls_and_hash\030\024 \001(\010:\005false\022F\n\014optimize_for" + "\030\t \001(\0162).google.protobuf.FileOptions.Opt" + "imizeMode:\005SPEED\022\"\n\023cc_generic_services\030" + "\020 \001(\010:\005false\022$\n\025java_generic_services\030\021 " + "\001(\010:\005false\022\"\n\023py_generic_services\030\022 \001(\010:" + "\005false\022C\n\024uninterpreted_option\030\347\007 \003(\0132$." + "google.protobuf.UninterpretedOption\":\n\014O" + "ptimizeMode\022\t\n\005SPEED\020\001\022\r\n\tCODE_SIZE\020\002\022\020\n" + "\014LITE_RUNTIME\020\003*\t\010\350\007\020\200\200\200\200\002\"\270\001\n\016MessageOp" + "tions\022&\n\027message_set_wire_format\030\001 \001(\010:\005" + "false\022.\n\037no_standard_descriptor_accessor" + "\030\002 \001(\010:\005false\022C\n\024uninterpreted_option\030\347\007" + " \003(\0132$.google.protobuf.UninterpretedOpti" + "on*\t\010\350\007\020\200\200\200\200\002\"\224\002\n\014FieldOptions\022:\n\005ctype\030" + "\001 \001(\0162#.google.protobuf.FieldOptions.CTy" + "pe:\006STRING\022\016\n\006packed\030\002 \001(\010\022\031\n\ndeprecated" + "\030\003 \001(\010:\005false\022\034\n\024experimental_map_key\030\t " + "\001(\t\022C\n\024uninterpreted_option\030\347\007 \003(\0132$.goo" + "gle.protobuf.UninterpretedOption\"/\n\005CTyp" + "e\022\n\n\006STRING\020\000\022\010\n\004CORD\020\001\022\020\n\014STRING_PIECE\020" + "\002*\t\010\350\007\020\200\200\200\200\002\"]\n\013EnumOptions\022C\n\024uninterpr" + "eted_option\030\347\007 \003(\0132$.google.protobuf.Uni" + "nterpretedOption*\t\010\350\007\020\200\200\200\200\002\"b\n\020EnumValue" + "Options\022C\n\024uninterpreted_option\030\347\007 \003(\0132$" + ".google.protobuf.UninterpretedOption*\t\010\350" + "\007\020\200\200\200\200\002\"`\n\016ServiceOptions\022C\n\024uninterpret" "ed_option\030\347\007 \003(\0132$.google.protobuf.Unint" - "erpretedOption*\t\010\350\007\020\200\200\200\200\002\"\224\002\n\014FieldOptio" - "ns\022:\n\005ctype\030\001 \001(\0162#.google.protobuf.Fiel" - "dOptions.CType:\006STRING\022\016\n\006packed\030\002 \001(\010\022\031" - "\n\ndeprecated\030\003 \001(\010:\005false\022\034\n\024experimenta" - "l_map_key\030\t \001(\t\022C\n\024uninterpreted_option\030" - "\347\007 \003(\0132$.google.protobuf.UninterpretedOp" - "tion\"/\n\005CType\022\n\n\006STRING\020\000\022\010\n\004CORD\020\001\022\020\n\014S" - "TRING_PIECE\020\002*\t\010\350\007\020\200\200\200\200\002\"]\n\013EnumOptions\022" - "C\n\024uninterpreted_option\030\347\007 \003(\0132$.google." - "protobuf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\"" - "b\n\020EnumValueOptions\022C\n\024uninterpreted_opt" - "ion\030\347\007 \003(\0132$.google.protobuf.Uninterpret" - "edOption*\t\010\350\007\020\200\200\200\200\002\"`\n\016ServiceOptions\022C\n" - "\024uninterpreted_option\030\347\007 \003(\0132$.google.pr" - "otobuf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\"_\n" - "\rMethodOptions\022C\n\024uninterpreted_option\030\347" - "\007 \003(\0132$.google.protobuf.UninterpretedOpt" - "ion*\t\010\350\007\020\200\200\200\200\002\"\205\002\n\023UninterpretedOption\022;" - "\n\004name\030\002 \003(\0132-.google.protobuf.Uninterpr" - "etedOption.NamePart\022\030\n\020identifier_value\030" - "\003 \001(\t\022\032\n\022positive_int_value\030\004 \001(\004\022\032\n\022neg" - "ative_int_value\030\005 \001(\003\022\024\n\014double_value\030\006 " - "\001(\001\022\024\n\014string_value\030\007 \001(\014\0323\n\010NamePart\022\021\n" - "\tname_part\030\001 \002(\t\022\024\n\014is_extension\030\002 \002(\010B)" - "\n\023com.google.protobufB\020DescriptorProtosH" - "\001", 3681); + "erpretedOption*\t\010\350\007\020\200\200\200\200\002\"_\n\rMethodOptio" + "ns\022C\n\024uninterpreted_option\030\347\007 \003(\0132$.goog" + "le.protobuf.UninterpretedOption*\t\010\350\007\020\200\200\200" + "\200\002\"\236\002\n\023UninterpretedOption\022;\n\004name\030\002 \003(\013" + "2-.google.protobuf.UninterpretedOption.N" + "amePart\022\030\n\020identifier_value\030\003 \001(\t\022\032\n\022pos" + "itive_int_value\030\004 \001(\004\022\032\n\022negative_int_va" + "lue\030\005 \001(\003\022\024\n\014double_value\030\006 \001(\001\022\024\n\014strin" + "g_value\030\007 \001(\014\022\027\n\017aggregate_value\030\010 \001(\t\0323" + "\n\010NamePart\022\021\n\tname_part\030\001 \002(\t\022\024\n\014is_exte" + "nsion\030\002 \002(\010\"|\n\016SourceCodeInfo\022:\n\010locatio" + "n\030\001 \003(\0132(.google.protobuf.SourceCodeInfo" + ".Location\032.\n\010Location\022\020\n\004path\030\001 \003(\005B\002\020\001\022" + "\020\n\004span\030\002 \003(\005B\002\020\001B)\n\023com.google.protobuf" + "B\020DescriptorProtosH\001", 3940); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/descriptor.proto", &protobuf_RegisterTypes); FileDescriptorSet::default_instance_ = new FileDescriptorSet(); @@ -620,6 +674,8 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { MethodOptions::default_instance_ = new MethodOptions(); UninterpretedOption::default_instance_ = new UninterpretedOption(); UninterpretedOption_NamePart::default_instance_ = new UninterpretedOption_NamePart(); + SourceCodeInfo::default_instance_ = new SourceCodeInfo(); + SourceCodeInfo_Location::default_instance_ = new SourceCodeInfo_Location(); FileDescriptorSet::default_instance_->InitAsDefaultInstance(); FileDescriptorProto::default_instance_->InitAsDefaultInstance(); DescriptorProto::default_instance_->InitAsDefaultInstance(); @@ -638,6 +694,8 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { MethodOptions::default_instance_->InitAsDefaultInstance(); UninterpretedOption::default_instance_->InitAsDefaultInstance(); UninterpretedOption_NamePart::default_instance_->InitAsDefaultInstance(); + SourceCodeInfo::default_instance_->InitAsDefaultInstance(); + SourceCodeInfo_Location::default_instance_->InitAsDefaultInstance(); ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto); } @@ -856,8 +914,6 @@ void FileDescriptorSet::Swap(FileDescriptorSet* other) { // =================================================================== -const ::std::string FileDescriptorProto::_default_name_; -const ::std::string FileDescriptorProto::_default_package_; #ifndef _MSC_VER const int FileDescriptorProto::kNameFieldNumber; const int FileDescriptorProto::kPackageFieldNumber; @@ -867,6 +923,7 @@ const int FileDescriptorProto::kEnumTypeFieldNumber; const int FileDescriptorProto::kServiceFieldNumber; const int FileDescriptorProto::kExtensionFieldNumber; const int FileDescriptorProto::kOptionsFieldNumber; +const int FileDescriptorProto::kSourceCodeInfoFieldNumber; #endif // !_MSC_VER FileDescriptorProto::FileDescriptorProto() @@ -876,6 +933,7 @@ FileDescriptorProto::FileDescriptorProto() void FileDescriptorProto::InitAsDefaultInstance() { options_ = const_cast< ::google::protobuf::FileOptions*>(&::google::protobuf::FileOptions::default_instance()); + source_code_info_ = const_cast< ::google::protobuf::SourceCodeInfo*>(&::google::protobuf::SourceCodeInfo::default_instance()); } FileDescriptorProto::FileDescriptorProto(const FileDescriptorProto& from) @@ -886,9 +944,10 @@ FileDescriptorProto::FileDescriptorProto(const FileDescriptorProto& from) void FileDescriptorProto::SharedCtor() { _cached_size_ = 0; - name_ = const_cast< ::std::string*>(&_default_name_); - package_ = const_cast< ::std::string*>(&_default_package_); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + package_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); options_ = NULL; + source_code_info_ = NULL; ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -897,14 +956,15 @@ FileDescriptorProto::~FileDescriptorProto() { } void FileDescriptorProto::SharedDtor() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { delete name_; } - if (package_ != &_default_package_) { + if (package_ != &::google::protobuf::internal::kEmptyString) { delete package_; } if (this != default_instance_) { delete options_; + delete source_code_info_; } } @@ -930,20 +990,25 @@ FileDescriptorProto* FileDescriptorProto::New() const { void FileDescriptorProto::Clear() { if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (_has_bit(0)) { - if (name_ != &_default_name_) { + if (has_name()) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } } - if (_has_bit(1)) { - if (package_ != &_default_package_) { + if (has_package()) { + if (package_ != &::google::protobuf::internal::kEmptyString) { package_->clear(); } } - if (_has_bit(7)) { + if (has_options()) { if (options_ != NULL) options_->::google::protobuf::FileOptions::Clear(); } } + if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) { + if (has_source_code_info()) { + if (source_code_info_ != NULL) source_code_info_->::google::protobuf::SourceCodeInfo::Clear(); + } + } dependency_.Clear(); message_type_.Clear(); enum_type_.Clear(); @@ -1080,6 +1145,20 @@ bool FileDescriptorProto::MergePartialFromCodedStream( } else { goto handle_uninterpreted; } + if (input->ExpectTag(74)) goto parse_source_code_info; + break; + } + + // optional .google.protobuf.SourceCodeInfo source_code_info = 9; + case 9: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_source_code_info: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_source_code_info())); + } else { + goto handle_uninterpreted; + } if (input->ExpectAtEnd()) return true; break; } @@ -1103,7 +1182,7 @@ bool FileDescriptorProto::MergePartialFromCodedStream( void FileDescriptorProto::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -1112,7 +1191,7 @@ void FileDescriptorProto::SerializeWithCachedSizes( } // optional string package = 2; - if (_has_bit(1)) { + if (has_package()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->package().data(), this->package().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -1154,11 +1233,17 @@ void FileDescriptorProto::SerializeWithCachedSizes( } // optional .google.protobuf.FileOptions options = 8; - if (_has_bit(7)) { + if (has_options()) { ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( 8, this->options(), output); } + // optional .google.protobuf.SourceCodeInfo source_code_info = 9; + if (has_source_code_info()) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 9, this->source_code_info(), output); + } + if (!unknown_fields().empty()) { ::google::protobuf::internal::WireFormat::SerializeUnknownFields( unknown_fields(), output); @@ -1168,7 +1253,7 @@ void FileDescriptorProto::SerializeWithCachedSizes( ::google::protobuf::uint8* FileDescriptorProto::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -1178,7 +1263,7 @@ void FileDescriptorProto::SerializeWithCachedSizes( } // optional string package = 2; - if (_has_bit(1)) { + if (has_package()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->package().data(), this->package().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -1225,12 +1310,19 @@ void FileDescriptorProto::SerializeWithCachedSizes( } // optional .google.protobuf.FileOptions options = 8; - if (_has_bit(7)) { + if (has_options()) { target = ::google::protobuf::internal::WireFormatLite:: WriteMessageNoVirtualToArray( 8, this->options(), target); } + // optional .google.protobuf.SourceCodeInfo source_code_info = 9; + if (has_source_code_info()) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 9, this->source_code_info(), target); + } + if (!unknown_fields().empty()) { target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( unknown_fields(), target); @@ -1264,6 +1356,15 @@ int FileDescriptorProto::ByteSize() const { } } + if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) { + // optional .google.protobuf.SourceCodeInfo source_code_info = 9; + if (has_source_code_info()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->source_code_info()); + } + + } // repeated string dependency = 3; total_size += 1 * this->dependency_size(); for (int i = 0; i < this->dependency_size(); i++) { @@ -1334,16 +1435,21 @@ void FileDescriptorProto::MergeFrom(const FileDescriptorProto& from) { service_.MergeFrom(from.service_); extension_.MergeFrom(from.extension_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_name()) { set_name(from.name()); } - if (from._has_bit(1)) { + if (from.has_package()) { set_package(from.package()); } - if (from._has_bit(7)) { + if (from.has_options()) { mutable_options()->::google::protobuf::FileOptions::MergeFrom(from.options()); } } + if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) { + if (from.has_source_code_info()) { + mutable_source_code_info()->::google::protobuf::SourceCodeInfo::MergeFrom(from.source_code_info()); + } + } mutable_unknown_fields()->MergeFrom(from.unknown_fields()); } @@ -1389,6 +1495,7 @@ void FileDescriptorProto::Swap(FileDescriptorProto* other) { service_.Swap(&other->service_); extension_.Swap(&other->extension_); std::swap(options_, other->options_); + std::swap(source_code_info_, other->source_code_info_); std::swap(_has_bits_[0], other->_has_bits_[0]); _unknown_fields_.Swap(&other->_unknown_fields_); std::swap(_cached_size_, other->_cached_size_); @@ -1483,7 +1590,7 @@ bool DescriptorProto_ExtensionRange::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( input, &start_))); - _set_bit(0); + set_has_start(); } else { goto handle_uninterpreted; } @@ -1499,7 +1606,7 @@ bool DescriptorProto_ExtensionRange::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( input, &end_))); - _set_bit(1); + set_has_end(); } else { goto handle_uninterpreted; } @@ -1526,12 +1633,12 @@ bool DescriptorProto_ExtensionRange::MergePartialFromCodedStream( void DescriptorProto_ExtensionRange::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional int32 start = 1; - if (_has_bit(0)) { + if (has_start()) { ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->start(), output); } // optional int32 end = 2; - if (_has_bit(1)) { + if (has_end()) { ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->end(), output); } @@ -1544,12 +1651,12 @@ void DescriptorProto_ExtensionRange::SerializeWithCachedSizes( ::google::protobuf::uint8* DescriptorProto_ExtensionRange::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional int32 start = 1; - if (_has_bit(0)) { + if (has_start()) { target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(1, this->start(), target); } // optional int32 end = 2; - if (_has_bit(1)) { + if (has_end()) { target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(2, this->end(), target); } @@ -1605,10 +1712,10 @@ void DescriptorProto_ExtensionRange::MergeFrom(const ::google::protobuf::Message void DescriptorProto_ExtensionRange::MergeFrom(const DescriptorProto_ExtensionRange& from) { GOOGLE_CHECK_NE(&from, this); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_start()) { set_start(from.start()); } - if (from._has_bit(1)) { + if (from.has_end()) { set_end(from.end()); } } @@ -1653,7 +1760,6 @@ void DescriptorProto_ExtensionRange::Swap(DescriptorProto_ExtensionRange* other) // ------------------------------------------------------------------- -const ::std::string DescriptorProto::_default_name_; #ifndef _MSC_VER const int DescriptorProto::kNameFieldNumber; const int DescriptorProto::kFieldFieldNumber; @@ -1681,7 +1787,7 @@ DescriptorProto::DescriptorProto(const DescriptorProto& from) void DescriptorProto::SharedCtor() { _cached_size_ = 0; - name_ = const_cast< ::std::string*>(&_default_name_); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); options_ = NULL; ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -1691,7 +1797,7 @@ DescriptorProto::~DescriptorProto() { } void DescriptorProto::SharedDtor() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { delete name_; } if (this != default_instance_) { @@ -1721,12 +1827,12 @@ DescriptorProto* DescriptorProto::New() const { void DescriptorProto::Clear() { if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (_has_bit(0)) { - if (name_ != &_default_name_) { + if (has_name()) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } } - if (_has_bit(6)) { + if (has_options()) { if (options_ != NULL) options_->::google::protobuf::MessageOptions::Clear(); } } @@ -1869,7 +1975,7 @@ bool DescriptorProto::MergePartialFromCodedStream( void DescriptorProto::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -1908,7 +2014,7 @@ void DescriptorProto::SerializeWithCachedSizes( } // optional .google.protobuf.MessageOptions options = 7; - if (_has_bit(6)) { + if (has_options()) { ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( 7, this->options(), output); } @@ -1922,7 +2028,7 @@ void DescriptorProto::SerializeWithCachedSizes( ::google::protobuf::uint8* DescriptorProto::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -1967,7 +2073,7 @@ void DescriptorProto::SerializeWithCachedSizes( } // optional .google.protobuf.MessageOptions options = 7; - if (_has_bit(6)) { + if (has_options()) { target = ::google::protobuf::internal::WireFormatLite:: WriteMessageNoVirtualToArray( 7, this->options(), target); @@ -2070,10 +2176,10 @@ void DescriptorProto::MergeFrom(const DescriptorProto& from) { enum_type_.MergeFrom(from.enum_type_); extension_range_.MergeFrom(from.extension_range_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_name()) { set_name(from.name()); } - if (from._has_bit(6)) { + if (from.has_options()) { mutable_options()->::google::protobuf::MessageOptions::MergeFrom(from.options()); } } @@ -2214,10 +2320,6 @@ const FieldDescriptorProto_Label FieldDescriptorProto::Label_MIN; const FieldDescriptorProto_Label FieldDescriptorProto::Label_MAX; const int FieldDescriptorProto::Label_ARRAYSIZE; #endif // _MSC_VER -const ::std::string FieldDescriptorProto::_default_name_; -const ::std::string FieldDescriptorProto::_default_type_name_; -const ::std::string FieldDescriptorProto::_default_extendee_; -const ::std::string FieldDescriptorProto::_default_default_value_; #ifndef _MSC_VER const int FieldDescriptorProto::kNameFieldNumber; const int FieldDescriptorProto::kNumberFieldNumber; @@ -2246,13 +2348,13 @@ FieldDescriptorProto::FieldDescriptorProto(const FieldDescriptorProto& from) void FieldDescriptorProto::SharedCtor() { _cached_size_ = 0; - name_ = const_cast< ::std::string*>(&_default_name_); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); number_ = 0; label_ = 1; type_ = 1; - type_name_ = const_cast< ::std::string*>(&_default_type_name_); - extendee_ = const_cast< ::std::string*>(&_default_extendee_); - default_value_ = const_cast< ::std::string*>(&_default_default_value_); + type_name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + extendee_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + default_value_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); options_ = NULL; ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -2262,16 +2364,16 @@ FieldDescriptorProto::~FieldDescriptorProto() { } void FieldDescriptorProto::SharedDtor() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { delete name_; } - if (type_name_ != &_default_type_name_) { + if (type_name_ != &::google::protobuf::internal::kEmptyString) { delete type_name_; } - if (extendee_ != &_default_extendee_) { + if (extendee_ != &::google::protobuf::internal::kEmptyString) { delete extendee_; } - if (default_value_ != &_default_default_value_) { + if (default_value_ != &::google::protobuf::internal::kEmptyString) { delete default_value_; } if (this != default_instance_) { @@ -2301,30 +2403,30 @@ FieldDescriptorProto* FieldDescriptorProto::New() const { void FieldDescriptorProto::Clear() { if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (_has_bit(0)) { - if (name_ != &_default_name_) { + if (has_name()) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } } number_ = 0; label_ = 1; type_ = 1; - if (_has_bit(4)) { - if (type_name_ != &_default_type_name_) { + if (has_type_name()) { + if (type_name_ != &::google::protobuf::internal::kEmptyString) { type_name_->clear(); } } - if (_has_bit(5)) { - if (extendee_ != &_default_extendee_) { + if (has_extendee()) { + if (extendee_ != &::google::protobuf::internal::kEmptyString) { extendee_->clear(); } } - if (_has_bit(6)) { - if (default_value_ != &_default_default_value_) { + if (has_default_value()) { + if (default_value_ != &::google::protobuf::internal::kEmptyString) { default_value_->clear(); } } - if (_has_bit(7)) { + if (has_options()) { if (options_ != NULL) options_->::google::protobuf::FieldOptions::Clear(); } } @@ -2379,7 +2481,7 @@ bool FieldDescriptorProto::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( input, &number_))); - _set_bit(1); + set_has_number(); } else { goto handle_uninterpreted; } @@ -2496,7 +2598,7 @@ bool FieldDescriptorProto::MergePartialFromCodedStream( void FieldDescriptorProto::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -2505,7 +2607,7 @@ void FieldDescriptorProto::SerializeWithCachedSizes( } // optional string extendee = 2; - if (_has_bit(5)) { + if (has_extendee()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->extendee().data(), this->extendee().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -2514,24 +2616,24 @@ void FieldDescriptorProto::SerializeWithCachedSizes( } // optional int32 number = 3; - if (_has_bit(1)) { + if (has_number()) { ::google::protobuf::internal::WireFormatLite::WriteInt32(3, this->number(), output); } // optional .google.protobuf.FieldDescriptorProto.Label label = 4; - if (_has_bit(2)) { + if (has_label()) { ::google::protobuf::internal::WireFormatLite::WriteEnum( 4, this->label(), output); } // optional .google.protobuf.FieldDescriptorProto.Type type = 5; - if (_has_bit(3)) { + if (has_type()) { ::google::protobuf::internal::WireFormatLite::WriteEnum( 5, this->type(), output); } // optional string type_name = 6; - if (_has_bit(4)) { + if (has_type_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->type_name().data(), this->type_name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -2540,7 +2642,7 @@ void FieldDescriptorProto::SerializeWithCachedSizes( } // optional string default_value = 7; - if (_has_bit(6)) { + if (has_default_value()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->default_value().data(), this->default_value().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -2549,7 +2651,7 @@ void FieldDescriptorProto::SerializeWithCachedSizes( } // optional .google.protobuf.FieldOptions options = 8; - if (_has_bit(7)) { + if (has_options()) { ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( 8, this->options(), output); } @@ -2563,7 +2665,7 @@ void FieldDescriptorProto::SerializeWithCachedSizes( ::google::protobuf::uint8* FieldDescriptorProto::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -2573,7 +2675,7 @@ void FieldDescriptorProto::SerializeWithCachedSizes( } // optional string extendee = 2; - if (_has_bit(5)) { + if (has_extendee()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->extendee().data(), this->extendee().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -2583,24 +2685,24 @@ void FieldDescriptorProto::SerializeWithCachedSizes( } // optional int32 number = 3; - if (_has_bit(1)) { + if (has_number()) { target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(3, this->number(), target); } // optional .google.protobuf.FieldDescriptorProto.Label label = 4; - if (_has_bit(2)) { + if (has_label()) { target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( 4, this->label(), target); } // optional .google.protobuf.FieldDescriptorProto.Type type = 5; - if (_has_bit(3)) { + if (has_type()) { target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( 5, this->type(), target); } // optional string type_name = 6; - if (_has_bit(4)) { + if (has_type_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->type_name().data(), this->type_name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -2610,7 +2712,7 @@ void FieldDescriptorProto::SerializeWithCachedSizes( } // optional string default_value = 7; - if (_has_bit(6)) { + if (has_default_value()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->default_value().data(), this->default_value().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -2620,7 +2722,7 @@ void FieldDescriptorProto::SerializeWithCachedSizes( } // optional .google.protobuf.FieldOptions options = 8; - if (_has_bit(7)) { + if (has_options()) { target = ::google::protobuf::internal::WireFormatLite:: WriteMessageNoVirtualToArray( 8, this->options(), target); @@ -2718,28 +2820,28 @@ void FieldDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { void FieldDescriptorProto::MergeFrom(const FieldDescriptorProto& from) { GOOGLE_CHECK_NE(&from, this); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_name()) { set_name(from.name()); } - if (from._has_bit(1)) { + if (from.has_number()) { set_number(from.number()); } - if (from._has_bit(2)) { + if (from.has_label()) { set_label(from.label()); } - if (from._has_bit(3)) { + if (from.has_type()) { set_type(from.type()); } - if (from._has_bit(4)) { + if (from.has_type_name()) { set_type_name(from.type_name()); } - if (from._has_bit(5)) { + if (from.has_extendee()) { set_extendee(from.extendee()); } - if (from._has_bit(6)) { + if (from.has_default_value()) { set_default_value(from.default_value()); } - if (from._has_bit(7)) { + if (from.has_options()) { mutable_options()->::google::protobuf::FieldOptions::MergeFrom(from.options()); } } @@ -2793,7 +2895,6 @@ void FieldDescriptorProto::Swap(FieldDescriptorProto* other) { // =================================================================== -const ::std::string EnumDescriptorProto::_default_name_; #ifndef _MSC_VER const int EnumDescriptorProto::kNameFieldNumber; const int EnumDescriptorProto::kValueFieldNumber; @@ -2817,7 +2918,7 @@ EnumDescriptorProto::EnumDescriptorProto(const EnumDescriptorProto& from) void EnumDescriptorProto::SharedCtor() { _cached_size_ = 0; - name_ = const_cast< ::std::string*>(&_default_name_); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); options_ = NULL; ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -2827,7 +2928,7 @@ EnumDescriptorProto::~EnumDescriptorProto() { } void EnumDescriptorProto::SharedDtor() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { delete name_; } if (this != default_instance_) { @@ -2857,12 +2958,12 @@ EnumDescriptorProto* EnumDescriptorProto::New() const { void EnumDescriptorProto::Clear() { if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (_has_bit(0)) { - if (name_ != &_default_name_) { + if (has_name()) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } } - if (_has_bit(2)) { + if (has_options()) { if (options_ != NULL) options_->::google::protobuf::EnumOptions::Clear(); } } @@ -2941,7 +3042,7 @@ bool EnumDescriptorProto::MergePartialFromCodedStream( void EnumDescriptorProto::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -2956,7 +3057,7 @@ void EnumDescriptorProto::SerializeWithCachedSizes( } // optional .google.protobuf.EnumOptions options = 3; - if (_has_bit(2)) { + if (has_options()) { ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( 3, this->options(), output); } @@ -2970,7 +3071,7 @@ void EnumDescriptorProto::SerializeWithCachedSizes( ::google::protobuf::uint8* EnumDescriptorProto::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -2987,7 +3088,7 @@ void EnumDescriptorProto::SerializeWithCachedSizes( } // optional .google.protobuf.EnumOptions options = 3; - if (_has_bit(2)) { + if (has_options()) { target = ::google::protobuf::internal::WireFormatLite:: WriteMessageNoVirtualToArray( 3, this->options(), target); @@ -3054,10 +3155,10 @@ void EnumDescriptorProto::MergeFrom(const EnumDescriptorProto& from) { GOOGLE_CHECK_NE(&from, this); value_.MergeFrom(from.value_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_name()) { set_name(from.name()); } - if (from._has_bit(2)) { + if (from.has_options()) { mutable_options()->::google::protobuf::EnumOptions::MergeFrom(from.options()); } } @@ -3109,7 +3210,6 @@ void EnumDescriptorProto::Swap(EnumDescriptorProto* other) { // =================================================================== -const ::std::string EnumValueDescriptorProto::_default_name_; #ifndef _MSC_VER const int EnumValueDescriptorProto::kNameFieldNumber; const int EnumValueDescriptorProto::kNumberFieldNumber; @@ -3133,7 +3233,7 @@ EnumValueDescriptorProto::EnumValueDescriptorProto(const EnumValueDescriptorProt void EnumValueDescriptorProto::SharedCtor() { _cached_size_ = 0; - name_ = const_cast< ::std::string*>(&_default_name_); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); number_ = 0; options_ = NULL; ::memset(_has_bits_, 0, sizeof(_has_bits_)); @@ -3144,7 +3244,7 @@ EnumValueDescriptorProto::~EnumValueDescriptorProto() { } void EnumValueDescriptorProto::SharedDtor() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { delete name_; } if (this != default_instance_) { @@ -3174,13 +3274,13 @@ EnumValueDescriptorProto* EnumValueDescriptorProto::New() const { void EnumValueDescriptorProto::Clear() { if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (_has_bit(0)) { - if (name_ != &_default_name_) { + if (has_name()) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } } number_ = 0; - if (_has_bit(2)) { + if (has_options()) { if (options_ != NULL) options_->::google::protobuf::EnumValueOptions::Clear(); } } @@ -3218,7 +3318,7 @@ bool EnumValueDescriptorProto::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( input, &number_))); - _set_bit(1); + set_has_number(); } else { goto handle_uninterpreted; } @@ -3259,7 +3359,7 @@ bool EnumValueDescriptorProto::MergePartialFromCodedStream( void EnumValueDescriptorProto::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -3268,12 +3368,12 @@ void EnumValueDescriptorProto::SerializeWithCachedSizes( } // optional int32 number = 2; - if (_has_bit(1)) { + if (has_number()) { ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->number(), output); } // optional .google.protobuf.EnumValueOptions options = 3; - if (_has_bit(2)) { + if (has_options()) { ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( 3, this->options(), output); } @@ -3287,7 +3387,7 @@ void EnumValueDescriptorProto::SerializeWithCachedSizes( ::google::protobuf::uint8* EnumValueDescriptorProto::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -3297,12 +3397,12 @@ void EnumValueDescriptorProto::SerializeWithCachedSizes( } // optional int32 number = 2; - if (_has_bit(1)) { + if (has_number()) { target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(2, this->number(), target); } // optional .google.protobuf.EnumValueOptions options = 3; - if (_has_bit(2)) { + if (has_options()) { target = ::google::protobuf::internal::WireFormatLite:: WriteMessageNoVirtualToArray( 3, this->options(), target); @@ -3367,13 +3467,13 @@ void EnumValueDescriptorProto::MergeFrom(const ::google::protobuf::Message& from void EnumValueDescriptorProto::MergeFrom(const EnumValueDescriptorProto& from) { GOOGLE_CHECK_NE(&from, this); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_name()) { set_name(from.name()); } - if (from._has_bit(1)) { + if (from.has_number()) { set_number(from.number()); } - if (from._has_bit(2)) { + if (from.has_options()) { mutable_options()->::google::protobuf::EnumValueOptions::MergeFrom(from.options()); } } @@ -3422,7 +3522,6 @@ void EnumValueDescriptorProto::Swap(EnumValueDescriptorProto* other) { // =================================================================== -const ::std::string ServiceDescriptorProto::_default_name_; #ifndef _MSC_VER const int ServiceDescriptorProto::kNameFieldNumber; const int ServiceDescriptorProto::kMethodFieldNumber; @@ -3446,7 +3545,7 @@ ServiceDescriptorProto::ServiceDescriptorProto(const ServiceDescriptorProto& fro void ServiceDescriptorProto::SharedCtor() { _cached_size_ = 0; - name_ = const_cast< ::std::string*>(&_default_name_); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); options_ = NULL; ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -3456,7 +3555,7 @@ ServiceDescriptorProto::~ServiceDescriptorProto() { } void ServiceDescriptorProto::SharedDtor() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { delete name_; } if (this != default_instance_) { @@ -3486,12 +3585,12 @@ ServiceDescriptorProto* ServiceDescriptorProto::New() const { void ServiceDescriptorProto::Clear() { if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (_has_bit(0)) { - if (name_ != &_default_name_) { + if (has_name()) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } } - if (_has_bit(2)) { + if (has_options()) { if (options_ != NULL) options_->::google::protobuf::ServiceOptions::Clear(); } } @@ -3570,7 +3669,7 @@ bool ServiceDescriptorProto::MergePartialFromCodedStream( void ServiceDescriptorProto::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -3585,7 +3684,7 @@ void ServiceDescriptorProto::SerializeWithCachedSizes( } // optional .google.protobuf.ServiceOptions options = 3; - if (_has_bit(2)) { + if (has_options()) { ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( 3, this->options(), output); } @@ -3599,7 +3698,7 @@ void ServiceDescriptorProto::SerializeWithCachedSizes( ::google::protobuf::uint8* ServiceDescriptorProto::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -3616,7 +3715,7 @@ void ServiceDescriptorProto::SerializeWithCachedSizes( } // optional .google.protobuf.ServiceOptions options = 3; - if (_has_bit(2)) { + if (has_options()) { target = ::google::protobuf::internal::WireFormatLite:: WriteMessageNoVirtualToArray( 3, this->options(), target); @@ -3683,10 +3782,10 @@ void ServiceDescriptorProto::MergeFrom(const ServiceDescriptorProto& from) { GOOGLE_CHECK_NE(&from, this); method_.MergeFrom(from.method_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_name()) { set_name(from.name()); } - if (from._has_bit(2)) { + if (from.has_options()) { mutable_options()->::google::protobuf::ServiceOptions::MergeFrom(from.options()); } } @@ -3738,9 +3837,6 @@ void ServiceDescriptorProto::Swap(ServiceDescriptorProto* other) { // =================================================================== -const ::std::string MethodDescriptorProto::_default_name_; -const ::std::string MethodDescriptorProto::_default_input_type_; -const ::std::string MethodDescriptorProto::_default_output_type_; #ifndef _MSC_VER const int MethodDescriptorProto::kNameFieldNumber; const int MethodDescriptorProto::kInputTypeFieldNumber; @@ -3765,9 +3861,9 @@ MethodDescriptorProto::MethodDescriptorProto(const MethodDescriptorProto& from) void MethodDescriptorProto::SharedCtor() { _cached_size_ = 0; - name_ = const_cast< ::std::string*>(&_default_name_); - input_type_ = const_cast< ::std::string*>(&_default_input_type_); - output_type_ = const_cast< ::std::string*>(&_default_output_type_); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + input_type_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + output_type_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); options_ = NULL; ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -3777,13 +3873,13 @@ MethodDescriptorProto::~MethodDescriptorProto() { } void MethodDescriptorProto::SharedDtor() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { delete name_; } - if (input_type_ != &_default_input_type_) { + if (input_type_ != &::google::protobuf::internal::kEmptyString) { delete input_type_; } - if (output_type_ != &_default_output_type_) { + if (output_type_ != &::google::protobuf::internal::kEmptyString) { delete output_type_; } if (this != default_instance_) { @@ -3813,22 +3909,22 @@ MethodDescriptorProto* MethodDescriptorProto::New() const { void MethodDescriptorProto::Clear() { if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (_has_bit(0)) { - if (name_ != &_default_name_) { + if (has_name()) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } } - if (_has_bit(1)) { - if (input_type_ != &_default_input_type_) { + if (has_input_type()) { + if (input_type_ != &::google::protobuf::internal::kEmptyString) { input_type_->clear(); } } - if (_has_bit(2)) { - if (output_type_ != &_default_output_type_) { + if (has_output_type()) { + if (output_type_ != &::google::protobuf::internal::kEmptyString) { output_type_->clear(); } } - if (_has_bit(3)) { + if (has_options()) { if (options_ != NULL) options_->::google::protobuf::MethodOptions::Clear(); } } @@ -3925,7 +4021,7 @@ bool MethodDescriptorProto::MergePartialFromCodedStream( void MethodDescriptorProto::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -3934,7 +4030,7 @@ void MethodDescriptorProto::SerializeWithCachedSizes( } // optional string input_type = 2; - if (_has_bit(1)) { + if (has_input_type()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->input_type().data(), this->input_type().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -3943,7 +4039,7 @@ void MethodDescriptorProto::SerializeWithCachedSizes( } // optional string output_type = 3; - if (_has_bit(2)) { + if (has_output_type()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->output_type().data(), this->output_type().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -3952,7 +4048,7 @@ void MethodDescriptorProto::SerializeWithCachedSizes( } // optional .google.protobuf.MethodOptions options = 4; - if (_has_bit(3)) { + if (has_options()) { ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( 4, this->options(), output); } @@ -3966,7 +4062,7 @@ void MethodDescriptorProto::SerializeWithCachedSizes( ::google::protobuf::uint8* MethodDescriptorProto::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional string name = 1; - if (_has_bit(0)) { + if (has_name()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name().data(), this->name().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -3976,7 +4072,7 @@ void MethodDescriptorProto::SerializeWithCachedSizes( } // optional string input_type = 2; - if (_has_bit(1)) { + if (has_input_type()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->input_type().data(), this->input_type().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -3986,7 +4082,7 @@ void MethodDescriptorProto::SerializeWithCachedSizes( } // optional string output_type = 3; - if (_has_bit(2)) { + if (has_output_type()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->output_type().data(), this->output_type().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -3996,7 +4092,7 @@ void MethodDescriptorProto::SerializeWithCachedSizes( } // optional .google.protobuf.MethodOptions options = 4; - if (_has_bit(3)) { + if (has_options()) { target = ::google::protobuf::internal::WireFormatLite:: WriteMessageNoVirtualToArray( 4, this->options(), target); @@ -4068,16 +4164,16 @@ void MethodDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { void MethodDescriptorProto::MergeFrom(const MethodDescriptorProto& from) { GOOGLE_CHECK_NE(&from, this); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_name()) { set_name(from.name()); } - if (from._has_bit(1)) { + if (from.has_input_type()) { set_input_type(from.input_type()); } - if (from._has_bit(2)) { + if (from.has_output_type()) { set_output_type(from.output_type()); } - if (from._has_bit(3)) { + if (from.has_options()) { mutable_options()->::google::protobuf::MethodOptions::MergeFrom(from.options()); } } @@ -4150,12 +4246,11 @@ const FileOptions_OptimizeMode FileOptions::OptimizeMode_MIN; const FileOptions_OptimizeMode FileOptions::OptimizeMode_MAX; const int FileOptions::OptimizeMode_ARRAYSIZE; #endif // _MSC_VER -const ::std::string FileOptions::_default_java_package_; -const ::std::string FileOptions::_default_java_outer_classname_; #ifndef _MSC_VER const int FileOptions::kJavaPackageFieldNumber; const int FileOptions::kJavaOuterClassnameFieldNumber; const int FileOptions::kJavaMultipleFilesFieldNumber; +const int FileOptions::kJavaGenerateEqualsAndHashFieldNumber; const int FileOptions::kOptimizeForFieldNumber; const int FileOptions::kCcGenericServicesFieldNumber; const int FileOptions::kJavaGenericServicesFieldNumber; @@ -4179,13 +4274,14 @@ FileOptions::FileOptions(const FileOptions& from) void FileOptions::SharedCtor() { _cached_size_ = 0; - java_package_ = const_cast< ::std::string*>(&_default_java_package_); - java_outer_classname_ = const_cast< ::std::string*>(&_default_java_outer_classname_); + java_package_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + java_outer_classname_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); java_multiple_files_ = false; + java_generate_equals_and_hash_ = false; optimize_for_ = 1; - cc_generic_services_ = true; - java_generic_services_ = true; - py_generic_services_ = true; + cc_generic_services_ = false; + java_generic_services_ = false; + py_generic_services_ = false; ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -4194,10 +4290,10 @@ FileOptions::~FileOptions() { } void FileOptions::SharedDtor() { - if (java_package_ != &_default_java_package_) { + if (java_package_ != &::google::protobuf::internal::kEmptyString) { delete java_package_; } - if (java_outer_classname_ != &_default_java_outer_classname_) { + if (java_outer_classname_ != &::google::protobuf::internal::kEmptyString) { delete java_outer_classname_; } if (this != default_instance_) { @@ -4227,21 +4323,22 @@ FileOptions* FileOptions::New() const { void FileOptions::Clear() { _extensions_.Clear(); if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (_has_bit(0)) { - if (java_package_ != &_default_java_package_) { + if (has_java_package()) { + if (java_package_ != &::google::protobuf::internal::kEmptyString) { java_package_->clear(); } } - if (_has_bit(1)) { - if (java_outer_classname_ != &_default_java_outer_classname_) { + if (has_java_outer_classname()) { + if (java_outer_classname_ != &::google::protobuf::internal::kEmptyString) { java_outer_classname_->clear(); } } java_multiple_files_ = false; + java_generate_equals_and_hash_ = false; optimize_for_ = 1; - cc_generic_services_ = true; - java_generic_services_ = true; - py_generic_services_ = true; + cc_generic_services_ = false; + java_generic_services_ = false; + py_generic_services_ = false; } uninterpreted_option_.Clear(); ::memset(_has_bits_, 0, sizeof(_has_bits_)); @@ -4316,7 +4413,7 @@ bool FileOptions::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( input, &java_multiple_files_))); - _set_bit(2); + set_has_java_multiple_files(); } else { goto handle_uninterpreted; } @@ -4324,7 +4421,7 @@ bool FileOptions::MergePartialFromCodedStream( break; } - // optional bool cc_generic_services = 16 [default = true]; + // optional bool cc_generic_services = 16 [default = false]; case 16: { if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { @@ -4332,7 +4429,7 @@ bool FileOptions::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( input, &cc_generic_services_))); - _set_bit(4); + set_has_cc_generic_services(); } else { goto handle_uninterpreted; } @@ -4340,7 +4437,7 @@ bool FileOptions::MergePartialFromCodedStream( break; } - // optional bool java_generic_services = 17 [default = true]; + // optional bool java_generic_services = 17 [default = false]; case 17: { if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { @@ -4348,7 +4445,7 @@ bool FileOptions::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( input, &java_generic_services_))); - _set_bit(5); + set_has_java_generic_services(); } else { goto handle_uninterpreted; } @@ -4356,7 +4453,7 @@ bool FileOptions::MergePartialFromCodedStream( break; } - // optional bool py_generic_services = 18 [default = true]; + // optional bool py_generic_services = 18 [default = false]; case 18: { if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { @@ -4364,7 +4461,23 @@ bool FileOptions::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( input, &py_generic_services_))); - _set_bit(6); + set_has_py_generic_services(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(160)) goto parse_java_generate_equals_and_hash; + break; + } + + // optional bool java_generate_equals_and_hash = 20 [default = false]; + case 20: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_java_generate_equals_and_hash: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( + input, &java_generate_equals_and_hash_))); + set_has_java_generate_equals_and_hash(); } else { goto handle_uninterpreted; } @@ -4411,7 +4524,7 @@ bool FileOptions::MergePartialFromCodedStream( void FileOptions::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional string java_package = 1; - if (_has_bit(0)) { + if (has_java_package()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->java_package().data(), this->java_package().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -4420,7 +4533,7 @@ void FileOptions::SerializeWithCachedSizes( } // optional string java_outer_classname = 8; - if (_has_bit(1)) { + if (has_java_outer_classname()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->java_outer_classname().data(), this->java_outer_classname().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -4429,31 +4542,36 @@ void FileOptions::SerializeWithCachedSizes( } // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; - if (_has_bit(3)) { + if (has_optimize_for()) { ::google::protobuf::internal::WireFormatLite::WriteEnum( 9, this->optimize_for(), output); } // optional bool java_multiple_files = 10 [default = false]; - if (_has_bit(2)) { + if (has_java_multiple_files()) { ::google::protobuf::internal::WireFormatLite::WriteBool(10, this->java_multiple_files(), output); } - // optional bool cc_generic_services = 16 [default = true]; - if (_has_bit(4)) { + // optional bool cc_generic_services = 16 [default = false]; + if (has_cc_generic_services()) { ::google::protobuf::internal::WireFormatLite::WriteBool(16, this->cc_generic_services(), output); } - // optional bool java_generic_services = 17 [default = true]; - if (_has_bit(5)) { + // optional bool java_generic_services = 17 [default = false]; + if (has_java_generic_services()) { ::google::protobuf::internal::WireFormatLite::WriteBool(17, this->java_generic_services(), output); } - // optional bool py_generic_services = 18 [default = true]; - if (_has_bit(6)) { + // optional bool py_generic_services = 18 [default = false]; + if (has_py_generic_services()) { ::google::protobuf::internal::WireFormatLite::WriteBool(18, this->py_generic_services(), output); } + // optional bool java_generate_equals_and_hash = 20 [default = false]; + if (has_java_generate_equals_and_hash()) { + ::google::protobuf::internal::WireFormatLite::WriteBool(20, this->java_generate_equals_and_hash(), output); + } + // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; for (int i = 0; i < this->uninterpreted_option_size(); i++) { ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( @@ -4473,7 +4591,7 @@ void FileOptions::SerializeWithCachedSizes( ::google::protobuf::uint8* FileOptions::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional string java_package = 1; - if (_has_bit(0)) { + if (has_java_package()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->java_package().data(), this->java_package().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -4483,7 +4601,7 @@ void FileOptions::SerializeWithCachedSizes( } // optional string java_outer_classname = 8; - if (_has_bit(1)) { + if (has_java_outer_classname()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->java_outer_classname().data(), this->java_outer_classname().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -4493,31 +4611,36 @@ void FileOptions::SerializeWithCachedSizes( } // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; - if (_has_bit(3)) { + if (has_optimize_for()) { target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( 9, this->optimize_for(), target); } // optional bool java_multiple_files = 10 [default = false]; - if (_has_bit(2)) { + if (has_java_multiple_files()) { target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(10, this->java_multiple_files(), target); } - // optional bool cc_generic_services = 16 [default = true]; - if (_has_bit(4)) { + // optional bool cc_generic_services = 16 [default = false]; + if (has_cc_generic_services()) { target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(16, this->cc_generic_services(), target); } - // optional bool java_generic_services = 17 [default = true]; - if (_has_bit(5)) { + // optional bool java_generic_services = 17 [default = false]; + if (has_java_generic_services()) { target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(17, this->java_generic_services(), target); } - // optional bool py_generic_services = 18 [default = true]; - if (_has_bit(6)) { + // optional bool py_generic_services = 18 [default = false]; + if (has_py_generic_services()) { target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(18, this->py_generic_services(), target); } + // optional bool java_generate_equals_and_hash = 20 [default = false]; + if (has_java_generate_equals_and_hash()) { + target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(20, this->java_generate_equals_and_hash(), target); + } + // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; for (int i = 0; i < this->uninterpreted_option_size(); i++) { target = ::google::protobuf::internal::WireFormatLite:: @@ -4559,23 +4682,28 @@ int FileOptions::ByteSize() const { total_size += 1 + 1; } + // optional bool java_generate_equals_and_hash = 20 [default = false]; + if (has_java_generate_equals_and_hash()) { + total_size += 2 + 1; + } + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; if (has_optimize_for()) { total_size += 1 + ::google::protobuf::internal::WireFormatLite::EnumSize(this->optimize_for()); } - // optional bool cc_generic_services = 16 [default = true]; + // optional bool cc_generic_services = 16 [default = false]; if (has_cc_generic_services()) { total_size += 2 + 1; } - // optional bool java_generic_services = 17 [default = true]; + // optional bool java_generic_services = 17 [default = false]; if (has_java_generic_services()) { total_size += 2 + 1; } - // optional bool py_generic_services = 18 [default = true]; + // optional bool py_generic_services = 18 [default = false]; if (has_py_generic_services()) { total_size += 2 + 1; } @@ -4618,25 +4746,28 @@ void FileOptions::MergeFrom(const FileOptions& from) { GOOGLE_CHECK_NE(&from, this); uninterpreted_option_.MergeFrom(from.uninterpreted_option_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_java_package()) { set_java_package(from.java_package()); } - if (from._has_bit(1)) { + if (from.has_java_outer_classname()) { set_java_outer_classname(from.java_outer_classname()); } - if (from._has_bit(2)) { + if (from.has_java_multiple_files()) { set_java_multiple_files(from.java_multiple_files()); } - if (from._has_bit(3)) { + if (from.has_java_generate_equals_and_hash()) { + set_java_generate_equals_and_hash(from.java_generate_equals_and_hash()); + } + if (from.has_optimize_for()) { set_optimize_for(from.optimize_for()); } - if (from._has_bit(4)) { + if (from.has_cc_generic_services()) { set_cc_generic_services(from.cc_generic_services()); } - if (from._has_bit(5)) { + if (from.has_java_generic_services()) { set_java_generic_services(from.java_generic_services()); } - if (from._has_bit(6)) { + if (from.has_py_generic_services()) { set_py_generic_services(from.py_generic_services()); } } @@ -4670,6 +4801,7 @@ void FileOptions::Swap(FileOptions* other) { std::swap(java_package_, other->java_package_); std::swap(java_outer_classname_, other->java_outer_classname_); std::swap(java_multiple_files_, other->java_multiple_files_); + std::swap(java_generate_equals_and_hash_, other->java_generate_equals_and_hash_); std::swap(optimize_for_, other->optimize_for_); std::swap(cc_generic_services_, other->cc_generic_services_); std::swap(java_generic_services_, other->java_generic_services_); @@ -4773,7 +4905,7 @@ bool MessageOptions::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( input, &message_set_wire_format_))); - _set_bit(0); + set_has_message_set_wire_format(); } else { goto handle_uninterpreted; } @@ -4789,7 +4921,7 @@ bool MessageOptions::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( input, &no_standard_descriptor_accessor_))); - _set_bit(1); + set_has_no_standard_descriptor_accessor(); } else { goto handle_uninterpreted; } @@ -4836,12 +4968,12 @@ bool MessageOptions::MergePartialFromCodedStream( void MessageOptions::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional bool message_set_wire_format = 1 [default = false]; - if (_has_bit(0)) { + if (has_message_set_wire_format()) { ::google::protobuf::internal::WireFormatLite::WriteBool(1, this->message_set_wire_format(), output); } // optional bool no_standard_descriptor_accessor = 2 [default = false]; - if (_has_bit(1)) { + if (has_no_standard_descriptor_accessor()) { ::google::protobuf::internal::WireFormatLite::WriteBool(2, this->no_standard_descriptor_accessor(), output); } @@ -4864,12 +4996,12 @@ void MessageOptions::SerializeWithCachedSizes( ::google::protobuf::uint8* MessageOptions::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional bool message_set_wire_format = 1 [default = false]; - if (_has_bit(0)) { + if (has_message_set_wire_format()) { target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(1, this->message_set_wire_format(), target); } // optional bool no_standard_descriptor_accessor = 2 [default = false]; - if (_has_bit(1)) { + if (has_no_standard_descriptor_accessor()) { target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(2, this->no_standard_descriptor_accessor(), target); } @@ -4943,10 +5075,10 @@ void MessageOptions::MergeFrom(const MessageOptions& from) { GOOGLE_CHECK_NE(&from, this); uninterpreted_option_.MergeFrom(from.uninterpreted_option_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_message_set_wire_format()) { set_message_set_wire_format(from.message_set_wire_format()); } - if (from._has_bit(1)) { + if (from.has_no_standard_descriptor_accessor()) { set_no_standard_descriptor_accessor(from.no_standard_descriptor_accessor()); } } @@ -5021,7 +5153,6 @@ const FieldOptions_CType FieldOptions::CType_MIN; const FieldOptions_CType FieldOptions::CType_MAX; const int FieldOptions::CType_ARRAYSIZE; #endif // _MSC_VER -const ::std::string FieldOptions::_default_experimental_map_key_; #ifndef _MSC_VER const int FieldOptions::kCtypeFieldNumber; const int FieldOptions::kPackedFieldNumber; @@ -5049,7 +5180,7 @@ void FieldOptions::SharedCtor() { ctype_ = 0; packed_ = false; deprecated_ = false; - experimental_map_key_ = const_cast< ::std::string*>(&_default_experimental_map_key_); + experimental_map_key_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -5058,7 +5189,7 @@ FieldOptions::~FieldOptions() { } void FieldOptions::SharedDtor() { - if (experimental_map_key_ != &_default_experimental_map_key_) { + if (experimental_map_key_ != &::google::protobuf::internal::kEmptyString) { delete experimental_map_key_; } if (this != default_instance_) { @@ -5091,8 +5222,8 @@ void FieldOptions::Clear() { ctype_ = 0; packed_ = false; deprecated_ = false; - if (_has_bit(3)) { - if (experimental_map_key_ != &_default_experimental_map_key_) { + if (has_experimental_map_key()) { + if (experimental_map_key_ != &::google::protobuf::internal::kEmptyString) { experimental_map_key_->clear(); } } @@ -5136,7 +5267,7 @@ bool FieldOptions::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( input, &packed_))); - _set_bit(1); + set_has_packed(); } else { goto handle_uninterpreted; } @@ -5152,7 +5283,7 @@ bool FieldOptions::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( input, &deprecated_))); - _set_bit(2); + set_has_deprecated(); } else { goto handle_uninterpreted; } @@ -5216,23 +5347,23 @@ bool FieldOptions::MergePartialFromCodedStream( void FieldOptions::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING]; - if (_has_bit(0)) { + if (has_ctype()) { ::google::protobuf::internal::WireFormatLite::WriteEnum( 1, this->ctype(), output); } // optional bool packed = 2; - if (_has_bit(1)) { + if (has_packed()) { ::google::protobuf::internal::WireFormatLite::WriteBool(2, this->packed(), output); } // optional bool deprecated = 3 [default = false]; - if (_has_bit(2)) { + if (has_deprecated()) { ::google::protobuf::internal::WireFormatLite::WriteBool(3, this->deprecated(), output); } // optional string experimental_map_key = 9; - if (_has_bit(3)) { + if (has_experimental_map_key()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->experimental_map_key().data(), this->experimental_map_key().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -5259,23 +5390,23 @@ void FieldOptions::SerializeWithCachedSizes( ::google::protobuf::uint8* FieldOptions::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING]; - if (_has_bit(0)) { + if (has_ctype()) { target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( 1, this->ctype(), target); } // optional bool packed = 2; - if (_has_bit(1)) { + if (has_packed()) { target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(2, this->packed(), target); } // optional bool deprecated = 3 [default = false]; - if (_has_bit(2)) { + if (has_deprecated()) { target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(3, this->deprecated(), target); } // optional string experimental_map_key = 9; - if (_has_bit(3)) { + if (has_experimental_map_key()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->experimental_map_key().data(), this->experimental_map_key().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -5367,16 +5498,16 @@ void FieldOptions::MergeFrom(const FieldOptions& from) { GOOGLE_CHECK_NE(&from, this); uninterpreted_option_.MergeFrom(from.uninterpreted_option_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_ctype()) { set_ctype(from.ctype()); } - if (from._has_bit(1)) { + if (from.has_packed()) { set_packed(from.packed()); } - if (from._has_bit(2)) { + if (from.has_deprecated()) { set_deprecated(from.deprecated()); } - if (from._has_bit(3)) { + if (from.has_experimental_map_key()) { set_experimental_map_key(from.experimental_map_key()); } } @@ -6326,7 +6457,6 @@ void MethodOptions::Swap(MethodOptions* other) { // =================================================================== -const ::std::string UninterpretedOption_NamePart::_default_name_part_; #ifndef _MSC_VER const int UninterpretedOption_NamePart::kNamePartFieldNumber; const int UninterpretedOption_NamePart::kIsExtensionFieldNumber; @@ -6348,7 +6478,7 @@ UninterpretedOption_NamePart::UninterpretedOption_NamePart(const UninterpretedOp void UninterpretedOption_NamePart::SharedCtor() { _cached_size_ = 0; - name_part_ = const_cast< ::std::string*>(&_default_name_part_); + name_part_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); is_extension_ = false; ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -6358,7 +6488,7 @@ UninterpretedOption_NamePart::~UninterpretedOption_NamePart() { } void UninterpretedOption_NamePart::SharedDtor() { - if (name_part_ != &_default_name_part_) { + if (name_part_ != &::google::protobuf::internal::kEmptyString) { delete name_part_; } if (this != default_instance_) { @@ -6387,8 +6517,8 @@ UninterpretedOption_NamePart* UninterpretedOption_NamePart::New() const { void UninterpretedOption_NamePart::Clear() { if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (_has_bit(0)) { - if (name_part_ != &_default_name_part_) { + if (has_name_part()) { + if (name_part_ != &::google::protobuf::internal::kEmptyString) { name_part_->clear(); } } @@ -6428,7 +6558,7 @@ bool UninterpretedOption_NamePart::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( input, &is_extension_))); - _set_bit(1); + set_has_is_extension(); } else { goto handle_uninterpreted; } @@ -6455,7 +6585,7 @@ bool UninterpretedOption_NamePart::MergePartialFromCodedStream( void UninterpretedOption_NamePart::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { // required string name_part = 1; - if (_has_bit(0)) { + if (has_name_part()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name_part().data(), this->name_part().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -6464,7 +6594,7 @@ void UninterpretedOption_NamePart::SerializeWithCachedSizes( } // required bool is_extension = 2; - if (_has_bit(1)) { + if (has_is_extension()) { ::google::protobuf::internal::WireFormatLite::WriteBool(2, this->is_extension(), output); } @@ -6477,7 +6607,7 @@ void UninterpretedOption_NamePart::SerializeWithCachedSizes( ::google::protobuf::uint8* UninterpretedOption_NamePart::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { // required string name_part = 1; - if (_has_bit(0)) { + if (has_name_part()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->name_part().data(), this->name_part().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -6487,7 +6617,7 @@ void UninterpretedOption_NamePart::SerializeWithCachedSizes( } // required bool is_extension = 2; - if (_has_bit(1)) { + if (has_is_extension()) { target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(2, this->is_extension(), target); } @@ -6541,10 +6671,10 @@ void UninterpretedOption_NamePart::MergeFrom(const ::google::protobuf::Message& void UninterpretedOption_NamePart::MergeFrom(const UninterpretedOption_NamePart& from) { GOOGLE_CHECK_NE(&from, this); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from._has_bit(0)) { + if (from.has_name_part()) { set_name_part(from.name_part()); } - if (from._has_bit(1)) { + if (from.has_is_extension()) { set_is_extension(from.is_extension()); } } @@ -6590,8 +6720,6 @@ void UninterpretedOption_NamePart::Swap(UninterpretedOption_NamePart* other) { // ------------------------------------------------------------------- -const ::std::string UninterpretedOption::_default_identifier_value_; -const ::std::string UninterpretedOption::_default_string_value_; #ifndef _MSC_VER const int UninterpretedOption::kNameFieldNumber; const int UninterpretedOption::kIdentifierValueFieldNumber; @@ -6599,6 +6727,7 @@ const int UninterpretedOption::kPositiveIntValueFieldNumber; const int UninterpretedOption::kNegativeIntValueFieldNumber; const int UninterpretedOption::kDoubleValueFieldNumber; const int UninterpretedOption::kStringValueFieldNumber; +const int UninterpretedOption::kAggregateValueFieldNumber; #endif // !_MSC_VER UninterpretedOption::UninterpretedOption() @@ -6617,11 +6746,12 @@ UninterpretedOption::UninterpretedOption(const UninterpretedOption& from) void UninterpretedOption::SharedCtor() { _cached_size_ = 0; - identifier_value_ = const_cast< ::std::string*>(&_default_identifier_value_); + identifier_value_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); positive_int_value_ = GOOGLE_ULONGLONG(0); negative_int_value_ = GOOGLE_LONGLONG(0); double_value_ = 0; - string_value_ = const_cast< ::std::string*>(&_default_string_value_); + string_value_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + aggregate_value_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -6630,12 +6760,15 @@ UninterpretedOption::~UninterpretedOption() { } void UninterpretedOption::SharedDtor() { - if (identifier_value_ != &_default_identifier_value_) { + if (identifier_value_ != &::google::protobuf::internal::kEmptyString) { delete identifier_value_; } - if (string_value_ != &_default_string_value_) { + if (string_value_ != &::google::protobuf::internal::kEmptyString) { delete string_value_; } + if (aggregate_value_ != &::google::protobuf::internal::kEmptyString) { + delete aggregate_value_; + } if (this != default_instance_) { } } @@ -6662,19 +6795,24 @@ UninterpretedOption* UninterpretedOption::New() const { void UninterpretedOption::Clear() { if (_has_bits_[1 / 32] & (0xffu << (1 % 32))) { - if (_has_bit(1)) { - if (identifier_value_ != &_default_identifier_value_) { + if (has_identifier_value()) { + if (identifier_value_ != &::google::protobuf::internal::kEmptyString) { identifier_value_->clear(); } } positive_int_value_ = GOOGLE_ULONGLONG(0); negative_int_value_ = GOOGLE_LONGLONG(0); double_value_ = 0; - if (_has_bit(5)) { - if (string_value_ != &_default_string_value_) { + if (has_string_value()) { + if (string_value_ != &::google::protobuf::internal::kEmptyString) { string_value_->clear(); } } + if (has_aggregate_value()) { + if (aggregate_value_ != &::google::protobuf::internal::kEmptyString) { + aggregate_value_->clear(); + } + } } name_.Clear(); ::memset(_has_bits_, 0, sizeof(_has_bits_)); @@ -6727,7 +6865,7 @@ bool UninterpretedOption::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( input, &positive_int_value_))); - _set_bit(2); + set_has_positive_int_value(); } else { goto handle_uninterpreted; } @@ -6743,7 +6881,7 @@ bool UninterpretedOption::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>( input, &negative_int_value_))); - _set_bit(3); + set_has_negative_int_value(); } else { goto handle_uninterpreted; } @@ -6759,7 +6897,7 @@ bool UninterpretedOption::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< double, ::google::protobuf::internal::WireFormatLite::TYPE_DOUBLE>( input, &double_value_))); - _set_bit(4); + set_has_double_value(); } else { goto handle_uninterpreted; } @@ -6777,6 +6915,23 @@ bool UninterpretedOption::MergePartialFromCodedStream( } else { goto handle_uninterpreted; } + if (input->ExpectTag(66)) goto parse_aggregate_value; + break; + } + + // optional string aggregate_value = 8; + case 8: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_aggregate_value: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_aggregate_value())); + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->aggregate_value().data(), this->aggregate_value().length(), + ::google::protobuf::internal::WireFormat::PARSE); + } else { + goto handle_uninterpreted; + } if (input->ExpectAtEnd()) return true; break; } @@ -6806,7 +6961,7 @@ void UninterpretedOption::SerializeWithCachedSizes( } // optional string identifier_value = 3; - if (_has_bit(1)) { + if (has_identifier_value()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->identifier_value().data(), this->identifier_value().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -6815,26 +6970,35 @@ void UninterpretedOption::SerializeWithCachedSizes( } // optional uint64 positive_int_value = 4; - if (_has_bit(2)) { + if (has_positive_int_value()) { ::google::protobuf::internal::WireFormatLite::WriteUInt64(4, this->positive_int_value(), output); } // optional int64 negative_int_value = 5; - if (_has_bit(3)) { + if (has_negative_int_value()) { ::google::protobuf::internal::WireFormatLite::WriteInt64(5, this->negative_int_value(), output); } // optional double double_value = 6; - if (_has_bit(4)) { + if (has_double_value()) { ::google::protobuf::internal::WireFormatLite::WriteDouble(6, this->double_value(), output); } // optional bytes string_value = 7; - if (_has_bit(5)) { + if (has_string_value()) { ::google::protobuf::internal::WireFormatLite::WriteBytes( 7, this->string_value(), output); } + // optional string aggregate_value = 8; + if (has_aggregate_value()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->aggregate_value().data(), this->aggregate_value().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormatLite::WriteString( + 8, this->aggregate_value(), output); + } + if (!unknown_fields().empty()) { ::google::protobuf::internal::WireFormat::SerializeUnknownFields( unknown_fields(), output); @@ -6851,7 +7015,7 @@ void UninterpretedOption::SerializeWithCachedSizes( } // optional string identifier_value = 3; - if (_has_bit(1)) { + if (has_identifier_value()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( this->identifier_value().data(), this->identifier_value().length(), ::google::protobuf::internal::WireFormat::SERIALIZE); @@ -6861,27 +7025,37 @@ void UninterpretedOption::SerializeWithCachedSizes( } // optional uint64 positive_int_value = 4; - if (_has_bit(2)) { + if (has_positive_int_value()) { target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(4, this->positive_int_value(), target); } // optional int64 negative_int_value = 5; - if (_has_bit(3)) { + if (has_negative_int_value()) { target = ::google::protobuf::internal::WireFormatLite::WriteInt64ToArray(5, this->negative_int_value(), target); } // optional double double_value = 6; - if (_has_bit(4)) { + if (has_double_value()) { target = ::google::protobuf::internal::WireFormatLite::WriteDoubleToArray(6, this->double_value(), target); } // optional bytes string_value = 7; - if (_has_bit(5)) { + if (has_string_value()) { target = ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( 7, this->string_value(), target); } + // optional string aggregate_value = 8; + if (has_aggregate_value()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->aggregate_value().data(), this->aggregate_value().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 8, this->aggregate_value(), target); + } + if (!unknown_fields().empty()) { target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( unknown_fields(), target); @@ -6926,6 +7100,13 @@ int UninterpretedOption::ByteSize() const { this->string_value()); } + // optional string aggregate_value = 8; + if (has_aggregate_value()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->aggregate_value()); + } + } // repeated .google.protobuf.UninterpretedOption.NamePart name = 2; total_size += 1 * this->name_size(); @@ -6962,21 +7143,24 @@ void UninterpretedOption::MergeFrom(const UninterpretedOption& from) { GOOGLE_CHECK_NE(&from, this); name_.MergeFrom(from.name_); if (from._has_bits_[1 / 32] & (0xffu << (1 % 32))) { - if (from._has_bit(1)) { + if (from.has_identifier_value()) { set_identifier_value(from.identifier_value()); } - if (from._has_bit(2)) { + if (from.has_positive_int_value()) { set_positive_int_value(from.positive_int_value()); } - if (from._has_bit(3)) { + if (from.has_negative_int_value()) { set_negative_int_value(from.negative_int_value()); } - if (from._has_bit(4)) { + if (from.has_double_value()) { set_double_value(from.double_value()); } - if (from._has_bit(5)) { + if (from.has_string_value()) { set_string_value(from.string_value()); } + if (from.has_aggregate_value()) { + set_aggregate_value(from.aggregate_value()); + } } mutable_unknown_fields()->MergeFrom(from.unknown_fields()); } @@ -7009,6 +7193,7 @@ void UninterpretedOption::Swap(UninterpretedOption* other) { std::swap(negative_int_value_, other->negative_int_value_); std::swap(double_value_, other->double_value_); std::swap(string_value_, other->string_value_); + std::swap(aggregate_value_, other->aggregate_value_); std::swap(_has_bits_[0], other->_has_bits_[0]); _unknown_fields_.Swap(&other->_unknown_fields_); std::swap(_cached_size_, other->_cached_size_); @@ -7024,6 +7209,497 @@ void UninterpretedOption::Swap(UninterpretedOption* other) { } +// =================================================================== + +#ifndef _MSC_VER +const int SourceCodeInfo_Location::kPathFieldNumber; +const int SourceCodeInfo_Location::kSpanFieldNumber; +#endif // !_MSC_VER + +SourceCodeInfo_Location::SourceCodeInfo_Location() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void SourceCodeInfo_Location::InitAsDefaultInstance() { +} + +SourceCodeInfo_Location::SourceCodeInfo_Location(const SourceCodeInfo_Location& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void SourceCodeInfo_Location::SharedCtor() { + _cached_size_ = 0; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +SourceCodeInfo_Location::~SourceCodeInfo_Location() { + SharedDtor(); +} + +void SourceCodeInfo_Location::SharedDtor() { + if (this != default_instance_) { + } +} + +void SourceCodeInfo_Location::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* SourceCodeInfo_Location::descriptor() { + protobuf_AssignDescriptorsOnce(); + return SourceCodeInfo_Location_descriptor_; +} + +const SourceCodeInfo_Location& SourceCodeInfo_Location::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); return *default_instance_; +} + +SourceCodeInfo_Location* SourceCodeInfo_Location::default_instance_ = NULL; + +SourceCodeInfo_Location* SourceCodeInfo_Location::New() const { + return new SourceCodeInfo_Location; +} + +void SourceCodeInfo_Location::Clear() { + path_.Clear(); + span_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool SourceCodeInfo_Location::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // repeated int32 path = 1 [packed = true]; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, this->mutable_path()))); + } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) + == ::google::protobuf::internal::WireFormatLite:: + WIRETYPE_VARINT) { + DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + 1, 10, input, this->mutable_path()))); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(18)) goto parse_span; + break; + } + + // repeated int32 span = 2 [packed = true]; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_span: + DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, this->mutable_span()))); + } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) + == ::google::protobuf::internal::WireFormatLite:: + WIRETYPE_VARINT) { + DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + 1, 18, input, this->mutable_span()))); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void SourceCodeInfo_Location::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // repeated int32 path = 1 [packed = true]; + if (this->path_size() > 0) { + ::google::protobuf::internal::WireFormatLite::WriteTag(1, ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); + output->WriteVarint32(_path_cached_byte_size_); + } + for (int i = 0; i < this->path_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteInt32NoTag( + this->path(i), output); + } + + // repeated int32 span = 2 [packed = true]; + if (this->span_size() > 0) { + ::google::protobuf::internal::WireFormatLite::WriteTag(2, ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); + output->WriteVarint32(_span_cached_byte_size_); + } + for (int i = 0; i < this->span_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteInt32NoTag( + this->span(i), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* SourceCodeInfo_Location::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // repeated int32 path = 1 [packed = true]; + if (this->path_size() > 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteTagToArray( + 1, + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, + target); + target = ::google::protobuf::io::CodedOutputStream::WriteVarint32ToArray( + _path_cached_byte_size_, target); + } + for (int i = 0; i < this->path_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteInt32NoTagToArray(this->path(i), target); + } + + // repeated int32 span = 2 [packed = true]; + if (this->span_size() > 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteTagToArray( + 2, + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, + target); + target = ::google::protobuf::io::CodedOutputStream::WriteVarint32ToArray( + _span_cached_byte_size_, target); + } + for (int i = 0; i < this->span_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteInt32NoTagToArray(this->span(i), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int SourceCodeInfo_Location::ByteSize() const { + int total_size = 0; + + // repeated int32 path = 1 [packed = true]; + { + int data_size = 0; + for (int i = 0; i < this->path_size(); i++) { + data_size += ::google::protobuf::internal::WireFormatLite:: + Int32Size(this->path(i)); + } + if (data_size > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size(data_size); + } + _path_cached_byte_size_ = data_size; + total_size += data_size; + } + + // repeated int32 span = 2 [packed = true]; + { + int data_size = 0; + for (int i = 0; i < this->span_size(); i++) { + data_size += ::google::protobuf::internal::WireFormatLite:: + Int32Size(this->span(i)); + } + if (data_size > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size(data_size); + } + _span_cached_byte_size_ = data_size; + total_size += data_size; + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void SourceCodeInfo_Location::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const SourceCodeInfo_Location* source = + ::google::protobuf::internal::dynamic_cast_if_available<const SourceCodeInfo_Location*>( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void SourceCodeInfo_Location::MergeFrom(const SourceCodeInfo_Location& from) { + GOOGLE_CHECK_NE(&from, this); + path_.MergeFrom(from.path_); + span_.MergeFrom(from.span_); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void SourceCodeInfo_Location::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void SourceCodeInfo_Location::CopyFrom(const SourceCodeInfo_Location& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool SourceCodeInfo_Location::IsInitialized() const { + + return true; +} + +void SourceCodeInfo_Location::Swap(SourceCodeInfo_Location* other) { + if (other != this) { + path_.Swap(&other->path_); + span_.Swap(&other->span_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata SourceCodeInfo_Location::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = SourceCodeInfo_Location_descriptor_; + metadata.reflection = SourceCodeInfo_Location_reflection_; + return metadata; +} + + +// ------------------------------------------------------------------- + +#ifndef _MSC_VER +const int SourceCodeInfo::kLocationFieldNumber; +#endif // !_MSC_VER + +SourceCodeInfo::SourceCodeInfo() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void SourceCodeInfo::InitAsDefaultInstance() { +} + +SourceCodeInfo::SourceCodeInfo(const SourceCodeInfo& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void SourceCodeInfo::SharedCtor() { + _cached_size_ = 0; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +SourceCodeInfo::~SourceCodeInfo() { + SharedDtor(); +} + +void SourceCodeInfo::SharedDtor() { + if (this != default_instance_) { + } +} + +void SourceCodeInfo::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* SourceCodeInfo::descriptor() { + protobuf_AssignDescriptorsOnce(); + return SourceCodeInfo_descriptor_; +} + +const SourceCodeInfo& SourceCodeInfo::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); return *default_instance_; +} + +SourceCodeInfo* SourceCodeInfo::default_instance_ = NULL; + +SourceCodeInfo* SourceCodeInfo::New() const { + return new SourceCodeInfo; +} + +void SourceCodeInfo::Clear() { + location_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool SourceCodeInfo::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // repeated .google.protobuf.SourceCodeInfo.Location location = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_location: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, add_location())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(10)) goto parse_location; + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void SourceCodeInfo::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // repeated .google.protobuf.SourceCodeInfo.Location location = 1; + for (int i = 0; i < this->location_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 1, this->location(i), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* SourceCodeInfo::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // repeated .google.protobuf.SourceCodeInfo.Location location = 1; + for (int i = 0; i < this->location_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 1, this->location(i), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int SourceCodeInfo::ByteSize() const { + int total_size = 0; + + // repeated .google.protobuf.SourceCodeInfo.Location location = 1; + total_size += 1 * this->location_size(); + for (int i = 0; i < this->location_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->location(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void SourceCodeInfo::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const SourceCodeInfo* source = + ::google::protobuf::internal::dynamic_cast_if_available<const SourceCodeInfo*>( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void SourceCodeInfo::MergeFrom(const SourceCodeInfo& from) { + GOOGLE_CHECK_NE(&from, this); + location_.MergeFrom(from.location_); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void SourceCodeInfo::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void SourceCodeInfo::CopyFrom(const SourceCodeInfo& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool SourceCodeInfo::IsInitialized() const { + + return true; +} + +void SourceCodeInfo::Swap(SourceCodeInfo* other) { + if (other != this) { + location_.Swap(&other->location_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata SourceCodeInfo::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = SourceCodeInfo_descriptor_; + metadata.reflection = SourceCodeInfo_reflection_; + return metadata; +} + + // @@protoc_insertion_point(namespace_scope) } // namespace protobuf diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h index 205c5476..e85a9336 100644 --- a/src/google/protobuf/descriptor.pb.h +++ b/src/google/protobuf/descriptor.pb.h @@ -51,6 +51,8 @@ class ServiceOptions; class MethodOptions; class UninterpretedOption; class UninterpretedOption_NamePart; +class SourceCodeInfo; +class SourceCodeInfo_Location; enum FieldDescriptorProto_Type { FieldDescriptorProto_Type_TYPE_DOUBLE = 1, @@ -217,26 +219,17 @@ class LIBPROTOBUF_EXPORT FileDescriptorSet : public ::google::protobuf::Message // @@protoc_insertion_point(class_scope:google.protobuf.FileDescriptorSet) private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto > file_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static FileDescriptorSet* default_instance_; @@ -306,6 +299,7 @@ class LIBPROTOBUF_EXPORT FileDescriptorProto : public ::google::protobuf::Messag inline void set_name(const char* value); inline void set_name(const char* value, size_t size); inline ::std::string* mutable_name(); + inline ::std::string* release_name(); // optional string package = 2; inline bool has_package() const; @@ -316,6 +310,7 @@ class LIBPROTOBUF_EXPORT FileDescriptorProto : public ::google::protobuf::Messag inline void set_package(const char* value); inline void set_package(const char* value, size_t size); inline ::std::string* mutable_package(); + inline ::std::string* release_package(); // repeated string dependency = 3; inline int dependency_size() const; @@ -387,39 +382,46 @@ class LIBPROTOBUF_EXPORT FileDescriptorProto : public ::google::protobuf::Messag static const int kOptionsFieldNumber = 8; inline const ::google::protobuf::FileOptions& options() const; inline ::google::protobuf::FileOptions* mutable_options(); + inline ::google::protobuf::FileOptions* release_options(); + + // optional .google.protobuf.SourceCodeInfo source_code_info = 9; + inline bool has_source_code_info() const; + inline void clear_source_code_info(); + static const int kSourceCodeInfoFieldNumber = 9; + inline const ::google::protobuf::SourceCodeInfo& source_code_info() const; + inline ::google::protobuf::SourceCodeInfo* mutable_source_code_info(); + inline ::google::protobuf::SourceCodeInfo* release_source_code_info(); // @@protoc_insertion_point(class_scope:google.protobuf.FileDescriptorProto) private: + inline void set_has_name(); + inline void clear_has_name(); + inline void set_has_package(); + inline void clear_has_package(); + inline void set_has_options(); + inline void clear_has_options(); + inline void set_has_source_code_info(); + inline void clear_has_source_code_info(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::std::string* name_; - static const ::std::string _default_name_; ::std::string* package_; - static const ::std::string _default_package_; ::google::protobuf::RepeatedPtrField< ::std::string> dependency_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto > message_type_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto > enum_type_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto > service_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto > extension_; ::google::protobuf::FileOptions* options_; + ::google::protobuf::SourceCodeInfo* source_code_info_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(9 + 31) / 32]; + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); - ::google::protobuf::uint32 _has_bits_[(8 + 31) / 32]; - - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } - void InitAsDefaultInstance(); static FileDescriptorProto* default_instance_; }; @@ -495,27 +497,22 @@ class LIBPROTOBUF_EXPORT DescriptorProto_ExtensionRange : public ::google::proto // @@protoc_insertion_point(class_scope:google.protobuf.DescriptorProto.ExtensionRange) private: + inline void set_has_start(); + inline void clear_has_start(); + inline void set_has_end(); + inline void clear_has_end(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::google::protobuf::int32 start_; ::google::protobuf::int32 end_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static DescriptorProto_ExtensionRange* default_instance_; @@ -587,6 +584,7 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { inline void set_name(const char* value); inline void set_name(const char* value, size_t size); inline ::std::string* mutable_name(); + inline ::std::string* release_name(); // repeated .google.protobuf.FieldDescriptorProto field = 2; inline int field_size() const; @@ -654,36 +652,31 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { static const int kOptionsFieldNumber = 7; inline const ::google::protobuf::MessageOptions& options() const; inline ::google::protobuf::MessageOptions* mutable_options(); + inline ::google::protobuf::MessageOptions* release_options(); // @@protoc_insertion_point(class_scope:google.protobuf.DescriptorProto) private: + inline void set_has_name(); + inline void clear_has_name(); + inline void set_has_options(); + inline void clear_has_options(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::std::string* name_; - static const ::std::string _default_name_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto > field_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto > extension_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto > nested_type_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto > enum_type_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange > extension_range_; ::google::protobuf::MessageOptions* options_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(7 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static DescriptorProto* default_instance_; @@ -818,6 +811,7 @@ class LIBPROTOBUF_EXPORT FieldDescriptorProto : public ::google::protobuf::Messa inline void set_name(const char* value); inline void set_name(const char* value, size_t size); inline ::std::string* mutable_name(); + inline ::std::string* release_name(); // optional int32 number = 3; inline bool has_number() const; @@ -849,6 +843,7 @@ class LIBPROTOBUF_EXPORT FieldDescriptorProto : public ::google::protobuf::Messa inline void set_type_name(const char* value); inline void set_type_name(const char* value, size_t size); inline ::std::string* mutable_type_name(); + inline ::std::string* release_type_name(); // optional string extendee = 2; inline bool has_extendee() const; @@ -859,6 +854,7 @@ class LIBPROTOBUF_EXPORT FieldDescriptorProto : public ::google::protobuf::Messa inline void set_extendee(const char* value); inline void set_extendee(const char* value, size_t size); inline ::std::string* mutable_extendee(); + inline ::std::string* release_extendee(); // optional string default_value = 7; inline bool has_default_value() const; @@ -869,6 +865,7 @@ class LIBPROTOBUF_EXPORT FieldDescriptorProto : public ::google::protobuf::Messa inline void set_default_value(const char* value); inline void set_default_value(const char* value, size_t size); inline ::std::string* mutable_default_value(); + inline ::std::string* release_default_value(); // optional .google.protobuf.FieldOptions options = 8; inline bool has_options() const; @@ -876,40 +873,44 @@ class LIBPROTOBUF_EXPORT FieldDescriptorProto : public ::google::protobuf::Messa static const int kOptionsFieldNumber = 8; inline const ::google::protobuf::FieldOptions& options() const; inline ::google::protobuf::FieldOptions* mutable_options(); + inline ::google::protobuf::FieldOptions* release_options(); // @@protoc_insertion_point(class_scope:google.protobuf.FieldDescriptorProto) private: + inline void set_has_name(); + inline void clear_has_name(); + inline void set_has_number(); + inline void clear_has_number(); + inline void set_has_label(); + inline void clear_has_label(); + inline void set_has_type(); + inline void clear_has_type(); + inline void set_has_type_name(); + inline void clear_has_type_name(); + inline void set_has_extendee(); + inline void clear_has_extendee(); + inline void set_has_default_value(); + inline void clear_has_default_value(); + inline void set_has_options(); + inline void clear_has_options(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::std::string* name_; - static const ::std::string _default_name_; ::google::protobuf::int32 number_; int label_; - int type_; ::std::string* type_name_; - static const ::std::string _default_type_name_; ::std::string* extendee_; - static const ::std::string _default_extendee_; ::std::string* default_value_; - static const ::std::string _default_default_value_; ::google::protobuf::FieldOptions* options_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + int type_; + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(8 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static FieldDescriptorProto* default_instance_; @@ -979,6 +980,7 @@ class LIBPROTOBUF_EXPORT EnumDescriptorProto : public ::google::protobuf::Messag inline void set_name(const char* value); inline void set_name(const char* value, size_t size); inline ::std::string* mutable_name(); + inline ::std::string* release_name(); // repeated .google.protobuf.EnumValueDescriptorProto value = 2; inline int value_size() const; @@ -998,32 +1000,27 @@ class LIBPROTOBUF_EXPORT EnumDescriptorProto : public ::google::protobuf::Messag static const int kOptionsFieldNumber = 3; inline const ::google::protobuf::EnumOptions& options() const; inline ::google::protobuf::EnumOptions* mutable_options(); + inline ::google::protobuf::EnumOptions* release_options(); // @@protoc_insertion_point(class_scope:google.protobuf.EnumDescriptorProto) private: + inline void set_has_name(); + inline void clear_has_name(); + inline void set_has_options(); + inline void clear_has_options(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::std::string* name_; - static const ::std::string _default_name_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto > value_; ::google::protobuf::EnumOptions* options_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static EnumDescriptorProto* default_instance_; @@ -1093,6 +1090,7 @@ class LIBPROTOBUF_EXPORT EnumValueDescriptorProto : public ::google::protobuf::M inline void set_name(const char* value); inline void set_name(const char* value, size_t size); inline ::std::string* mutable_name(); + inline ::std::string* release_name(); // optional int32 number = 2; inline bool has_number() const; @@ -1107,32 +1105,29 @@ class LIBPROTOBUF_EXPORT EnumValueDescriptorProto : public ::google::protobuf::M static const int kOptionsFieldNumber = 3; inline const ::google::protobuf::EnumValueOptions& options() const; inline ::google::protobuf::EnumValueOptions* mutable_options(); + inline ::google::protobuf::EnumValueOptions* release_options(); // @@protoc_insertion_point(class_scope:google.protobuf.EnumValueDescriptorProto) private: + inline void set_has_name(); + inline void clear_has_name(); + inline void set_has_number(); + inline void clear_has_number(); + inline void set_has_options(); + inline void clear_has_options(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::std::string* name_; - static const ::std::string _default_name_; - ::google::protobuf::int32 number_; ::google::protobuf::EnumValueOptions* options_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + ::google::protobuf::int32 number_; + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static EnumValueDescriptorProto* default_instance_; @@ -1202,6 +1197,7 @@ class LIBPROTOBUF_EXPORT ServiceDescriptorProto : public ::google::protobuf::Mes inline void set_name(const char* value); inline void set_name(const char* value, size_t size); inline ::std::string* mutable_name(); + inline ::std::string* release_name(); // repeated .google.protobuf.MethodDescriptorProto method = 2; inline int method_size() const; @@ -1221,32 +1217,27 @@ class LIBPROTOBUF_EXPORT ServiceDescriptorProto : public ::google::protobuf::Mes static const int kOptionsFieldNumber = 3; inline const ::google::protobuf::ServiceOptions& options() const; inline ::google::protobuf::ServiceOptions* mutable_options(); + inline ::google::protobuf::ServiceOptions* release_options(); // @@protoc_insertion_point(class_scope:google.protobuf.ServiceDescriptorProto) private: + inline void set_has_name(); + inline void clear_has_name(); + inline void set_has_options(); + inline void clear_has_options(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::std::string* name_; - static const ::std::string _default_name_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto > method_; ::google::protobuf::ServiceOptions* options_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static ServiceDescriptorProto* default_instance_; @@ -1316,6 +1307,7 @@ class LIBPROTOBUF_EXPORT MethodDescriptorProto : public ::google::protobuf::Mess inline void set_name(const char* value); inline void set_name(const char* value, size_t size); inline ::std::string* mutable_name(); + inline ::std::string* release_name(); // optional string input_type = 2; inline bool has_input_type() const; @@ -1326,6 +1318,7 @@ class LIBPROTOBUF_EXPORT MethodDescriptorProto : public ::google::protobuf::Mess inline void set_input_type(const char* value); inline void set_input_type(const char* value, size_t size); inline ::std::string* mutable_input_type(); + inline ::std::string* release_input_type(); // optional string output_type = 3; inline bool has_output_type() const; @@ -1336,6 +1329,7 @@ class LIBPROTOBUF_EXPORT MethodDescriptorProto : public ::google::protobuf::Mess inline void set_output_type(const char* value); inline void set_output_type(const char* value, size_t size); inline ::std::string* mutable_output_type(); + inline ::std::string* release_output_type(); // optional .google.protobuf.MethodOptions options = 4; inline bool has_options() const; @@ -1343,35 +1337,32 @@ class LIBPROTOBUF_EXPORT MethodDescriptorProto : public ::google::protobuf::Mess static const int kOptionsFieldNumber = 4; inline const ::google::protobuf::MethodOptions& options() const; inline ::google::protobuf::MethodOptions* mutable_options(); + inline ::google::protobuf::MethodOptions* release_options(); // @@protoc_insertion_point(class_scope:google.protobuf.MethodDescriptorProto) private: + inline void set_has_name(); + inline void clear_has_name(); + inline void set_has_input_type(); + inline void clear_has_input_type(); + inline void set_has_output_type(); + inline void clear_has_output_type(); + inline void set_has_options(); + inline void clear_has_options(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::std::string* name_; - static const ::std::string _default_name_; ::std::string* input_type_; - static const ::std::string _default_input_type_; ::std::string* output_type_; - static const ::std::string _default_output_type_; ::google::protobuf::MethodOptions* options_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static MethodDescriptorProto* default_instance_; @@ -1466,6 +1457,7 @@ class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message { inline void set_java_package(const char* value); inline void set_java_package(const char* value, size_t size); inline ::std::string* mutable_java_package(); + inline ::std::string* release_java_package(); // optional string java_outer_classname = 8; inline bool has_java_outer_classname() const; @@ -1476,6 +1468,7 @@ class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message { inline void set_java_outer_classname(const char* value); inline void set_java_outer_classname(const char* value, size_t size); inline ::std::string* mutable_java_outer_classname(); + inline ::std::string* release_java_outer_classname(); // optional bool java_multiple_files = 10 [default = false]; inline bool has_java_multiple_files() const; @@ -1484,6 +1477,13 @@ class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message { inline bool java_multiple_files() const; inline void set_java_multiple_files(bool value); + // optional bool java_generate_equals_and_hash = 20 [default = false]; + inline bool has_java_generate_equals_and_hash() const; + inline void clear_java_generate_equals_and_hash(); + static const int kJavaGenerateEqualsAndHashFieldNumber = 20; + inline bool java_generate_equals_and_hash() const; + inline void set_java_generate_equals_and_hash(bool value); + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; inline bool has_optimize_for() const; inline void clear_optimize_for(); @@ -1491,21 +1491,21 @@ class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message { inline ::google::protobuf::FileOptions_OptimizeMode optimize_for() const; inline void set_optimize_for(::google::protobuf::FileOptions_OptimizeMode value); - // optional bool cc_generic_services = 16 [default = true]; + // optional bool cc_generic_services = 16 [default = false]; inline bool has_cc_generic_services() const; inline void clear_cc_generic_services(); static const int kCcGenericServicesFieldNumber = 16; inline bool cc_generic_services() const; inline void set_cc_generic_services(bool value); - // optional bool java_generic_services = 17 [default = true]; + // optional bool java_generic_services = 17 [default = false]; inline bool has_java_generic_services() const; inline void clear_java_generic_services(); static const int kJavaGenericServicesFieldNumber = 17; inline bool java_generic_services() const; inline void set_java_generic_services(bool value); - // optional bool py_generic_services = 18 [default = true]; + // optional bool py_generic_services = 18 [default = false]; inline bool has_py_generic_services() const; inline void clear_py_generic_services(); static const int kPyGenericServicesFieldNumber = 18; @@ -1527,37 +1527,44 @@ class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message { GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(FileOptions) // @@protoc_insertion_point(class_scope:google.protobuf.FileOptions) private: + inline void set_has_java_package(); + inline void clear_has_java_package(); + inline void set_has_java_outer_classname(); + inline void clear_has_java_outer_classname(); + inline void set_has_java_multiple_files(); + inline void clear_has_java_multiple_files(); + inline void set_has_java_generate_equals_and_hash(); + inline void clear_has_java_generate_equals_and_hash(); + inline void set_has_optimize_for(); + inline void clear_has_optimize_for(); + inline void set_has_cc_generic_services(); + inline void clear_has_cc_generic_services(); + inline void set_has_java_generic_services(); + inline void clear_has_java_generic_services(); + inline void set_has_py_generic_services(); + inline void clear_has_py_generic_services(); + ::google::protobuf::internal::ExtensionSet _extensions_; + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::std::string* java_package_; - static const ::std::string _default_java_package_; ::std::string* java_outer_classname_; - static const ::std::string _default_java_outer_classname_; - bool java_multiple_files_; int optimize_for_; + bool java_multiple_files_; + bool java_generate_equals_and_hash_; bool cc_generic_services_; bool java_generic_services_; - bool py_generic_services_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; + bool py_generic_services_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(9 + 31) / 32]; + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); - ::google::protobuf::uint32 _has_bits_[(8 + 31) / 32]; - - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } - void InitAsDefaultInstance(); static FileOptions* default_instance_; }; @@ -1646,29 +1653,25 @@ class LIBPROTOBUF_EXPORT MessageOptions : public ::google::protobuf::Message { GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(MessageOptions) // @@protoc_insertion_point(class_scope:google.protobuf.MessageOptions) private: + inline void set_has_message_set_wire_format(); + inline void clear_has_message_set_wire_format(); + inline void set_has_no_standard_descriptor_accessor(); + inline void clear_has_no_standard_descriptor_accessor(); + ::google::protobuf::internal::ExtensionSet _extensions_; + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; bool message_set_wire_format_; bool no_standard_descriptor_accessor_; - ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static MessageOptions* default_instance_; @@ -1784,6 +1787,7 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { inline void set_experimental_map_key(const char* value); inline void set_experimental_map_key(const char* value, size_t size); inline ::std::string* mutable_experimental_map_key(); + inline ::std::string* release_experimental_map_key(); // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; inline int uninterpreted_option_size() const; @@ -1800,32 +1804,31 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(FieldOptions) // @@protoc_insertion_point(class_scope:google.protobuf.FieldOptions) private: + inline void set_has_ctype(); + inline void clear_has_ctype(); + inline void set_has_packed(); + inline void clear_has_packed(); + inline void set_has_deprecated(); + inline void clear_has_deprecated(); + inline void set_has_experimental_map_key(); + inline void clear_has_experimental_map_key(); + ::google::protobuf::internal::ExtensionSet _extensions_; + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; int ctype_; bool packed_; bool deprecated_; ::std::string* experimental_map_key_; - static const ::std::string _default_experimental_map_key_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(5 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static FieldOptions* default_instance_; @@ -1901,27 +1904,19 @@ class LIBPROTOBUF_EXPORT EnumOptions : public ::google::protobuf::Message { GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(EnumOptions) // @@protoc_insertion_point(class_scope:google.protobuf.EnumOptions) private: + ::google::protobuf::internal::ExtensionSet _extensions_; + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static EnumOptions* default_instance_; @@ -1997,27 +1992,19 @@ class LIBPROTOBUF_EXPORT EnumValueOptions : public ::google::protobuf::Message { GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(EnumValueOptions) // @@protoc_insertion_point(class_scope:google.protobuf.EnumValueOptions) private: + ::google::protobuf::internal::ExtensionSet _extensions_; + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static EnumValueOptions* default_instance_; @@ -2093,27 +2080,19 @@ class LIBPROTOBUF_EXPORT ServiceOptions : public ::google::protobuf::Message { GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(ServiceOptions) // @@protoc_insertion_point(class_scope:google.protobuf.ServiceOptions) private: + ::google::protobuf::internal::ExtensionSet _extensions_; + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static ServiceOptions* default_instance_; @@ -2189,27 +2168,19 @@ class LIBPROTOBUF_EXPORT MethodOptions : public ::google::protobuf::Message { GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(MethodOptions) // @@protoc_insertion_point(class_scope:google.protobuf.MethodOptions) private: + ::google::protobuf::internal::ExtensionSet _extensions_; + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static MethodOptions* default_instance_; @@ -2279,6 +2250,7 @@ class LIBPROTOBUF_EXPORT UninterpretedOption_NamePart : public ::google::protobu inline void set_name_part(const char* value); inline void set_name_part(const char* value, size_t size); inline ::std::string* mutable_name_part(); + inline ::std::string* release_name_part(); // required bool is_extension = 2; inline bool has_is_extension() const; @@ -2289,28 +2261,22 @@ class LIBPROTOBUF_EXPORT UninterpretedOption_NamePart : public ::google::protobu // @@protoc_insertion_point(class_scope:google.protobuf.UninterpretedOption.NamePart) private: + inline void set_has_name_part(); + inline void clear_has_name_part(); + inline void set_has_is_extension(); + inline void clear_has_is_extension(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::std::string* name_part_; - static const ::std::string _default_name_part_; bool is_extension_; - friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); - friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + mutable int _cached_size_; ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32]; - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; - } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); - } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); - } + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); void InitAsDefaultInstance(); static UninterpretedOption_NamePart* default_instance_; @@ -2394,6 +2360,7 @@ class LIBPROTOBUF_EXPORT UninterpretedOption : public ::google::protobuf::Messag inline void set_identifier_value(const char* value); inline void set_identifier_value(const char* value, size_t size); inline ::std::string* mutable_identifier_value(); + inline ::std::string* release_identifier_value(); // optional uint64 positive_int_value = 4; inline bool has_positive_int_value() const; @@ -2425,39 +2392,240 @@ class LIBPROTOBUF_EXPORT UninterpretedOption : public ::google::protobuf::Messag inline void set_string_value(const char* value); inline void set_string_value(const void* value, size_t size); inline ::std::string* mutable_string_value(); + inline ::std::string* release_string_value(); + + // optional string aggregate_value = 8; + inline bool has_aggregate_value() const; + inline void clear_aggregate_value(); + static const int kAggregateValueFieldNumber = 8; + inline const ::std::string& aggregate_value() const; + inline void set_aggregate_value(const ::std::string& value); + inline void set_aggregate_value(const char* value); + inline void set_aggregate_value(const char* value, size_t size); + inline ::std::string* mutable_aggregate_value(); + inline ::std::string* release_aggregate_value(); // @@protoc_insertion_point(class_scope:google.protobuf.UninterpretedOption) private: + inline void set_has_identifier_value(); + inline void clear_has_identifier_value(); + inline void set_has_positive_int_value(); + inline void clear_has_positive_int_value(); + inline void set_has_negative_int_value(); + inline void clear_has_negative_int_value(); + inline void set_has_double_value(); + inline void clear_has_double_value(); + inline void set_has_string_value(); + inline void clear_has_string_value(); + inline void set_has_aggregate_value(); + inline void clear_has_aggregate_value(); + ::google::protobuf::UnknownFieldSet _unknown_fields_; - mutable int _cached_size_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption_NamePart > name_; ::std::string* identifier_value_; - static const ::std::string _default_identifier_value_; ::google::protobuf::uint64 positive_int_value_; ::google::protobuf::int64 negative_int_value_; double double_value_; ::std::string* string_value_; - static const ::std::string _default_string_value_; + ::std::string* aggregate_value_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(7 + 31) / 32]; + + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + + void InitAsDefaultInstance(); + static UninterpretedOption* default_instance_; +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT SourceCodeInfo_Location : public ::google::protobuf::Message { + public: + SourceCodeInfo_Location(); + virtual ~SourceCodeInfo_Location(); + + SourceCodeInfo_Location(const SourceCodeInfo_Location& from); + + inline SourceCodeInfo_Location& operator=(const SourceCodeInfo_Location& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const SourceCodeInfo_Location& default_instance(); + + void Swap(SourceCodeInfo_Location* other); + + // implements Message ---------------------------------------------- + + SourceCodeInfo_Location* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const SourceCodeInfo_Location& from); + void MergeFrom(const SourceCodeInfo_Location& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // repeated int32 path = 1 [packed = true]; + inline int path_size() const; + inline void clear_path(); + static const int kPathFieldNumber = 1; + inline ::google::protobuf::int32 path(int index) const; + inline void set_path(int index, ::google::protobuf::int32 value); + inline void add_path(::google::protobuf::int32 value); + inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >& + path() const; + inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >* + mutable_path(); + + // repeated int32 span = 2 [packed = true]; + inline int span_size() const; + inline void clear_span(); + static const int kSpanFieldNumber = 2; + inline ::google::protobuf::int32 span(int index) const; + inline void set_span(int index, ::google::protobuf::int32 value); + inline void add_span(::google::protobuf::int32 value); + inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >& + span() const; + inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >* + mutable_span(); + + // @@protoc_insertion_point(class_scope:google.protobuf.SourceCodeInfo.Location) + private: + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::google::protobuf::RepeatedField< ::google::protobuf::int32 > path_; + mutable int _path_cached_byte_size_; + ::google::protobuf::RepeatedField< ::google::protobuf::int32 > span_; + mutable int _span_cached_byte_size_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32]; + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); - ::google::protobuf::uint32 _has_bits_[(6 + 31) / 32]; + void InitAsDefaultInstance(); + static SourceCodeInfo_Location* default_instance_; +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT SourceCodeInfo : public ::google::protobuf::Message { + public: + SourceCodeInfo(); + virtual ~SourceCodeInfo(); + + SourceCodeInfo(const SourceCodeInfo& from); - // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? - inline bool _has_bit(int index) const { - return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + inline SourceCodeInfo& operator=(const SourceCodeInfo& from) { + CopyFrom(from); + return *this; } - inline void _set_bit(int index) { - _has_bits_[index / 32] |= (1u << (index % 32)); + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; } - inline void _clear_bit(int index) { - _has_bits_[index / 32] &= ~(1u << (index % 32)); + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; } + static const ::google::protobuf::Descriptor* descriptor(); + static const SourceCodeInfo& default_instance(); + + void Swap(SourceCodeInfo* other); + + // implements Message ---------------------------------------------- + + SourceCodeInfo* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const SourceCodeInfo& from); + void MergeFrom(const SourceCodeInfo& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + typedef SourceCodeInfo_Location Location; + + // accessors ------------------------------------------------------- + + // repeated .google.protobuf.SourceCodeInfo.Location location = 1; + inline int location_size() const; + inline void clear_location(); + static const int kLocationFieldNumber = 1; + inline const ::google::protobuf::SourceCodeInfo_Location& location(int index) const; + inline ::google::protobuf::SourceCodeInfo_Location* mutable_location(int index); + inline ::google::protobuf::SourceCodeInfo_Location* add_location(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >& + location() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >* + mutable_location(); + + // @@protoc_insertion_point(class_scope:google.protobuf.SourceCodeInfo) + private: + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location > location_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32]; + + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + void InitAsDefaultInstance(); - static UninterpretedOption* default_instance_; + static SourceCodeInfo* default_instance_; }; // =================================================================== @@ -2497,87 +2665,119 @@ FileDescriptorSet::mutable_file() { // optional string name = 1; inline bool FileDescriptorProto::has_name() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void FileDescriptorProto::set_has_name() { + _has_bits_[0] |= 0x00000001u; +} +inline void FileDescriptorProto::clear_has_name() { + _has_bits_[0] &= ~0x00000001u; } inline void FileDescriptorProto::clear_name() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } - _clear_bit(0); + clear_has_name(); } inline const ::std::string& FileDescriptorProto::name() const { return *name_; } inline void FileDescriptorProto::set_name(const ::std::string& value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void FileDescriptorProto::set_name(const char* value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void FileDescriptorProto::set_name(const char* value, size_t size) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* FileDescriptorProto::mutable_name() { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } return name_; } +inline ::std::string* FileDescriptorProto::release_name() { + clear_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = name_; + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional string package = 2; inline bool FileDescriptorProto::has_package() const { - return _has_bit(1); + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void FileDescriptorProto::set_has_package() { + _has_bits_[0] |= 0x00000002u; +} +inline void FileDescriptorProto::clear_has_package() { + _has_bits_[0] &= ~0x00000002u; } inline void FileDescriptorProto::clear_package() { - if (package_ != &_default_package_) { + if (package_ != &::google::protobuf::internal::kEmptyString) { package_->clear(); } - _clear_bit(1); + clear_has_package(); } inline const ::std::string& FileDescriptorProto::package() const { return *package_; } inline void FileDescriptorProto::set_package(const ::std::string& value) { - _set_bit(1); - if (package_ == &_default_package_) { + set_has_package(); + if (package_ == &::google::protobuf::internal::kEmptyString) { package_ = new ::std::string; } package_->assign(value); } inline void FileDescriptorProto::set_package(const char* value) { - _set_bit(1); - if (package_ == &_default_package_) { + set_has_package(); + if (package_ == &::google::protobuf::internal::kEmptyString) { package_ = new ::std::string; } package_->assign(value); } inline void FileDescriptorProto::set_package(const char* value, size_t size) { - _set_bit(1); - if (package_ == &_default_package_) { + set_has_package(); + if (package_ == &::google::protobuf::internal::kEmptyString) { package_ = new ::std::string; } package_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* FileDescriptorProto::mutable_package() { - _set_bit(1); - if (package_ == &_default_package_) { + set_has_package(); + if (package_ == &::google::protobuf::internal::kEmptyString) { package_ = new ::std::string; } return package_; } +inline ::std::string* FileDescriptorProto::release_package() { + clear_has_package(); + if (package_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = package_; + package_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // repeated string dependency = 3; inline int FileDescriptorProto::dependency_size() const { @@ -2725,20 +2925,61 @@ FileDescriptorProto::mutable_extension() { // optional .google.protobuf.FileOptions options = 8; inline bool FileDescriptorProto::has_options() const { - return _has_bit(7); + return (_has_bits_[0] & 0x00000080u) != 0; +} +inline void FileDescriptorProto::set_has_options() { + _has_bits_[0] |= 0x00000080u; +} +inline void FileDescriptorProto::clear_has_options() { + _has_bits_[0] &= ~0x00000080u; } inline void FileDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::FileOptions::Clear(); - _clear_bit(7); + clear_has_options(); } inline const ::google::protobuf::FileOptions& FileDescriptorProto::options() const { return options_ != NULL ? *options_ : *default_instance_->options_; } inline ::google::protobuf::FileOptions* FileDescriptorProto::mutable_options() { - _set_bit(7); + set_has_options(); if (options_ == NULL) options_ = new ::google::protobuf::FileOptions; return options_; } +inline ::google::protobuf::FileOptions* FileDescriptorProto::release_options() { + clear_has_options(); + ::google::protobuf::FileOptions* temp = options_; + options_ = NULL; + return temp; +} + +// optional .google.protobuf.SourceCodeInfo source_code_info = 9; +inline bool FileDescriptorProto::has_source_code_info() const { + return (_has_bits_[0] & 0x00000100u) != 0; +} +inline void FileDescriptorProto::set_has_source_code_info() { + _has_bits_[0] |= 0x00000100u; +} +inline void FileDescriptorProto::clear_has_source_code_info() { + _has_bits_[0] &= ~0x00000100u; +} +inline void FileDescriptorProto::clear_source_code_info() { + if (source_code_info_ != NULL) source_code_info_->::google::protobuf::SourceCodeInfo::Clear(); + clear_has_source_code_info(); +} +inline const ::google::protobuf::SourceCodeInfo& FileDescriptorProto::source_code_info() const { + return source_code_info_ != NULL ? *source_code_info_ : *default_instance_->source_code_info_; +} +inline ::google::protobuf::SourceCodeInfo* FileDescriptorProto::mutable_source_code_info() { + set_has_source_code_info(); + if (source_code_info_ == NULL) source_code_info_ = new ::google::protobuf::SourceCodeInfo; + return source_code_info_; +} +inline ::google::protobuf::SourceCodeInfo* FileDescriptorProto::release_source_code_info() { + clear_has_source_code_info(); + ::google::protobuf::SourceCodeInfo* temp = source_code_info_; + source_code_info_ = NULL; + return temp; +} // ------------------------------------------------------------------- @@ -2746,33 +2987,45 @@ inline ::google::protobuf::FileOptions* FileDescriptorProto::mutable_options() { // optional int32 start = 1; inline bool DescriptorProto_ExtensionRange::has_start() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void DescriptorProto_ExtensionRange::set_has_start() { + _has_bits_[0] |= 0x00000001u; +} +inline void DescriptorProto_ExtensionRange::clear_has_start() { + _has_bits_[0] &= ~0x00000001u; } inline void DescriptorProto_ExtensionRange::clear_start() { start_ = 0; - _clear_bit(0); + clear_has_start(); } inline ::google::protobuf::int32 DescriptorProto_ExtensionRange::start() const { return start_; } inline void DescriptorProto_ExtensionRange::set_start(::google::protobuf::int32 value) { - _set_bit(0); + set_has_start(); start_ = value; } // optional int32 end = 2; inline bool DescriptorProto_ExtensionRange::has_end() const { - return _has_bit(1); + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void DescriptorProto_ExtensionRange::set_has_end() { + _has_bits_[0] |= 0x00000002u; +} +inline void DescriptorProto_ExtensionRange::clear_has_end() { + _has_bits_[0] &= ~0x00000002u; } inline void DescriptorProto_ExtensionRange::clear_end() { end_ = 0; - _clear_bit(1); + clear_has_end(); } inline ::google::protobuf::int32 DescriptorProto_ExtensionRange::end() const { return end_; } inline void DescriptorProto_ExtensionRange::set_end(::google::protobuf::int32 value) { - _set_bit(1); + set_has_end(); end_ = value; } @@ -2782,45 +3035,61 @@ inline void DescriptorProto_ExtensionRange::set_end(::google::protobuf::int32 va // optional string name = 1; inline bool DescriptorProto::has_name() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void DescriptorProto::set_has_name() { + _has_bits_[0] |= 0x00000001u; +} +inline void DescriptorProto::clear_has_name() { + _has_bits_[0] &= ~0x00000001u; } inline void DescriptorProto::clear_name() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } - _clear_bit(0); + clear_has_name(); } inline const ::std::string& DescriptorProto::name() const { return *name_; } inline void DescriptorProto::set_name(const ::std::string& value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void DescriptorProto::set_name(const char* value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void DescriptorProto::set_name(const char* value, size_t size) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* DescriptorProto::mutable_name() { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } return name_; } +inline ::std::string* DescriptorProto::release_name() { + clear_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = name_; + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // repeated .google.protobuf.FieldDescriptorProto field = 2; inline int DescriptorProto::field_size() const { @@ -2949,20 +3218,32 @@ DescriptorProto::mutable_extension_range() { // optional .google.protobuf.MessageOptions options = 7; inline bool DescriptorProto::has_options() const { - return _has_bit(6); + return (_has_bits_[0] & 0x00000040u) != 0; +} +inline void DescriptorProto::set_has_options() { + _has_bits_[0] |= 0x00000040u; +} +inline void DescriptorProto::clear_has_options() { + _has_bits_[0] &= ~0x00000040u; } inline void DescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::MessageOptions::Clear(); - _clear_bit(6); + clear_has_options(); } inline const ::google::protobuf::MessageOptions& DescriptorProto::options() const { return options_ != NULL ? *options_ : *default_instance_->options_; } inline ::google::protobuf::MessageOptions* DescriptorProto::mutable_options() { - _set_bit(6); + set_has_options(); if (options_ == NULL) options_ = new ::google::protobuf::MessageOptions; return options_; } +inline ::google::protobuf::MessageOptions* DescriptorProto::release_options() { + clear_has_options(); + ::google::protobuf::MessageOptions* temp = options_; + options_ = NULL; + return temp; +} // ------------------------------------------------------------------- @@ -2970,238 +3251,332 @@ inline ::google::protobuf::MessageOptions* DescriptorProto::mutable_options() { // optional string name = 1; inline bool FieldDescriptorProto::has_name() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void FieldDescriptorProto::set_has_name() { + _has_bits_[0] |= 0x00000001u; +} +inline void FieldDescriptorProto::clear_has_name() { + _has_bits_[0] &= ~0x00000001u; } inline void FieldDescriptorProto::clear_name() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } - _clear_bit(0); + clear_has_name(); } inline const ::std::string& FieldDescriptorProto::name() const { return *name_; } inline void FieldDescriptorProto::set_name(const ::std::string& value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void FieldDescriptorProto::set_name(const char* value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void FieldDescriptorProto::set_name(const char* value, size_t size) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* FieldDescriptorProto::mutable_name() { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } return name_; } +inline ::std::string* FieldDescriptorProto::release_name() { + clear_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = name_; + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional int32 number = 3; inline bool FieldDescriptorProto::has_number() const { - return _has_bit(1); + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void FieldDescriptorProto::set_has_number() { + _has_bits_[0] |= 0x00000002u; +} +inline void FieldDescriptorProto::clear_has_number() { + _has_bits_[0] &= ~0x00000002u; } inline void FieldDescriptorProto::clear_number() { number_ = 0; - _clear_bit(1); + clear_has_number(); } inline ::google::protobuf::int32 FieldDescriptorProto::number() const { return number_; } inline void FieldDescriptorProto::set_number(::google::protobuf::int32 value) { - _set_bit(1); + set_has_number(); number_ = value; } // optional .google.protobuf.FieldDescriptorProto.Label label = 4; inline bool FieldDescriptorProto::has_label() const { - return _has_bit(2); + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void FieldDescriptorProto::set_has_label() { + _has_bits_[0] |= 0x00000004u; +} +inline void FieldDescriptorProto::clear_has_label() { + _has_bits_[0] &= ~0x00000004u; } inline void FieldDescriptorProto::clear_label() { label_ = 1; - _clear_bit(2); + clear_has_label(); } inline ::google::protobuf::FieldDescriptorProto_Label FieldDescriptorProto::label() const { return static_cast< ::google::protobuf::FieldDescriptorProto_Label >(label_); } inline void FieldDescriptorProto::set_label(::google::protobuf::FieldDescriptorProto_Label value) { GOOGLE_DCHECK(::google::protobuf::FieldDescriptorProto_Label_IsValid(value)); - _set_bit(2); + set_has_label(); label_ = value; } // optional .google.protobuf.FieldDescriptorProto.Type type = 5; inline bool FieldDescriptorProto::has_type() const { - return _has_bit(3); + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void FieldDescriptorProto::set_has_type() { + _has_bits_[0] |= 0x00000008u; +} +inline void FieldDescriptorProto::clear_has_type() { + _has_bits_[0] &= ~0x00000008u; } inline void FieldDescriptorProto::clear_type() { type_ = 1; - _clear_bit(3); + clear_has_type(); } inline ::google::protobuf::FieldDescriptorProto_Type FieldDescriptorProto::type() const { return static_cast< ::google::protobuf::FieldDescriptorProto_Type >(type_); } inline void FieldDescriptorProto::set_type(::google::protobuf::FieldDescriptorProto_Type value) { GOOGLE_DCHECK(::google::protobuf::FieldDescriptorProto_Type_IsValid(value)); - _set_bit(3); + set_has_type(); type_ = value; } // optional string type_name = 6; inline bool FieldDescriptorProto::has_type_name() const { - return _has_bit(4); + return (_has_bits_[0] & 0x00000010u) != 0; +} +inline void FieldDescriptorProto::set_has_type_name() { + _has_bits_[0] |= 0x00000010u; +} +inline void FieldDescriptorProto::clear_has_type_name() { + _has_bits_[0] &= ~0x00000010u; } inline void FieldDescriptorProto::clear_type_name() { - if (type_name_ != &_default_type_name_) { + if (type_name_ != &::google::protobuf::internal::kEmptyString) { type_name_->clear(); } - _clear_bit(4); + clear_has_type_name(); } inline const ::std::string& FieldDescriptorProto::type_name() const { return *type_name_; } inline void FieldDescriptorProto::set_type_name(const ::std::string& value) { - _set_bit(4); - if (type_name_ == &_default_type_name_) { + set_has_type_name(); + if (type_name_ == &::google::protobuf::internal::kEmptyString) { type_name_ = new ::std::string; } type_name_->assign(value); } inline void FieldDescriptorProto::set_type_name(const char* value) { - _set_bit(4); - if (type_name_ == &_default_type_name_) { + set_has_type_name(); + if (type_name_ == &::google::protobuf::internal::kEmptyString) { type_name_ = new ::std::string; } type_name_->assign(value); } inline void FieldDescriptorProto::set_type_name(const char* value, size_t size) { - _set_bit(4); - if (type_name_ == &_default_type_name_) { + set_has_type_name(); + if (type_name_ == &::google::protobuf::internal::kEmptyString) { type_name_ = new ::std::string; } type_name_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* FieldDescriptorProto::mutable_type_name() { - _set_bit(4); - if (type_name_ == &_default_type_name_) { + set_has_type_name(); + if (type_name_ == &::google::protobuf::internal::kEmptyString) { type_name_ = new ::std::string; } return type_name_; } +inline ::std::string* FieldDescriptorProto::release_type_name() { + clear_has_type_name(); + if (type_name_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = type_name_; + type_name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional string extendee = 2; inline bool FieldDescriptorProto::has_extendee() const { - return _has_bit(5); + return (_has_bits_[0] & 0x00000020u) != 0; +} +inline void FieldDescriptorProto::set_has_extendee() { + _has_bits_[0] |= 0x00000020u; +} +inline void FieldDescriptorProto::clear_has_extendee() { + _has_bits_[0] &= ~0x00000020u; } inline void FieldDescriptorProto::clear_extendee() { - if (extendee_ != &_default_extendee_) { + if (extendee_ != &::google::protobuf::internal::kEmptyString) { extendee_->clear(); } - _clear_bit(5); + clear_has_extendee(); } inline const ::std::string& FieldDescriptorProto::extendee() const { return *extendee_; } inline void FieldDescriptorProto::set_extendee(const ::std::string& value) { - _set_bit(5); - if (extendee_ == &_default_extendee_) { + set_has_extendee(); + if (extendee_ == &::google::protobuf::internal::kEmptyString) { extendee_ = new ::std::string; } extendee_->assign(value); } inline void FieldDescriptorProto::set_extendee(const char* value) { - _set_bit(5); - if (extendee_ == &_default_extendee_) { + set_has_extendee(); + if (extendee_ == &::google::protobuf::internal::kEmptyString) { extendee_ = new ::std::string; } extendee_->assign(value); } inline void FieldDescriptorProto::set_extendee(const char* value, size_t size) { - _set_bit(5); - if (extendee_ == &_default_extendee_) { + set_has_extendee(); + if (extendee_ == &::google::protobuf::internal::kEmptyString) { extendee_ = new ::std::string; } extendee_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* FieldDescriptorProto::mutable_extendee() { - _set_bit(5); - if (extendee_ == &_default_extendee_) { + set_has_extendee(); + if (extendee_ == &::google::protobuf::internal::kEmptyString) { extendee_ = new ::std::string; } return extendee_; } +inline ::std::string* FieldDescriptorProto::release_extendee() { + clear_has_extendee(); + if (extendee_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = extendee_; + extendee_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional string default_value = 7; inline bool FieldDescriptorProto::has_default_value() const { - return _has_bit(6); + return (_has_bits_[0] & 0x00000040u) != 0; +} +inline void FieldDescriptorProto::set_has_default_value() { + _has_bits_[0] |= 0x00000040u; +} +inline void FieldDescriptorProto::clear_has_default_value() { + _has_bits_[0] &= ~0x00000040u; } inline void FieldDescriptorProto::clear_default_value() { - if (default_value_ != &_default_default_value_) { + if (default_value_ != &::google::protobuf::internal::kEmptyString) { default_value_->clear(); } - _clear_bit(6); + clear_has_default_value(); } inline const ::std::string& FieldDescriptorProto::default_value() const { return *default_value_; } inline void FieldDescriptorProto::set_default_value(const ::std::string& value) { - _set_bit(6); - if (default_value_ == &_default_default_value_) { + set_has_default_value(); + if (default_value_ == &::google::protobuf::internal::kEmptyString) { default_value_ = new ::std::string; } default_value_->assign(value); } inline void FieldDescriptorProto::set_default_value(const char* value) { - _set_bit(6); - if (default_value_ == &_default_default_value_) { + set_has_default_value(); + if (default_value_ == &::google::protobuf::internal::kEmptyString) { default_value_ = new ::std::string; } default_value_->assign(value); } inline void FieldDescriptorProto::set_default_value(const char* value, size_t size) { - _set_bit(6); - if (default_value_ == &_default_default_value_) { + set_has_default_value(); + if (default_value_ == &::google::protobuf::internal::kEmptyString) { default_value_ = new ::std::string; } default_value_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* FieldDescriptorProto::mutable_default_value() { - _set_bit(6); - if (default_value_ == &_default_default_value_) { + set_has_default_value(); + if (default_value_ == &::google::protobuf::internal::kEmptyString) { default_value_ = new ::std::string; } return default_value_; } +inline ::std::string* FieldDescriptorProto::release_default_value() { + clear_has_default_value(); + if (default_value_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = default_value_; + default_value_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional .google.protobuf.FieldOptions options = 8; inline bool FieldDescriptorProto::has_options() const { - return _has_bit(7); + return (_has_bits_[0] & 0x00000080u) != 0; +} +inline void FieldDescriptorProto::set_has_options() { + _has_bits_[0] |= 0x00000080u; +} +inline void FieldDescriptorProto::clear_has_options() { + _has_bits_[0] &= ~0x00000080u; } inline void FieldDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::FieldOptions::Clear(); - _clear_bit(7); + clear_has_options(); } inline const ::google::protobuf::FieldOptions& FieldDescriptorProto::options() const { return options_ != NULL ? *options_ : *default_instance_->options_; } inline ::google::protobuf::FieldOptions* FieldDescriptorProto::mutable_options() { - _set_bit(7); + set_has_options(); if (options_ == NULL) options_ = new ::google::protobuf::FieldOptions; return options_; } +inline ::google::protobuf::FieldOptions* FieldDescriptorProto::release_options() { + clear_has_options(); + ::google::protobuf::FieldOptions* temp = options_; + options_ = NULL; + return temp; +} // ------------------------------------------------------------------- @@ -3209,45 +3584,61 @@ inline ::google::protobuf::FieldOptions* FieldDescriptorProto::mutable_options() // optional string name = 1; inline bool EnumDescriptorProto::has_name() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void EnumDescriptorProto::set_has_name() { + _has_bits_[0] |= 0x00000001u; +} +inline void EnumDescriptorProto::clear_has_name() { + _has_bits_[0] &= ~0x00000001u; } inline void EnumDescriptorProto::clear_name() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } - _clear_bit(0); + clear_has_name(); } inline const ::std::string& EnumDescriptorProto::name() const { return *name_; } inline void EnumDescriptorProto::set_name(const ::std::string& value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void EnumDescriptorProto::set_name(const char* value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void EnumDescriptorProto::set_name(const char* value, size_t size) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* EnumDescriptorProto::mutable_name() { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } return name_; } +inline ::std::string* EnumDescriptorProto::release_name() { + clear_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = name_; + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // repeated .google.protobuf.EnumValueDescriptorProto value = 2; inline int EnumDescriptorProto::value_size() const { @@ -3276,20 +3667,32 @@ EnumDescriptorProto::mutable_value() { // optional .google.protobuf.EnumOptions options = 3; inline bool EnumDescriptorProto::has_options() const { - return _has_bit(2); + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void EnumDescriptorProto::set_has_options() { + _has_bits_[0] |= 0x00000004u; +} +inline void EnumDescriptorProto::clear_has_options() { + _has_bits_[0] &= ~0x00000004u; } inline void EnumDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::EnumOptions::Clear(); - _clear_bit(2); + clear_has_options(); } inline const ::google::protobuf::EnumOptions& EnumDescriptorProto::options() const { return options_ != NULL ? *options_ : *default_instance_->options_; } inline ::google::protobuf::EnumOptions* EnumDescriptorProto::mutable_options() { - _set_bit(2); + set_has_options(); if (options_ == NULL) options_ = new ::google::protobuf::EnumOptions; return options_; } +inline ::google::protobuf::EnumOptions* EnumDescriptorProto::release_options() { + clear_has_options(); + ::google::protobuf::EnumOptions* temp = options_; + options_ = NULL; + return temp; +} // ------------------------------------------------------------------- @@ -3297,78 +3700,112 @@ inline ::google::protobuf::EnumOptions* EnumDescriptorProto::mutable_options() { // optional string name = 1; inline bool EnumValueDescriptorProto::has_name() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void EnumValueDescriptorProto::set_has_name() { + _has_bits_[0] |= 0x00000001u; +} +inline void EnumValueDescriptorProto::clear_has_name() { + _has_bits_[0] &= ~0x00000001u; } inline void EnumValueDescriptorProto::clear_name() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } - _clear_bit(0); + clear_has_name(); } inline const ::std::string& EnumValueDescriptorProto::name() const { return *name_; } inline void EnumValueDescriptorProto::set_name(const ::std::string& value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void EnumValueDescriptorProto::set_name(const char* value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void EnumValueDescriptorProto::set_name(const char* value, size_t size) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* EnumValueDescriptorProto::mutable_name() { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } return name_; } +inline ::std::string* EnumValueDescriptorProto::release_name() { + clear_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = name_; + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional int32 number = 2; inline bool EnumValueDescriptorProto::has_number() const { - return _has_bit(1); + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void EnumValueDescriptorProto::set_has_number() { + _has_bits_[0] |= 0x00000002u; +} +inline void EnumValueDescriptorProto::clear_has_number() { + _has_bits_[0] &= ~0x00000002u; } inline void EnumValueDescriptorProto::clear_number() { number_ = 0; - _clear_bit(1); + clear_has_number(); } inline ::google::protobuf::int32 EnumValueDescriptorProto::number() const { return number_; } inline void EnumValueDescriptorProto::set_number(::google::protobuf::int32 value) { - _set_bit(1); + set_has_number(); number_ = value; } // optional .google.protobuf.EnumValueOptions options = 3; inline bool EnumValueDescriptorProto::has_options() const { - return _has_bit(2); + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void EnumValueDescriptorProto::set_has_options() { + _has_bits_[0] |= 0x00000004u; +} +inline void EnumValueDescriptorProto::clear_has_options() { + _has_bits_[0] &= ~0x00000004u; } inline void EnumValueDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::EnumValueOptions::Clear(); - _clear_bit(2); + clear_has_options(); } inline const ::google::protobuf::EnumValueOptions& EnumValueDescriptorProto::options() const { return options_ != NULL ? *options_ : *default_instance_->options_; } inline ::google::protobuf::EnumValueOptions* EnumValueDescriptorProto::mutable_options() { - _set_bit(2); + set_has_options(); if (options_ == NULL) options_ = new ::google::protobuf::EnumValueOptions; return options_; } +inline ::google::protobuf::EnumValueOptions* EnumValueDescriptorProto::release_options() { + clear_has_options(); + ::google::protobuf::EnumValueOptions* temp = options_; + options_ = NULL; + return temp; +} // ------------------------------------------------------------------- @@ -3376,45 +3813,61 @@ inline ::google::protobuf::EnumValueOptions* EnumValueDescriptorProto::mutable_o // optional string name = 1; inline bool ServiceDescriptorProto::has_name() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void ServiceDescriptorProto::set_has_name() { + _has_bits_[0] |= 0x00000001u; +} +inline void ServiceDescriptorProto::clear_has_name() { + _has_bits_[0] &= ~0x00000001u; } inline void ServiceDescriptorProto::clear_name() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } - _clear_bit(0); + clear_has_name(); } inline const ::std::string& ServiceDescriptorProto::name() const { return *name_; } inline void ServiceDescriptorProto::set_name(const ::std::string& value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void ServiceDescriptorProto::set_name(const char* value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void ServiceDescriptorProto::set_name(const char* value, size_t size) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* ServiceDescriptorProto::mutable_name() { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } return name_; } +inline ::std::string* ServiceDescriptorProto::release_name() { + clear_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = name_; + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // repeated .google.protobuf.MethodDescriptorProto method = 2; inline int ServiceDescriptorProto::method_size() const { @@ -3443,20 +3896,32 @@ ServiceDescriptorProto::mutable_method() { // optional .google.protobuf.ServiceOptions options = 3; inline bool ServiceDescriptorProto::has_options() const { - return _has_bit(2); + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void ServiceDescriptorProto::set_has_options() { + _has_bits_[0] |= 0x00000004u; +} +inline void ServiceDescriptorProto::clear_has_options() { + _has_bits_[0] &= ~0x00000004u; } inline void ServiceDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::ServiceOptions::Clear(); - _clear_bit(2); + clear_has_options(); } inline const ::google::protobuf::ServiceOptions& ServiceDescriptorProto::options() const { return options_ != NULL ? *options_ : *default_instance_->options_; } inline ::google::protobuf::ServiceOptions* ServiceDescriptorProto::mutable_options() { - _set_bit(2); + set_has_options(); if (options_ == NULL) options_ = new ::google::protobuf::ServiceOptions; return options_; } +inline ::google::protobuf::ServiceOptions* ServiceDescriptorProto::release_options() { + clear_has_options(); + ::google::protobuf::ServiceOptions* temp = options_; + options_ = NULL; + return temp; +} // ------------------------------------------------------------------- @@ -3464,146 +3929,206 @@ inline ::google::protobuf::ServiceOptions* ServiceDescriptorProto::mutable_optio // optional string name = 1; inline bool MethodDescriptorProto::has_name() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void MethodDescriptorProto::set_has_name() { + _has_bits_[0] |= 0x00000001u; +} +inline void MethodDescriptorProto::clear_has_name() { + _has_bits_[0] &= ~0x00000001u; } inline void MethodDescriptorProto::clear_name() { - if (name_ != &_default_name_) { + if (name_ != &::google::protobuf::internal::kEmptyString) { name_->clear(); } - _clear_bit(0); + clear_has_name(); } inline const ::std::string& MethodDescriptorProto::name() const { return *name_; } inline void MethodDescriptorProto::set_name(const ::std::string& value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void MethodDescriptorProto::set_name(const char* value) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(value); } inline void MethodDescriptorProto::set_name(const char* value, size_t size) { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } name_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* MethodDescriptorProto::mutable_name() { - _set_bit(0); - if (name_ == &_default_name_) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { name_ = new ::std::string; } return name_; } +inline ::std::string* MethodDescriptorProto::release_name() { + clear_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = name_; + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional string input_type = 2; inline bool MethodDescriptorProto::has_input_type() const { - return _has_bit(1); + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void MethodDescriptorProto::set_has_input_type() { + _has_bits_[0] |= 0x00000002u; +} +inline void MethodDescriptorProto::clear_has_input_type() { + _has_bits_[0] &= ~0x00000002u; } inline void MethodDescriptorProto::clear_input_type() { - if (input_type_ != &_default_input_type_) { + if (input_type_ != &::google::protobuf::internal::kEmptyString) { input_type_->clear(); } - _clear_bit(1); + clear_has_input_type(); } inline const ::std::string& MethodDescriptorProto::input_type() const { return *input_type_; } inline void MethodDescriptorProto::set_input_type(const ::std::string& value) { - _set_bit(1); - if (input_type_ == &_default_input_type_) { + set_has_input_type(); + if (input_type_ == &::google::protobuf::internal::kEmptyString) { input_type_ = new ::std::string; } input_type_->assign(value); } inline void MethodDescriptorProto::set_input_type(const char* value) { - _set_bit(1); - if (input_type_ == &_default_input_type_) { + set_has_input_type(); + if (input_type_ == &::google::protobuf::internal::kEmptyString) { input_type_ = new ::std::string; } input_type_->assign(value); } inline void MethodDescriptorProto::set_input_type(const char* value, size_t size) { - _set_bit(1); - if (input_type_ == &_default_input_type_) { + set_has_input_type(); + if (input_type_ == &::google::protobuf::internal::kEmptyString) { input_type_ = new ::std::string; } input_type_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* MethodDescriptorProto::mutable_input_type() { - _set_bit(1); - if (input_type_ == &_default_input_type_) { + set_has_input_type(); + if (input_type_ == &::google::protobuf::internal::kEmptyString) { input_type_ = new ::std::string; } return input_type_; } +inline ::std::string* MethodDescriptorProto::release_input_type() { + clear_has_input_type(); + if (input_type_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = input_type_; + input_type_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional string output_type = 3; inline bool MethodDescriptorProto::has_output_type() const { - return _has_bit(2); + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void MethodDescriptorProto::set_has_output_type() { + _has_bits_[0] |= 0x00000004u; +} +inline void MethodDescriptorProto::clear_has_output_type() { + _has_bits_[0] &= ~0x00000004u; } inline void MethodDescriptorProto::clear_output_type() { - if (output_type_ != &_default_output_type_) { + if (output_type_ != &::google::protobuf::internal::kEmptyString) { output_type_->clear(); } - _clear_bit(2); + clear_has_output_type(); } inline const ::std::string& MethodDescriptorProto::output_type() const { return *output_type_; } inline void MethodDescriptorProto::set_output_type(const ::std::string& value) { - _set_bit(2); - if (output_type_ == &_default_output_type_) { + set_has_output_type(); + if (output_type_ == &::google::protobuf::internal::kEmptyString) { output_type_ = new ::std::string; } output_type_->assign(value); } inline void MethodDescriptorProto::set_output_type(const char* value) { - _set_bit(2); - if (output_type_ == &_default_output_type_) { + set_has_output_type(); + if (output_type_ == &::google::protobuf::internal::kEmptyString) { output_type_ = new ::std::string; } output_type_->assign(value); } inline void MethodDescriptorProto::set_output_type(const char* value, size_t size) { - _set_bit(2); - if (output_type_ == &_default_output_type_) { + set_has_output_type(); + if (output_type_ == &::google::protobuf::internal::kEmptyString) { output_type_ = new ::std::string; } output_type_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* MethodDescriptorProto::mutable_output_type() { - _set_bit(2); - if (output_type_ == &_default_output_type_) { + set_has_output_type(); + if (output_type_ == &::google::protobuf::internal::kEmptyString) { output_type_ = new ::std::string; } return output_type_; } +inline ::std::string* MethodDescriptorProto::release_output_type() { + clear_has_output_type(); + if (output_type_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = output_type_; + output_type_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional .google.protobuf.MethodOptions options = 4; inline bool MethodDescriptorProto::has_options() const { - return _has_bit(3); + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void MethodDescriptorProto::set_has_options() { + _has_bits_[0] |= 0x00000008u; +} +inline void MethodDescriptorProto::clear_has_options() { + _has_bits_[0] &= ~0x00000008u; } inline void MethodDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::MethodOptions::Clear(); - _clear_bit(3); + clear_has_options(); } inline const ::google::protobuf::MethodOptions& MethodDescriptorProto::options() const { return options_ != NULL ? *options_ : *default_instance_->options_; } inline ::google::protobuf::MethodOptions* MethodDescriptorProto::mutable_options() { - _set_bit(3); + set_has_options(); if (options_ == NULL) options_ = new ::google::protobuf::MethodOptions; return options_; } +inline ::google::protobuf::MethodOptions* MethodDescriptorProto::release_options() { + clear_has_options(); + ::google::protobuf::MethodOptions* temp = options_; + options_ = NULL; + return temp; +} // ------------------------------------------------------------------- @@ -3611,166 +4136,250 @@ inline ::google::protobuf::MethodOptions* MethodDescriptorProto::mutable_options // optional string java_package = 1; inline bool FileOptions::has_java_package() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void FileOptions::set_has_java_package() { + _has_bits_[0] |= 0x00000001u; +} +inline void FileOptions::clear_has_java_package() { + _has_bits_[0] &= ~0x00000001u; } inline void FileOptions::clear_java_package() { - if (java_package_ != &_default_java_package_) { + if (java_package_ != &::google::protobuf::internal::kEmptyString) { java_package_->clear(); } - _clear_bit(0); + clear_has_java_package(); } inline const ::std::string& FileOptions::java_package() const { return *java_package_; } inline void FileOptions::set_java_package(const ::std::string& value) { - _set_bit(0); - if (java_package_ == &_default_java_package_) { + set_has_java_package(); + if (java_package_ == &::google::protobuf::internal::kEmptyString) { java_package_ = new ::std::string; } java_package_->assign(value); } inline void FileOptions::set_java_package(const char* value) { - _set_bit(0); - if (java_package_ == &_default_java_package_) { + set_has_java_package(); + if (java_package_ == &::google::protobuf::internal::kEmptyString) { java_package_ = new ::std::string; } java_package_->assign(value); } inline void FileOptions::set_java_package(const char* value, size_t size) { - _set_bit(0); - if (java_package_ == &_default_java_package_) { + set_has_java_package(); + if (java_package_ == &::google::protobuf::internal::kEmptyString) { java_package_ = new ::std::string; } java_package_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* FileOptions::mutable_java_package() { - _set_bit(0); - if (java_package_ == &_default_java_package_) { + set_has_java_package(); + if (java_package_ == &::google::protobuf::internal::kEmptyString) { java_package_ = new ::std::string; } return java_package_; } +inline ::std::string* FileOptions::release_java_package() { + clear_has_java_package(); + if (java_package_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = java_package_; + java_package_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional string java_outer_classname = 8; inline bool FileOptions::has_java_outer_classname() const { - return _has_bit(1); + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void FileOptions::set_has_java_outer_classname() { + _has_bits_[0] |= 0x00000002u; +} +inline void FileOptions::clear_has_java_outer_classname() { + _has_bits_[0] &= ~0x00000002u; } inline void FileOptions::clear_java_outer_classname() { - if (java_outer_classname_ != &_default_java_outer_classname_) { + if (java_outer_classname_ != &::google::protobuf::internal::kEmptyString) { java_outer_classname_->clear(); } - _clear_bit(1); + clear_has_java_outer_classname(); } inline const ::std::string& FileOptions::java_outer_classname() const { return *java_outer_classname_; } inline void FileOptions::set_java_outer_classname(const ::std::string& value) { - _set_bit(1); - if (java_outer_classname_ == &_default_java_outer_classname_) { + set_has_java_outer_classname(); + if (java_outer_classname_ == &::google::protobuf::internal::kEmptyString) { java_outer_classname_ = new ::std::string; } java_outer_classname_->assign(value); } inline void FileOptions::set_java_outer_classname(const char* value) { - _set_bit(1); - if (java_outer_classname_ == &_default_java_outer_classname_) { + set_has_java_outer_classname(); + if (java_outer_classname_ == &::google::protobuf::internal::kEmptyString) { java_outer_classname_ = new ::std::string; } java_outer_classname_->assign(value); } inline void FileOptions::set_java_outer_classname(const char* value, size_t size) { - _set_bit(1); - if (java_outer_classname_ == &_default_java_outer_classname_) { + set_has_java_outer_classname(); + if (java_outer_classname_ == &::google::protobuf::internal::kEmptyString) { java_outer_classname_ = new ::std::string; } java_outer_classname_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* FileOptions::mutable_java_outer_classname() { - _set_bit(1); - if (java_outer_classname_ == &_default_java_outer_classname_) { + set_has_java_outer_classname(); + if (java_outer_classname_ == &::google::protobuf::internal::kEmptyString) { java_outer_classname_ = new ::std::string; } return java_outer_classname_; } +inline ::std::string* FileOptions::release_java_outer_classname() { + clear_has_java_outer_classname(); + if (java_outer_classname_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = java_outer_classname_; + java_outer_classname_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional bool java_multiple_files = 10 [default = false]; inline bool FileOptions::has_java_multiple_files() const { - return _has_bit(2); + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void FileOptions::set_has_java_multiple_files() { + _has_bits_[0] |= 0x00000004u; +} +inline void FileOptions::clear_has_java_multiple_files() { + _has_bits_[0] &= ~0x00000004u; } inline void FileOptions::clear_java_multiple_files() { java_multiple_files_ = false; - _clear_bit(2); + clear_has_java_multiple_files(); } inline bool FileOptions::java_multiple_files() const { return java_multiple_files_; } inline void FileOptions::set_java_multiple_files(bool value) { - _set_bit(2); + set_has_java_multiple_files(); java_multiple_files_ = value; } +// optional bool java_generate_equals_and_hash = 20 [default = false]; +inline bool FileOptions::has_java_generate_equals_and_hash() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void FileOptions::set_has_java_generate_equals_and_hash() { + _has_bits_[0] |= 0x00000008u; +} +inline void FileOptions::clear_has_java_generate_equals_and_hash() { + _has_bits_[0] &= ~0x00000008u; +} +inline void FileOptions::clear_java_generate_equals_and_hash() { + java_generate_equals_and_hash_ = false; + clear_has_java_generate_equals_and_hash(); +} +inline bool FileOptions::java_generate_equals_and_hash() const { + return java_generate_equals_and_hash_; +} +inline void FileOptions::set_java_generate_equals_and_hash(bool value) { + set_has_java_generate_equals_and_hash(); + java_generate_equals_and_hash_ = value; +} + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; inline bool FileOptions::has_optimize_for() const { - return _has_bit(3); + return (_has_bits_[0] & 0x00000010u) != 0; +} +inline void FileOptions::set_has_optimize_for() { + _has_bits_[0] |= 0x00000010u; +} +inline void FileOptions::clear_has_optimize_for() { + _has_bits_[0] &= ~0x00000010u; } inline void FileOptions::clear_optimize_for() { optimize_for_ = 1; - _clear_bit(3); + clear_has_optimize_for(); } inline ::google::protobuf::FileOptions_OptimizeMode FileOptions::optimize_for() const { return static_cast< ::google::protobuf::FileOptions_OptimizeMode >(optimize_for_); } inline void FileOptions::set_optimize_for(::google::protobuf::FileOptions_OptimizeMode value) { GOOGLE_DCHECK(::google::protobuf::FileOptions_OptimizeMode_IsValid(value)); - _set_bit(3); + set_has_optimize_for(); optimize_for_ = value; } -// optional bool cc_generic_services = 16 [default = true]; +// optional bool cc_generic_services = 16 [default = false]; inline bool FileOptions::has_cc_generic_services() const { - return _has_bit(4); + return (_has_bits_[0] & 0x00000020u) != 0; +} +inline void FileOptions::set_has_cc_generic_services() { + _has_bits_[0] |= 0x00000020u; +} +inline void FileOptions::clear_has_cc_generic_services() { + _has_bits_[0] &= ~0x00000020u; } inline void FileOptions::clear_cc_generic_services() { - cc_generic_services_ = true; - _clear_bit(4); + cc_generic_services_ = false; + clear_has_cc_generic_services(); } inline bool FileOptions::cc_generic_services() const { return cc_generic_services_; } inline void FileOptions::set_cc_generic_services(bool value) { - _set_bit(4); + set_has_cc_generic_services(); cc_generic_services_ = value; } -// optional bool java_generic_services = 17 [default = true]; +// optional bool java_generic_services = 17 [default = false]; inline bool FileOptions::has_java_generic_services() const { - return _has_bit(5); + return (_has_bits_[0] & 0x00000040u) != 0; +} +inline void FileOptions::set_has_java_generic_services() { + _has_bits_[0] |= 0x00000040u; +} +inline void FileOptions::clear_has_java_generic_services() { + _has_bits_[0] &= ~0x00000040u; } inline void FileOptions::clear_java_generic_services() { - java_generic_services_ = true; - _clear_bit(5); + java_generic_services_ = false; + clear_has_java_generic_services(); } inline bool FileOptions::java_generic_services() const { return java_generic_services_; } inline void FileOptions::set_java_generic_services(bool value) { - _set_bit(5); + set_has_java_generic_services(); java_generic_services_ = value; } -// optional bool py_generic_services = 18 [default = true]; +// optional bool py_generic_services = 18 [default = false]; inline bool FileOptions::has_py_generic_services() const { - return _has_bit(6); + return (_has_bits_[0] & 0x00000080u) != 0; +} +inline void FileOptions::set_has_py_generic_services() { + _has_bits_[0] |= 0x00000080u; +} +inline void FileOptions::clear_has_py_generic_services() { + _has_bits_[0] &= ~0x00000080u; } inline void FileOptions::clear_py_generic_services() { - py_generic_services_ = true; - _clear_bit(6); + py_generic_services_ = false; + clear_has_py_generic_services(); } inline bool FileOptions::py_generic_services() const { return py_generic_services_; } inline void FileOptions::set_py_generic_services(bool value) { - _set_bit(6); + set_has_py_generic_services(); py_generic_services_ = value; } @@ -3805,33 +4414,45 @@ FileOptions::mutable_uninterpreted_option() { // optional bool message_set_wire_format = 1 [default = false]; inline bool MessageOptions::has_message_set_wire_format() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void MessageOptions::set_has_message_set_wire_format() { + _has_bits_[0] |= 0x00000001u; +} +inline void MessageOptions::clear_has_message_set_wire_format() { + _has_bits_[0] &= ~0x00000001u; } inline void MessageOptions::clear_message_set_wire_format() { message_set_wire_format_ = false; - _clear_bit(0); + clear_has_message_set_wire_format(); } inline bool MessageOptions::message_set_wire_format() const { return message_set_wire_format_; } inline void MessageOptions::set_message_set_wire_format(bool value) { - _set_bit(0); + set_has_message_set_wire_format(); message_set_wire_format_ = value; } // optional bool no_standard_descriptor_accessor = 2 [default = false]; inline bool MessageOptions::has_no_standard_descriptor_accessor() const { - return _has_bit(1); + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void MessageOptions::set_has_no_standard_descriptor_accessor() { + _has_bits_[0] |= 0x00000002u; +} +inline void MessageOptions::clear_has_no_standard_descriptor_accessor() { + _has_bits_[0] &= ~0x00000002u; } inline void MessageOptions::clear_no_standard_descriptor_accessor() { no_standard_descriptor_accessor_ = false; - _clear_bit(1); + clear_has_no_standard_descriptor_accessor(); } inline bool MessageOptions::no_standard_descriptor_accessor() const { return no_standard_descriptor_accessor_; } inline void MessageOptions::set_no_standard_descriptor_accessor(bool value) { - _set_bit(1); + set_has_no_standard_descriptor_accessor(); no_standard_descriptor_accessor_ = value; } @@ -3866,94 +4487,128 @@ MessageOptions::mutable_uninterpreted_option() { // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING]; inline bool FieldOptions::has_ctype() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void FieldOptions::set_has_ctype() { + _has_bits_[0] |= 0x00000001u; +} +inline void FieldOptions::clear_has_ctype() { + _has_bits_[0] &= ~0x00000001u; } inline void FieldOptions::clear_ctype() { ctype_ = 0; - _clear_bit(0); + clear_has_ctype(); } inline ::google::protobuf::FieldOptions_CType FieldOptions::ctype() const { return static_cast< ::google::protobuf::FieldOptions_CType >(ctype_); } inline void FieldOptions::set_ctype(::google::protobuf::FieldOptions_CType value) { GOOGLE_DCHECK(::google::protobuf::FieldOptions_CType_IsValid(value)); - _set_bit(0); + set_has_ctype(); ctype_ = value; } // optional bool packed = 2; inline bool FieldOptions::has_packed() const { - return _has_bit(1); + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void FieldOptions::set_has_packed() { + _has_bits_[0] |= 0x00000002u; +} +inline void FieldOptions::clear_has_packed() { + _has_bits_[0] &= ~0x00000002u; } inline void FieldOptions::clear_packed() { packed_ = false; - _clear_bit(1); + clear_has_packed(); } inline bool FieldOptions::packed() const { return packed_; } inline void FieldOptions::set_packed(bool value) { - _set_bit(1); + set_has_packed(); packed_ = value; } // optional bool deprecated = 3 [default = false]; inline bool FieldOptions::has_deprecated() const { - return _has_bit(2); + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void FieldOptions::set_has_deprecated() { + _has_bits_[0] |= 0x00000004u; +} +inline void FieldOptions::clear_has_deprecated() { + _has_bits_[0] &= ~0x00000004u; } inline void FieldOptions::clear_deprecated() { deprecated_ = false; - _clear_bit(2); + clear_has_deprecated(); } inline bool FieldOptions::deprecated() const { return deprecated_; } inline void FieldOptions::set_deprecated(bool value) { - _set_bit(2); + set_has_deprecated(); deprecated_ = value; } // optional string experimental_map_key = 9; inline bool FieldOptions::has_experimental_map_key() const { - return _has_bit(3); + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void FieldOptions::set_has_experimental_map_key() { + _has_bits_[0] |= 0x00000008u; +} +inline void FieldOptions::clear_has_experimental_map_key() { + _has_bits_[0] &= ~0x00000008u; } inline void FieldOptions::clear_experimental_map_key() { - if (experimental_map_key_ != &_default_experimental_map_key_) { + if (experimental_map_key_ != &::google::protobuf::internal::kEmptyString) { experimental_map_key_->clear(); } - _clear_bit(3); + clear_has_experimental_map_key(); } inline const ::std::string& FieldOptions::experimental_map_key() const { return *experimental_map_key_; } inline void FieldOptions::set_experimental_map_key(const ::std::string& value) { - _set_bit(3); - if (experimental_map_key_ == &_default_experimental_map_key_) { + set_has_experimental_map_key(); + if (experimental_map_key_ == &::google::protobuf::internal::kEmptyString) { experimental_map_key_ = new ::std::string; } experimental_map_key_->assign(value); } inline void FieldOptions::set_experimental_map_key(const char* value) { - _set_bit(3); - if (experimental_map_key_ == &_default_experimental_map_key_) { + set_has_experimental_map_key(); + if (experimental_map_key_ == &::google::protobuf::internal::kEmptyString) { experimental_map_key_ = new ::std::string; } experimental_map_key_->assign(value); } inline void FieldOptions::set_experimental_map_key(const char* value, size_t size) { - _set_bit(3); - if (experimental_map_key_ == &_default_experimental_map_key_) { + set_has_experimental_map_key(); + if (experimental_map_key_ == &::google::protobuf::internal::kEmptyString) { experimental_map_key_ = new ::std::string; } experimental_map_key_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* FieldOptions::mutable_experimental_map_key() { - _set_bit(3); - if (experimental_map_key_ == &_default_experimental_map_key_) { + set_has_experimental_map_key(); + if (experimental_map_key_ == &::google::protobuf::internal::kEmptyString) { experimental_map_key_ = new ::std::string; } return experimental_map_key_; } +inline ::std::string* FieldOptions::release_experimental_map_key() { + clear_has_experimental_map_key(); + if (experimental_map_key_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = experimental_map_key_; + experimental_map_key_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; inline int FieldOptions::uninterpreted_option_size() const { @@ -4102,59 +4757,81 @@ MethodOptions::mutable_uninterpreted_option() { // required string name_part = 1; inline bool UninterpretedOption_NamePart::has_name_part() const { - return _has_bit(0); + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void UninterpretedOption_NamePart::set_has_name_part() { + _has_bits_[0] |= 0x00000001u; +} +inline void UninterpretedOption_NamePart::clear_has_name_part() { + _has_bits_[0] &= ~0x00000001u; } inline void UninterpretedOption_NamePart::clear_name_part() { - if (name_part_ != &_default_name_part_) { + if (name_part_ != &::google::protobuf::internal::kEmptyString) { name_part_->clear(); } - _clear_bit(0); + clear_has_name_part(); } inline const ::std::string& UninterpretedOption_NamePart::name_part() const { return *name_part_; } inline void UninterpretedOption_NamePart::set_name_part(const ::std::string& value) { - _set_bit(0); - if (name_part_ == &_default_name_part_) { + set_has_name_part(); + if (name_part_ == &::google::protobuf::internal::kEmptyString) { name_part_ = new ::std::string; } name_part_->assign(value); } inline void UninterpretedOption_NamePart::set_name_part(const char* value) { - _set_bit(0); - if (name_part_ == &_default_name_part_) { + set_has_name_part(); + if (name_part_ == &::google::protobuf::internal::kEmptyString) { name_part_ = new ::std::string; } name_part_->assign(value); } inline void UninterpretedOption_NamePart::set_name_part(const char* value, size_t size) { - _set_bit(0); - if (name_part_ == &_default_name_part_) { + set_has_name_part(); + if (name_part_ == &::google::protobuf::internal::kEmptyString) { name_part_ = new ::std::string; } name_part_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* UninterpretedOption_NamePart::mutable_name_part() { - _set_bit(0); - if (name_part_ == &_default_name_part_) { + set_has_name_part(); + if (name_part_ == &::google::protobuf::internal::kEmptyString) { name_part_ = new ::std::string; } return name_part_; } +inline ::std::string* UninterpretedOption_NamePart::release_name_part() { + clear_has_name_part(); + if (name_part_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = name_part_; + name_part_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // required bool is_extension = 2; inline bool UninterpretedOption_NamePart::has_is_extension() const { - return _has_bit(1); + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void UninterpretedOption_NamePart::set_has_is_extension() { + _has_bits_[0] |= 0x00000002u; +} +inline void UninterpretedOption_NamePart::clear_has_is_extension() { + _has_bits_[0] &= ~0x00000002u; } inline void UninterpretedOption_NamePart::clear_is_extension() { is_extension_ = false; - _clear_bit(1); + clear_has_is_extension(); } inline bool UninterpretedOption_NamePart::is_extension() const { return is_extension_; } inline void UninterpretedOption_NamePart::set_is_extension(bool value) { - _set_bit(1); + set_has_is_extension(); is_extension_ = value; } @@ -4189,135 +4866,326 @@ UninterpretedOption::mutable_name() { // optional string identifier_value = 3; inline bool UninterpretedOption::has_identifier_value() const { - return _has_bit(1); + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void UninterpretedOption::set_has_identifier_value() { + _has_bits_[0] |= 0x00000002u; +} +inline void UninterpretedOption::clear_has_identifier_value() { + _has_bits_[0] &= ~0x00000002u; } inline void UninterpretedOption::clear_identifier_value() { - if (identifier_value_ != &_default_identifier_value_) { + if (identifier_value_ != &::google::protobuf::internal::kEmptyString) { identifier_value_->clear(); } - _clear_bit(1); + clear_has_identifier_value(); } inline const ::std::string& UninterpretedOption::identifier_value() const { return *identifier_value_; } inline void UninterpretedOption::set_identifier_value(const ::std::string& value) { - _set_bit(1); - if (identifier_value_ == &_default_identifier_value_) { + set_has_identifier_value(); + if (identifier_value_ == &::google::protobuf::internal::kEmptyString) { identifier_value_ = new ::std::string; } identifier_value_->assign(value); } inline void UninterpretedOption::set_identifier_value(const char* value) { - _set_bit(1); - if (identifier_value_ == &_default_identifier_value_) { + set_has_identifier_value(); + if (identifier_value_ == &::google::protobuf::internal::kEmptyString) { identifier_value_ = new ::std::string; } identifier_value_->assign(value); } inline void UninterpretedOption::set_identifier_value(const char* value, size_t size) { - _set_bit(1); - if (identifier_value_ == &_default_identifier_value_) { + set_has_identifier_value(); + if (identifier_value_ == &::google::protobuf::internal::kEmptyString) { identifier_value_ = new ::std::string; } identifier_value_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* UninterpretedOption::mutable_identifier_value() { - _set_bit(1); - if (identifier_value_ == &_default_identifier_value_) { + set_has_identifier_value(); + if (identifier_value_ == &::google::protobuf::internal::kEmptyString) { identifier_value_ = new ::std::string; } return identifier_value_; } +inline ::std::string* UninterpretedOption::release_identifier_value() { + clear_has_identifier_value(); + if (identifier_value_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = identifier_value_; + identifier_value_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} // optional uint64 positive_int_value = 4; inline bool UninterpretedOption::has_positive_int_value() const { - return _has_bit(2); + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void UninterpretedOption::set_has_positive_int_value() { + _has_bits_[0] |= 0x00000004u; +} +inline void UninterpretedOption::clear_has_positive_int_value() { + _has_bits_[0] &= ~0x00000004u; } inline void UninterpretedOption::clear_positive_int_value() { positive_int_value_ = GOOGLE_ULONGLONG(0); - _clear_bit(2); + clear_has_positive_int_value(); } inline ::google::protobuf::uint64 UninterpretedOption::positive_int_value() const { return positive_int_value_; } inline void UninterpretedOption::set_positive_int_value(::google::protobuf::uint64 value) { - _set_bit(2); + set_has_positive_int_value(); positive_int_value_ = value; } // optional int64 negative_int_value = 5; inline bool UninterpretedOption::has_negative_int_value() const { - return _has_bit(3); + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void UninterpretedOption::set_has_negative_int_value() { + _has_bits_[0] |= 0x00000008u; +} +inline void UninterpretedOption::clear_has_negative_int_value() { + _has_bits_[0] &= ~0x00000008u; } inline void UninterpretedOption::clear_negative_int_value() { negative_int_value_ = GOOGLE_LONGLONG(0); - _clear_bit(3); + clear_has_negative_int_value(); } inline ::google::protobuf::int64 UninterpretedOption::negative_int_value() const { return negative_int_value_; } inline void UninterpretedOption::set_negative_int_value(::google::protobuf::int64 value) { - _set_bit(3); + set_has_negative_int_value(); negative_int_value_ = value; } // optional double double_value = 6; inline bool UninterpretedOption::has_double_value() const { - return _has_bit(4); + return (_has_bits_[0] & 0x00000010u) != 0; +} +inline void UninterpretedOption::set_has_double_value() { + _has_bits_[0] |= 0x00000010u; +} +inline void UninterpretedOption::clear_has_double_value() { + _has_bits_[0] &= ~0x00000010u; } inline void UninterpretedOption::clear_double_value() { double_value_ = 0; - _clear_bit(4); + clear_has_double_value(); } inline double UninterpretedOption::double_value() const { return double_value_; } inline void UninterpretedOption::set_double_value(double value) { - _set_bit(4); + set_has_double_value(); double_value_ = value; } // optional bytes string_value = 7; inline bool UninterpretedOption::has_string_value() const { - return _has_bit(5); + return (_has_bits_[0] & 0x00000020u) != 0; +} +inline void UninterpretedOption::set_has_string_value() { + _has_bits_[0] |= 0x00000020u; +} +inline void UninterpretedOption::clear_has_string_value() { + _has_bits_[0] &= ~0x00000020u; } inline void UninterpretedOption::clear_string_value() { - if (string_value_ != &_default_string_value_) { + if (string_value_ != &::google::protobuf::internal::kEmptyString) { string_value_->clear(); } - _clear_bit(5); + clear_has_string_value(); } inline const ::std::string& UninterpretedOption::string_value() const { return *string_value_; } inline void UninterpretedOption::set_string_value(const ::std::string& value) { - _set_bit(5); - if (string_value_ == &_default_string_value_) { + set_has_string_value(); + if (string_value_ == &::google::protobuf::internal::kEmptyString) { string_value_ = new ::std::string; } string_value_->assign(value); } inline void UninterpretedOption::set_string_value(const char* value) { - _set_bit(5); - if (string_value_ == &_default_string_value_) { + set_has_string_value(); + if (string_value_ == &::google::protobuf::internal::kEmptyString) { string_value_ = new ::std::string; } string_value_->assign(value); } inline void UninterpretedOption::set_string_value(const void* value, size_t size) { - _set_bit(5); - if (string_value_ == &_default_string_value_) { + set_has_string_value(); + if (string_value_ == &::google::protobuf::internal::kEmptyString) { string_value_ = new ::std::string; } string_value_->assign(reinterpret_cast<const char*>(value), size); } inline ::std::string* UninterpretedOption::mutable_string_value() { - _set_bit(5); - if (string_value_ == &_default_string_value_) { + set_has_string_value(); + if (string_value_ == &::google::protobuf::internal::kEmptyString) { string_value_ = new ::std::string; } return string_value_; } +inline ::std::string* UninterpretedOption::release_string_value() { + clear_has_string_value(); + if (string_value_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = string_value_; + string_value_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} + +// optional string aggregate_value = 8; +inline bool UninterpretedOption::has_aggregate_value() const { + return (_has_bits_[0] & 0x00000040u) != 0; +} +inline void UninterpretedOption::set_has_aggregate_value() { + _has_bits_[0] |= 0x00000040u; +} +inline void UninterpretedOption::clear_has_aggregate_value() { + _has_bits_[0] &= ~0x00000040u; +} +inline void UninterpretedOption::clear_aggregate_value() { + if (aggregate_value_ != &::google::protobuf::internal::kEmptyString) { + aggregate_value_->clear(); + } + clear_has_aggregate_value(); +} +inline const ::std::string& UninterpretedOption::aggregate_value() const { + return *aggregate_value_; +} +inline void UninterpretedOption::set_aggregate_value(const ::std::string& value) { + set_has_aggregate_value(); + if (aggregate_value_ == &::google::protobuf::internal::kEmptyString) { + aggregate_value_ = new ::std::string; + } + aggregate_value_->assign(value); +} +inline void UninterpretedOption::set_aggregate_value(const char* value) { + set_has_aggregate_value(); + if (aggregate_value_ == &::google::protobuf::internal::kEmptyString) { + aggregate_value_ = new ::std::string; + } + aggregate_value_->assign(value); +} +inline void UninterpretedOption::set_aggregate_value(const char* value, size_t size) { + set_has_aggregate_value(); + if (aggregate_value_ == &::google::protobuf::internal::kEmptyString) { + aggregate_value_ = new ::std::string; + } + aggregate_value_->assign(reinterpret_cast<const char*>(value), size); +} +inline ::std::string* UninterpretedOption::mutable_aggregate_value() { + set_has_aggregate_value(); + if (aggregate_value_ == &::google::protobuf::internal::kEmptyString) { + aggregate_value_ = new ::std::string; + } + return aggregate_value_; +} +inline ::std::string* UninterpretedOption::release_aggregate_value() { + clear_has_aggregate_value(); + if (aggregate_value_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = aggregate_value_; + aggregate_value_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} + +// ------------------------------------------------------------------- + +// SourceCodeInfo_Location + +// repeated int32 path = 1 [packed = true]; +inline int SourceCodeInfo_Location::path_size() const { + return path_.size(); +} +inline void SourceCodeInfo_Location::clear_path() { + path_.Clear(); +} +inline ::google::protobuf::int32 SourceCodeInfo_Location::path(int index) const { + return path_.Get(index); +} +inline void SourceCodeInfo_Location::set_path(int index, ::google::protobuf::int32 value) { + path_.Set(index, value); +} +inline void SourceCodeInfo_Location::add_path(::google::protobuf::int32 value) { + path_.Add(value); +} +inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >& +SourceCodeInfo_Location::path() const { + return path_; +} +inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >* +SourceCodeInfo_Location::mutable_path() { + return &path_; +} + +// repeated int32 span = 2 [packed = true]; +inline int SourceCodeInfo_Location::span_size() const { + return span_.size(); +} +inline void SourceCodeInfo_Location::clear_span() { + span_.Clear(); +} +inline ::google::protobuf::int32 SourceCodeInfo_Location::span(int index) const { + return span_.Get(index); +} +inline void SourceCodeInfo_Location::set_span(int index, ::google::protobuf::int32 value) { + span_.Set(index, value); +} +inline void SourceCodeInfo_Location::add_span(::google::protobuf::int32 value) { + span_.Add(value); +} +inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >& +SourceCodeInfo_Location::span() const { + return span_; +} +inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >* +SourceCodeInfo_Location::mutable_span() { + return &span_; +} + +// ------------------------------------------------------------------- + +// SourceCodeInfo + +// repeated .google.protobuf.SourceCodeInfo.Location location = 1; +inline int SourceCodeInfo::location_size() const { + return location_.size(); +} +inline void SourceCodeInfo::clear_location() { + location_.Clear(); +} +inline const ::google::protobuf::SourceCodeInfo_Location& SourceCodeInfo::location(int index) const { + return location_.Get(index); +} +inline ::google::protobuf::SourceCodeInfo_Location* SourceCodeInfo::mutable_location(int index) { + return location_.Mutable(index); +} +inline ::google::protobuf::SourceCodeInfo_Location* SourceCodeInfo::add_location() { + return location_.Add(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >& +SourceCodeInfo::location() const { + return location_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >* +SourceCodeInfo::mutable_location() { + return &location_; +} // @@protoc_insertion_point(namespace_scope) diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto index cc04aa8e..233f8794 100644 --- a/src/google/protobuf/descriptor.proto +++ b/src/google/protobuf/descriptor.proto @@ -67,6 +67,12 @@ message FileDescriptorProto { repeated FieldDescriptorProto extension = 7; optional FileOptions options = 8; + + // This field contains optional information about the original source code. + // You may safely remove this entire field whithout harming runtime + // functionality of the descriptors -- the information is needed only by + // development tools. + optional SourceCodeInfo source_code_info = 9; } // Describes a message type. @@ -245,6 +251,12 @@ message FileOptions { // top-level extensions defined in the file. optional bool java_multiple_files = 10 [default=false]; + // If set true, then the Java code generator will generate equals() and + // hashCode() methods for all messages defined in the .proto file. This is + // purely a speed optimization, as the AbstractMessage base class includes + // reflection-based implementations of these methods. + optional bool java_generate_equals_and_hash = 20 [default=false]; + // Generated classes can be optimized for speed or code size. enum OptimizeMode { SPEED = 1; // Generate complete code for parsing, serialization, @@ -264,13 +276,12 @@ message FileOptions { // early versions of proto2. // // Generic services are now considered deprecated in favor of using plugins - // that generate code specific to your particular RPC system. If you are - // using such a plugin, set these to false. In the future, we may change - // the default to false, so if you explicitly want generic services, you - // should explicitly set these to true. - optional bool cc_generic_services = 16 [default=true]; - optional bool java_generic_services = 17 [default=true]; - optional bool py_generic_services = 18 [default=true]; + // that generate code specific to your particular RPC system. Therefore, + // these default to false. Old code which depends on generic services should + // explicitly set them to true. + optional bool cc_generic_services = 16 [default=false]; + optional bool java_generic_services = 17 [default=false]; + optional bool py_generic_services = 18 [default=false]; // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; @@ -430,4 +441,93 @@ message UninterpretedOption { optional int64 negative_int_value = 5; optional double double_value = 6; optional bytes string_value = 7; + optional string aggregate_value = 8; +} + +// =================================================================== +// Optional source code info + +// Encapsulates information about the original source file from which a +// FileDescriptorProto was generated. +message SourceCodeInfo { + // A Location identifies a piece of source code in a .proto file which + // corresponds to a particular definition. This information is intended + // to be useful to IDEs, code indexers, documentation generators, and similar + // tools. + // + // For example, say we have a file like: + // message Foo { + // optional string foo = 1; + // } + // Let's look at just the field definition: + // optional string foo = 1; + // ^ ^^ ^^ ^ ^^^ + // a bc de f ghi + // We have the following locations: + // span path represents + // [a,i) [ 4, 0, 2, 0 ] The whole field definition. + // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). + // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). + // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). + // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). + // + // Notes: + // - A location may refer to a repeated field itself (i.e. not to any + // particular index within it). This is used whenever a set of elements are + // logically enclosed in a single code segment. For example, an entire + // extend block (possibly containing multiple extension definitions) will + // have an outer location whose path refers to the "extensions" repeated + // field without an index. + // - Multiple locations may have the same path. This happens when a single + // logical declaration is spread out across multiple places. The most + // obvious example is the "extend" block again -- there may be multiple + // extend blocks in the same scope, each of which will have the same path. + // - A location's span is not always a subset of its parent's span. For + // example, the "extendee" of an extension declaration appears at the + // beginning of the "extend" block and is shared by all extensions within + // the block. + // - Just because a location's span is a subset of some other location's span + // does not mean that it is a descendent. For example, a "group" defines + // both a type and a field in a single declaration. Thus, the locations + // corresponding to the type and field and their components will overlap. + // - Code which tries to interpret locations should probably be designed to + // ignore those that it doesn't understand, as more types of locations could + // be recorded in the future. + repeated Location location = 1; + message Location { + // Identifies which part of the FileDescriptorProto was defined at this + // location. + // + // Each element is a field number or an index. They form a path from + // the root FileDescriptorProto to the place where the definition. For + // example, this path: + // [ 4, 3, 2, 7, 1 ] + // refers to: + // file.message_type(3) // 4, 3 + // .field(7) // 2, 7 + // .name() // 1 + // This is because FileDescriptorProto.message_type has field number 4: + // repeated DescriptorProto message_type = 4; + // and DescriptorProto.field has field number 2: + // repeated FieldDescriptorProto field = 2; + // and FieldDescriptorProto.name has field number 1: + // optional string name = 1; + // + // Thus, the above path gives the location of a field name. If we removed + // the last element: + // [ 4, 3, 2, 7 ] + // this path refers to the whole field declaration (from the beginning + // of the label to the terminating semicolon). + repeated int32 path = 1 [packed=true]; + + // Always has exactly three or four elements: start line, start column, + // end line (optional, otherwise assumed same as start line), end column. + // These are packed into a single field for efficiency. Note that line + // and column numbers are zero-based -- typically you will want to add + // 1 to each before displaying to a user. + repeated int32 span = 2 [packed=true]; + + // TODO(kenton): Record comments appearing before and after the + // declaration. + } } diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc index d70c9bf1..55aebfd1 100644 --- a/src/google/protobuf/descriptor_unittest.cc +++ b/src/google/protobuf/descriptor_unittest.cc @@ -2212,6 +2212,45 @@ TEST(CustomOptions, MessageOptionThreeFieldsSet) { EXPECT_EQ(1234, options.GetExtension(protobuf_unittest::complex_opt1).foo()); } +// Check that aggregate options were parsed and saved correctly in +// the appropriate descriptors. +TEST(CustomOptions, AggregateOptions) { + const Descriptor* msg = protobuf_unittest::AggregateMessage::descriptor(); + const FileDescriptor* file = msg->file(); + const FieldDescriptor* field = msg->FindFieldByName("fieldname"); + const EnumDescriptor* enumd = file->FindEnumTypeByName("AggregateEnum"); + const EnumValueDescriptor* enumv = enumd->FindValueByName("VALUE"); + const ServiceDescriptor* service = file->FindServiceByName( + "AggregateService"); + const MethodDescriptor* method = service->FindMethodByName("Method"); + + // Tests for the different types of data embedded in fileopt + const protobuf_unittest::Aggregate& file_options = + file->options().GetExtension(protobuf_unittest::fileopt); + EXPECT_EQ(100, file_options.i()); + EXPECT_EQ("FileAnnotation", file_options.s()); + EXPECT_EQ("NestedFileAnnotation", file_options.sub().s()); + EXPECT_EQ("FileExtensionAnnotation", + file_options.file().GetExtension(protobuf_unittest::fileopt).s()); + EXPECT_EQ("EmbeddedMessageSetElement", + file_options.mset().GetExtension( + protobuf_unittest::AggregateMessageSetElement + ::message_set_extension).s()); + + // Simple tests for all the other types of annotations + EXPECT_EQ("MessageAnnotation", + msg->options().GetExtension(protobuf_unittest::msgopt).s()); + EXPECT_EQ("FieldAnnotation", + field->options().GetExtension(protobuf_unittest::fieldopt).s()); + EXPECT_EQ("EnumAnnotation", + enumd->options().GetExtension(protobuf_unittest::enumopt).s()); + EXPECT_EQ("EnumValueAnnotation", + enumv->options().GetExtension(protobuf_unittest::enumvalopt).s()); + EXPECT_EQ("ServiceAnnotation", + service->options().GetExtension(protobuf_unittest::serviceopt).s()); + EXPECT_EQ("MethodAnnotation", + method->options().GetExtension(protobuf_unittest::methodopt).s()); +} // =================================================================== @@ -3425,25 +3464,50 @@ TEST_F(ValidationErrorTest, StringOptionValueIsNotString) { "string option \"foo\".\n"); } -TEST_F(ValidationErrorTest, TryingToSetMessageValuedOption) { +// Helper function for tests that check for aggregate value parsing +// errors. The "value" argument is embedded inside the +// "uninterpreted_option" portion of the result. +static string EmbedAggregateValue(const char* value) { + return strings::Substitute( + "name: \"foo.proto\" " + "dependency: \"google/protobuf/descriptor.proto\" " + "message_type { name: \"Foo\" } " + "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL " + " type: TYPE_MESSAGE type_name: \"Foo\" " + " extendee: \"google.protobuf.FileOptions\" }" + "options { uninterpreted_option { name { name_part: \"foo\" " + " is_extension: true } " + " $0 } }", + value); +} + +TEST_F(ValidationErrorTest, AggregateValueNotFound) { BuildDescriptorMessagesInTestPool(); BuildFileWithErrors( - "name: \"foo.proto\" " - "dependency: \"google/protobuf/descriptor.proto\" " - "message_type { " - " name: \"TestMessage\" " - " field { name:\"baz\" number:1 label:LABEL_OPTIONAL type:TYPE_STRING }" - "}" - "extension { name: \"bar\" number: 7672757 label: LABEL_OPTIONAL " - " type: TYPE_MESSAGE type_name: \"TestMessage\" " - " extendee: \"google.protobuf.FileOptions\" }" - "options { uninterpreted_option { name { name_part: \"bar\" " - " is_extension: true } " - " identifier_value: \"QUUX\" } }", + EmbedAggregateValue("string_value: \"\""), + "foo.proto: foo.proto: OPTION_VALUE: Option \"foo\" is a message. " + "To set the entire message, use syntax like " + "\"foo = { <proto text format> }\". To set fields within it, use " + "syntax like \"foo.foo = value\".\n"); +} - "foo.proto: foo.proto: OPTION_NAME: Option field \"(bar)\" cannot be of " - "message type.\n"); +TEST_F(ValidationErrorTest, AggregateValueParseError) { + BuildDescriptorMessagesInTestPool(); + + BuildFileWithErrors( + EmbedAggregateValue("aggregate_value: \"1+2\""), + "foo.proto: foo.proto: OPTION_VALUE: Error while parsing option " + "value for \"foo\": Expected identifier.\n"); +} + +TEST_F(ValidationErrorTest, AggregateValueUnknownFields) { + BuildDescriptorMessagesInTestPool(); + + BuildFileWithErrors( + EmbedAggregateValue("aggregate_value: \"x:100\""), + "foo.proto: foo.proto: OPTION_VALUE: Error while parsing option " + "value for \"foo\": Message type \"Foo\" has no field named \"x\".\n"); } TEST_F(ValidationErrorTest, NotLiteImportsLite) { @@ -3483,7 +3547,11 @@ TEST_F(ValidationErrorTest, LiteExtendsNotLite) { TEST_F(ValidationErrorTest, NoLiteServices) { BuildFileWithErrors( "name: \"foo.proto\" " - "options { optimize_for: LITE_RUNTIME } " + "options {" + " optimize_for: LITE_RUNTIME" + " cc_generic_services: true" + " java_generic_services: true" + "} " "service { name: \"Foo\" }", "foo.proto: Foo: NAME: Files with optimize_for = LITE_RUNTIME cannot " @@ -3497,7 +3565,7 @@ TEST_F(ValidationErrorTest, NoLiteServices) { " cc_generic_services: false" " java_generic_services: false" "} " - "service { name: \"Foo\" }"); + "service { name: \"Bar\" }"); } TEST_F(ValidationErrorTest, RollbackAfterError) { diff --git a/src/google/protobuf/extension_set.cc b/src/google/protobuf/extension_set.cc index 6084885b..1a6f5dfc 100644 --- a/src/google/protobuf/extension_set.cc +++ b/src/google/protobuf/extension_set.cc @@ -186,6 +186,18 @@ int ExtensionSet::ExtensionSize(int number) const { return iter->second.GetSize(); } +FieldType ExtensionSet::ExtensionType(int number) const { + map<int, Extension>::const_iterator iter = extensions_.find(number); + if (iter == extensions_.end()) { + GOOGLE_LOG(DFATAL) << "Don't lookup extension types if they aren't present (1). "; + return 0; + } + if (iter->second.is_cleared) { + GOOGLE_LOG(DFATAL) << "Don't lookup extension types if they aren't present (2). "; + } + return iter->second.type; +} + void ExtensionSet::ClearExtension(int number) { map<int, Extension>::iterator iter = extensions_.find(number); if (iter == extensions_.end()) return; diff --git a/src/google/protobuf/extension_set.h b/src/google/protobuf/extension_set.h index 14d5d150..ac1ada02 100644 --- a/src/google/protobuf/extension_set.h +++ b/src/google/protobuf/extension_set.h @@ -39,11 +39,11 @@ #define GOOGLE_PROTOBUF_EXTENSION_SET_H__ #include <vector> -#include <stack> #include <map> #include <utility> #include <string> + #include <google/protobuf/stubs/common.h> namespace google { @@ -214,6 +214,7 @@ class LIBPROTOBUF_EXPORT ExtensionSet { bool Has(int number) const; int ExtensionSize(int number) const; // Size of a repeated extension. + FieldType ExtensionType(int number) const; void ClearExtension(int number); // singular fields ------------------------------------------------- @@ -451,6 +452,7 @@ class LIBPROTOBUF_EXPORT ExtensionSet { int SpaceUsedExcludingSelf() const; }; + // Gets the extension with the given number, creating it if it does not // already exist. Returns true if the extension did not already exist. bool MaybeNewExtension(int number, const FieldDescriptor* descriptor, diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc index 0f065ff2..a0dd75c6 100644 --- a/src/google/protobuf/generated_message_reflection.cc +++ b/src/google/protobuf/generated_message_reflection.cc @@ -45,8 +45,6 @@ namespace google { namespace protobuf { namespace internal { -namespace { const string kEmptyString; } - int StringSpaceUsedExcludingSelf(const string& str) { const void* start = &str; const void* end = &str + 1; @@ -69,7 +67,6 @@ bool ParseNamedEnum(const EnumDescriptor* descriptor, } const string& NameOfEnum(const EnumDescriptor* descriptor, int value) { - static string kEmptyString; const EnumValueDescriptor* d = descriptor->FindValueByNumber(value); return (d == NULL ? kEmptyString : d->name()); } diff --git a/src/google/protobuf/generated_message_util.cc b/src/google/protobuf/generated_message_util.cc index 7ac015d0..76e547bb 100644 --- a/src/google/protobuf/generated_message_util.cc +++ b/src/google/protobuf/generated_message_util.cc @@ -47,6 +47,8 @@ double NaN() { return std::numeric_limits<double>::quiet_NaN(); } +const ::std::string kEmptyString; + } // namespace internal } // namespace protobuf diff --git a/src/google/protobuf/generated_message_util.h b/src/google/protobuf/generated_message_util.h index daa16f77..1a2343d4 100644 --- a/src/google/protobuf/generated_message_util.h +++ b/src/google/protobuf/generated_message_util.h @@ -38,6 +38,8 @@ #ifndef GOOGLE_PROTOBUF_GENERATED_MESSAGE_UTIL_H__ #define GOOGLE_PROTOBUF_GENERATED_MESSAGE_UTIL_H__ +#include <string> + #include <google/protobuf/stubs/common.h> @@ -66,8 +68,11 @@ namespace internal { // Constants for special floating point values. -LIBPROTOBUF_EXPORT double Infinity(); -LIBPROTOBUF_EXPORT double NaN(); +double Infinity(); +double NaN(); + +// Constant used for empty default strings. +extern const ::std::string kEmptyString; } // namespace internal diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc index 6a91a13d..57d486f9 100644 --- a/src/google/protobuf/io/coded_stream.cc +++ b/src/google/protobuf/io/coded_stream.cc @@ -56,6 +56,15 @@ static const int kMaxVarintBytes = 10; static const int kMaxVarint32Bytes = 5; +inline bool NextNonEmpty(ZeroCopyInputStream* input, + const void** data, int* size) { + bool success; + do { + success = input->Next(data, size); + } while (success && *size == 0); + return success; +} + } // namespace // CodedInputStream ================================================== @@ -489,7 +498,7 @@ bool CodedInputStream::Refresh() { const void* void_buffer; int buffer_size; - if (input_->Next(&void_buffer, &buffer_size)) { + if (NextNonEmpty(input_, &void_buffer, &buffer_size)) { buffer_ = reinterpret_cast<const uint8*>(void_buffer); buffer_end_ = buffer_ + buffer_size; GOOGLE_CHECK_GE(buffer_size, 0); diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h index 93effc87..73a54ddd 100644 --- a/src/google/protobuf/io/coded_stream.h +++ b/src/google/protobuf/io/coded_stream.h @@ -129,8 +129,8 @@ #endif #include <google/protobuf/stubs/common.h> -namespace google { +namespace google { namespace protobuf { class DescriptorPool; @@ -782,7 +782,8 @@ inline const uint8* CodedInputStream::ReadLittleEndian64FromArray( } inline bool CodedInputStream::ReadLittleEndian32(uint32* value) { -#if defined(PROTOBUF_LITTLE_ENDIAN) +#if !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST) && \ + defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN if (GOOGLE_PREDICT_TRUE(BufferSize() >= static_cast<int>(sizeof(*value)))) { memcpy(value, buffer_, sizeof(*value)); Advance(sizeof(*value)); @@ -796,7 +797,8 @@ inline bool CodedInputStream::ReadLittleEndian32(uint32* value) { } inline bool CodedInputStream::ReadLittleEndian64(uint64* value) { -#if defined(PROTOBUF_LITTLE_ENDIAN) +#if !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST) && \ + defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN if (GOOGLE_PREDICT_TRUE(BufferSize() >= static_cast<int>(sizeof(*value)))) { memcpy(value, buffer_, sizeof(*value)); Advance(sizeof(*value)); @@ -1093,10 +1095,10 @@ inline CodedInputStream::~CodedInputStream() { } // namespace io } // namespace protobuf -} // namespace google #if defined(_MSC_VER) && _MSC_VER >= 1300 #pragma runtime_checks("c", restore) #endif // _MSC_VER +} // namespace google #endif // GOOGLE_PROTOBUF_IO_CODED_STREAM_H__ diff --git a/src/google/protobuf/io/coded_stream_unittest.cc b/src/google/protobuf/io/coded_stream_unittest.cc index 7d298332..ff268ab9 100644 --- a/src/google/protobuf/io/coded_stream_unittest.cc +++ b/src/google/protobuf/io/coded_stream_unittest.cc @@ -208,6 +208,33 @@ TEST_2D(CodedStreamTest, ReadTag, kVarintCases, kBlockSizes) { EXPECT_EQ(kVarintCases_case.size, input.ByteCount()); } +// This is the regression test that verifies that there is no issues +// with the empty input buffers handling. +TEST_F(CodedStreamTest, EmptyInputBeforeEos) { + class In : public ZeroCopyInputStream { + public: + In() : count_(0) {} + private: + virtual bool Next(const void** data, int* size) { + *data = NULL; + *size = 0; + return count_++ < 2; + } + virtual void BackUp(int count) { + GOOGLE_LOG(FATAL) << "Tests never call this."; + } + virtual bool Skip(int count) { + GOOGLE_LOG(FATAL) << "Tests never call this."; + return false; + } + virtual int64 ByteCount() const { return 0; } + int count_; + } in; + CodedInputStream input(&in); + input.ReadTag(); + EXPECT_TRUE(input.ConsumedEntireMessage()); +} + TEST_1D(CodedStreamTest, ExpectTag, kVarintCases) { // Leave one byte at the beginning of the buffer so we can read it // to force the first buffer to be loaded. @@ -995,6 +1022,7 @@ TEST_F(CodedStreamTest, TotalBytesLimitNotValidMessageEnd) { EXPECT_FALSE(coded_input.ConsumedEntireMessage()); } + TEST_F(CodedStreamTest, RecursionLimit) { ArrayInputStream input(buffer_, sizeof(buffer_)); CodedInputStream coded_input(&input); diff --git a/src/google/protobuf/io/gzip_stream.cc b/src/google/protobuf/io/gzip_stream.cc index 84d277f4..0f1ff872 100644 --- a/src/google/protobuf/io/gzip_stream.cc +++ b/src/google/protobuf/io/gzip_stream.cc @@ -73,6 +73,17 @@ GzipInputStream::~GzipInputStream() { zerror_ = inflateEnd(&zcontext_); } +static inline int internalInflateInit2( + z_stream* zcontext, GzipInputStream::Format format) { + int windowBitsFormat = 0; + switch (format) { + case GzipInputStream::GZIP: windowBitsFormat = 16; break; + case GzipInputStream::AUTO: windowBitsFormat = 32; break; + case GzipInputStream::ZLIB: windowBitsFormat = 0; break; + } + return inflateInit2(zcontext, /* windowBits */15 | windowBitsFormat); +} + int GzipInputStream::Inflate(int flush) { if ((zerror_ == Z_OK) && (zcontext_.avail_out == 0)) { // previous inflate filled output buffer. don't change input params yet. @@ -89,14 +100,7 @@ int GzipInputStream::Inflate(int flush) { zcontext_.next_in = static_cast<Bytef*>(const_cast<void*>(in)); zcontext_.avail_in = in_size; if (first) { - int windowBitsFormat = 0; - switch (format_) { - case GZIP: windowBitsFormat = 16; break; - case AUTO: windowBitsFormat = 32; break; - case ZLIB: windowBitsFormat = 0; break; - } - int error = inflateInit2(&zcontext_, - /* windowBits */15 | windowBitsFormat); + int error = internalInflateInit2(&zcontext_, format_); if (error != Z_OK) { return error; } @@ -127,9 +131,21 @@ bool GzipInputStream::Next(const void** data, int* size) { return true; } if (zerror_ == Z_STREAM_END) { - *data = NULL; - *size = 0; - return false; + if (zcontext_.next_out != NULL) { + // sub_stream_ may have concatenated streams to follow + zerror_ = inflateEnd(&zcontext_); + if (zerror_ != Z_OK) { + return false; + } + zerror_ = internalInflateInit2(&zcontext_, format_); + if (zerror_ != Z_OK) { + return false; + } + } else { + *data = NULL; + *size = 0; + return false; + } } zerror_ = Inflate(Z_NO_FLUSH); if ((zerror_ == Z_STREAM_END) && (zcontext_.next_out == NULL)) { @@ -251,8 +267,7 @@ int GzipOutputStream::Deflate(int flush) { } error = deflate(&zcontext_, flush); } while (error == Z_OK && zcontext_.avail_out == 0); - if (((flush == Z_FULL_FLUSH) || (flush == Z_FINISH)) - && (zcontext_.avail_out != sub_data_size_)) { + if ((flush == Z_FULL_FLUSH) || (flush == Z_FINISH)) { // Notify lower layer of data. sub_stream_->BackUp(zcontext_.avail_out); // We don't own the buffer anymore. diff --git a/src/google/protobuf/io/printer.cc b/src/google/protobuf/io/printer.cc index c7d3074d..9ab90dee 100644 --- a/src/google/protobuf/io/printer.cc +++ b/src/google/protobuf/io/printer.cc @@ -132,6 +132,17 @@ void Printer::Print(const char* text, Print(vars, text); } +void Printer::Print(const char* text, + const char* variable1, const string& value1, + const char* variable2, const string& value2, + const char* variable3, const string& value3) { + map<string, string> vars; + vars[variable1] = value1; + vars[variable2] = value2; + vars[variable3] = value3; + Print(vars, text); +} + void Printer::Indent() { indent_ += " "; } diff --git a/src/google/protobuf/io/printer.h b/src/google/protobuf/io/printer.h index de085389..5be48543 100644 --- a/src/google/protobuf/io/printer.h +++ b/src/google/protobuf/io/printer.h @@ -82,7 +82,11 @@ class LIBPROTOBUF_EXPORT Printer { // Like the first Print(), except the substitutions are given as parameters. void Print(const char* text, const char* variable1, const string& value1, const char* variable2, const string& value2); - // TODO(kenton): Overloaded versions with more variables? Two seems + // Like the first Print(), except the substitutions are given as parameters. + void Print(const char* text, const char* variable1, const string& value1, + const char* variable2, const string& value2, + const char* variable3, const string& value3); + // TODO(kenton): Overloaded versions with more variables? Three seems // to be enough. // Indent text by two spaces. After calling Indent(), two spaces will be diff --git a/src/google/protobuf/io/tokenizer.cc b/src/google/protobuf/io/tokenizer.cc index 38fa351c..513831d5 100644 --- a/src/google/protobuf/io/tokenizer.cc +++ b/src/google/protobuf/io/tokenizer.cc @@ -193,6 +193,7 @@ Tokenizer::Tokenizer(ZeroCopyInputStream* input, current_.line = 0; current_.column = 0; + current_.end_column = 0; current_.type = TYPE_START; Refresh(); @@ -277,6 +278,7 @@ inline void Tokenizer::EndToken() { current_.text.append(buffer_ + token_start_, buffer_pos_ - token_start_); } token_start_ = -1; + current_.end_column = column_; } // ------------------------------------------------------------------- @@ -462,7 +464,7 @@ void Tokenizer::ConsumeBlockComment() { // ------------------------------------------------------------------- bool Tokenizer::Next() { - TokenType last_token_type = current_.type; + previous_ = current_; // Did we skip any characters after the last token? bool skipped_stuff = false; @@ -517,7 +519,7 @@ bool Tokenizer::Next() { if (TryConsumeOne<Digit>()) { // It's a floating-point number. - if (last_token_type == TYPE_IDENTIFIER && !skipped_stuff) { + if (previous_.type == TYPE_IDENTIFIER && !skipped_stuff) { // We don't accept syntax like "blah.123". error_collector_->AddError(line_, column_ - 2, "Need space between identifier and decimal point."); @@ -551,6 +553,7 @@ bool Tokenizer::Next() { current_.text.clear(); current_.line = line_; current_.column = column_; + current_.end_column = column_; return false; } diff --git a/src/google/protobuf/io/tokenizer.h b/src/google/protobuf/io/tokenizer.h index d115161f..8f759abb 100644 --- a/src/google/protobuf/io/tokenizer.h +++ b/src/google/protobuf/io/tokenizer.h @@ -122,12 +122,17 @@ class LIBPROTOBUF_EXPORT Tokenizer { // the token within the input stream. They are zero-based. int line; int column; + int end_column; }; // Get the current token. This is updated when Next() is called. Before // the first call to Next(), current() has type TYPE_START and no contents. const Token& current(); + // Return the previous token -- i.e. what current() returned before the + // previous call to Next(). + const Token& previous(); + // Advance to the next token. Returns false if the end of the input is // reached. bool Next(); @@ -180,6 +185,7 @@ class LIBPROTOBUF_EXPORT Tokenizer { GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Tokenizer); Token current_; // Returned by current(). + Token previous_; // Returned by previous(). ZeroCopyInputStream* input_; ErrorCollector* error_collector_; @@ -291,6 +297,10 @@ inline const Tokenizer::Token& Tokenizer::current() { return current_; } +inline const Tokenizer::Token& Tokenizer::previous() { + return previous_; +} + inline void Tokenizer::ParseString(const string& text, string* output) { output->clear(); ParseStringAppend(text, output); diff --git a/src/google/protobuf/io/tokenizer_unittest.cc b/src/google/protobuf/io/tokenizer_unittest.cc index 358ec567..106d080f 100644 --- a/src/google/protobuf/io/tokenizer_unittest.cc +++ b/src/google/protobuf/io/tokenizer_unittest.cc @@ -257,6 +257,7 @@ TEST_2D(TokenizerTest, SimpleTokens, kSimpleTokenCases, kBlockSizes) { EXPECT_EQ("", tokenizer.current().text); EXPECT_EQ(0, tokenizer.current().line); EXPECT_EQ(0, tokenizer.current().column); + EXPECT_EQ(0, tokenizer.current().end_column); // Parse the token. ASSERT_TRUE(tokenizer.Next()); @@ -268,6 +269,8 @@ TEST_2D(TokenizerTest, SimpleTokens, kSimpleTokenCases, kBlockSizes) { // Check that it is located at the beginning of the input EXPECT_EQ(0, tokenizer.current().line); EXPECT_EQ(0, tokenizer.current().column); + EXPECT_EQ(kSimpleTokenCases_case.input.size(), + tokenizer.current().end_column); // There should be no more input. EXPECT_FALSE(tokenizer.Next()); @@ -277,6 +280,8 @@ TEST_2D(TokenizerTest, SimpleTokens, kSimpleTokenCases, kBlockSizes) { EXPECT_EQ("", tokenizer.current().text); EXPECT_EQ(0, tokenizer.current().line); EXPECT_EQ(kSimpleTokenCases_case.input.size(), tokenizer.current().column); + EXPECT_EQ(kSimpleTokenCases_case.input.size(), + tokenizer.current().end_column); // There should be no errors. EXPECT_TRUE(error_collector.text_.empty()); @@ -339,76 +344,83 @@ MultiTokenCase kMultiTokenCases[] = { // Test all token types at the same time. { "foo 1 1.2 + 'bar'", { - { Tokenizer::TYPE_IDENTIFIER, "foo" , 0, 0 }, - { Tokenizer::TYPE_INTEGER , "1" , 0, 4 }, - { Tokenizer::TYPE_FLOAT , "1.2" , 0, 6 }, - { Tokenizer::TYPE_SYMBOL , "+" , 0, 10 }, - { Tokenizer::TYPE_STRING , "'bar'", 0, 12 }, - { Tokenizer::TYPE_END , "" , 0, 17 }, + { Tokenizer::TYPE_IDENTIFIER, "foo" , 0, 0, 3 }, + { Tokenizer::TYPE_INTEGER , "1" , 0, 4, 5 }, + { Tokenizer::TYPE_FLOAT , "1.2" , 0, 6, 9 }, + { Tokenizer::TYPE_SYMBOL , "+" , 0, 10, 11 }, + { Tokenizer::TYPE_STRING , "'bar'", 0, 12, 17 }, + { Tokenizer::TYPE_END , "" , 0, 17, 17 }, }}, // Test that consecutive symbols are parsed as separate tokens. { "!@+%", { - { Tokenizer::TYPE_SYMBOL , "!" , 0, 0 }, - { Tokenizer::TYPE_SYMBOL , "@" , 0, 1 }, - { Tokenizer::TYPE_SYMBOL , "+" , 0, 2 }, - { Tokenizer::TYPE_SYMBOL , "%" , 0, 3 }, - { Tokenizer::TYPE_END , "" , 0, 4 }, + { Tokenizer::TYPE_SYMBOL , "!" , 0, 0, 1 }, + { Tokenizer::TYPE_SYMBOL , "@" , 0, 1, 2 }, + { Tokenizer::TYPE_SYMBOL , "+" , 0, 2, 3 }, + { Tokenizer::TYPE_SYMBOL , "%" , 0, 3, 4 }, + { Tokenizer::TYPE_END , "" , 0, 4, 4 }, }}, // Test that newlines affect line numbers correctly. { "foo bar\nrab oof", { - { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, - { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 4 }, - { Tokenizer::TYPE_IDENTIFIER, "rab", 1, 0 }, - { Tokenizer::TYPE_IDENTIFIER, "oof", 1, 4 }, - { Tokenizer::TYPE_END , "" , 1, 7 }, + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0, 3 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 4, 7 }, + { Tokenizer::TYPE_IDENTIFIER, "rab", 1, 0, 3 }, + { Tokenizer::TYPE_IDENTIFIER, "oof", 1, 4, 7 }, + { Tokenizer::TYPE_END , "" , 1, 7, 7 }, }}, // Test that tabs affect column numbers correctly. { "foo\tbar \tbaz", { - { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, - { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 8 }, - { Tokenizer::TYPE_IDENTIFIER, "baz", 0, 16 }, - { Tokenizer::TYPE_END , "" , 0, 19 }, + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0, 3 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 8, 11 }, + { Tokenizer::TYPE_IDENTIFIER, "baz", 0, 16, 19 }, + { Tokenizer::TYPE_END , "" , 0, 19, 19 }, + }}, + + // Test that tabs in string literals affect column numbers correctly. + { "\"foo\tbar\" baz", { + { Tokenizer::TYPE_STRING , "\"foo\tbar\"", 0, 0, 12 }, + { Tokenizer::TYPE_IDENTIFIER, "baz" , 0, 13, 16 }, + { Tokenizer::TYPE_END , "" , 0, 16, 16 }, }}, // Test that line comments are ignored. { "foo // This is a comment\n" "bar // This is another comment", { - { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, - { Tokenizer::TYPE_IDENTIFIER, "bar", 1, 0 }, - { Tokenizer::TYPE_END , "" , 1, 30 }, + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0, 3 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 1, 0, 3 }, + { Tokenizer::TYPE_END , "" , 1, 30, 30 }, }}, // Test that block comments are ignored. { "foo /* This is a block comment */ bar", { - { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, - { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 34 }, - { Tokenizer::TYPE_END , "" , 0, 37 }, + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0, 3 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 34, 37 }, + { Tokenizer::TYPE_END , "" , 0, 37, 37 }, }}, // Test that sh-style comments are not ignored by default. { "foo # bar\n" "baz", { - { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, - { Tokenizer::TYPE_SYMBOL , "#" , 0, 4 }, - { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 6 }, - { Tokenizer::TYPE_IDENTIFIER, "baz", 1, 0 }, - { Tokenizer::TYPE_END , "" , 1, 3 }, + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0, 3 }, + { Tokenizer::TYPE_SYMBOL , "#" , 0, 4, 5 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 6, 9 }, + { Tokenizer::TYPE_IDENTIFIER, "baz", 1, 0, 3 }, + { Tokenizer::TYPE_END , "" , 1, 3, 3 }, }}, // Bytes with the high-order bit set should not be seen as control characters. { "\300", { - { Tokenizer::TYPE_SYMBOL, "\300", 0, 0 }, - { Tokenizer::TYPE_END , "" , 0, 1 }, + { Tokenizer::TYPE_SYMBOL, "\300", 0, 0, 1 }, + { Tokenizer::TYPE_END , "" , 0, 1, 1 }, }}, // Test all whitespace chars { "foo\n\t\r\v\fbar", { - { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, - { Tokenizer::TYPE_IDENTIFIER, "bar", 1, 11 }, - { Tokenizer::TYPE_END , "" , 1, 14 }, + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0, 3 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 1, 11, 14 }, + { Tokenizer::TYPE_END , "" , 1, 14, 14 }, }}, }; @@ -425,6 +437,7 @@ TEST_2D(TokenizerTest, MultipleTokens, kMultiTokenCases, kBlockSizes) { EXPECT_EQ("", tokenizer.current().text); EXPECT_EQ(0, tokenizer.current().line); EXPECT_EQ(0, tokenizer.current().column); + EXPECT_EQ(0, tokenizer.current().end_column); // Loop through all expected tokens. int i = 0; @@ -434,6 +447,8 @@ TEST_2D(TokenizerTest, MultipleTokens, kMultiTokenCases, kBlockSizes) { SCOPED_TRACE(testing::Message() << "Token #" << i << ": " << token.text); + Tokenizer::Token previous = tokenizer.current(); + // Next() should only return false when it hits the end token. if (token.type != Tokenizer::TYPE_END) { ASSERT_TRUE(tokenizer.Next()); @@ -441,11 +456,19 @@ TEST_2D(TokenizerTest, MultipleTokens, kMultiTokenCases, kBlockSizes) { ASSERT_FALSE(tokenizer.Next()); } + // Check that the previous token is set correctly. + EXPECT_EQ(previous.type, tokenizer.previous().type); + EXPECT_EQ(previous.text, tokenizer.previous().text); + EXPECT_EQ(previous.line, tokenizer.previous().line); + EXPECT_EQ(previous.column, tokenizer.previous().column); + EXPECT_EQ(previous.end_column, tokenizer.previous().end_column); + // Check that the token matches the expected one. EXPECT_EQ(token.type, tokenizer.current().type); EXPECT_EQ(token.text, tokenizer.current().text); EXPECT_EQ(token.line, tokenizer.current().line); EXPECT_EQ(token.column, tokenizer.current().column); + EXPECT_EQ(token.end_column, tokenizer.current().end_column); } while (token.type != Tokenizer::TYPE_END); diff --git a/src/google/protobuf/io/zero_copy_stream_unittest.cc b/src/google/protobuf/io/zero_copy_stream_unittest.cc index 8229ee6d..5196d905 100644 --- a/src/google/protobuf/io/zero_copy_stream_unittest.cc +++ b/src/google/protobuf/io/zero_copy_stream_unittest.cc @@ -61,6 +61,7 @@ #include <sstream> #include <google/protobuf/io/zero_copy_stream_impl.h> +#include <google/protobuf/io/coded_stream.h> #if HAVE_ZLIB #include <google/protobuf/io/gzip_stream.h> @@ -285,6 +286,57 @@ TEST_F(IoTest, ArrayIo) { } } +TEST_F(IoTest, TwoSessionWrite) { + // Test that two concatenated write sessions read correctly + + static const char* strA = "0123456789"; + static const char* strB = "WhirledPeas"; + const int kBufferSize = 2*1024; + uint8* buffer = new uint8[kBufferSize]; + char* temp_buffer = new char[40]; + + for (int i = 0; i < kBlockSizeCount; i++) { + for (int j = 0; j < kBlockSizeCount; j++) { + ArrayOutputStream* output = + new ArrayOutputStream(buffer, kBufferSize, kBlockSizes[i]); + CodedOutputStream* coded_output = new CodedOutputStream(output); + coded_output->WriteVarint32(strlen(strA)); + coded_output->WriteRaw(strA, strlen(strA)); + delete coded_output; // flush + int64 pos = output->ByteCount(); + delete output; + output = new ArrayOutputStream( + buffer + pos, kBufferSize - pos, kBlockSizes[i]); + coded_output = new CodedOutputStream(output); + coded_output->WriteVarint32(strlen(strB)); + coded_output->WriteRaw(strB, strlen(strB)); + delete coded_output; // flush + int64 size = pos + output->ByteCount(); + delete output; + + ArrayInputStream* input = + new ArrayInputStream(buffer, size, kBlockSizes[j]); + CodedInputStream* coded_input = new CodedInputStream(input); + uint32 insize; + EXPECT_TRUE(coded_input->ReadVarint32(&insize)); + EXPECT_EQ(strlen(strA), insize); + EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize)); + EXPECT_EQ(0, memcmp(temp_buffer, strA, insize)); + + EXPECT_TRUE(coded_input->ReadVarint32(&insize)); + EXPECT_EQ(strlen(strB), insize); + EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize)); + EXPECT_EQ(0, memcmp(temp_buffer, strB, insize)); + + delete coded_input; + delete input; + } + } + + delete [] temp_buffer; + delete [] buffer; +} + #if HAVE_ZLIB TEST_F(IoTest, GzipIo) { const int kBufferSize = 2*1024; @@ -296,8 +348,12 @@ TEST_F(IoTest, GzipIo) { int size; { ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]); - GzipOutputStream gzout( - &output, GzipOutputStream::GZIP, gzip_buffer_size); + GzipOutputStream::Options options; + options.format = GzipOutputStream::GZIP; + if (gzip_buffer_size != -1) { + options.buffer_size = gzip_buffer_size; + } + GzipOutputStream gzout(&output, options); WriteStuff(&gzout); gzout.Close(); size = output.ByteCount(); @@ -324,8 +380,12 @@ TEST_F(IoTest, ZlibIo) { int size; { ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]); - GzipOutputStream gzout( - &output, GzipOutputStream::ZLIB, gzip_buffer_size); + GzipOutputStream::Options options; + options.format = GzipOutputStream::ZLIB; + if (gzip_buffer_size != -1) { + options.buffer_size = gzip_buffer_size; + } + GzipOutputStream gzout(&output, options); WriteStuff(&gzout); gzout.Close(); size = output.ByteCount(); @@ -348,7 +408,9 @@ TEST_F(IoTest, ZlibIoInputAutodetect) { int size; { ArrayOutputStream output(buffer, kBufferSize); - GzipOutputStream gzout(&output, GzipOutputStream::ZLIB); + GzipOutputStream::Options options; + options.format = GzipOutputStream::ZLIB; + GzipOutputStream gzout(&output, options); WriteStuff(&gzout); gzout.Close(); size = output.ByteCount(); @@ -360,7 +422,9 @@ TEST_F(IoTest, ZlibIoInputAutodetect) { } { ArrayOutputStream output(buffer, kBufferSize); - GzipOutputStream gzout(&output, GzipOutputStream::GZIP); + GzipOutputStream::Options options; + options.format = GzipOutputStream::GZIP; + GzipOutputStream gzout(&output, options); WriteStuff(&gzout); gzout.Close(); size = output.ByteCount(); @@ -432,6 +496,71 @@ TEST_F(IoTest, CompressionOptions) { EXPECT_TRUE(Uncompress(gzip_compressed) == golden); EXPECT_TRUE(Uncompress(zlib_compressed) == golden); } + +TEST_F(IoTest, TwoSessionWriteGzip) { + // Test that two concatenated gzip streams can be read correctly + + static const char* strA = "0123456789"; + static const char* strB = "QuickBrownFox"; + const int kBufferSize = 2*1024; + uint8* buffer = new uint8[kBufferSize]; + char* temp_buffer = new char[40]; + + for (int i = 0; i < kBlockSizeCount; i++) { + for (int j = 0; j < kBlockSizeCount; j++) { + ArrayOutputStream* output = + new ArrayOutputStream(buffer, kBufferSize, kBlockSizes[i]); + GzipOutputStream* gzout = new GzipOutputStream(output); + CodedOutputStream* coded_output = new CodedOutputStream(gzout); + int32 outlen = strlen(strA) + 1; + coded_output->WriteVarint32(outlen); + coded_output->WriteRaw(strA, outlen); + delete coded_output; // flush + delete gzout; // flush + int64 pos = output->ByteCount(); + delete output; + output = new ArrayOutputStream( + buffer + pos, kBufferSize - pos, kBlockSizes[i]); + gzout = new GzipOutputStream(output); + coded_output = new CodedOutputStream(gzout); + outlen = strlen(strB) + 1; + coded_output->WriteVarint32(outlen); + coded_output->WriteRaw(strB, outlen); + delete coded_output; // flush + delete gzout; // flush + int64 size = pos + output->ByteCount(); + delete output; + + ArrayInputStream* input = + new ArrayInputStream(buffer, size, kBlockSizes[j]); + GzipInputStream* gzin = new GzipInputStream(input); + CodedInputStream* coded_input = new CodedInputStream(gzin); + uint32 insize; + EXPECT_TRUE(coded_input->ReadVarint32(&insize)); + EXPECT_EQ(strlen(strA) + 1, insize); + EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize)); + EXPECT_EQ(0, memcmp(temp_buffer, strA, insize)) + << "strA=" << strA << " in=" << temp_buffer; + + EXPECT_TRUE(coded_input->ReadVarint32(&insize)); + EXPECT_EQ(strlen(strB) + 1, insize); + EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize)); + EXPECT_EQ(0, memcmp(temp_buffer, strB, insize)) + << " out_block_size=" << kBlockSizes[i] + << " in_block_size=" << kBlockSizes[j] + << " pos=" << pos + << " size=" << size + << " strB=" << strB << " in=" << temp_buffer; + + delete coded_input; + delete gzin; + delete input; + } + } + + delete [] temp_buffer; + delete [] buffer; +} #endif // There is no string input, only string output. Also, it doesn't support diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h index d117fe25..4bbc6418 100644 --- a/src/google/protobuf/message.h +++ b/src/google/protobuf/message.h @@ -123,6 +123,7 @@ #include <google/protobuf/stubs/common.h> + namespace google { namespace protobuf { diff --git a/src/google/protobuf/repeated_field.cc b/src/google/protobuf/repeated_field.cc index f7beb110..09377742 100644 --- a/src/google/protobuf/repeated_field.cc +++ b/src/google/protobuf/repeated_field.cc @@ -32,11 +32,14 @@ // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. +#include <algorithm> + #include <google/protobuf/repeated_field.h> #include <google/protobuf/stubs/common.h> namespace google { namespace protobuf { + namespace internal { void RepeatedPtrFieldBase::Reserve(int new_size) { @@ -88,8 +91,8 @@ void StringTypeHandlerBase::Delete(string* value) { delete value; } +} // namespace internal -} // namespace internal } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h index cc07fdb8..ae96107a 100644 --- a/src/google/protobuf/repeated_field.h +++ b/src/google/protobuf/repeated_field.h @@ -72,8 +72,11 @@ template <typename Element> class RepeatedField { public: RepeatedField(); + RepeatedField(const RepeatedField& other); ~RepeatedField(); + RepeatedField& operator=(const RepeatedField& other); + int size() const; const Element& Get(int index) const; @@ -90,6 +93,7 @@ class RepeatedField { void RemoveLast(); void Clear(); void MergeFrom(const RepeatedField& other); + void CopyFrom(const RepeatedField& other); // Reserve space to expand the field to at least the given size. If the // array is grown, it will always be at least doubled in size. @@ -116,6 +120,7 @@ class RepeatedField { // STL-like iterator support typedef Element* iterator; typedef const Element* const_iterator; + typedef Element value_type; iterator begin(); const_iterator begin() const; @@ -127,8 +132,6 @@ class RepeatedField { int SpaceUsedExcludingSelf() const; private: - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedField); - static const int kInitialSize = 4; Element* elements_; @@ -203,6 +206,8 @@ class LIBPROTOBUF_EXPORT RepeatedPtrFieldBase { void Clear(); template <typename TypeHandler> void MergeFrom(const RepeatedPtrFieldBase& other); + template <typename TypeHandler> + void CopyFrom(const RepeatedPtrFieldBase& other); void Reserve(int new_size); @@ -300,7 +305,7 @@ class LIBPROTOBUF_EXPORT StringTypeHandlerBase { static void Merge(const string& from, string* to) { *to = from; } }; -class StringTypeHandler : public StringTypeHandlerBase { +class LIBPROTOBUF_EXPORT StringTypeHandler : public StringTypeHandlerBase { public: static int SpaceUsed(const string& value) { return sizeof(value) + StringSpaceUsedExcludingSelf(value); @@ -316,9 +321,11 @@ template <typename Element> class RepeatedPtrField : public internal::RepeatedPtrFieldBase { public: RepeatedPtrField(); - + RepeatedPtrField(const RepeatedPtrField& other); ~RepeatedPtrField(); + RepeatedPtrField& operator=(const RepeatedPtrField& other); + int size() const; const Element& Get(int index) const; @@ -327,6 +334,7 @@ class RepeatedPtrField : public internal::RepeatedPtrFieldBase { void RemoveLast(); // Remove the last element in the array. void Clear(); void MergeFrom(const RepeatedPtrField& other); + void CopyFrom(const RepeatedPtrField& other); // Reserve space to expand the field to at least the given size. This only // resizes the pointer array; it doesn't allocate any objects. If the @@ -349,6 +357,7 @@ class RepeatedPtrField : public internal::RepeatedPtrFieldBase { // STL-like iterator support typedef internal::RepeatedPtrIterator<Element> iterator; typedef internal::RepeatedPtrIterator<const Element> const_iterator; + typedef Element value_type; iterator begin(); const_iterator begin() const; @@ -365,11 +374,6 @@ class RepeatedPtrField : public internal::RepeatedPtrFieldBase { // excluding sizeof(*this). int SpaceUsedExcludingSelf() const; - // The spaced used just by the pointer array, not counting the objects pointed - // at. Returns zero if the array is inlined (i.e. initial_space_ is being - // used). - int SpaceUsedByArray() const; - // Advanced memory management -------------------------------------- // When hardcore memory management becomes necessary -- as it often // does here at Google -- the following methods may be useful. @@ -410,9 +414,6 @@ class RepeatedPtrField : public internal::RepeatedPtrFieldBase { // methods on RepeatedPtrFieldBase. class TypeHandler; - - private: - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPtrField); }; // implementation ==================================================== @@ -425,6 +426,14 @@ inline RepeatedField<Element>::RepeatedField() } template <typename Element> +inline RepeatedField<Element>::RepeatedField(const RepeatedField& other) + : elements_(initial_space_), + current_size_(0), + total_size_(kInitialSize) { + CopyFrom(other); +} + +template <typename Element> RepeatedField<Element>::~RepeatedField() { if (elements_ != initial_space_) { delete [] elements_; @@ -432,6 +441,13 @@ RepeatedField<Element>::~RepeatedField() { } template <typename Element> +inline RepeatedField<Element>& +RepeatedField<Element>::operator=(const RepeatedField& other) { + CopyFrom(other); + return *this; +} + +template <typename Element> inline int RepeatedField<Element>::size() const { return current_size_; } @@ -502,6 +518,12 @@ inline void RepeatedField<Element>::MergeFrom(const RepeatedField& other) { } template <typename Element> +inline void RepeatedField<Element>::CopyFrom(const RepeatedField& other) { + Clear(); + MergeFrom(other); +} + +template <typename Element> inline Element* RepeatedField<Element>::mutable_data() { return elements_; } @@ -679,6 +701,12 @@ inline void RepeatedPtrFieldBase::MergeFrom(const RepeatedPtrFieldBase& other) { } } +template <typename TypeHandler> +inline void RepeatedPtrFieldBase::CopyFrom(const RepeatedPtrFieldBase& other) { + RepeatedPtrFieldBase::Clear<TypeHandler>(); + RepeatedPtrFieldBase::MergeFrom<TypeHandler>(other); +} + inline int RepeatedPtrFieldBase::Capacity() const { return total_size_; } @@ -805,11 +833,24 @@ template <typename Element> inline RepeatedPtrField<Element>::RepeatedPtrField() {} template <typename Element> +inline RepeatedPtrField<Element>::RepeatedPtrField( + const RepeatedPtrField& other) { + CopyFrom(other); +} + +template <typename Element> RepeatedPtrField<Element>::~RepeatedPtrField() { Destroy<TypeHandler>(); } template <typename Element> +inline RepeatedPtrField<Element>& RepeatedPtrField<Element>::operator=( + const RepeatedPtrField& other) { + CopyFrom(other); + return *this; +} + +template <typename Element> inline int RepeatedPtrField<Element>::size() const { return RepeatedPtrFieldBase::size(); } @@ -846,6 +887,12 @@ inline void RepeatedPtrField<Element>::MergeFrom( } template <typename Element> +inline void RepeatedPtrField<Element>::CopyFrom( + const RepeatedPtrField& other) { + RepeatedPtrFieldBase::CopyFrom<TypeHandler>(other); +} + +template <typename Element> inline Element** RepeatedPtrField<Element>::mutable_data() { return RepeatedPtrFieldBase::mutable_data<TypeHandler>(); } @@ -944,7 +991,7 @@ class RepeatedPtrIterator template<typename OtherElement> RepeatedPtrIterator(const RepeatedPtrIterator<OtherElement>& other) : it_(other.it_) { - // Force a compiler error if the other type is not convertable to ours. + // Force a compiler error if the other type is not convertible to ours. if (false) { implicit_cast<Element*, OtherElement*>(0); } diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc index 7c35f604..e7e1e99b 100644 --- a/src/google/protobuf/repeated_field_unittest.cc +++ b/src/google/protobuf/repeated_field_unittest.cc @@ -232,6 +232,54 @@ TEST(RepeatedField, MergeFrom) { EXPECT_EQ(5, destination.Get(4)); } +TEST(RepeatedField, CopyFrom) { + RepeatedField<int> source, destination; + + source.Add(4); + source.Add(5); + + destination.Add(1); + destination.Add(2); + destination.Add(3); + + destination.CopyFrom(source); + + ASSERT_EQ(2, destination.size()); + + EXPECT_EQ(4, destination.Get(0)); + EXPECT_EQ(5, destination.Get(1)); +} + +TEST(RepeatedField, CopyConstruct) { + RepeatedField<int> source; + source.Add(1); + source.Add(2); + + RepeatedField<int> destination(source); + + ASSERT_EQ(2, destination.size()); + EXPECT_EQ(1, destination.Get(0)); + EXPECT_EQ(2, destination.Get(1)); +} + +TEST(RepeatedField, CopyAssign) { + RepeatedField<int> source, destination; + + source.Add(4); + source.Add(5); + + destination.Add(1); + destination.Add(2); + destination.Add(3); + + destination = source; + + ASSERT_EQ(2, destination.size()); + + EXPECT_EQ(4, destination.Get(0)); + EXPECT_EQ(5, destination.Get(1)); +} + TEST(RepeatedField, MutableDataIsMutable) { RepeatedField<int> field; field.Add(1); @@ -536,6 +584,55 @@ TEST(RepeatedPtrField, MergeFrom) { EXPECT_EQ("5", destination.Get(4)); } +TEST(RepeatedPtrField, CopyFrom) { + RepeatedPtrField<string> source, destination; + + source.Add()->assign("4"); + source.Add()->assign("5"); + + destination.Add()->assign("1"); + destination.Add()->assign("2"); + destination.Add()->assign("3"); + + destination.CopyFrom(source); + + ASSERT_EQ(2, destination.size()); + + EXPECT_EQ("4", destination.Get(0)); + EXPECT_EQ("5", destination.Get(1)); +} + +TEST(RepeatedPtrField, CopyConstruct) { + RepeatedPtrField<string> source; + + source.Add()->assign("1"); + source.Add()->assign("2"); + + RepeatedPtrField<string> destination(source); + + ASSERT_EQ(2, destination.size()); + EXPECT_EQ("1", destination.Get(0)); + EXPECT_EQ("2", destination.Get(1)); +} + +TEST(RepeatedPtrField, CopyAssign) { + RepeatedPtrField<string> source, destination; + + source.Add()->assign("4"); + source.Add()->assign("5"); + + destination.Add()->assign("1"); + destination.Add()->assign("2"); + destination.Add()->assign("3"); + + destination = source; + + ASSERT_EQ(2, destination.size()); + + EXPECT_EQ("4", destination.Get(0)); + EXPECT_EQ("5", destination.Get(1)); +} + TEST(RepeatedPtrField, MutableDataIsMutable) { RepeatedPtrField<string> field; *field.Add() = "1"; @@ -564,7 +661,8 @@ class RepeatedFieldIteratorTest : public testing::Test { TEST_F(RepeatedFieldIteratorTest, Convertible) { RepeatedField<int>::iterator iter = proto_array_.begin(); RepeatedField<int>::const_iterator c_iter = iter; - EXPECT_EQ(0, *c_iter); + RepeatedField<int>::value_type value = *c_iter; + EXPECT_EQ(0, value); } TEST_F(RepeatedFieldIteratorTest, MutableIteration) { @@ -613,6 +711,8 @@ class RepeatedPtrFieldIteratorTest : public testing::Test { TEST_F(RepeatedPtrFieldIteratorTest, Convertible) { RepeatedPtrField<string>::iterator iter = proto_array_.begin(); RepeatedPtrField<string>::const_iterator c_iter = iter; + RepeatedPtrField<string>::value_type value = *c_iter; + EXPECT_EQ("foo", value); } TEST_F(RepeatedPtrFieldIteratorTest, MutableIteration) { diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc index 137cbcee..1494ebd7 100644 --- a/src/google/protobuf/text_format.cc +++ b/src/google/protobuf/text_format.cc @@ -37,6 +37,7 @@ #include <stdio.h> #include <stack> #include <limits> +#include <vector> #include <google/protobuf/text_format.h> @@ -118,8 +119,10 @@ class TextFormat::Parser::ParserImpl { ParserImpl(const Descriptor* root_message_type, io::ZeroCopyInputStream* input_stream, io::ErrorCollector* error_collector, + TextFormat::Finder* finder, SingularOverwritePolicy singular_overwrite_policy) : error_collector_(error_collector), + finder_(finder), tokenizer_error_collector_(this), tokenizer_(input_stream, &tokenizer_error_collector_), root_message_type_(root_message_type), @@ -249,7 +252,9 @@ class TextFormat::Parser::ParserImpl { } DO(Consume("]")); - field = reflection->FindKnownExtensionByName(field_name); + field = (finder_ != NULL + ? finder_->FindExtension(message, field_name) + : reflection->FindKnownExtensionByName(field_name)); if (field == NULL) { ReportError("Extension \"" + field_name + "\" is not defined or " @@ -301,9 +306,24 @@ class TextFormat::Parser::ParserImpl { DO(ConsumeFieldMessage(message, reflection, field)); } else { DO(Consume(":")); - DO(ConsumeFieldValue(message, reflection, field)); + if (field->is_repeated() && TryConsume("[")) { + // Short repeated format, e.g. "foo: [1, 2, 3]" + while (true) { + DO(ConsumeFieldValue(message, reflection, field)); + if (TryConsume("]")) { + break; + } + DO(Consume(",")); + } + } else { + DO(ConsumeFieldValue(message, reflection, field)); + } } + // For historical reasons, fields may optionally be separated by commas or + // semicolons. + TryConsume(";") || TryConsume(","); + if (field->options().deprecated()) { ReportWarning("text format contains deprecated field \"" + field_name + "\""); @@ -397,29 +417,46 @@ class TextFormat::Parser::ParserImpl { } case FieldDescriptor::CPPTYPE_BOOL: { - string value; - DO(ConsumeIdentifier(&value)); - - if (value == "true") { - SET_FIELD(Bool, true); - } else if (value == "false") { - SET_FIELD(Bool, false); + if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) { + uint64 value; + DO(ConsumeUnsignedInteger(&value, 1)); + SET_FIELD(Bool, value); } else { - ReportError("Invalid value for boolean field \"" + field->name() - + "\". Value: \"" + value + "\"."); - return false; + string value; + DO(ConsumeIdentifier(&value)); + if (value == "true" || value == "t") { + SET_FIELD(Bool, true); + } else if (value == "false" || value == "f") { + SET_FIELD(Bool, false); + } else { + ReportError("Invalid value for boolean field \"" + field->name() + + "\". Value: \"" + value + "\"."); + return false; + } } break; } case FieldDescriptor::CPPTYPE_ENUM: { string value; - DO(ConsumeIdentifier(&value)); - - // Find the enumeration value. const EnumDescriptor* enum_type = field->enum_type(); - const EnumValueDescriptor* enum_value - = enum_type->FindValueByName(value); + const EnumValueDescriptor* enum_value = NULL; + + if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) { + DO(ConsumeIdentifier(&value)); + // Find the enumeration value. + enum_value = enum_type->FindValueByName(value); + + } else if (LookingAt("-") || + LookingAtType(io::Tokenizer::TYPE_INTEGER)) { + int64 int_value; + DO(ConsumeSignedInteger(&int_value, kint32max)); + value = SimpleItoa(int_value); // for error reporting + enum_value = enum_type->FindValueByNumber(int_value); + } else { + ReportError("Expected integer or identifier."); + return false; + } if (enum_value == NULL) { ReportError("Unknown enumeration value of \"" + value + "\" for " @@ -632,6 +669,7 @@ class TextFormat::Parser::ParserImpl { }; io::ErrorCollector* error_collector_; + TextFormat::Finder* finder_; ParserErrorCollector tokenizer_error_collector_; io::Tokenizer tokenizer_; const Descriptor* root_message_type_; @@ -765,9 +803,14 @@ class TextFormat::Printer::TextGenerator { // =========================================================================== +TextFormat::Finder::~Finder() { +} + TextFormat::Parser::Parser() : error_collector_(NULL), - allow_partial_(false) {} + finder_(NULL), + allow_partial_(false) { +} TextFormat::Parser::~Parser() {} @@ -775,7 +818,7 @@ bool TextFormat::Parser::Parse(io::ZeroCopyInputStream* input, Message* output) { output->Clear(); ParserImpl parser(output->GetDescriptor(), input, error_collector_, - ParserImpl::FORBID_SINGULAR_OVERWRITES); + finder_, ParserImpl::FORBID_SINGULAR_OVERWRITES); return MergeUsingImpl(input, output, &parser); } @@ -788,7 +831,7 @@ bool TextFormat::Parser::ParseFromString(const string& input, bool TextFormat::Parser::Merge(io::ZeroCopyInputStream* input, Message* output) { ParserImpl parser(output->GetDescriptor(), input, error_collector_, - ParserImpl::ALLOW_SINGULAR_OVERWRITES); + finder_, ParserImpl::ALLOW_SINGULAR_OVERWRITES); return MergeUsingImpl(input, output, &parser); } @@ -818,7 +861,7 @@ bool TextFormat::Parser::ParseFieldValueFromString( Message* output) { io::ArrayInputStream input_stream(input.data(), input.size()); ParserImpl parser(output->GetDescriptor(), &input_stream, error_collector_, - ParserImpl::ALLOW_SINGULAR_OVERWRITES); + finder_, ParserImpl::ALLOW_SINGULAR_OVERWRITES); return parser.ParseField(field, output); } @@ -853,7 +896,7 @@ TextFormat::Printer::Printer() TextFormat::Printer::~Printer() {} bool TextFormat::Printer::PrintToString(const Message& message, - string* output) { + string* output) const { GOOGLE_DCHECK(output) << "output specified is NULL"; output->clear(); @@ -866,7 +909,7 @@ bool TextFormat::Printer::PrintToString(const Message& message, bool TextFormat::Printer::PrintUnknownFieldsToString( const UnknownFieldSet& unknown_fields, - string* output) { + string* output) const { GOOGLE_DCHECK(output) << "output specified is NULL"; output->clear(); @@ -875,7 +918,7 @@ bool TextFormat::Printer::PrintUnknownFieldsToString( } bool TextFormat::Printer::Print(const Message& message, - io::ZeroCopyOutputStream* output) { + io::ZeroCopyOutputStream* output) const { TextGenerator generator(output, initial_indent_level_); Print(message, generator); @@ -886,7 +929,7 @@ bool TextFormat::Printer::Print(const Message& message, bool TextFormat::Printer::PrintUnknownFields( const UnknownFieldSet& unknown_fields, - io::ZeroCopyOutputStream* output) { + io::ZeroCopyOutputStream* output) const { TextGenerator generator(output, initial_indent_level_); PrintUnknownFields(unknown_fields, generator); @@ -896,7 +939,7 @@ bool TextFormat::Printer::PrintUnknownFields( } void TextFormat::Printer::Print(const Message& message, - TextGenerator& generator) { + TextGenerator& generator) const { const Reflection* reflection = message.GetReflection(); vector<const FieldDescriptor*> fields; reflection->ListFields(message, &fields); @@ -910,7 +953,7 @@ void TextFormat::Printer::PrintFieldValueToString( const Message& message, const FieldDescriptor* field, int index, - string* output) { + string* output) const { GOOGLE_DCHECK(output) << "output specified is NULL"; @@ -924,7 +967,7 @@ void TextFormat::Printer::PrintFieldValueToString( void TextFormat::Printer::PrintField(const Message& message, const Reflection* reflection, const FieldDescriptor* field, - TextGenerator& generator) { + TextGenerator& generator) const { if (use_short_repeated_primitives_ && field->is_repeated() && field->cpp_type() != FieldDescriptor::CPPTYPE_STRING && @@ -980,10 +1023,11 @@ void TextFormat::Printer::PrintField(const Message& message, } } -void TextFormat::Printer::PrintShortRepeatedField(const Message& message, - const Reflection* reflection, - const FieldDescriptor* field, - TextGenerator& generator) { +void TextFormat::Printer::PrintShortRepeatedField( + const Message& message, + const Reflection* reflection, + const FieldDescriptor* field, + TextGenerator& generator) const { // Print primitive repeated field in short form. PrintFieldName(message, reflection, field, generator); @@ -1003,7 +1047,7 @@ void TextFormat::Printer::PrintShortRepeatedField(const Message& message, void TextFormat::Printer::PrintFieldName(const Message& message, const Reflection* reflection, const FieldDescriptor* field, - TextGenerator& generator) { + TextGenerator& generator) const { if (field->is_extension()) { generator.Print("["); // We special-case MessageSet elements for compatibility with proto1. @@ -1031,7 +1075,7 @@ void TextFormat::Printer::PrintFieldValue( const Reflection* reflection, const FieldDescriptor* field, int index, - TextGenerator& generator) { + TextGenerator& generator) const { GOOGLE_DCHECK(field->is_repeated() || (index == -1)) << "Index must be -1 for non-repeated fields"; @@ -1143,7 +1187,7 @@ static string PaddedHex(IntType value) { } void TextFormat::Printer::PrintUnknownFields( - const UnknownFieldSet& unknown_fields, TextGenerator& generator) { + const UnknownFieldSet& unknown_fields, TextGenerator& generator) const { for (int i = 0; i < unknown_fields.field_count(); i++) { const UnknownField& field = unknown_fields.field(i); string field_number = SimpleItoa(field.number()); diff --git a/src/google/protobuf/text_format.h b/src/google/protobuf/text_format.h index e78e1042..03c04910 100644 --- a/src/google/protobuf/text_format.h +++ b/src/google/protobuf/text_format.h @@ -90,20 +90,20 @@ class LIBPROTOBUF_EXPORT TextFormat { ~Printer(); // Like TextFormat::Print - bool Print(const Message& message, io::ZeroCopyOutputStream* output); + bool Print(const Message& message, io::ZeroCopyOutputStream* output) const; // Like TextFormat::PrintUnknownFields bool PrintUnknownFields(const UnknownFieldSet& unknown_fields, - io::ZeroCopyOutputStream* output); + io::ZeroCopyOutputStream* output) const; // Like TextFormat::PrintToString - bool PrintToString(const Message& message, string* output); + bool PrintToString(const Message& message, string* output) const; // Like TextFormat::PrintUnknownFieldsToString bool PrintUnknownFieldsToString(const UnknownFieldSet& unknown_fields, - string* output); + string* output) const; // Like TextFormat::PrintFieldValueToString void PrintFieldValueToString(const Message& message, const FieldDescriptor* field, int index, - string* output); + string* output) const; // Adjust the initial indent level of all output. Each indent level is // equal to two spaces. @@ -121,8 +121,7 @@ class LIBPROTOBUF_EXPORT TextFormat { // field_name: [1, 2, 3, 4] // instead of printing each value on its own line. Short format applies // only to primitive values -- i.e. everything except strings and - // sub-messages/groups. Note that at present this format is not recognized - // by the parser. + // sub-messages/groups. void SetUseShortRepeatedPrimitives(bool use_short_repeated_primitives) { use_short_repeated_primitives_ = use_short_repeated_primitives; } @@ -143,26 +142,26 @@ class LIBPROTOBUF_EXPORT TextFormat { // Internal Print method, used for writing to the OutputStream via // the TextGenerator class. void Print(const Message& message, - TextGenerator& generator); + TextGenerator& generator) const; // Print a single field. void PrintField(const Message& message, const Reflection* reflection, const FieldDescriptor* field, - TextGenerator& generator); + TextGenerator& generator) const; // Print a repeated primitive field in short form. void PrintShortRepeatedField(const Message& message, const Reflection* reflection, const FieldDescriptor* field, - TextGenerator& generator); + TextGenerator& generator) const; // Print the name of a field -- i.e. everything that comes before the // ':' for a single name/value pair. void PrintFieldName(const Message& message, const Reflection* reflection, const FieldDescriptor* field, - TextGenerator& generator); + TextGenerator& generator) const; // Outputs a textual representation of the value of the field supplied on // the message supplied or the default value if not set. @@ -170,13 +169,13 @@ class LIBPROTOBUF_EXPORT TextFormat { const Reflection* reflection, const FieldDescriptor* field, int index, - TextGenerator& generator); + TextGenerator& generator) const; // Print the fields in an UnknownFieldSet. They are printed by tag number // only. Embedded messages are heuristically identified by attempting to // parse them. void PrintUnknownFields(const UnknownFieldSet& unknown_fields, - TextGenerator& generator); + TextGenerator& generator) const; int initial_indent_level_; @@ -207,6 +206,20 @@ class LIBPROTOBUF_EXPORT TextFormat { const FieldDescriptor* field, Message* message); + // Interface that TextFormat::Parser can use to find extensions. + // This class may be extended in the future to find more information + // like fields, etc. + class LIBPROTOBUF_EXPORT Finder { + public: + virtual ~Finder(); + + // Try to find an extension of *message by fully-qualified field + // name. Returns NULL if no extension is known for this name or number. + virtual const FieldDescriptor* FindExtension( + Message* message, + const string& name) const = 0; + }; + // For more control over parsing, use this class. class LIBPROTOBUF_EXPORT Parser { public: @@ -228,6 +241,13 @@ class LIBPROTOBUF_EXPORT TextFormat { error_collector_ = error_collector; } + // Set how parser finds extensions. If NULL (the default), the + // parser will use the standard Reflection object associated with + // the message being parsed. + void SetFinder(Finder* finder) { + finder_ = finder; + } + // Normally parsing fails if, after parsing, output->IsInitialized() // returns false. Call AllowPartialMessage(true) to skip this check. void AllowPartialMessage(bool allow) { @@ -251,6 +271,7 @@ class LIBPROTOBUF_EXPORT TextFormat { ParserImpl* parser_impl); io::ErrorCollector* error_collector_; + Finder* finder_; bool allow_partial_; }; diff --git a/src/google/protobuf/text_format_unittest.cc b/src/google/protobuf/text_format_unittest.cc index ddf8ff7f..00ea8c3c 100644 --- a/src/google/protobuf/text_format_unittest.cc +++ b/src/google/protobuf/text_format_unittest.cc @@ -353,6 +353,25 @@ TEST_F(TextFormatExtensionsTest, ParseExtensions) { TestUtil::ExpectAllExtensionsSet(proto_); } +TEST_F(TextFormatTest, ParseEnumFieldFromNumber) { + // Create a parse string with a numerical value for an enum field. + string parse_string = strings::Substitute("optional_nested_enum: $0", + unittest::TestAllTypes::BAZ); + EXPECT_TRUE(TextFormat::ParseFromString(parse_string, &proto_)); + EXPECT_TRUE(proto_.has_optional_nested_enum()); + EXPECT_EQ(unittest::TestAllTypes::BAZ, proto_.optional_nested_enum()); +} + +TEST_F(TextFormatTest, ParseEnumFieldFromNegativeNumber) { + ASSERT_LT(unittest::SPARSE_E, 0); + string parse_string = strings::Substitute("sparse_enum: $0", + unittest::SPARSE_E); + unittest::SparseEnumMessage proto; + EXPECT_TRUE(TextFormat::ParseFromString(parse_string, &proto)); + EXPECT_TRUE(proto.has_sparse_enum()); + EXPECT_EQ(unittest::SPARSE_E, proto.sparse_enum()); +} + TEST_F(TextFormatTest, ParseStringEscape) { // Create a parse string with escpaed characters in it. string parse_string = "optional_string: " @@ -406,6 +425,34 @@ TEST_F(TextFormatTest, ParseFloatWithSuffix) { EXPECT_EQ(1.0, proto_.optional_float()); } +TEST_F(TextFormatTest, ParseShortRepeatedForm) { + string parse_string = + // Mixed short-form and long-form are simply concatenated. + "repeated_int32: 1\n" + "repeated_int32: [456, 789]\n" + "repeated_nested_enum: [ FOO ,BAR, # comment\n" + " 3]\n" + // Note that while the printer won't print repeated strings in short-form, + // the parser will accept them. + "repeated_string: [ \"foo\", 'bar' ]\n"; + + ASSERT_TRUE(TextFormat::ParseFromString(parse_string, &proto_)); + + ASSERT_EQ(3, proto_.repeated_int32_size()); + EXPECT_EQ(1, proto_.repeated_int32(0)); + EXPECT_EQ(456, proto_.repeated_int32(1)); + EXPECT_EQ(789, proto_.repeated_int32(2)); + + ASSERT_EQ(3, proto_.repeated_nested_enum_size()); + EXPECT_EQ(unittest::TestAllTypes::FOO, proto_.repeated_nested_enum(0)); + EXPECT_EQ(unittest::TestAllTypes::BAR, proto_.repeated_nested_enum(1)); + EXPECT_EQ(unittest::TestAllTypes::BAZ, proto_.repeated_nested_enum(2)); + + ASSERT_EQ(2, proto_.repeated_string_size()); + EXPECT_EQ("foo", proto_.repeated_string(0)); + EXPECT_EQ("bar", proto_.repeated_string(1)); +} + TEST_F(TextFormatTest, Comments) { // Test that comments are ignored. @@ -771,7 +818,12 @@ TEST_F(TextFormatParserTest, ParseFieldValueFromString) { // bool EXPECT_FIELD(bool, true, "true"); EXPECT_FIELD(bool, false, "false"); - EXPECT_INVALID(bool, "1"); + EXPECT_FIELD(bool, true, "1"); + EXPECT_FIELD(bool, true, "t"); + EXPECT_FIELD(bool, false, "0"); + EXPECT_FIELD(bool, false, "f"); + EXPECT_INVALID(bool, "2"); + EXPECT_INVALID(bool, "-0"); EXPECT_INVALID(bool, "on"); EXPECT_INVALID(bool, "a"); EXPECT_INVALID(bool, "True"); @@ -799,7 +851,8 @@ TEST_F(TextFormatParserTest, ParseFieldValueFromString) { // enum EXPECT_FIELD(nested_enum, unittest::TestAllTypes::BAR, "BAR"); - EXPECT_INVALID(nested_enum, "1"); // number not supported + EXPECT_FIELD(nested_enum, unittest::TestAllTypes::BAZ, + SimpleItoa(unittest::TestAllTypes::BAZ)); EXPECT_INVALID(nested_enum, "FOOBAR"); // message @@ -820,7 +873,7 @@ TEST_F(TextFormatParserTest, InvalidToken) { ExpectFailure("optional_bool: true\n-5\n", "Expected identifier.", 2, 1); - ExpectFailure("optional_bool: true;\n", "Expected identifier.", 1, 20); + ExpectFailure("optional_bool: true!\n", "Expected identifier.", 1, 20); ExpectFailure("\"some string\"", "Expected identifier.", 1, 1); } @@ -890,7 +943,7 @@ TEST_F(TextFormatParserTest, InvalidFieldValues) { // Invalid values for a boolean field. ExpectFailure("optional_bool: \"hello\"\n", "Expected identifier.", 1, 16); - ExpectFailure("optional_bool: 5\n", "Expected identifier.", 1, 16); + ExpectFailure("optional_bool: 5\n", "Integer out of range.", 1, 16); ExpectFailure("optional_bool: -7.5\n", "Expected identifier.", 1, 16); ExpectFailure("optional_bool: !\n", "Expected identifier.", 1, 16); @@ -911,12 +964,18 @@ TEST_F(TextFormatParserTest, InvalidFieldValues) { 1, 17); // Invalid values for an enumeration field. - ExpectFailure("optional_nested_enum: \"hello\"\n", "Expected identifier.", - 1, 23); - - ExpectFailure("optional_nested_enum: 5\n", "Expected identifier.", 1, 23); - ExpectFailure("optional_nested_enum: -7.5\n", "Expected identifier.", 1, 23); - ExpectFailure("optional_nested_enum: !\n", "Expected identifier.", 1, 23); + ExpectFailure("optional_nested_enum: \"hello\"\n", + "Expected integer or identifier.", 1, 23); + + // Valid token, but enum value is not defined. + ExpectFailure("optional_nested_enum: 5\n", + "Unknown enumeration value of \"5\" for field " + "\"optional_nested_enum\".", 2, 1); + // We consume the negative sign, so the error position starts one character + // later. + ExpectFailure("optional_nested_enum: -7.5\n", "Expected integer.", 1, 24); + ExpectFailure("optional_nested_enum: !\n", + "Expected integer or identifier.", 1, 23); ExpectFailure( "optional_nested_enum: grah\n", @@ -986,6 +1045,14 @@ TEST_F(TextFormatParserTest, MergeDuplicateOptional) { EXPECT_EQ(2, message.c()); } +TEST_F(TextFormatParserTest, ExplicitDelimiters) { + unittest::TestRequired message; + EXPECT_TRUE(TextFormat::ParseFromString("a:1,b:2;c:3", &message)); + EXPECT_EQ(1, message.a()); + EXPECT_EQ(2, message.b()); + EXPECT_EQ(3, message.c()); +} + TEST_F(TextFormatParserTest, PrintErrorsToStderr) { vector<string> errors; diff --git a/src/google/protobuf/unittest.proto b/src/google/protobuf/unittest.proto index d51fa1e7..de4425be 100644 --- a/src/google/protobuf/unittest.proto +++ b/src/google/protobuf/unittest.proto @@ -35,6 +35,12 @@ // A proto file we will use for unit testing. +// Some generic_services option(s) added automatically. +// See: http://go/proto2-generic-services-default +option cc_generic_services = true; // auto-added +option java_generic_services = true; // auto-added +option py_generic_services = true; // auto-added + import "google/protobuf/unittest_import.proto"; // We don't put this in a package within proto2 because we need to make sure @@ -489,6 +495,10 @@ message TestExtremeDefaultValues { optional float nan_float = 19 [default = nan]; } +message SparseEnumMessage { + optional TestSparseEnum sparse_enum = 1; +} + // Test String and Bytes: string is for valid UTF-8 strings message OneString { optional string data = 1; @@ -598,6 +608,7 @@ message TestRepeatedScalarDifferentTagSizes { repeated uint64 repeated_uint64 = 262143; } + // Test that RPC services work. message FooRequest {} message FooResponse {} diff --git a/src/google/protobuf/unittest_custom_options.proto b/src/google/protobuf/unittest_custom_options.proto index b6ee03da..a610785f 100644 --- a/src/google/protobuf/unittest_custom_options.proto +++ b/src/google/protobuf/unittest_custom_options.proto @@ -35,6 +35,12 @@ // A proto file used to test the "custom options" feature of proto2. +// Some generic_services option(s) added automatically. +// See: http://go/proto2-generic-services-default +option cc_generic_services = true; // auto-added +option java_generic_services = true; // auto-added +option py_generic_services = true; + // A custom file option (defined below). option (file_opt1) = 9876543210; @@ -273,3 +279,88 @@ message VariousComplexOptions { option (complex_opt3).complexoptiontype5.plugh = 22; option (complexopt6).xyzzy = 24; } + +// ------------------------------------------------------ +// Definitions for testing aggregate option parsing. +// See descriptor_unittest.cc. + +message AggregateMessageSet { + option message_set_wire_format = true; + extensions 4 to max; +} + +message AggregateMessageSetElement { + extend AggregateMessageSet { + optional AggregateMessageSetElement message_set_extension = 15447542; + } + optional string s = 1; +} + +// A helper type used to test aggregate option parsing +message Aggregate { + optional int32 i = 1; + optional string s = 2; + + // A nested object + optional Aggregate sub = 3; + + // To test the parsing of extensions inside aggregate values + optional google.protobuf.FileOptions file = 4; + extend google.protobuf.FileOptions { + optional Aggregate nested = 15476903; + } + + // An embedded message set + optional AggregateMessageSet mset = 5; +} + +// Allow Aggregate to be used as an option at all possible locations +// in the .proto grammer. +extend google.protobuf.FileOptions { optional Aggregate fileopt = 15478479; } +extend google.protobuf.MessageOptions { optional Aggregate msgopt = 15480088; } +extend google.protobuf.FieldOptions { optional Aggregate fieldopt = 15481374; } +extend google.protobuf.EnumOptions { optional Aggregate enumopt = 15483218; } +extend google.protobuf.EnumValueOptions { optional Aggregate enumvalopt = 15486921; } +extend google.protobuf.ServiceOptions { optional Aggregate serviceopt = 15497145; } +extend google.protobuf.MethodOptions { optional Aggregate methodopt = 15512713; } + +// Try using AggregateOption at different points in the proto grammar +option (fileopt) = { + s: 'FileAnnotation' + // Also test the handling of comments + /* of both types */ i: 100 + + sub { s: 'NestedFileAnnotation' } + + // Include a google.protobuf.FileOptions and recursively extend it with + // another fileopt. + file { + [protobuf_unittest.fileopt] { + s:'FileExtensionAnnotation' + } + } + + // A message set inside an option value + mset { + [protobuf_unittest.AggregateMessageSetElement.message_set_extension] { + s: 'EmbeddedMessageSetElement' + } + } +}; + +message AggregateMessage { + option (msgopt) = { i:101 s:'MessageAnnotation' }; + optional int32 fieldname = 1 [(fieldopt) = { s:'FieldAnnotation' }]; +} + +service AggregateService { + option (serviceopt) = { s:'ServiceAnnotation' }; + rpc Method (AggregateMessage) returns (AggregateMessage) { + option (methodopt) = { s:'MethodAnnotation' }; + } +} + +enum AggregateEnum { + option (enumopt) = { s:'EnumAnnotation' }; + VALUE = 1 [(enumvalopt) = { s:'EnumValueAnnotation' }]; +} diff --git a/src/google/protobuf/unittest_no_generic_services.proto b/src/google/protobuf/unittest_no_generic_services.proto index fcae4214..cffb4122 100644 --- a/src/google/protobuf/unittest_no_generic_services.proto +++ b/src/google/protobuf/unittest_no_generic_services.proto @@ -32,9 +32,7 @@ package google.protobuf.no_generic_services_test; -option cc_generic_services = false; -option java_generic_services = false; -option py_generic_services = false; +// *_generic_services are false by default. message TestMessage { optional int32 a = 1; diff --git a/src/google/protobuf/wire_format_lite_inl.h b/src/google/protobuf/wire_format_lite_inl.h index d7b2c302..a461a6ca 100644 --- a/src/google/protobuf/wire_format_lite_inl.h +++ b/src/google/protobuf/wire_format_lite_inl.h @@ -368,12 +368,24 @@ inline bool WireFormatLite::ReadMessage(io::CodedInputStream* input, return true; } -template<typename MessageType> -inline bool WireFormatLite::ReadGroupNoVirtual(int field_number, - io::CodedInputStream* input, - MessageType* value) { +// We name the template parameter something long and extremely unlikely to occur +// elsewhere because a *qualified* member access expression designed to avoid +// virtual dispatch, C++03 [basic.lookup.classref] 3.4.5/4 requires that the +// name of the qualifying class to be looked up both in the context of the full +// expression (finding the template parameter) and in the context of the object +// whose member we are accessing. This could potentially find a nested type +// within that object. The standard goes on to require these names to refer to +// the same entity, which this collision would violate. The lack of a safe way +// to avoid this collision appears to be a defect in the standard, but until it +// is corrected, we choose the name to avoid accidental collisions. +template<typename MessageType_WorkAroundCppLookupDefect> +inline bool WireFormatLite::ReadGroupNoVirtual( + int field_number, io::CodedInputStream* input, + MessageType_WorkAroundCppLookupDefect* value) { if (!input->IncrementRecursionDepth()) return false; - if (!value->MessageType::MergePartialFromCodedStream(input)) return false; + if (!value-> + MessageType_WorkAroundCppLookupDefect::MergePartialFromCodedStream(input)) + return false; input->DecrementRecursionDepth(); // Make sure the last thing read was an end tag for this group. if (!input->LastTagWas(MakeTag(field_number, WIRETYPE_END_GROUP))) { @@ -381,14 +393,16 @@ inline bool WireFormatLite::ReadGroupNoVirtual(int field_number, } return true; } -template<typename MessageType> -inline bool WireFormatLite::ReadMessageNoVirtual(io::CodedInputStream* input, - MessageType* value) { +template<typename MessageType_WorkAroundCppLookupDefect> +inline bool WireFormatLite::ReadMessageNoVirtual( + io::CodedInputStream* input, MessageType_WorkAroundCppLookupDefect* value) { uint32 length; if (!input->ReadVarint32(&length)) return false; if (!input->IncrementRecursionDepth()) return false; io::CodedInputStream::Limit limit = input->PushLimit(length); - if (!value->MessageType::MergePartialFromCodedStream(input)) return false; + if (!value-> + MessageType_WorkAroundCppLookupDefect::MergePartialFromCodedStream(input)) + return false; // Make sure that parsing stopped when the limit was hit, not at an endgroup // tag. if (!input->ConsumedEntireMessage()) return false; @@ -461,21 +475,24 @@ inline void WireFormatLite::WriteEnumNoTag(int value, output->WriteVarint32SignExtended(value); } -template<typename MessageType> -inline void WireFormatLite::WriteGroupNoVirtual(int field_number, - const MessageType& value, - io::CodedOutputStream* output) { +// See comment on ReadGroupNoVirtual to understand the need for this template +// parameter name. +template<typename MessageType_WorkAroundCppLookupDefect> +inline void WireFormatLite::WriteGroupNoVirtual( + int field_number, const MessageType_WorkAroundCppLookupDefect& value, + io::CodedOutputStream* output) { WriteTag(field_number, WIRETYPE_START_GROUP, output); - value.MessageType::SerializeWithCachedSizes(output); + value.MessageType_WorkAroundCppLookupDefect::SerializeWithCachedSizes(output); WriteTag(field_number, WIRETYPE_END_GROUP, output); } -template<typename MessageType> -inline void WireFormatLite::WriteMessageNoVirtual(int field_number, - const MessageType& value, - io::CodedOutputStream* output) { +template<typename MessageType_WorkAroundCppLookupDefect> +inline void WireFormatLite::WriteMessageNoVirtual( + int field_number, const MessageType_WorkAroundCppLookupDefect& value, + io::CodedOutputStream* output) { WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(value.MessageType::GetCachedSize()); - value.MessageType::SerializeWithCachedSizes(output); + output->WriteVarint32( + value.MessageType_WorkAroundCppLookupDefect::GetCachedSize()); + value.MessageType_WorkAroundCppLookupDefect::SerializeWithCachedSizes(output); } // =================================================================== @@ -672,20 +689,26 @@ inline uint8* WireFormatLite::WriteMessageToArray(int field_number, return value.SerializeWithCachedSizesToArray(target); } -template<typename MessageType> +// See comment on ReadGroupNoVirtual to understand the need for this template +// parameter name. +template<typename MessageType_WorkAroundCppLookupDefect> inline uint8* WireFormatLite::WriteGroupNoVirtualToArray( - int field_number, const MessageType& value, uint8* target) { + int field_number, const MessageType_WorkAroundCppLookupDefect& value, + uint8* target) { target = WriteTagToArray(field_number, WIRETYPE_START_GROUP, target); - target = value.MessageType::SerializeWithCachedSizesToArray(target); + target = value.MessageType_WorkAroundCppLookupDefect + ::SerializeWithCachedSizesToArray(target); return WriteTagToArray(field_number, WIRETYPE_END_GROUP, target); } -template<typename MessageType> +template<typename MessageType_WorkAroundCppLookupDefect> inline uint8* WireFormatLite::WriteMessageNoVirtualToArray( - int field_number, const MessageType& value, uint8* target) { + int field_number, const MessageType_WorkAroundCppLookupDefect& value, + uint8* target) { target = WriteTagToArray(field_number, WIRETYPE_LENGTH_DELIMITED, target); target = io::CodedOutputStream::WriteVarint32ToArray( - value.MessageType::GetCachedSize(), target); - return value.MessageType::SerializeWithCachedSizesToArray(target); + value.MessageType_WorkAroundCppLookupDefect::GetCachedSize(), target); + return value.MessageType_WorkAroundCppLookupDefect + ::SerializeWithCachedSizesToArray(target); } // =================================================================== @@ -730,13 +753,17 @@ inline int WireFormatLite::MessageSize(const MessageLite& value) { return io::CodedOutputStream::VarintSize32(size) + size; } -template<typename MessageType> -inline int WireFormatLite::GroupSizeNoVirtual(const MessageType& value) { - return value.MessageType::ByteSize(); +// See comment on ReadGroupNoVirtual to understand the need for this template +// parameter name. +template<typename MessageType_WorkAroundCppLookupDefect> +inline int WireFormatLite::GroupSizeNoVirtual( + const MessageType_WorkAroundCppLookupDefect& value) { + return value.MessageType_WorkAroundCppLookupDefect::ByteSize(); } -template<typename MessageType> -inline int WireFormatLite::MessageSizeNoVirtual(const MessageType& value) { - int size = value.MessageType::ByteSize(); +template<typename MessageType_WorkAroundCppLookupDefect> +inline int WireFormatLite::MessageSizeNoVirtual( + const MessageType_WorkAroundCppLookupDefect& value) { + int size = value.MessageType_WorkAroundCppLookupDefect::ByteSize(); return io::CodedOutputStream::VarintSize32(size) + size; } |