From 35a1cc7a7c7cfd205641f15258ca991f6d3ec2bc Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Wed, 1 Apr 2015 17:23:48 -0700 Subject: Added first version of conformance tests. Change-Id: Ib75664194491643f8e4f1503a2ed942a2d1e1655 --- conformance/Makefile.am | 50 +++++ conformance/README.md | 45 +++++ conformance/conformance.proto | 219 ++++++++++++++++++++++ conformance/conformance_cpp.cc | 156 ++++++++++++++++ conformance/conformance_test.cc | 395 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 865 insertions(+) create mode 100644 conformance/Makefile.am create mode 100644 conformance/README.md create mode 100644 conformance/conformance.proto create mode 100644 conformance/conformance_cpp.cc create mode 100644 conformance/conformance_test.cc (limited to 'conformance') diff --git a/conformance/Makefile.am b/conformance/Makefile.am new file mode 100644 index 00000000..2e1de17d --- /dev/null +++ b/conformance/Makefile.am @@ -0,0 +1,50 @@ +## Process this file with automake to produce Makefile.in + +protoc_inputs = \ + conformance.proto + +protoc_outputs = \ + conformance.pb.cc \ + conformance.pb.h + +bin_PROGRAMS = conformance-test conformance-cpp + +conformance_test_LDADD = $(top_srcdir)/src/libprotobuf.la +conformance_test_SOURCES = conformance_test.cc +nodist_conformance_test_SOURCES = conformance.pb.cc +conformance_test_CPPFLAGS = -I$(top_srcdir)/src + +conformance_cpp_LDADD = $(top_srcdir)/src/libprotobuf.la +conformance_cpp_SOURCES = conformance_cpp.cc +nodist_conformance_cpp_SOURCES = conformance.pb.cc +conformance_cpp_CPPFLAGS = -I$(top_srcdir)/src + +if USE_EXTERNAL_PROTOC + +unittest_proto_middleman: $(protoc_inputs) + $(PROTOC) -I$(srcdir) --cpp_out=. $^ + touch unittest_proto_middleman + +else + +# We have to cd to $(srcdir) before executing protoc because $(protoc_inputs) is +# relative to srcdir, which may not be the same as the current directory when +# building out-of-tree. +unittest_proto_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(protoc_inputs) + oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. --cpp_out=$$oldpwd $(protoc_inputs) ) + touch unittest_proto_middleman + +endif + +$(protoc_outputs): unittest_proto_middleman + +BUILT_SOURCES = $(protoc_outputs) + +CLEANFILES = $(protoc_outputs) unittest_proto_middleman + +MAINTAINERCLEANFILES = \ + Makefile.in + +# Targets for actually running tests. +test_cpp: unittest_proto_middleman conformance-test conformance-cpp + ./conformance-test ./conformance-cpp diff --git a/conformance/README.md b/conformance/README.md new file mode 100644 index 00000000..9388055f --- /dev/null +++ b/conformance/README.md @@ -0,0 +1,45 @@ +Protocol Buffers - Google's data interchange format +=================================================== + +[![Build Status](https://travis-ci.org/google/protobuf.svg?branch=master)](https://travis-ci.org/google/protobuf) + +Copyright 2008 Google Inc. + +This directory contains conformance tests for testing completeness and +correctness of Protocol Buffers implementations. These tests are designed +to be easy to run against any Protocol Buffers implementation. + +This directory contains the tester process `conformance-test`, which +contains all of the tests themselves. Then separate programs written +in whatever language you want to test communicate with the tester +program over a pipe. + +Before running any of these tests, make sure you run `make` in the base +directory to build `protoc`, since all the tests depend on it. + + $ make + +Then to run the tests against the C++ implementation, run: + + $ cd conformance && make test_cpp + +More tests and languages will be added soon! + +Testing other Protocol Buffer implementations +--------------------------------------------- + +To run these tests against a new Protocol Buffers implementation, write a +program in your language that uses the protobuf implementation you want +to test. This program should implement the testing protocol defined in +[conformance.proto](https://github.com/google/protobuf/blob/master/conformance/conformance.proto). +This is designed to be as easy as possible: the C++ version is only +150 lines and is a good example for what this program should look like +(see [conformance_cpp.cc](https://github.com/google/protobuf/blob/master/conformance/conformance_cpp.cc)). +The program only needs to be able to read from stdin and write to stdout. + +Portability +----------- + +Note that the test runner currently does not work on Windows. Patches +to fix this are welcome! (But please get in touch first to settle on +a general implementation strategy). diff --git a/conformance/conformance.proto b/conformance/conformance.proto new file mode 100644 index 00000000..34fec45f --- /dev/null +++ b/conformance/conformance.proto @@ -0,0 +1,219 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; +package conformance; + +// This defines the conformance testing protocol. This protocol exists between +// the conformance tester process (the "tester") and the process whose protobuf +// implemention is being tested (the "testee"). The tester forks the testee and +// communicates with it over its stdin/stdout: +// +// +--------+ pipe +----------+ +// | tester | <------> | testee | +// | | | | +// | C++ | | any lang | +// +--------+ +----------+ +// +// The tester contains all of the test cases and their expected output. +// The testee is a simple program written in the target language that reads +// each test case and attempts to produce acceptable output for it. +// +// Every test consists of a ConformanceRequest/ConformanceResponse +// request/reply pair. The protocol on the pipe is simply: +// +// 1. tester sends 4-byte length N +// 2. tester sends N bytes representing a ConformanceRequest proto +// 3. testee sends 4-byte length M +// 4. testee sends M bytes representing a ConformanceResponse proto + +// Represents a single test case's input. The testee should: +// +// 1. parse this proto (which should always succeed) +// 2. parse the protobuf or JSON payload in "payload" (which may fail) +// 3. if the parse succeeded, serialize the message in the requested format. +message ConformanceRequest { + // The payload (whether protobuf of JSON) is always for a TestAllTypes proto + // (see below). + oneof payload { + bytes protobuf_payload = 1; + string json_payload = 2; + } + + enum RequestedOutput { + UNSPECIFIED = 0; + PROTOBUF = 1; + JSON = 2; + } + + // Which format should the testee serialize its message to? + optional RequestedOutput requested_output = 3; +} + +// Represents a single test case's output. +message ConformanceResponse { + oneof result { + // This string should be set to indicate parsing failed. The string can + // provide more information about the parse error if it is available. + // + // Setting this string does not necessarily mean the testee failed the + // test. Some of the test cases are intentionally invalid input. + string parse_error = 1; + + // This should be set if some other error occurred. This will always + // indicate that the test failed. The string can provide more information + // about the failure. + string runtime_error = 2; + + // If the input was successfully parsed and the requested output was + // protobuf, serialize it to protobuf and set it in this field. + bytes protobuf_payload = 3; + + // If the input was successfully parsed and the requested output was JSON, + // serialize to JSON and set it in this field. + string json_payload = 4; + } +} + +// This proto includes every type of field in both singular and repeated +// forms. +message TestAllTypes { + message NestedMessage { + optional int32 a = 1; + optional TestAllTypes corecursive = 2; + } + + enum NestedEnum { + FOO = 0; + BAR = 1; + BAZ = 2; + NEG = -1; // Intentionally negative. + } + + // Singular + optional int32 optional_int32 = 1; + optional int64 optional_int64 = 2; + optional uint32 optional_uint32 = 3; + optional uint64 optional_uint64 = 4; + optional sint32 optional_sint32 = 5; + optional sint64 optional_sint64 = 6; + optional fixed32 optional_fixed32 = 7; + optional fixed64 optional_fixed64 = 8; + optional sfixed32 optional_sfixed32 = 9; + optional sfixed64 optional_sfixed64 = 10; + optional float optional_float = 11; + optional double optional_double = 12; + optional bool optional_bool = 13; + optional string optional_string = 14; + optional bytes optional_bytes = 15; + + optional group OptionalGroup = 16 { + optional int32 a = 17; + } + + optional NestedMessage optional_nested_message = 18; + optional ForeignMessage optional_foreign_message = 19; + + optional NestedEnum optional_nested_enum = 21; + optional ForeignEnum optional_foreign_enum = 22; + + optional string optional_string_piece = 24 [ctype=STRING_PIECE]; + optional string optional_cord = 25 [ctype=CORD]; + + optional TestAllTypes recursive_message = 27; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated int64 repeated_int64 = 32; + repeated uint32 repeated_uint32 = 33; + repeated uint64 repeated_uint64 = 34; + repeated sint32 repeated_sint32 = 35; + repeated sint64 repeated_sint64 = 36; + repeated fixed32 repeated_fixed32 = 37; + repeated fixed64 repeated_fixed64 = 38; + repeated sfixed32 repeated_sfixed32 = 39; + repeated sfixed64 repeated_sfixed64 = 40; + repeated float repeated_float = 41; + repeated double repeated_double = 42; + repeated bool repeated_bool = 43; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + + repeated group RepeatedGroup = 46 { + optional int32 a = 47; + } + + repeated NestedMessage repeated_nested_message = 48; + repeated ForeignMessage repeated_foreign_message = 49; + + repeated NestedEnum repeated_nested_enum = 51; + repeated ForeignEnum repeated_foreign_enum = 52; + + repeated string repeated_string_piece = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord = 55 [ctype=CORD]; + + // Map + map < int32, int32> map_int32_int32 = 56; + map < int64, int64> map_int64_int64 = 57; + map < uint32, uint32> map_uint32_uint32 = 58; + map < uint64, uint64> map_uint64_uint64 = 59; + map < sint32, sint32> map_sint32_sint32 = 60; + map < sint64, sint64> map_sint64_sint64 = 61; + map < fixed32, fixed32> map_fixed32_fixed32 = 62; + map < fixed64, fixed64> map_fixed64_fixed64 = 63; + map map_sfixed32_sfixed32 = 64; + map map_sfixed64_sfixed64 = 65; + map < int32, float> map_int32_float = 66; + map < int32, double> map_int32_double = 67; + map < bool, bool> map_bool_bool = 68; + map < string, string> map_string_string = 69; + map < string, bytes> map_string_bytes = 70; + map < string, NestedMessage> map_string_nested_message = 71; + map < string, ForeignMessage> map_string_foreign_message = 72; + map < string, NestedEnum> map_string_nested_enum = 73; + map < string, ForeignEnum> map_string_foreign_enum = 74; + + oneof oneof_field { + uint32 oneof_uint32 = 111; + NestedMessage oneof_nested_message = 112; + string oneof_string = 113; + bytes oneof_bytes = 114; + } +} + +message ForeignMessage { + optional int32 c = 1; +} + +enum ForeignEnum { + FOREIGN_FOO = 0; + FOREIGN_BAR = 1; + FOREIGN_BAZ = 2; +} diff --git a/conformance/conformance_cpp.cc b/conformance/conformance_cpp.cc new file mode 100644 index 00000000..ff47fbbf --- /dev/null +++ b/conformance/conformance_cpp.cc @@ -0,0 +1,156 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include + +#include "conformance.pb.h" + +using std::string; +using conformance::ConformanceRequest; +using conformance::ConformanceResponse; +using conformance::TestAllTypes; + +int test_count = 0; +bool verbose = false; + +bool CheckedRead(int fd, void *buf, size_t len) { + size_t ofs = 0; + while (len > 0) { + ssize_t bytes_read = read(fd, (char*)buf + ofs, len); + + if (bytes_read == 0) return false; + + if (bytes_read < 0) { + GOOGLE_LOG(FATAL) << "Error reading from test runner: " << strerror(errno); + } + + len -= bytes_read; + ofs += bytes_read; + } + + return true; +} + +void CheckedWrite(int fd, const void *buf, size_t len) { + if (write(fd, buf, len) != len) { + GOOGLE_LOG(FATAL) << "Error writing to test runner: " << strerror(errno); + } +} + +void DoTest(const ConformanceRequest& request, ConformanceResponse* response) { + TestAllTypes test_message; + + switch (request.payload_case()) { + case ConformanceRequest::kProtobufPayload: + if (!test_message.ParseFromString(request.protobuf_payload())) { + // Getting parse details would involve something like: + // http://stackoverflow.com/questions/22121922/how-can-i-get-more-details-about-errors-generated-during-protobuf-parsing-c + response->set_parse_error("Parse error (no more details available)."); + return; + } + break; + + case ConformanceRequest::kJsonPayload: + response->set_runtime_error("JSON input is not yet supported."); + break; + + case ConformanceRequest::PAYLOAD_NOT_SET: + GOOGLE_LOG(FATAL) << "Request didn't have payload."; + break; + } + + switch (request.requested_output()) { + case ConformanceRequest::UNSPECIFIED: + GOOGLE_LOG(FATAL) << "Unspecified output format"; + break; + + case ConformanceRequest::PROTOBUF: + test_message.SerializeToString(response->mutable_protobuf_payload()); + break; + + case ConformanceRequest::JSON: + response->set_runtime_error("JSON output is not yet supported."); + break; + } +} + +bool DoTestIo() { + string serialized_input; + string serialized_output; + ConformanceRequest request; + ConformanceResponse response; + uint32_t bytes; + + if (!CheckedRead(STDIN_FILENO, &bytes, sizeof(uint32_t))) { + // EOF. + return false; + } + + serialized_input.resize(bytes); + + if (!CheckedRead(STDIN_FILENO, (char*)serialized_input.c_str(), bytes)) { + GOOGLE_LOG(ERROR) << "Unexpected EOF on stdin. " << strerror(errno); + } + + if (!request.ParseFromString(serialized_input)) { + GOOGLE_LOG(FATAL) << "Parse of ConformanceRequest proto failed."; + return false; + } + + DoTest(request, &response); + + response.SerializeToString(&serialized_output); + + bytes = serialized_output.size(); + CheckedWrite(STDOUT_FILENO, &bytes, sizeof(uint32_t)); + CheckedWrite(STDOUT_FILENO, serialized_output.c_str(), bytes); + + if (verbose) { + fprintf(stderr, "conformance-cpp: request=%s, response=%s\n", + request.ShortDebugString().c_str(), + response.ShortDebugString().c_str()); + } + + test_count++; + + return true; +} + +int main() { + while (1) { + if (!DoTestIo()) { + fprintf(stderr, "conformance-cpp: received EOF from test runner " + "after %d tests, exiting\n", test_count); + return 0; + } + } +} diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc new file mode 100644 index 00000000..ee75031b --- /dev/null +++ b/conformance/conformance_test.cc @@ -0,0 +1,395 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include + +#include "conformance.pb.h" +#include +#include + +using conformance::ConformanceRequest; +using conformance::ConformanceResponse; +using conformance::TestAllTypes; +using google::protobuf::Descriptor; +using google::protobuf::FieldDescriptor; +using google::protobuf::internal::WireFormatLite; +using std::string; + +int write_fd; +int read_fd; +int successes; +int failures; +bool verbose = false; + +string Escape(const string& str) { + // TODO. + return str; +} + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define CHECK_SYSCALL(call) \ + if (call < 0) { \ + perror(#call " " __FILE__ ":" TOSTRING(__LINE__)); \ + exit(1); \ + } + +// TODO(haberman): make this work on Windows, instead of using these +// UNIX-specific APIs. +// +// There is a platform-agnostic API in +// src/google/protobuf/compiler/subprocess.h +// +// However that API only supports sending a single message to the subprocess. +// We really want to be able to send messages and receive responses one at a +// time: +// +// 1. Spawning a new process for each test would take way too long for thousands +// of tests and subprocesses like java that can take 100ms or more to start +// up. +// +// 2. Sending all the tests in one big message and receiving all results in one +// big message would take away our visibility about which test(s) caused a +// crash or other fatal error. It would also give us only a single failure +// instead of all of them. +void SpawnTestProgram(char *executable) { + int toproc_pipe_fd[2]; + int fromproc_pipe_fd[2]; + if (pipe(toproc_pipe_fd) < 0 || pipe(fromproc_pipe_fd) < 0) { + perror("pipe"); + exit(1); + } + + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + + if (pid) { + // Parent. + CHECK_SYSCALL(close(toproc_pipe_fd[0])); + CHECK_SYSCALL(close(fromproc_pipe_fd[1])); + write_fd = toproc_pipe_fd[1]; + read_fd = fromproc_pipe_fd[0]; + } else { + // Child. + CHECK_SYSCALL(close(STDIN_FILENO)); + CHECK_SYSCALL(close(STDOUT_FILENO)); + CHECK_SYSCALL(dup2(toproc_pipe_fd[0], STDIN_FILENO)); + CHECK_SYSCALL(dup2(fromproc_pipe_fd[1], STDOUT_FILENO)); + + CHECK_SYSCALL(close(toproc_pipe_fd[0])); + CHECK_SYSCALL(close(fromproc_pipe_fd[1])); + CHECK_SYSCALL(close(toproc_pipe_fd[1])); + CHECK_SYSCALL(close(fromproc_pipe_fd[0])); + + char *const argv[] = {executable, NULL}; + CHECK_SYSCALL(execv(executable, argv)); // Never returns. + } +} + +/* Invoking of tests **********************************************************/ + +void ReportSuccess() { + successes++; +} + +void ReportFailure(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + failures++; +} + +void CheckedWrite(int fd, const void *buf, size_t len) { + if (write(fd, buf, len) != len) { + GOOGLE_LOG(FATAL) << "Error writing to test program: " << strerror(errno); + } +} + +void CheckedRead(int fd, void *buf, size_t len) { + size_t ofs = 0; + while (len > 0) { + ssize_t bytes_read = read(fd, (char*)buf + ofs, len); + + if (bytes_read == 0) { + GOOGLE_LOG(FATAL) << "Unexpected EOF from test program"; + } else if (bytes_read < 0) { + GOOGLE_LOG(FATAL) << "Error reading from test program: " << strerror(errno); + } + + len -= bytes_read; + ofs += bytes_read; + } +} + +void RunTest(const ConformanceRequest& request, ConformanceResponse* response) { + string serialized; + request.SerializeToString(&serialized); + uint32_t len = serialized.size(); + CheckedWrite(write_fd, &len, sizeof(uint32_t)); + CheckedWrite(write_fd, serialized.c_str(), serialized.size()); + CheckedRead(read_fd, &len, sizeof(uint32_t)); + serialized.resize(len); + CheckedRead(read_fd, (void*)serialized.c_str(), len); + if (!response->ParseFromString(serialized)) { + GOOGLE_LOG(FATAL) << "Could not parse response proto from tested process."; + } + + if (verbose) { + fprintf(stderr, "conformance_test: request=%s, response=%s\n", + request.ShortDebugString().c_str(), + response->ShortDebugString().c_str()); + } +} + +void DoExpectParseFailureForProto(const string& proto, int line) { + ConformanceRequest request; + ConformanceResponse response; + request.set_protobuf_payload(proto); + + // We don't expect output, but if the program erroneously accepts the protobuf + // we let it send its response as this. We must not leave it unspecified. + request.set_requested_output(ConformanceRequest::PROTOBUF); + + RunTest(request, &response); + if (response.result_case() == ConformanceResponse::kParseError) { + ReportSuccess(); + } else { + ReportFailure("Should have failed, but didn't. Line: %d, Request: %s, " + "response: %s\n", + line, + request.ShortDebugString().c_str(), + response.ShortDebugString().c_str()); + } +} + +// Expect that this precise protobuf will cause a parse error. +#define ExpectParseFailureForProto(proto) DoExpectParseFailureForProto(proto, __LINE__) + +// Expect that this protobuf will cause a parse error, even if it is followed +// by valid protobuf data. We can try running this twice: once with this +// data verbatim and once with this data followed by some valid data. +// +// TODO(haberman): implement the second of these. +#define ExpectHardParseFailureForProto(proto) DoExpectParseFailureForProto(proto, __LINE__) + + +/* Routines for building arbitrary protos *************************************/ + +// We would use CodedOutputStream except that we want more freedom to build +// arbitrary protos (even invalid ones). + +const string empty; + +string cat(const string& a, const string& b, + const string& c = empty, + const string& d = empty, + const string& e = empty, + const string& f = empty, + const string& g = empty, + const string& h = empty, + const string& i = empty, + const string& j = empty, + const string& k = empty, + const string& l = empty) { + string ret; + ret.reserve(a.size() + b.size() + c.size() + d.size() + e.size() + f.size() + + g.size() + h.size() + i.size() + j.size() + k.size() + l.size()); + ret.append(a); + ret.append(b); + ret.append(c); + ret.append(d); + ret.append(e); + ret.append(f); + ret.append(g); + ret.append(h); + ret.append(i); + ret.append(j); + ret.append(k); + ret.append(l); + return ret; +} + +// The maximum number of bytes that it takes to encode a 64-bit varint. +#define VARINT_MAX_LEN 10 + +size_t vencode64(uint64_t val, char *buf) { + if (val == 0) { buf[0] = 0; return 1; } + size_t i = 0; + while (val) { + uint8_t byte = val & 0x7fU; + val >>= 7; + if (val) byte |= 0x80U; + buf[i++] = byte; + } + return i; +} + +string varint(uint64_t x) { + char buf[VARINT_MAX_LEN]; + size_t len = vencode64(x, buf); + return string(buf, len); +} + +// TODO: proper byte-swapping for big-endian machines. +string fixed32(void *data) { return string(static_cast(data), 4); } +string fixed64(void *data) { return string(static_cast(data), 8); } + +string delim(const string& buf) { return cat(varint(buf.size()), buf); } +string uint32(uint32_t u32) { return fixed32(&u32); } +string uint64(uint64_t u64) { return fixed64(&u64); } +string flt(float f) { return fixed32(&f); } +string dbl(double d) { return fixed64(&d); } +string zz32(int32_t x) { return varint(WireFormatLite::ZigZagEncode32(x)); } +string zz64(int64_t x) { return varint(WireFormatLite::ZigZagEncode64(x)); } + +string tag(uint32_t fieldnum, char wire_type) { + return varint((fieldnum << 3) | wire_type); +} + +string submsg(uint32_t fn, const string& buf) { + return cat( tag(fn, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), delim(buf) ); +} + +#define UNKNOWN_FIELD 666 + +uint32_t GetFieldNumberForType(WireFormatLite::FieldType type, bool repeated) { + const Descriptor* d = TestAllTypes().GetDescriptor(); + for (int i = 0; i < d->field_count(); i++) { + const FieldDescriptor* f = d->field(i); + if (static_cast(f->type()) == type && + f->is_repeated() == repeated) { + return f->number(); + } + } + GOOGLE_LOG(FATAL) << "Couldn't find field with type " << (int)type; + return 0; +} + +void TestPrematureEOFForType(WireFormatLite::FieldType type) { + // Incomplete values for each wire type. + static const string incompletes[6] = { + string("\x80"), // VARINT + string("abcdefg"), // 64BIT + string("\x80"), // DELIMITED (partial length) + string(), // START_GROUP (no value required) + string(), // END_GROUP (no value required) + string("abc") // 32BIT + }; + + uint32_t fieldnum = GetFieldNumberForType(type, false); + uint32_t rep_fieldnum = GetFieldNumberForType(type, true); + WireFormatLite::WireType wire_type = + WireFormatLite::WireTypeForFieldType(type); + const string& incomplete = incompletes[wire_type]; + + // EOF before a known non-repeated value. + ExpectParseFailureForProto(tag(fieldnum, wire_type)); + + // EOF before a known repeated value. + ExpectParseFailureForProto(tag(rep_fieldnum, wire_type)); + + // EOF before an unknown value. + ExpectParseFailureForProto(tag(UNKNOWN_FIELD, wire_type)); + + // EOF inside a known non-repeated value. + ExpectParseFailureForProto( + cat( tag(fieldnum, wire_type), incomplete )); + + // EOF inside a known repeated value. + ExpectParseFailureForProto( + cat( tag(rep_fieldnum, wire_type), incomplete )); + + // EOF inside an unknown value. + ExpectParseFailureForProto( + cat( tag(UNKNOWN_FIELD, wire_type), incomplete )); + + if (wire_type == WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + // EOF in the middle of delimited data for known non-repeated value. + ExpectParseFailureForProto( + cat( tag(fieldnum, wire_type), varint(1) )); + + // EOF in the middle of delimited data for known repeated value. + ExpectParseFailureForProto( + cat( tag(rep_fieldnum, wire_type), varint(1) )); + + // EOF in the middle of delimited data for unknown value. + ExpectParseFailureForProto( + cat( tag(UNKNOWN_FIELD, wire_type), varint(1) )); + + if (type == WireFormatLite::TYPE_MESSAGE) { + // Submessage ends in the middle of a value. + string incomplete_submsg = + cat( tag(WireFormatLite::TYPE_INT32, WireFormatLite::WIRETYPE_VARINT), + incompletes[WireFormatLite::WIRETYPE_VARINT] ); + ExpectHardParseFailureForProto( + cat( tag(fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), + varint(incomplete_submsg.size()), + incomplete_submsg )); + } + } else if (type != WireFormatLite::TYPE_GROUP) { + // Non-delimited, non-group: eligible for packing. + + // Packed region ends in the middle of a value. + ExpectHardParseFailureForProto( + cat( tag(rep_fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), + varint(incomplete.size()), + incomplete )); + + // EOF in the middle of packed region. + ExpectParseFailureForProto( + cat( tag(rep_fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), + varint(1) )); + } +} + + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(stderr, "Usage: conformance_test \n"); + exit(1); + } + + SpawnTestProgram(argv[1]); + + for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) { + TestPrematureEOFForType(static_cast(i)); + } + + fprintf(stderr, "conformance_test: completed %d tests for %s, %d successes, " + "%d failures.\n", successes + failures, argv[1], successes, + failures); +} -- cgit v1.2.3 From 4e63b52088ce409bbe43e4a37863d43b136143f0 Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Tue, 14 Apr 2015 13:45:39 -0700 Subject: Factored conformance tests so they can run in-process. This is necessary for testing on iOS. Change-Id: I54ec1e3aa2e9fbfff9a6cd6580920a6a62069b63 --- conformance/Makefile.am | 14 +- conformance/conformance.proto | 30 ++-- conformance/conformance_test.cc | 265 +++++++++++---------------------- conformance/conformance_test.h | 109 ++++++++++++++ conformance/conformance_test_runner.cc | 196 ++++++++++++++++++++++++ 5 files changed, 415 insertions(+), 199 deletions(-) create mode 100644 conformance/conformance_test.h create mode 100644 conformance/conformance_test_runner.cc (limited to 'conformance') diff --git a/conformance/Makefile.am b/conformance/Makefile.am index 2e1de17d..0c4eae75 100644 --- a/conformance/Makefile.am +++ b/conformance/Makefile.am @@ -7,12 +7,12 @@ protoc_outputs = \ conformance.pb.cc \ conformance.pb.h -bin_PROGRAMS = conformance-test conformance-cpp +bin_PROGRAMS = conformance-test-runner conformance-cpp -conformance_test_LDADD = $(top_srcdir)/src/libprotobuf.la -conformance_test_SOURCES = conformance_test.cc -nodist_conformance_test_SOURCES = conformance.pb.cc -conformance_test_CPPFLAGS = -I$(top_srcdir)/src +conformance_test_runner_LDADD = $(top_srcdir)/src/libprotobuf.la +conformance_test_runner_SOURCES = conformance_test.cc conformance_test_runner.cc +nodist_conformance_test_runner_SOURCES = conformance.pb.cc +conformance_test_runner_CPPFLAGS = -I$(top_srcdir)/src conformance_cpp_LDADD = $(top_srcdir)/src/libprotobuf.la conformance_cpp_SOURCES = conformance_cpp.cc @@ -46,5 +46,5 @@ MAINTAINERCLEANFILES = \ Makefile.in # Targets for actually running tests. -test_cpp: unittest_proto_middleman conformance-test conformance-cpp - ./conformance-test ./conformance-cpp +test_cpp: unittest_proto_middleman conformance-test-runner conformance-cpp + ./conformance-test-runner ./conformance-cpp diff --git a/conformance/conformance.proto b/conformance/conformance.proto index 34fec45f..0fb66cf9 100644 --- a/conformance/conformance.proto +++ b/conformance/conformance.proto @@ -32,27 +32,23 @@ syntax = "proto3"; package conformance; // This defines the conformance testing protocol. This protocol exists between -// the conformance tester process (the "tester") and the process whose protobuf -// implemention is being tested (the "testee"). The tester forks the testee and -// communicates with it over its stdin/stdout: +// the conformance test suite itself and the code being tested. For each test, +// the suite will send a ConformanceRequest message and expect a +// ConformanceResponse message. // -// +--------+ pipe +----------+ -// | tester | <------> | testee | -// | | | | -// | C++ | | any lang | -// +--------+ +----------+ +// You can either run the tests in two different ways: // -// The tester contains all of the test cases and their expected output. -// The testee is a simple program written in the target language that reads -// each test case and attempts to produce acceptable output for it. +// 1. in-process (using the interface in conformance_test.h). // -// Every test consists of a ConformanceRequest/ConformanceResponse -// request/reply pair. The protocol on the pipe is simply: +// 2. as a sub-process communicating over a pipe. Information about how to +// do this is in conformance_test_runner.cc. // -// 1. tester sends 4-byte length N -// 2. tester sends N bytes representing a ConformanceRequest proto -// 3. testee sends 4-byte length M -// 4. testee sends M bytes representing a ConformanceResponse proto +// Pros/cons of the two approaches: +// +// - running as a sub-process is much simpler for languages other than C/C++. +// +// - running as a sub-process may be more tricky in unusual environments like +// iOS apps, where fork/stdin/stdout are not available. // Represents a single test case's input. The testee should: // diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc index ee75031b..61029e44 100644 --- a/conformance/conformance_test.cc +++ b/conformance/conformance_test.cc @@ -28,13 +28,13 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include #include -#include #include #include "conformance.pb.h" +#include "conformance_test.h" #include +#include #include using conformance::ConformanceRequest; @@ -45,167 +45,7 @@ using google::protobuf::FieldDescriptor; using google::protobuf::internal::WireFormatLite; using std::string; -int write_fd; -int read_fd; -int successes; -int failures; -bool verbose = false; - -string Escape(const string& str) { - // TODO. - return str; -} - -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) -#define CHECK_SYSCALL(call) \ - if (call < 0) { \ - perror(#call " " __FILE__ ":" TOSTRING(__LINE__)); \ - exit(1); \ - } - -// TODO(haberman): make this work on Windows, instead of using these -// UNIX-specific APIs. -// -// There is a platform-agnostic API in -// src/google/protobuf/compiler/subprocess.h -// -// However that API only supports sending a single message to the subprocess. -// We really want to be able to send messages and receive responses one at a -// time: -// -// 1. Spawning a new process for each test would take way too long for thousands -// of tests and subprocesses like java that can take 100ms or more to start -// up. -// -// 2. Sending all the tests in one big message and receiving all results in one -// big message would take away our visibility about which test(s) caused a -// crash or other fatal error. It would also give us only a single failure -// instead of all of them. -void SpawnTestProgram(char *executable) { - int toproc_pipe_fd[2]; - int fromproc_pipe_fd[2]; - if (pipe(toproc_pipe_fd) < 0 || pipe(fromproc_pipe_fd) < 0) { - perror("pipe"); - exit(1); - } - - pid_t pid = fork(); - if (pid < 0) { - perror("fork"); - exit(1); - } - - if (pid) { - // Parent. - CHECK_SYSCALL(close(toproc_pipe_fd[0])); - CHECK_SYSCALL(close(fromproc_pipe_fd[1])); - write_fd = toproc_pipe_fd[1]; - read_fd = fromproc_pipe_fd[0]; - } else { - // Child. - CHECK_SYSCALL(close(STDIN_FILENO)); - CHECK_SYSCALL(close(STDOUT_FILENO)); - CHECK_SYSCALL(dup2(toproc_pipe_fd[0], STDIN_FILENO)); - CHECK_SYSCALL(dup2(fromproc_pipe_fd[1], STDOUT_FILENO)); - - CHECK_SYSCALL(close(toproc_pipe_fd[0])); - CHECK_SYSCALL(close(fromproc_pipe_fd[1])); - CHECK_SYSCALL(close(toproc_pipe_fd[1])); - CHECK_SYSCALL(close(fromproc_pipe_fd[0])); - - char *const argv[] = {executable, NULL}; - CHECK_SYSCALL(execv(executable, argv)); // Never returns. - } -} - -/* Invoking of tests **********************************************************/ - -void ReportSuccess() { - successes++; -} - -void ReportFailure(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - failures++; -} - -void CheckedWrite(int fd, const void *buf, size_t len) { - if (write(fd, buf, len) != len) { - GOOGLE_LOG(FATAL) << "Error writing to test program: " << strerror(errno); - } -} - -void CheckedRead(int fd, void *buf, size_t len) { - size_t ofs = 0; - while (len > 0) { - ssize_t bytes_read = read(fd, (char*)buf + ofs, len); - - if (bytes_read == 0) { - GOOGLE_LOG(FATAL) << "Unexpected EOF from test program"; - } else if (bytes_read < 0) { - GOOGLE_LOG(FATAL) << "Error reading from test program: " << strerror(errno); - } - - len -= bytes_read; - ofs += bytes_read; - } -} - -void RunTest(const ConformanceRequest& request, ConformanceResponse* response) { - string serialized; - request.SerializeToString(&serialized); - uint32_t len = serialized.size(); - CheckedWrite(write_fd, &len, sizeof(uint32_t)); - CheckedWrite(write_fd, serialized.c_str(), serialized.size()); - CheckedRead(read_fd, &len, sizeof(uint32_t)); - serialized.resize(len); - CheckedRead(read_fd, (void*)serialized.c_str(), len); - if (!response->ParseFromString(serialized)) { - GOOGLE_LOG(FATAL) << "Could not parse response proto from tested process."; - } - - if (verbose) { - fprintf(stderr, "conformance_test: request=%s, response=%s\n", - request.ShortDebugString().c_str(), - response->ShortDebugString().c_str()); - } -} - -void DoExpectParseFailureForProto(const string& proto, int line) { - ConformanceRequest request; - ConformanceResponse response; - request.set_protobuf_payload(proto); - - // We don't expect output, but if the program erroneously accepts the protobuf - // we let it send its response as this. We must not leave it unspecified. - request.set_requested_output(ConformanceRequest::PROTOBUF); - - RunTest(request, &response); - if (response.result_case() == ConformanceResponse::kParseError) { - ReportSuccess(); - } else { - ReportFailure("Should have failed, but didn't. Line: %d, Request: %s, " - "response: %s\n", - line, - request.ShortDebugString().c_str(), - response.ShortDebugString().c_str()); - } -} - -// Expect that this precise protobuf will cause a parse error. -#define ExpectParseFailureForProto(proto) DoExpectParseFailureForProto(proto, __LINE__) - -// Expect that this protobuf will cause a parse error, even if it is followed -// by valid protobuf data. We can try running this twice: once with this -// data verbatim and once with this data followed by some valid data. -// -// TODO(haberman): implement the second of these. -#define ExpectHardParseFailureForProto(proto) DoExpectParseFailureForProto(proto, __LINE__) - +namespace { /* Routines for building arbitrary protos *************************************/ @@ -299,7 +139,78 @@ uint32_t GetFieldNumberForType(WireFormatLite::FieldType type, bool repeated) { return 0; } -void TestPrematureEOFForType(WireFormatLite::FieldType type) { +} // anonymous namespace + +namespace google { +namespace protobuf { + +void ConformanceTestSuite::ReportSuccess() { + successes_++; +} + +void ConformanceTestSuite::ReportFailure(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + StringAppendV(&output_, fmt, args); + va_end(args); + failures_++; +} + +void ConformanceTestSuite::RunTest(const ConformanceRequest& request, + ConformanceResponse* response) { + string serialized_request; + string serialized_response; + request.SerializeToString(&serialized_request); + + runner_->RunTest(serialized_request, &serialized_response); + + if (!response->ParseFromString(serialized_response)) { + response->Clear(); + response->set_runtime_error("response proto could not be parsed."); + } + + if (verbose_) { + StringAppendF(&output_, "conformance test: request=%s, response=%s\n", + request.ShortDebugString().c_str(), + response->ShortDebugString().c_str()); + } +} + +void ConformanceTestSuite::DoExpectParseFailureForProto(const string& proto, + int line) { + ConformanceRequest request; + ConformanceResponse response; + request.set_protobuf_payload(proto); + + // We don't expect output, but if the program erroneously accepts the protobuf + // we let it send its response as this. We must not leave it unspecified. + request.set_requested_output(ConformanceRequest::PROTOBUF); + + RunTest(request, &response); + if (response.result_case() == ConformanceResponse::kParseError) { + ReportSuccess(); + } else { + ReportFailure("Should have failed, but didn't. Line: %d, Request: %s, " + "response: %s\n", + line, + request.ShortDebugString().c_str(), + response.ShortDebugString().c_str()); + } +} + +// Expect that this precise protobuf will cause a parse error. +#define ExpectParseFailureForProto(proto) DoExpectParseFailureForProto(proto, __LINE__) + +// Expect that this protobuf will cause a parse error, even if it is followed +// by valid protobuf data. We can try running this twice: once with this +// data verbatim and once with this data followed by some valid data. +// +// TODO(haberman): implement the second of these. +#define ExpectHardParseFailureForProto(proto) DoExpectParseFailureForProto(proto, __LINE__) + + +void ConformanceTestSuite::TestPrematureEOFForType( + WireFormatLite::FieldType type) { // Incomplete values for each wire type. static const string incompletes[6] = { string("\x80"), // VARINT @@ -376,20 +287,24 @@ void TestPrematureEOFForType(WireFormatLite::FieldType type) { } } - -int main(int argc, char *argv[]) { - if (argc < 2) { - fprintf(stderr, "Usage: conformance_test \n"); - exit(1); - } - - SpawnTestProgram(argv[1]); +void ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, + std::string* output) { + runner_ = runner; + output_.clear(); + successes_ = 0; + failures_ = 0; for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) { TestPrematureEOFForType(static_cast(i)); } - fprintf(stderr, "conformance_test: completed %d tests for %s, %d successes, " - "%d failures.\n", successes + failures, argv[1], successes, - failures); + StringAppendF(&output_, + "CONFORMANCE SUITE FINISHED: completed %d tests, %d successes, " + "%d failures.\n", + successes_ + failures_, successes_, failures_); + + output->assign(output_); } + +} // namespace protobuf +} // namespace google diff --git a/conformance/conformance_test.h b/conformance/conformance_test.h new file mode 100644 index 00000000..c16f9c0b --- /dev/null +++ b/conformance/conformance_test.h @@ -0,0 +1,109 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// This file defines a protocol for running the conformance test suite +// in-process. In other words, the suite itself will run in the same process as +// the code under test. +// +// For pros and cons of this approach, please see conformance.proto. + +#ifndef CONFORMANCE_CONFORMANCE_TEST_H +#define CONFORMANCE_CONFORMANCE_TEST_H + +#include +#include + +namespace conformance { +class ConformanceRequest; +class ConformanceResponse; +} // namespace conformance + +namespace google { +namespace protobuf { + +class ConformanceTestRunner { + public: + // Call to run a single conformance test. + // + // "input" is a serialized conformance.ConformanceRequest. + // "output" should be set to a serialized conformance.ConformanceResponse. + // + // If there is any error in running the test itself, set "runtime_error" in + // the response. + virtual void RunTest(const std::string& input, std::string* output) = 0; +}; + +// Class representing the test suite itself. To run it, implement your own +// class derived from ConformanceTestRunner and then write code like: +// +// class MyConformanceTestRunner : public ConformanceTestRunner { +// public: +// virtual void RunTest(...) { +// // INSERT YOUR FRAMEWORK-SPECIFIC CODE HERE. +// } +// }; +// +// int main() { +// MyConformanceTestRunner runner; +// google::protobuf::ConformanceTestSuite suite; +// +// std::string output; +// suite.RunSuite(&runner, &output); +// } +// +class ConformanceTestSuite { + public: + ConformanceTestSuite() : verbose_(false) {} + + // Run all the conformance tests against the given test runner. + // Test output will be stored in "output". + void RunSuite(ConformanceTestRunner* runner, std::string* output); + + private: + void ReportSuccess(); + void ReportFailure(const char* fmt, ...); + void RunTest(const conformance::ConformanceRequest& request, + conformance::ConformanceResponse* response); + void DoExpectParseFailureForProto(const std::string& proto, int line); + void TestPrematureEOFForType( + google::protobuf::internal::WireFormatLite::FieldType type); + + ConformanceTestRunner* runner_; + int successes_; + int failures_; + bool verbose_; + std::string output_; +}; + +} // namespace protobuf +} // namespace google + +#endif // CONFORMANCE_CONFORMANCE_TEST_H diff --git a/conformance/conformance_test_runner.cc b/conformance/conformance_test_runner.cc new file mode 100644 index 00000000..21277700 --- /dev/null +++ b/conformance/conformance_test_runner.cc @@ -0,0 +1,196 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file contains a program for running the test suite in a separate +// process. The other alternative is to run the suite in-process. See +// conformance.proto for pros/cons of these two options. +// +// This program will fork the process under test and communicate with it over +// its stdin/stdout: +// +// +--------+ pipe +----------+ +// | tester | <------> | testee | +// | | | | +// | C++ | | any lang | +// +--------+ +----------+ +// +// The tester contains all of the test cases and their expected output. +// The testee is a simple program written in the target language that reads +// each test case and attempts to produce acceptable output for it. +// +// Every test consists of a ConformanceRequest/ConformanceResponse +// request/reply pair. The protocol on the pipe is simply: +// +// 1. tester sends 4-byte length N +// 2. tester sends N bytes representing a ConformanceRequest proto +// 3. testee sends 4-byte length M +// 4. testee sends M bytes representing a ConformanceResponse proto + +#include +#include + +#include "conformance.pb.h" +#include "conformance_test.h" + +using conformance::ConformanceRequest; +using conformance::ConformanceResponse; +using google::protobuf::internal::scoped_array; + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define CHECK_SYSCALL(call) \ + if (call < 0) { \ + perror(#call " " __FILE__ ":" TOSTRING(__LINE__)); \ + exit(1); \ + } + +// Test runner that spawns the process being tested and communicates with it +// over a pipe. +class ForkPipeRunner : public google::protobuf::ConformanceTestRunner { + public: + ForkPipeRunner(const std::string &executable) + : executable_(executable), running_(false) {} + + void RunTest(const std::string& request, std::string* response) { + if (!running_) { + SpawnTestProgram(); + } + + uint32_t len = request.size(); + CheckedWrite(write_fd_, &len, sizeof(uint32_t)); + CheckedWrite(write_fd_, request.c_str(), request.size()); + CheckedRead(read_fd_, &len, sizeof(uint32_t)); + response->resize(len); + CheckedRead(read_fd_, (void*)response->c_str(), len); + } + + private: + // TODO(haberman): make this work on Windows, instead of using these + // UNIX-specific APIs. + // + // There is a platform-agnostic API in + // src/google/protobuf/compiler/subprocess.h + // + // However that API only supports sending a single message to the subprocess. + // We really want to be able to send messages and receive responses one at a + // time: + // + // 1. Spawning a new process for each test would take way too long for thousands + // of tests and subprocesses like java that can take 100ms or more to start + // up. + // + // 2. Sending all the tests in one big message and receiving all results in one + // big message would take away our visibility about which test(s) caused a + // crash or other fatal error. It would also give us only a single failure + // instead of all of them. + void SpawnTestProgram() { + int toproc_pipe_fd[2]; + int fromproc_pipe_fd[2]; + if (pipe(toproc_pipe_fd) < 0 || pipe(fromproc_pipe_fd) < 0) { + perror("pipe"); + exit(1); + } + + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + + if (pid) { + // Parent. + CHECK_SYSCALL(close(toproc_pipe_fd[0])); + CHECK_SYSCALL(close(fromproc_pipe_fd[1])); + write_fd_ = toproc_pipe_fd[1]; + read_fd_ = fromproc_pipe_fd[0]; + running_ = true; + } else { + // Child. + CHECK_SYSCALL(close(STDIN_FILENO)); + CHECK_SYSCALL(close(STDOUT_FILENO)); + CHECK_SYSCALL(dup2(toproc_pipe_fd[0], STDIN_FILENO)); + CHECK_SYSCALL(dup2(fromproc_pipe_fd[1], STDOUT_FILENO)); + + CHECK_SYSCALL(close(toproc_pipe_fd[0])); + CHECK_SYSCALL(close(fromproc_pipe_fd[1])); + CHECK_SYSCALL(close(toproc_pipe_fd[1])); + CHECK_SYSCALL(close(fromproc_pipe_fd[0])); + + scoped_array executable(new char[executable_.size()]); + memcpy(executable.get(), executable_.c_str(), executable_.size()); + + char *const argv[] = {executable.get(), NULL}; + CHECK_SYSCALL(execv(executable.get(), argv)); // Never returns. + } + } + + void CheckedWrite(int fd, const void *buf, size_t len) { + if (write(fd, buf, len) != len) { + GOOGLE_LOG(FATAL) << "Error writing to test program: " << strerror(errno); + } + } + + void CheckedRead(int fd, void *buf, size_t len) { + size_t ofs = 0; + while (len > 0) { + ssize_t bytes_read = read(fd, (char*)buf + ofs, len); + + if (bytes_read == 0) { + GOOGLE_LOG(FATAL) << "Unexpected EOF from test program"; + } else if (bytes_read < 0) { + GOOGLE_LOG(FATAL) << "Error reading from test program: " << strerror(errno); + } + + len -= bytes_read; + ofs += bytes_read; + } + } + + int write_fd_; + int read_fd_; + bool running_; + std::string executable_; +}; + + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(stderr, "Usage: conformance-test-runner \n"); + exit(1); + } + + ForkPipeRunner runner(argv[1]); + google::protobuf::ConformanceTestSuite suite; + + std::string output; + suite.RunSuite(&runner, &output); + + fwrite(output.c_str(), 1, output.size(), stderr); +} -- cgit v1.2.3 From 420f938bac63af49c00292c7c09eb0d237ce4e0b Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Thu, 16 Apr 2015 12:50:39 -0700 Subject: Added conformance test support for Java. Change-Id: I4c81808e6ace77d2b5737a43417045321b0b10f0 --- conformance/ConformanceJava.java | 120 +++++++++++++++++++++++++++++++++ conformance/Makefile.am | 31 ++++++--- conformance/conformance.proto | 1 + conformance/conformance_test_runner.cc | 4 +- 4 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 conformance/ConformanceJava.java (limited to 'conformance') diff --git a/conformance/ConformanceJava.java b/conformance/ConformanceJava.java new file mode 100644 index 00000000..c1a53141 --- /dev/null +++ b/conformance/ConformanceJava.java @@ -0,0 +1,120 @@ + +import com.google.protobuf.conformance.Conformance; +import com.google.protobuf.InvalidProtocolBufferException; + +class ConformanceJava { + private int testCount = 0; + + private boolean readFromStdin(byte[] buf, int len) throws Exception { + int ofs = 0; + while (len > 0) { + int read = System.in.read(buf, ofs, len); + if (read == -1) { + return false; // EOF + } + ofs += read; + len -= read; + } + + return true; + } + + private void writeToStdout(byte[] buf) throws Exception { + System.out.write(buf); + } + + // Returns -1 on EOF (the actual values will always be positive). + private int readLittleEndianIntFromStdin() throws Exception { + byte[] buf = new byte[4]; + if (!readFromStdin(buf, 4)) { + return -1; + } + return buf[0] | (buf[1] << 1) | (buf[2] << 2) | (buf[3] << 3); + } + + private void writeLittleEndianIntToStdout(int val) throws Exception { + byte[] buf = new byte[4]; + buf[0] = (byte)val; + buf[1] = (byte)(val >> 8); + buf[2] = (byte)(val >> 16); + buf[3] = (byte)(val >> 24); + writeToStdout(buf); + } + + private Conformance.ConformanceResponse doTest(Conformance.ConformanceRequest request) { + Conformance.TestAllTypes testMessage; + + switch (request.getPayloadCase()) { + case PROTOBUF_PAYLOAD: { + try { + testMessage = Conformance.TestAllTypes.parseFrom(request.getProtobufPayload()); + } catch (InvalidProtocolBufferException e) { + return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build(); + } + break; + } + case JSON_PAYLOAD: { + return Conformance.ConformanceResponse.newBuilder().setRuntimeError("JSON not yet supported.").build(); + } + case PAYLOAD_NOT_SET: { + throw new RuntimeException("Request didn't have payload."); + } + + default: { + throw new RuntimeException("Unexpected payload case."); + } + } + + switch (request.getRequestedOutput()) { + case UNSPECIFIED: + throw new RuntimeException("Unspecified output format."); + + case PROTOBUF: + return Conformance.ConformanceResponse.newBuilder().setProtobufPayload(testMessage.toByteString()).build(); + + case JSON: + return Conformance.ConformanceResponse.newBuilder().setRuntimeError("JSON not yet supported.").build(); + + default: { + throw new RuntimeException("Unexpected request output."); + } + } + } + + private boolean doTestIo() throws Exception { + int bytes = readLittleEndianIntFromStdin(); + + if (bytes == -1) { + return false; // EOF + } + + byte[] serializedInput = new byte[bytes]; + + if (!readFromStdin(serializedInput, bytes)) { + throw new RuntimeException("Unexpected EOF from test program."); + } + + Conformance.ConformanceRequest request = + Conformance.ConformanceRequest.parseFrom(serializedInput); + Conformance.ConformanceResponse response = doTest(request); + byte[] serializedOutput = response.toByteArray(); + + writeLittleEndianIntToStdout(serializedOutput.length); + writeToStdout(serializedOutput); + + return true; + } + + public void run() throws Exception { + while (doTestIo()) { + // Empty. + } + + System.err.println("ConformanceJava: received EOF from test runner after " + + this.testCount + " tests"); + } + + public static void main(String[] args) throws Exception { + new ConformanceJava().run(); + } +} diff --git a/conformance/Makefile.am b/conformance/Makefile.am index 0c4eae75..59bb2576 100644 --- a/conformance/Makefile.am +++ b/conformance/Makefile.am @@ -21,30 +21,43 @@ conformance_cpp_CPPFLAGS = -I$(top_srcdir)/src if USE_EXTERNAL_PROTOC -unittest_proto_middleman: $(protoc_inputs) - $(PROTOC) -I$(srcdir) --cpp_out=. $^ - touch unittest_proto_middleman +protoc_middleman: $(protoc_inputs) + $(PROTOC) -I$(srcdir) --cpp_out=. --java_out=. $^ + touch protoc_middleman else # We have to cd to $(srcdir) before executing protoc because $(protoc_inputs) is # relative to srcdir, which may not be the same as the current directory when # building out-of-tree. -unittest_proto_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(protoc_inputs) - oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. --cpp_out=$$oldpwd $(protoc_inputs) ) - touch unittest_proto_middleman +protoc_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(protoc_inputs) + oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. --cpp_out=$$oldpwd --java_out=$$oldpwd $(protoc_inputs) ) + touch protoc_middleman endif -$(protoc_outputs): unittest_proto_middleman +$(protoc_outputs): protoc_middleman BUILT_SOURCES = $(protoc_outputs) -CLEANFILES = $(protoc_outputs) unittest_proto_middleman +CLEANFILES = $(protoc_outputs) protoc_middleman javac_middleman conformance-java MAINTAINERCLEANFILES = \ Makefile.in +javac_middleman: ConformanceJava.java protoc_middleman + javac ConformanceJava.java com/google/protobuf/conformance/Conformance.java + @touch javac_middleman + +conformance-java: javac_middleman + @echo "Writing shortcut script conformance-java..." + @echo '#! /bin/sh' > conformance-java + @echo 'java -classpath .:$$CLASSPATH ConformanceJava "$$@"' >> conformance-java + @chmod +x conformance-java + # Targets for actually running tests. -test_cpp: unittest_proto_middleman conformance-test-runner conformance-cpp +test_cpp: protoc_middleman conformance-test-runner conformance-cpp ./conformance-test-runner ./conformance-cpp + +test_java: protoc_middleman conformance-test-runner conformance-java + ./conformance-test-runner ./conformance-java diff --git a/conformance/conformance.proto b/conformance/conformance.proto index 0fb66cf9..7b676898 100644 --- a/conformance/conformance.proto +++ b/conformance/conformance.proto @@ -30,6 +30,7 @@ syntax = "proto3"; package conformance; +option java_package = "com.google.protobuf.conformance"; // This defines the conformance testing protocol. This protocol exists between // the conformance test suite itself and the code being tested. For each test, diff --git a/conformance/conformance_test_runner.cc b/conformance/conformance_test_runner.cc index 21277700..ca5877bd 100644 --- a/conformance/conformance_test_runner.cc +++ b/conformance/conformance_test_runner.cc @@ -48,9 +48,9 @@ // Every test consists of a ConformanceRequest/ConformanceResponse // request/reply pair. The protocol on the pipe is simply: // -// 1. tester sends 4-byte length N +// 1. tester sends 4-byte length N (little endian) // 2. tester sends N bytes representing a ConformanceRequest proto -// 3. testee sends 4-byte length M +// 3. testee sends 4-byte length M (little endian) // 4. testee sends M bytes representing a ConformanceResponse proto #include -- cgit v1.2.3 From 9681ef42733d4cadf2b560fedfbba5da872cb59e Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Wed, 6 May 2015 14:07:44 -0700 Subject: Run conformance tests against Java for the Travis build. Change-Id: I8ef8664f7facf86028be3f4f0d5d2efc8a685d6d --- .travis.yml | 1 + conformance/Makefile.am | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'conformance') diff --git a/.travis.yml b/.travis.yml index cbddac58..354be896 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ script: - cd python && python setup.py build && python setup.py test && cd .. - export LD_LIBRARY_PATH=../src/.libs - cd python && python setup.py build --cpp_implementation && python setup.py test --cpp_implementation && cd .. + - cd conformance && make test_java && cd .. - make distcheck -j2 notifications: email: false diff --git a/conformance/Makefile.am b/conformance/Makefile.am index 59bb2576..95343f14 100644 --- a/conformance/Makefile.am +++ b/conformance/Makefile.am @@ -46,13 +46,13 @@ MAINTAINERCLEANFILES = \ Makefile.in javac_middleman: ConformanceJava.java protoc_middleman - javac ConformanceJava.java com/google/protobuf/conformance/Conformance.java + javac -classpath ../java/target/classes ConformanceJava.java com/google/protobuf/conformance/Conformance.java @touch javac_middleman conformance-java: javac_middleman @echo "Writing shortcut script conformance-java..." @echo '#! /bin/sh' > conformance-java - @echo 'java -classpath .:$$CLASSPATH ConformanceJava "$$@"' >> conformance-java + @echo 'java -classpath .:../java/target/classes ConformanceJava "$$@"' >> conformance-java @chmod +x conformance-java # Targets for actually running tests. -- cgit v1.2.3 From 5db217305f37a79eeccd70f000088a06ec82fcec Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Thu, 21 May 2015 14:28:59 -0700 Subject: down-integrate internal changes --- .gitignore | 1 + Android.mk | 6 + Makefile.am | 14 + conformance/conformance.proto | 68 +- conformance/conformance_test.cc | 1 + editors/proto.vim | 2 +- editors/protobuf-mode.el | 2 +- generate_descriptor_proto.sh | 5 + java/pom.xml | 15 + .../com/google/protobuf/AbstractMessageLite.java | 16 +- .../com/google/protobuf/AbstractProtobufList.java | 136 ++ .../java/com/google/protobuf/BooleanArrayList.java | 244 +++ .../main/java/com/google/protobuf/Descriptors.java | 42 +- .../java/com/google/protobuf/DoubleArrayList.java | 243 +++ .../java/com/google/protobuf/FloatArrayList.java | 242 +++ .../com/google/protobuf/GeneratedMessageLite.java | 396 +++-- .../java/com/google/protobuf/IntArrayList.java | 242 +++ .../main/java/com/google/protobuf/Internal.java | 129 ++ .../com/google/protobuf/LazyStringArrayList.java | 87 +- .../java/com/google/protobuf/LongArrayList.java | 242 +++ .../main/java/com/google/protobuf/MapField.java | 63 +- .../java/com/google/protobuf/MapFieldLite.java | 376 +++- .../java/com/google/protobuf/MutabilityOracle.java | 48 + .../com/google/protobuf/ProtobufArrayList.java | 95 + .../main/java/com/google/protobuf/TextFormat.java | 4 +- .../com/google/protobuf/BooleanArrayListTest.java | 473 +++++ .../java/com/google/protobuf/DescriptorsTest.java | 17 + .../com/google/protobuf/DoubleArrayListTest.java | 473 +++++ .../com/google/protobuf/FloatArrayListTest.java | 473 +++++ .../java/com/google/protobuf/IntArrayListTest.java | 473 +++++ .../google/protobuf/LazyStringArrayListTest.java | 188 ++ .../test/java/com/google/protobuf/LiteTest.java | 1314 +++++++++++++- .../com/google/protobuf/LiteralByteStringTest.java | 3 +- .../com/google/protobuf/LongArrayListTest.java | 473 +++++ .../com/google/protobuf/MapForProto2LiteTest.java | 161 ++ .../java/com/google/protobuf/MapForProto2Test.java | 148 ++ .../src/test/java/com/google/protobuf/MapTest.java | 133 ++ .../com/google/protobuf/ProtobufArrayListTest.java | 303 ++++ .../com/google/protobuf/field_presence_test.proto | 26 +- .../protobuf/map_initialization_order_test.proto | 61 + .../test/java/com/google/protobuf/map_test.proto | 4 +- python/google/protobuf/descriptor.py | 46 +- python/google/protobuf/descriptor_pool.py | 39 +- python/google/protobuf/internal/containers.py | 297 ++++ python/google/protobuf/internal/decoder.py | 44 + .../protobuf/internal/descriptor_database_test.py | 1 - .../protobuf/internal/descriptor_pool_test.py | 45 + python/google/protobuf/internal/descriptor_test.py | 2 +- python/google/protobuf/internal/encoder.py | 55 +- python/google/protobuf/internal/generator_test.py | 1 - .../protobuf/internal/message_factory_test.py | 1 - python/google/protobuf/internal/message_test.py | 521 +++++- .../google/protobuf/internal/proto_builder_test.py | 21 +- python/google/protobuf/internal/python_message.py | 159 +- python/google/protobuf/internal/reflection_test.py | 6 +- .../protobuf/internal/service_reflection_test.py | 3 +- .../protobuf/internal/symbol_database_test.py | 1 - python/google/protobuf/internal/test_util.py | 24 +- .../google/protobuf/internal/text_encoding_test.py | 1 - .../google/protobuf/internal/text_format_test.py | 141 +- python/google/protobuf/internal/type_checkers.py | 9 + .../protobuf/internal/unknown_fields_test.py | 1 - .../google/protobuf/internal/wire_format_test.py | 1 - python/google/protobuf/proto_builder.py | 20 +- python/google/protobuf/pyext/descriptor.cc | 287 +-- python/google/protobuf/pyext/descriptor.h | 21 +- .../google/protobuf/pyext/descriptor_containers.cc | 26 +- .../google/protobuf/pyext/descriptor_containers.h | 5 + python/google/protobuf/pyext/descriptor_pool.cc | 185 +- python/google/protobuf/pyext/descriptor_pool.h | 8 +- python/google/protobuf/pyext/extension_dict.cc | 17 +- python/google/protobuf/pyext/message.cc | 432 ++++- python/google/protobuf/pyext/message.h | 13 +- .../google/protobuf/pyext/message_map_container.cc | 540 ++++++ .../google/protobuf/pyext/message_map_container.h | 117 ++ .../protobuf/pyext/repeated_composite_container.cc | 56 +- .../protobuf/pyext/repeated_composite_container.h | 10 +- .../protobuf/pyext/repeated_scalar_container.cc | 8 +- .../google/protobuf/pyext/scalar_map_container.cc | 514 ++++++ .../google/protobuf/pyext/scalar_map_container.h | 110 ++ python/google/protobuf/reflection.py | 1 - python/google/protobuf/text_format.py | 36 +- python/setup.py | 1 + ruby/tests/generated_code.proto | 26 +- src/Makefile.am | 27 + src/google/protobuf/any.cc | 100 ++ src/google/protobuf/any.h | 90 + src/google/protobuf/any.pb.cc | 23 +- src/google/protobuf/any.pb.h | 10 + src/google/protobuf/any_test.cc | 89 + src/google/protobuf/any_test.proto | 41 + src/google/protobuf/api.pb.cc | 74 +- src/google/protobuf/api.pb.h | 4 +- src/google/protobuf/arena.cc | 9 +- src/google/protobuf/arena.h | 346 +++- src/google/protobuf/arena_nc_test.py | 1 + src/google/protobuf/arena_test_util.h | 1 + src/google/protobuf/arena_unittest.cc | 75 +- src/google/protobuf/compiler/code_generator.h | 31 + .../protobuf/compiler/command_line_interface.cc | 46 +- .../compiler/command_line_interface_unittest.cc | 10 + src/google/protobuf/compiler/cpp/cpp_enum.cc | 14 +- src/google/protobuf/compiler/cpp/cpp_enum.h | 6 + src/google/protobuf/compiler/cpp/cpp_enum_field.cc | 14 +- src/google/protobuf/compiler/cpp/cpp_field.h | 26 + src/google/protobuf/compiler/cpp/cpp_file.cc | 505 +++--- src/google/protobuf/compiler/cpp/cpp_file.h | 29 + src/google/protobuf/compiler/cpp/cpp_generator.cc | 1 + src/google/protobuf/compiler/cpp/cpp_helpers.cc | 59 +- src/google/protobuf/compiler/cpp/cpp_helpers.h | 21 + src/google/protobuf/compiler/cpp/cpp_map_field.cc | 9 +- src/google/protobuf/compiler/cpp/cpp_message.cc | 688 ++++++-- src/google/protobuf/compiler/cpp/cpp_message.h | 25 +- .../protobuf/compiler/cpp/cpp_message_field.cc | 33 +- .../protobuf/compiler/cpp/cpp_message_field.h | 6 + src/google/protobuf/compiler/cpp/cpp_options.h | 5 +- .../protobuf/compiler/cpp/cpp_primitive_field.cc | 14 +- .../protobuf/compiler/cpp/cpp_string_field.cc | 1 - .../compiler/cpp/cpp_test_bad_identifiers.proto | 34 +- src/google/protobuf/compiler/cpp/cpp_unittest.cc | 16 + .../compiler/cpp/test_large_enum_value.proto | 43 + .../protobuf/compiler/java/java_enum_field.cc | 16 +- .../protobuf/compiler/java/java_enum_field_lite.cc | 967 ++++++++++ .../protobuf/compiler/java/java_enum_field_lite.h | 159 ++ src/google/protobuf/compiler/java/java_field.cc | 108 ++ src/google/protobuf/compiler/java/java_field.h | 32 +- src/google/protobuf/compiler/java/java_file.cc | 10 +- .../compiler/java/java_generator_factory.cc | 8 +- src/google/protobuf/compiler/java/java_helpers.h | 10 +- .../compiler/java/java_lazy_message_field.cc | 226 ++- .../compiler/java/java_lazy_message_field_lite.cc | 708 ++++++++ .../compiler/java/java_lazy_message_field_lite.h | 118 ++ .../protobuf/compiler/java/java_map_field.cc | 51 +- src/google/protobuf/compiler/java/java_map_field.h | 1 - .../protobuf/compiler/java/java_map_field_lite.cc | 461 +++++ .../protobuf/compiler/java/java_map_field_lite.h | 81 + src/google/protobuf/compiler/java/java_message.cc | 1184 +++---------- src/google/protobuf/compiler/java/java_message.h | 11 +- .../protobuf/compiler/java/java_message_builder.cc | 661 +++++++ .../protobuf/compiler/java/java_message_builder.h | 86 + .../compiler/java/java_message_builder_lite.cc | 192 ++ .../compiler/java/java_message_builder_lite.h | 83 + .../protobuf/compiler/java/java_message_field.cc | 411 ++--- .../compiler/java/java_message_field_lite.cc | 944 ++++++++++ .../compiler/java/java_message_field_lite.h | 157 ++ .../protobuf/compiler/java/java_message_lite.cc | 1174 ++++++++++++ .../protobuf/compiler/java/java_message_lite.h | 91 + .../protobuf/compiler/java/java_primitive_field.cc | 15 +- .../compiler/java/java_primitive_field_lite.cc | 892 ++++++++++ .../compiler/java/java_primitive_field_lite.h | 163 ++ .../protobuf/compiler/java/java_string_field.cc | 2 +- .../compiler/java/java_string_field_lite.cc | 1013 +++++++++++ .../compiler/java/java_string_field_lite.h | 158 ++ src/google/protobuf/compiler/parser.cc | 80 + src/google/protobuf/compiler/parser.h | 8 + src/google/protobuf/compiler/parser_unittest.cc | 75 +- src/google/protobuf/compiler/plugin.cc | 28 +- src/google/protobuf/compiler/plugin.pb.cc | 90 +- src/google/protobuf/compiler/plugin.pb.h | 4 + src/google/protobuf/descriptor.cc | 183 +- src/google/protobuf/descriptor.h | 67 +- src/google/protobuf/descriptor.pb.cc | 1863 ++++++++++++++------ src/google/protobuf/descriptor.pb.h | 414 ++++- src/google/protobuf/descriptor.proto | 45 +- src/google/protobuf/descriptor_database.h | 4 +- src/google/protobuf/descriptor_unittest.cc | 246 ++- src/google/protobuf/duration.pb.cc | 12 +- src/google/protobuf/empty.pb.cc | 8 +- src/google/protobuf/extension_set.cc | 61 +- src/google/protobuf/extension_set.h | 6 +- src/google/protobuf/extension_set_heavy.cc | 2 +- src/google/protobuf/field_mask.pb.cc | 12 +- src/google/protobuf/generated_message_reflection.h | 37 + src/google/protobuf/generated_message_util.h | 13 + src/google/protobuf/io/coded_stream.cc | 104 +- src/google/protobuf/io/coded_stream.h | 84 +- src/google/protobuf/io/coded_stream_inl.h | 17 + src/google/protobuf/io/printer.cc | 72 + src/google/protobuf/io/printer.h | 32 +- src/google/protobuf/lite_unittest.cc | 378 ++++ src/google/protobuf/map.h | 1 + src/google/protobuf/map_entry_lite.h | 18 +- src/google/protobuf/map_field.cc | 18 +- src/google/protobuf/map_field.h | 1 + src/google/protobuf/map_field_inl.h | 2 +- src/google/protobuf/map_field_lite.h | 2 +- src/google/protobuf/map_field_test.cc | 13 +- src/google/protobuf/map_test.cc | 21 + src/google/protobuf/map_type_handler.h | 4 +- src/google/protobuf/map_unittest.proto | 16 +- src/google/protobuf/message.cc | 19 +- src/google/protobuf/message.h | 15 +- src/google/protobuf/message_lite.cc | 2 +- src/google/protobuf/message_lite.h | 3 + src/google/protobuf/message_unittest.cc | 31 +- src/google/protobuf/proto3_arena_unittest.cc | 10 + src/google/protobuf/reflection.h | 1 + src/google/protobuf/repeated_field.h | 60 +- src/google/protobuf/repeated_field_unittest.cc | 22 + src/google/protobuf/source_context.pb.cc | 10 +- src/google/protobuf/struct.pb.cc | 95 +- src/google/protobuf/struct.pb.h | 4 + src/google/protobuf/stubs/common.h | 8 + src/google/protobuf/stubs/strutil.cc | 18 - src/google/protobuf/stubs/strutil.h | 6 - src/google/protobuf/testdata/golden_message_proto3 | Bin 398 -> 248 bytes src/google/protobuf/testdata/map_test_data.txt | 140 ++ src/google/protobuf/text_format.cc | 267 ++- src/google/protobuf/text_format.h | 15 + src/google/protobuf/text_format_unittest.cc | 21 +- src/google/protobuf/timestamp.pb.cc | 12 +- src/google/protobuf/type.pb.cc | 161 +- src/google/protobuf/type.pb.h | 14 +- src/google/protobuf/unittest.proto | 6 + .../protobuf/unittest_drop_unknown_fields.proto | 10 +- .../protobuf/unittest_no_field_presence.proto | 56 +- .../protobuf/unittest_preserve_unknown_enum.proto | 4 +- src/google/protobuf/unittest_proto3_arena.proto | 90 +- src/google/protobuf/unknown_field_set.cc | 8 +- src/google/protobuf/unknown_field_set.h | 2 +- src/google/protobuf/wire_format.cc | 4 +- src/google/protobuf/wire_format_lite.h | 11 + src/google/protobuf/wire_format_lite_inl.h | 21 +- src/google/protobuf/wire_format_unittest.cc | 132 ++ src/google/protobuf/wrappers.pb.cc | 90 +- src/google/protobuf/wrappers.pb.h | 16 + vsprojects/libprotoc.vcproj | 64 + 227 files changed, 25713 insertions(+), 3789 deletions(-) create mode 100644 java/src/main/java/com/google/protobuf/AbstractProtobufList.java create mode 100644 java/src/main/java/com/google/protobuf/BooleanArrayList.java create mode 100644 java/src/main/java/com/google/protobuf/DoubleArrayList.java create mode 100644 java/src/main/java/com/google/protobuf/FloatArrayList.java create mode 100644 java/src/main/java/com/google/protobuf/IntArrayList.java create mode 100644 java/src/main/java/com/google/protobuf/LongArrayList.java create mode 100644 java/src/main/java/com/google/protobuf/MutabilityOracle.java create mode 100644 java/src/main/java/com/google/protobuf/ProtobufArrayList.java create mode 100644 java/src/test/java/com/google/protobuf/BooleanArrayListTest.java create mode 100644 java/src/test/java/com/google/protobuf/DoubleArrayListTest.java create mode 100644 java/src/test/java/com/google/protobuf/FloatArrayListTest.java create mode 100644 java/src/test/java/com/google/protobuf/IntArrayListTest.java create mode 100644 java/src/test/java/com/google/protobuf/LongArrayListTest.java create mode 100644 java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java create mode 100644 java/src/test/java/com/google/protobuf/map_initialization_order_test.proto create mode 100644 python/google/protobuf/pyext/message_map_container.cc create mode 100644 python/google/protobuf/pyext/message_map_container.h create mode 100644 python/google/protobuf/pyext/scalar_map_container.cc create mode 100644 python/google/protobuf/pyext/scalar_map_container.h create mode 100644 src/google/protobuf/any.cc create mode 100644 src/google/protobuf/any.h create mode 100644 src/google/protobuf/any_test.cc create mode 100644 src/google/protobuf/any_test.proto create mode 100644 src/google/protobuf/compiler/cpp/test_large_enum_value.proto create mode 100644 src/google/protobuf/compiler/java/java_enum_field_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_enum_field_lite.h create mode 100644 src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_lazy_message_field_lite.h create mode 100644 src/google/protobuf/compiler/java/java_map_field_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_map_field_lite.h create mode 100644 src/google/protobuf/compiler/java/java_message_builder.cc create mode 100644 src/google/protobuf/compiler/java/java_message_builder.h create mode 100644 src/google/protobuf/compiler/java/java_message_builder_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_message_builder_lite.h create mode 100644 src/google/protobuf/compiler/java/java_message_field_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_message_field_lite.h create mode 100644 src/google/protobuf/compiler/java/java_message_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_message_lite.h create mode 100644 src/google/protobuf/compiler/java/java_primitive_field_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_primitive_field_lite.h create mode 100644 src/google/protobuf/compiler/java/java_string_field_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_string_field_lite.h create mode 100644 src/google/protobuf/testdata/map_test_data.txt (limited to 'conformance') diff --git a/.gitignore b/.gitignore index 6508eccb..38755c96 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ src/.libs .dirstamp +any_test.pb.* map*unittest.pb.* unittest*.pb.* cpp_test*.pb.* diff --git a/Android.mk b/Android.mk index 18bdd091..bbd15740 100644 --- a/Android.mk +++ b/Android.mk @@ -90,14 +90,20 @@ COMPILER_SRC_FILES := \ src/google/protobuf/compiler/cpp/cpp_string_field.cc \ src/google/protobuf/compiler/java/java_enum.cc \ src/google/protobuf/compiler/java/java_enum_field.cc \ + src/google/protobuf/compiler/java/java_enum_field_lite.cc \ src/google/protobuf/compiler/java/java_extension.cc \ src/google/protobuf/compiler/java/java_field.cc \ src/google/protobuf/compiler/java/java_file.cc \ src/google/protobuf/compiler/java/java_generator.cc \ src/google/protobuf/compiler/java/java_helpers.cc \ src/google/protobuf/compiler/java/java_message.cc \ + src/google/protobuf/compiler/java/java_message_lite.cc \ + src/google/protobuf/compiler/java/java_message_builder.cc \ + src/google/protobuf/compiler/java/java_message_builder_lite.cc \ src/google/protobuf/compiler/java/java_message_field.cc \ + src/google/protobuf/compiler/java/java_message_field_lite.cc \ src/google/protobuf/compiler/java/java_primitive_field.cc \ + src/google/protobuf/compiler/java/java_primitive_field_lite.cc \ src/google/protobuf/compiler/java/java_service.cc \ src/google/protobuf/compiler/javamicro/javamicro_enum.cc \ src/google/protobuf/compiler/javamicro/javamicro_enum_field.cc \ diff --git a/Makefile.am b/Makefile.am index 45d44f5c..645c924b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -43,28 +43,34 @@ java_EXTRA_DIST= \ java/src/main/java/com/google/protobuf/AbstractMessage.java \ java/src/main/java/com/google/protobuf/AbstractMessageLite.java \ java/src/main/java/com/google/protobuf/AbstractParser.java \ + java/src/main/java/com/google/protobuf/AbstractProtobufList.java \ java/src/main/java/com/google/protobuf/BlockingRpcChannel.java \ java/src/main/java/com/google/protobuf/BlockingService.java \ java/src/main/java/com/google/protobuf/BoundedByteString.java \ + java/src/main/java/com/google/protobuf/BooleanArrayList.java \ java/src/main/java/com/google/protobuf/ByteString.java \ java/src/main/java/com/google/protobuf/CodedInputStream.java \ java/src/main/java/com/google/protobuf/CodedOutputStream.java \ java/src/main/java/com/google/protobuf/Descriptors.java \ + java/src/main/java/com/google/protobuf/DoubleArrayList.java \ java/src/main/java/com/google/protobuf/DynamicMessage.java \ java/src/main/java/com/google/protobuf/Extension.java \ java/src/main/java/com/google/protobuf/ExtensionLite.java \ java/src/main/java/com/google/protobuf/ExtensionRegistry.java \ java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java \ java/src/main/java/com/google/protobuf/FieldSet.java \ + java/src/main/java/com/google/protobuf/FloatArrayList.java \ java/src/main/java/com/google/protobuf/GeneratedMessage.java \ 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/IntArrayList.java \ java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java \ java/src/main/java/com/google/protobuf/LazyField.java \ java/src/main/java/com/google/protobuf/LazyFieldLite.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/LiteralByteString.java \ + java/src/main/java/com/google/protobuf/LongArrayList.java \ java/src/main/java/com/google/protobuf/MapEntry.java \ java/src/main/java/com/google/protobuf/MapEntryLite.java \ java/src/main/java/com/google/protobuf/MapField.java \ @@ -74,7 +80,9 @@ java_EXTRA_DIST= \ 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/MessageReflection.java \ + java/src/main/java/com/google/protobuf/MutabilityOracle.java \ java/src/main/java/com/google/protobuf/Parser.java \ + java/src/main/java/com/google/protobuf/ProtobufArrayList.java \ java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java \ java/src/main/java/com/google/protobuf/ProtocolStringList.java \ java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java \ @@ -96,16 +104,20 @@ java_EXTRA_DIST= \ 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/BoundedByteStringTest.java \ + java/src/test/java/com/google/protobuf/BooleanArrayListTest.java \ java/src/test/java/com/google/protobuf/ByteStringTest.java \ java/src/test/java/com/google/protobuf/CheckUtf8Test.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/DoubleArrayListTest.java \ java/src/test/java/com/google/protobuf/DynamicMessageTest.java \ java/src/test/java/com/google/protobuf/FieldPresenceTest.java \ + java/src/test/java/com/google/protobuf/FloatArrayListTest.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/IntArrayListTest.java \ java/src/test/java/com/google/protobuf/IsValidUtf8Test.java \ java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java \ java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java \ @@ -116,12 +128,14 @@ java_EXTRA_DIST= \ java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java \ java/src/test/java/com/google/protobuf/LiteralByteStringTest.java \ java/src/test/java/com/google/protobuf/LiteTest.java \ + java/src/test/java/com/google/protobuf/LongArrayListTest.java \ java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java \ java/src/test/java/com/google/protobuf/MapForProto2Test.java \ java/src/test/java/com/google/protobuf/MapTest.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/ParserTest.java \ + java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java \ java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java \ java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java \ java/src/test/java/com/google/protobuf/RopeByteStringTest.java \ diff --git a/conformance/conformance.proto b/conformance/conformance.proto index 7b676898..39eafdbb 100644 --- a/conformance/conformance.proto +++ b/conformance/conformance.proto @@ -71,7 +71,7 @@ message ConformanceRequest { } // Which format should the testee serialize its message to? - optional RequestedOutput requested_output = 3; + RequestedOutput requested_output = 3; } // Represents a single test case's output. @@ -103,8 +103,8 @@ message ConformanceResponse { // forms. message TestAllTypes { message NestedMessage { - optional int32 a = 1; - optional TestAllTypes corecursive = 2; + int32 a = 1; + TestAllTypes corecursive = 2; } enum NestedEnum { @@ -115,36 +115,32 @@ message TestAllTypes { } // Singular - optional int32 optional_int32 = 1; - optional int64 optional_int64 = 2; - optional uint32 optional_uint32 = 3; - optional uint64 optional_uint64 = 4; - optional sint32 optional_sint32 = 5; - optional sint64 optional_sint64 = 6; - optional fixed32 optional_fixed32 = 7; - optional fixed64 optional_fixed64 = 8; - optional sfixed32 optional_sfixed32 = 9; - optional sfixed64 optional_sfixed64 = 10; - optional float optional_float = 11; - optional double optional_double = 12; - optional bool optional_bool = 13; - optional string optional_string = 14; - optional bytes optional_bytes = 15; - - optional group OptionalGroup = 16 { - optional int32 a = 17; - } - - optional NestedMessage optional_nested_message = 18; - optional ForeignMessage optional_foreign_message = 19; - - optional NestedEnum optional_nested_enum = 21; - optional ForeignEnum optional_foreign_enum = 22; - - optional string optional_string_piece = 24 [ctype=STRING_PIECE]; - optional string optional_cord = 25 [ctype=CORD]; - - optional TestAllTypes recursive_message = 27; + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + + NestedMessage optional_nested_message = 18; + ForeignMessage optional_foreign_message = 19; + + NestedEnum optional_nested_enum = 21; + ForeignEnum optional_foreign_enum = 22; + + string optional_string_piece = 24 [ctype=STRING_PIECE]; + string optional_cord = 25 [ctype=CORD]; + + TestAllTypes recursive_message = 27; // Repeated repeated int32 repeated_int32 = 31; @@ -163,10 +159,6 @@ message TestAllTypes { repeated string repeated_string = 44; repeated bytes repeated_bytes = 45; - repeated group RepeatedGroup = 46 { - optional int32 a = 47; - } - repeated NestedMessage repeated_nested_message = 48; repeated ForeignMessage repeated_foreign_message = 49; @@ -206,7 +198,7 @@ message TestAllTypes { } message ForeignMessage { - optional int32 c = 1; + int32 c = 1; } enum ForeignEnum { diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc index 61029e44..857f2152 100644 --- a/conformance/conformance_test.cc +++ b/conformance/conformance_test.cc @@ -295,6 +295,7 @@ void ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, failures_ = 0; for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) { + if (i == FieldDescriptor::TYPE_GROUP) continue; TestPrematureEOFForType(static_cast(i)); } diff --git a/editors/proto.vim b/editors/proto.vim index 23085a28..7f1aeb73 100644 --- a/editors/proto.vim +++ b/editors/proto.vim @@ -57,7 +57,7 @@ syn keyword pbSyntax syntax import option syn keyword pbStructure package message group oneof syn keyword pbRepeat optional required repeated syn keyword pbDefault default -syn keyword pbExtend extend extensions to max +syn keyword pbExtend extend extensions to max reserved syn keyword pbRPC service rpc returns syn keyword pbType int32 int64 uint32 uint64 sint32 sint64 diff --git a/editors/protobuf-mode.el b/editors/protobuf-mode.el index 09aecc93..f615a0af 100644 --- a/editors/protobuf-mode.el +++ b/editors/protobuf-mode.el @@ -106,7 +106,7 @@ ;; cc-mode. So, we approximate as best we can. (c-lang-defconst c-type-list-kwds - protobuf '("extensions" "to")) + protobuf '("extensions" "to" "reserved")) (c-lang-defconst c-typeless-decl-kwds protobuf '("extend" "rpc" "option" "returns")) diff --git a/generate_descriptor_proto.sh b/generate_descriptor_proto.sh index 27d293a2..89449e10 100755 --- a/generate_descriptor_proto.sh +++ b/generate_descriptor_proto.sh @@ -43,8 +43,11 @@ declare -a RUNTIME_PROTO_FILES=(\ google/protobuf/wrappers.proto) CORE_PROTO_IS_CORRECT=0 +PROCESS_ROUND=1 +echo "Updating descriptor protos..." while [ $CORE_PROTO_IS_CORRECT -ne 1 ] do + echo "Round $PROCESS_ROUND" CORE_PROTO_IS_CORRECT=1 for PROTO_FILE in ${RUNTIME_PROTO_FILES[@]}; do BASE_NAME=${PROTO_FILE%.*} @@ -86,5 +89,7 @@ do done rm google/protobuf/compiler/plugin.pb.h.tmp rm google/protobuf/compiler/plugin.pb.cc.tmp + + PROCESS_ROUND=$((PROCESS_ROUND + 1)) done cd .. diff --git a/java/pom.xml b/java/pom.xml index 3eb7a703..9f270113 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -134,6 +134,7 @@ + target/generated-test-sources @@ -227,25 +228,33 @@ **/AbstractMessageLite.java **/AbstractParser.java + **/AbstractProtobufList.java **/BoundedByteString.java + **/BooleanArrayList.java **/ByteString.java **/CodedInputStream.java **/CodedOutputStream.java + **/DoublerrayList.java **/ExtensionLite.java **/ExtensionRegistryLite.java **/FieldSet.java + **/FloatArrayList.java **/GeneratedMessageLite.java + **/IntArrayList.java **/Internal.java **/InvalidProtocolBufferException.java **/LazyFieldLite.java **/LazyStringArrayList.java **/LazyStringList.java **/LiteralByteString.java + **/LongArrayList.java **/MapEntryLite.java **/MapFieldLite.java **/MessageLite.java **/MessageLiteOrBuilder.java + **/MutabilityOracle.java **/Parser.java + **/ProtobufArrayList.java **/ProtocolStringList.java **/RopeByteString.java **/SmallSortedMap.java @@ -257,8 +266,14 @@ **/*Lite.java + **/BooleanArrayListTest.java + **/DoubleArrayListTest.java + **/FloatArrayListTest.java + **/IntArrayListTest.java **/LazyMessageLiteTest.java **/LiteTest.java + **/LongArrayListTest.java + **/ProtobufArrayListTest.java **/UnknownFieldSetLiteTest.java diff --git a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java index aac4fa77..12384983 100644 --- a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java +++ b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java @@ -31,8 +31,8 @@ package com.google.protobuf; import java.io.FilterInputStream; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; @@ -109,6 +109,11 @@ public abstract class AbstractMessageLite implements MessageLite { } } + protected static void addAll(final Iterable values, + final Collection list) { + Builder.addAll(values, list); + } + /** * A partial implementation of the {@link Message.Builder} interface which * implements as many methods of that interface as possible in terms of @@ -320,12 +325,15 @@ public abstract class AbstractMessageLite implements MessageLite { * Adds the {@code values} to the {@code list}. This is a helper method * used by generated code. Users should ignore it. * - * @throws NullPointerException if any of the elements of {@code values} is - * null. When that happens, some elements of {@code values} may have already - * been added to the result {@code list}. + * @throws NullPointerException if {@code values} or any of the elements of + * {@code values} is null. When that happens, some elements of + * {@code values} may have already been added to the result {@code list}. */ protected static void addAll(final Iterable values, final Collection list) { + if (values == null) { + throw new NullPointerException(); + } if (values instanceof LazyStringList) { // For StringOrByteStringLists, check the underlying elements to avoid // forcing conversions of ByteStrings to Strings. diff --git a/java/src/main/java/com/google/protobuf/AbstractProtobufList.java b/java/src/main/java/com/google/protobuf/AbstractProtobufList.java new file mode 100644 index 00000000..bb6446b2 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/AbstractProtobufList.java @@ -0,0 +1,136 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.ProtobufList; + +import java.util.AbstractList; +import java.util.Collection; + +/** + * An abstract implementation of {@link ProtobufList} which manages mutability semantics. All mutate + * methods are check if the list is mutable before proceeding. Subclasses must invoke + * {@link #ensureIsMutable()} manually when overriding those methods. + */ +abstract class AbstractProtobufList extends AbstractList implements ProtobufList { + + /** + * Whether or not this list is modifiable. + */ + private boolean isMutable; + + /** + * Constructs a mutable list by default. + */ + AbstractProtobufList() { + isMutable = true; + } + + @Override + public boolean add(E e) { + ensureIsMutable(); + return super.add(e); + } + + @Override + public void add(int index, E element) { + ensureIsMutable(); + super.add(index, element); + } + + @Override + public boolean addAll(Collection c) { + ensureIsMutable(); + return super.addAll(c); + } + + @Override + public boolean addAll(int index, Collection c) { + ensureIsMutable(); + return super.addAll(index, c); + } + + @Override + public void clear() { + ensureIsMutable(); + super.clear(); + } + + @Override + public boolean isModifiable() { + return isMutable; + } + + @Override + public final void makeImmutable() { + isMutable = false; + } + + @Override + public E remove(int index) { + ensureIsMutable(); + return super.remove(index); + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + return super.remove(o); + } + + @Override + public boolean removeAll(Collection c) { + ensureIsMutable(); + return super.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + ensureIsMutable(); + return super.retainAll(c); + } + + @Override + public E set(int index, E element) { + ensureIsMutable(); + return super.set(index, element); + } + + /** + * Throws an {@link UnsupportedOperationException} if the list is immutable. Subclasses are + * responsible for invoking this method on mutate operations. + */ + protected void ensureIsMutable() { + if (!isMutable) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/java/src/main/java/com/google/protobuf/BooleanArrayList.java b/java/src/main/java/com/google/protobuf/BooleanArrayList.java new file mode 100644 index 00000000..45492d2f --- /dev/null +++ b/java/src/main/java/com/google/protobuf/BooleanArrayList.java @@ -0,0 +1,244 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.BooleanList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link BooleanList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class BooleanArrayList + extends AbstractProtobufList implements BooleanList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static BooleanArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private boolean[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code BooleanArrayList}. + */ + BooleanArrayList() { + array = new boolean[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code BooleanArrayList} containing the same elements as + * {@code other}. + */ + BooleanArrayList(List other) { + if (other instanceof BooleanArrayList) { + BooleanArrayList list = (BooleanArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new boolean[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Boolean get(int index) { + return getBoolean(index); + } + + @Override + public boolean getBoolean(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Boolean set(int index, Boolean element) { + return setBoolean(index, element); + } + + @Override + public boolean setBoolean(int index, boolean element) { + ensureIsMutable(); + ensureIndexInRange(index); + boolean previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Boolean element) { + addBoolean(index, element); + } + + /** + * Like {@link #add(Boolean)} but more efficient in that it doesn't box the element. + */ + @Override + public void addBoolean(boolean element) { + addBoolean(size, element); + } + + /** + * Like {@link #add(int, Boolean)} but more efficient in that it doesn't box the element. + */ + private void addBoolean(int index, boolean element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + boolean[] newArray = new boolean[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another BooleanArrayList to avoid boxing elements. + if (!(collection instanceof BooleanArrayList)) { + return super.addAll(collection); + } + + BooleanArrayList list = (BooleanArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Boolean remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + boolean value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java index b60bd36b..3658410c 100644 --- a/java/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -644,6 +644,30 @@ public final class Descriptors { return false; } + /** Determines if the given field number is reserved. */ + public boolean isReservedNumber(final int number) { + for (final DescriptorProto.ReservedRange range : + proto.getReservedRangeList()) { + if (range.getStart() <= number && number < range.getEnd()) { + return true; + } + } + return false; + } + + /** Determines if the given field name is reserved. */ + public boolean isReservedName(final String name) { + if (name == null) { + throw new NullPointerException(); + } + for (final String reservedName : proto.getReservedNameList()) { + if (reservedName.equals(name)) { + return true; + } + } + return false; + } + /** * Indicates whether the message can be extended. That is, whether it has * any "extensions x to y" ranges declared on it. @@ -917,9 +941,18 @@ public final class Descriptors { return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED; } - /** Does this field have the {@code [packed = true]} option? */ + /** Does this field have the {@code [packed = true]} option or is this field + * packable in proto3 and not explicitly setted to unpacked? + */ public boolean isPacked() { - return getOptions().getPacked(); + if (!isPackable()) { + return false; + } + if (getFile().getSyntax() == FileDescriptor.Syntax.PROTO2) { + return getOptions().getPacked(); + } else { + return !getOptions().hasPacked() || getOptions().getPacked(); + } } /** Can this field be packed? i.e. is it a repeated primitive field? */ @@ -2317,6 +2350,11 @@ public final class Descriptors { public int getFieldCount() { return fieldCount; } + /** Get a list of this message type's fields. */ + public List getFields() { + return Collections.unmodifiableList(Arrays.asList(fields)); + } + public FieldDescriptor getField(int index) { return fields[index]; } diff --git a/java/src/main/java/com/google/protobuf/DoubleArrayList.java b/java/src/main/java/com/google/protobuf/DoubleArrayList.java new file mode 100644 index 00000000..90ebe109 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/DoubleArrayList.java @@ -0,0 +1,243 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.DoubleList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link DoubleList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class DoubleArrayList + extends AbstractProtobufList implements DoubleList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static DoubleArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private double[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code DoubleArrayList}. + */ + DoubleArrayList() { + array = new double[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code DoubleArrayList} containing the same elements as {@code other}. + */ + DoubleArrayList(List other) { + if (other instanceof DoubleArrayList) { + DoubleArrayList list = (DoubleArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new double[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Double get(int index) { + return getDouble(index); + } + + @Override + public double getDouble(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Double set(int index, Double element) { + return setDouble(index, element); + } + + @Override + public double setDouble(int index, double element) { + ensureIsMutable(); + ensureIndexInRange(index); + double previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Double element) { + addDouble(index, element); + } + + /** + * Like {@link #add(Double)} but more efficient in that it doesn't box the element. + */ + @Override + public void addDouble(double element) { + addDouble(size, element); + } + + /** + * Like {@link #add(int, Double)} but more efficient in that it doesn't box the element. + */ + private void addDouble(int index, double element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + double[] newArray = new double[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another DoubleArrayList to avoid boxing elements. + if (!(collection instanceof DoubleArrayList)) { + return super.addAll(collection); + } + + DoubleArrayList list = (DoubleArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Double remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + double value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/FloatArrayList.java b/java/src/main/java/com/google/protobuf/FloatArrayList.java new file mode 100644 index 00000000..293eaff6 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/FloatArrayList.java @@ -0,0 +1,242 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.FloatList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link FloatList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class FloatArrayList extends AbstractProtobufList implements FloatList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final FloatArrayList EMPTY_LIST = new FloatArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static FloatArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private float[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code FloatArrayList}. + */ + FloatArrayList() { + array = new float[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code FloatArrayList} containing the same elements as {@code other}. + */ + FloatArrayList(List other) { + if (other instanceof FloatArrayList) { + FloatArrayList list = (FloatArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new float[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Float get(int index) { + return getFloat(index); + } + + @Override + public float getFloat(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Float set(int index, Float element) { + return setFloat(index, element); + } + + @Override + public float setFloat(int index, float element) { + ensureIsMutable(); + ensureIndexInRange(index); + float previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Float element) { + addFloat(index, element); + } + + /** + * Like {@link #add(Float)} but more efficient in that it doesn't box the element. + */ + @Override + public void addFloat(float element) { + addFloat(size, element); + } + + /** + * Like {@link #add(int, Float)} but more efficient in that it doesn't box the element. + */ + private void addFloat(int index, float element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + float[] newArray = new float[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another FloatArrayList to avoid boxing elements. + if (!(collection instanceof FloatArrayList)) { + return super.addAll(collection); + } + + FloatArrayList list = (FloatArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Float remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + float value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java index 6839c9dd..bd6bc463 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java @@ -30,6 +30,12 @@ package com.google.protobuf; +import com.google.protobuf.Internal.BooleanList; +import com.google.protobuf.Internal.DoubleList; +import com.google.protobuf.Internal.FloatList; +import com.google.protobuf.Internal.IntList; +import com.google.protobuf.Internal.LongList; +import com.google.protobuf.Internal.ProtobufList; import com.google.protobuf.WireFormat.FieldType; import java.io.IOException; @@ -76,7 +82,11 @@ public abstract class GeneratedMessageLite< private static final long serialVersionUID = 1L; /** For use by generated code only. */ - protected UnknownFieldSetLite unknownFields; + protected UnknownFieldSetLite unknownFields = + UnknownFieldSetLite.getDefaultInstance(); + + /** For use by generated code only. */ + protected int memoizedSerializedSize = -1; @SuppressWarnings("unchecked") // Guaranteed by runtime. public final Parser getParserForType() { @@ -109,10 +119,67 @@ public abstract class GeneratedMessageLite< return unknownFields.mergeFieldFrom(tag, input); } - // The default behavior. If a message has required fields in its subtree, the - // generated code will override. - public boolean isInitialized() { - return true; + public final boolean isInitialized() { + return dynamicMethod(MethodToInvoke.IS_INITIALIZED, Boolean.TRUE) != null; + } + + public final BuilderType toBuilder() { + BuilderType builder = (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER); + builder.mergeFrom((MessageType) this); + return builder; + } + + /** + * Defines which method path to invoke in {@link GeneratedMessageLite + * #dynamicMethod(MethodToInvoke, Object...)}. + *

+ * For use by generated code only. + */ + public static enum MethodToInvoke { + IS_INITIALIZED, + PARSE_PARTIAL_FROM, + MERGE_FROM, + MAKE_IMMUTABLE, + NEW_INSTANCE, + NEW_BUILDER; + } + + /** + * A method that implements different types of operations described in {@link MethodToInvoke}. + * Theses different kinds of operations are required to implement message-level operations for + * builders in the runtime. This method bundles those operations to reduce the generated methods + * count. + *

    + *
  • {@code PARSE_PARTIAL_FROM} is parameterized with an {@link CodedInputStream} and + * {@link ExtensionRegistryLite}. It consumes the input stream, parsing the contents into the + * returned protocol buffer. If parsing throws an {@link InvalidProtocolBufferException}, the + * implementation wraps it in a RuntimeException + *
  • {@code NEW_INSTANCE} returns a new instance of the protocol buffer + *
  • {@code IS_INITIALIZED} is parameterized with a {@code Boolean} detailing whether to + * memoize. It returns {@code null} for false and the default instance for true. We optionally + * memoize to support the Builder case, where memoization is not desired. + *
  • {@code NEW_BUILDER} returns a {@code BuilderType} instance. + *
  • {@code MERGE_FROM} is parameterized with a {@code MessageType} and merges the fields from + * that instance into this instance. + *
  • {@code MAKE_IMMUTABLE} sets all internal fields to an immutable state. + *
+ * This method, plus the implementation of the Builder, enables the Builder class to be proguarded + * away entirely on Android. + *

+ * For use by generated code only. + */ + protected abstract Object dynamicMethod( + MethodToInvoke method, + Object... args); + + /** + * Merge some unknown fields into the {@link UnknownFieldSetLite} for this + * message. + * + *

For use by generated code only. + */ + protected final void mergeUnknownFields(UnknownFieldSetLite unknownFields) { + this.unknownFields = UnknownFieldSetLite.concat(this.unknownFields, unknownFields); } @SuppressWarnings("unchecked") @@ -122,24 +189,37 @@ public abstract class GeneratedMessageLite< extends AbstractMessageLite.Builder { private final MessageType defaultInstance; - - /** For use by generated code only. */ - protected UnknownFieldSetLite unknownFields = - UnknownFieldSetLite.getDefaultInstance(); + protected MessageType instance; + protected boolean isBuilt; protected Builder(MessageType defaultInstance) { this.defaultInstance = defaultInstance; + this.instance = (MessageType) defaultInstance.dynamicMethod(MethodToInvoke.NEW_INSTANCE); + isBuilt = false; } - // The default behavior. If a message has required fields in its subtree, - // the generated code will override. - public boolean isInitialized() { - return true; + /** + * Called before any method that would mutate the builder to ensure that it correctly copies + * any state before the write happens to preserve immutability guarantees. + */ + protected void copyOnWrite() { + if (isBuilt) { + MessageType newInstance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_INSTANCE); + newInstance.dynamicMethod(MethodToInvoke.MERGE_FROM, instance); + instance = newInstance; + isBuilt = false; + } + } + + //@Override (Java 1.6 override semantics, but we must support 1.5) + public final boolean isInitialized() { + return GeneratedMessageLite.isInitialized(instance, false /* shouldMemoize */); } //@Override (Java 1.6 override semantics, but we must support 1.5) - public BuilderType clear() { - unknownFields = UnknownFieldSetLite.getDefaultInstance(); + public final BuilderType clear() { + // No need to copy on write since we're dropping the instance anyways. + instance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_INSTANCE); return (BuilderType) this; } @@ -151,8 +231,12 @@ public abstract class GeneratedMessageLite< return builder; } - /** All subclasses implement this. */ - public abstract MessageType buildPartial(); + //@Override (Java 1.6 override semantics, but we must support 1.5) + public MessageType buildPartial() { + instance.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE); + isBuilt = true; + return instance; + } //@Override (Java 1.6 override semantics, but we must support 1.5) public final MessageType build() { @@ -162,9 +246,13 @@ public abstract class GeneratedMessageLite< } return result; } - + /** All subclasses implement this. */ - public abstract BuilderType mergeFrom(MessageType message); + public BuilderType mergeFrom(MessageType message) { + copyOnWrite(); + instance.dynamicMethod(MethodToInvoke.MERGE_FROM, message); + return (BuilderType) this; + } public MessageType getDefaultInstanceForType() { return defaultInstance; @@ -181,18 +269,6 @@ public abstract class GeneratedMessageLite< int tag) throws IOException { return unknownFields.mergeFieldFrom(tag, input); } - - /** - * Merge some unknown fields into the {@link UnknownFieldSetLite} for this - * message. - * - *

For use by generated code only. - */ - protected final BuilderType mergeUnknownFields( - final UnknownFieldSetLite unknownFields) { - this.unknownFields = UnknownFieldSetLite.concat(this.unknownFields, unknownFields); - return (BuilderType) this; - } public BuilderType mergeFrom( com.google.protobuf.CodedInputStream input, @@ -259,19 +335,13 @@ public abstract class GeneratedMessageLite< */ protected FieldSet extensions = FieldSet.newFieldSet(); - // -1 => not memoized, 0 => false, 1 => true. - private byte memoizedIsInitialized = -1; - - // The default behavior. If a message has required fields in its subtree, - // the generated code will override. - public boolean isInitialized() { - if (memoizedIsInitialized == -1) { - memoizedIsInitialized = (byte) (extensions.isInitialized() ? 1 : 0); + protected final void mergeExtensionFields(final MessageType other) { + if (extensions.isImmutable()) { + extensions = extensions.clone(); } - - return memoizedIsInitialized == 1; + extensions.mergeFrom(((ExtendableMessage) other).extensions); } - + private void verifyExtensionContainingType( final GeneratedExtension extension) { if (extension.getContainingTypeDefaultInstance() != @@ -420,46 +490,38 @@ public abstract class GeneratedMessageLite< implements ExtendableMessageOrBuilder { protected ExtendableBuilder(MessageType defaultInstance) { super(defaultInstance); - } - - private FieldSet extensions = FieldSet.emptySet(); - private boolean extensionsIsMutable; - - // The default behavior. If a message has required fields in its subtree, - // the generated code will override. - public boolean isInitialized() { - return extensions.isInitialized(); + + // TODO(dweis): This is kind of an unnecessary clone since we construct a + // new instance in the parent constructor which makes the extensions + // immutable. This extra allocation shouldn't matter in practice + // though. + instance.extensions = instance.extensions.clone(); } // For immutable message conversion. void internalSetExtensionSet(FieldSet extensions) { - this.extensions = extensions; + copyOnWrite(); + instance.extensions = extensions; } - @Override - public BuilderType clear() { - extensions.clear(); - extensionsIsMutable = false; - return super.clear(); + // @Override (Java 1.6 override semantics, but we must support 1.5) + protected void copyOnWrite() { + if (!isBuilt) { + return; + } + + super.copyOnWrite(); + instance.extensions = instance.extensions.clone(); } - private void ensureExtensionsIsMutable() { - if (!extensionsIsMutable) { - extensions = extensions.clone(); - extensionsIsMutable = true; + // @Override (Java 1.6 override semantics, but we must support 1.5) + public final MessageType buildPartial() { + if (isBuilt) { + return instance; } - } - /** - * Called by the build code path to create a copy of the extensions for - * building the message. - *

- * For use by generated code only. - */ - protected final FieldSet buildExtensions() { - extensions.makeImmutable(); - extensionsIsMutable = false; - return extensions; + instance.extensions.makeImmutable(); + return super.buildPartial(); } private void verifyExtensionContainingType( @@ -477,22 +539,14 @@ public abstract class GeneratedMessageLite< //@Override (Java 1.6 override semantics, but we must support 1.5) public final boolean hasExtension( final ExtensionLite extension) { - GeneratedExtension extensionLite = - checkIsLite(extension); - - verifyExtensionContainingType(extensionLite); - return extensions.hasField(extensionLite.descriptor); + return instance.hasExtension(extension); } /** Get the number of elements in a repeated extension. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final int getExtensionCount( final ExtensionLite> extension) { - GeneratedExtension> extensionLite = - checkIsLite(extension); - - verifyExtensionContainingType(extensionLite); - return extensions.getRepeatedFieldCount(extensionLite.descriptor); + return instance.getExtensionCount(extension); } /** Get the value of an extension. */ @@ -500,16 +554,7 @@ public abstract class GeneratedMessageLite< @SuppressWarnings("unchecked") public final Type getExtension( final ExtensionLite extension) { - GeneratedExtension extensionLite = - checkIsLite(extension); - - verifyExtensionContainingType(extensionLite); - final Object value = extensions.getField(extensionLite.descriptor); - if (value == null) { - return extensionLite.defaultValue; - } else { - return (Type) extensionLite.fromFieldSetType(value); - } + return instance.getExtension(extension); } /** Get one element of a repeated extension. */ @@ -518,12 +563,7 @@ public abstract class GeneratedMessageLite< public final Type getExtension( final ExtensionLite> extension, final int index) { - GeneratedExtension> extensionLite = - checkIsLite(extension); - - verifyExtensionContainingType(extensionLite); - return (Type) extensionLite.singularFromFieldSetType( - extensions.getRepeatedField(extensionLite.descriptor, index)); + return instance.getExtension(extension, index); } // This is implemented here only to work around an apparent bug in the @@ -542,9 +582,8 @@ public abstract class GeneratedMessageLite< checkIsLite(extension); verifyExtensionContainingType(extensionLite); - ensureExtensionsIsMutable(); - extensions.setField(extensionLite.descriptor, - extensionLite.toFieldSetType(value)); + copyOnWrite(); + instance.extensions.setField(extensionLite.descriptor, extensionLite.toFieldSetType(value)); return (BuilderType) this; } @@ -556,9 +595,9 @@ public abstract class GeneratedMessageLite< checkIsLite(extension); verifyExtensionContainingType(extensionLite); - ensureExtensionsIsMutable(); - extensions.setRepeatedField(extensionLite.descriptor, index, - extensionLite.singularToFieldSetType(value)); + copyOnWrite(); + instance.extensions.setRepeatedField( + extensionLite.descriptor, index, extensionLite.singularToFieldSetType(value)); return (BuilderType) this; } @@ -570,9 +609,9 @@ public abstract class GeneratedMessageLite< checkIsLite(extension); verifyExtensionContainingType(extensionLite); - ensureExtensionsIsMutable(); - extensions.addRepeatedField(extensionLite.descriptor, - extensionLite.singularToFieldSetType(value)); + copyOnWrite(); + instance.extensions.addRepeatedField( + extensionLite.descriptor, extensionLite.singularToFieldSetType(value)); return (BuilderType) this; } @@ -582,20 +621,10 @@ public abstract class GeneratedMessageLite< GeneratedExtension extensionLite = checkIsLite(extension); verifyExtensionContainingType(extensionLite); - ensureExtensionsIsMutable(); - extensions.clearField(extensionLite.descriptor); + copyOnWrite(); + instance.extensions.clearField(extensionLite.descriptor); return (BuilderType) this; } - - /** Called by subclasses to check if all extensions are initialized. */ - protected boolean extensionsAreInitialized() { - return extensions.isInitialized(); - } - - protected final void mergeExtensionFields(final MessageType other) { - ensureExtensionsIsMutable(); - extensions.mergeFrom(((ExtendableMessage) other).extensions); - } } //----------------------------------------------------------------- @@ -1113,4 +1142,133 @@ public abstract class GeneratedMessageLite< return (BuilderType) defaultInstance.toBuilder(); } } + + /** + * A static helper method for checking if a message is initialized, optionally memoizing. + *

+ * For use by generated code only. + */ + protected static final > boolean isInitialized( + T message, boolean shouldMemoize) { + return message.dynamicMethod(MethodToInvoke.IS_INITIALIZED, shouldMemoize) != null; + } + + protected static final > void makeImmutable(T message) { + message.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE); + } + + /** + * A static helper method for parsing a partial from input using the extension registry and the + * instance. + */ + static > T parsePartialFrom( + T instance, CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + try { + return (T) instance.dynamicMethod( + MethodToInvoke.PARSE_PARTIAL_FROM, input, extensionRegistry); + } catch (RuntimeException e) { + if (e.getCause() instanceof InvalidProtocolBufferException) { + throw (InvalidProtocolBufferException) e.getCause(); + } + throw e; + } + } + + /** + * A {@link Parser} implementation that delegates to the default instance. + *

+ * For use by generated code only. + */ + protected static class DefaultInstanceBasedParser> + extends AbstractParser { + + private T defaultInstance; + + public DefaultInstanceBasedParser(T defaultInstance) { + this.defaultInstance = defaultInstance; + } + + @Override + public T parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + return GeneratedMessageLite.parsePartialFrom(defaultInstance, input, extensionRegistry); + } + } + + protected static IntList newIntList() { + return new IntArrayList(); + } + + protected static IntList newIntList(List toCopy) { + return new IntArrayList(toCopy); + } + + protected static IntList emptyIntList() { + return IntArrayList.emptyList(); + } + + protected static LongList newLongList() { + return new LongArrayList(); + } + + protected static LongList newLongList(List toCopy) { + return new LongArrayList(toCopy); + } + + protected static LongList emptyLongList() { + return LongArrayList.emptyList(); + } + + protected static FloatList newFloatList() { + return new FloatArrayList(); + } + + protected static FloatList newFloatList(List toCopy) { + return new FloatArrayList(toCopy); + } + + protected static FloatList emptyFloatList() { + return FloatArrayList.emptyList(); + } + + protected static DoubleList newDoubleList() { + return new DoubleArrayList(); + } + + protected static DoubleList newDoubleList(List toCopy) { + return new DoubleArrayList(toCopy); + } + + protected static DoubleList emptyDoubleList() { + return DoubleArrayList.emptyList(); + } + + protected static BooleanList newBooleanList() { + return new BooleanArrayList(); + } + + protected static BooleanList newBooleanList(List toCopy) { + return new BooleanArrayList(toCopy); + } + + protected static BooleanList emptyBooleanList() { + return BooleanArrayList.emptyList(); + } + + protected static ProtobufList newProtobufList() { + return new ProtobufArrayList(); + } + + protected static ProtobufList newProtobufList(List toCopy) { + return new ProtobufArrayList(toCopy); + } + + protected static ProtobufList emptyProtobufList() { + return ProtobufArrayList.emptyList(); + } + + protected static LazyStringArrayList emptyLazyStringArrayList() { + return LazyStringArrayList.emptyList(); + } } diff --git a/java/src/main/java/com/google/protobuf/IntArrayList.java b/java/src/main/java/com/google/protobuf/IntArrayList.java new file mode 100644 index 00000000..f7609cc9 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/IntArrayList.java @@ -0,0 +1,242 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.IntList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link IntList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class IntArrayList extends AbstractProtobufList implements IntList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final IntArrayList EMPTY_LIST = new IntArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static IntArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private int[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code IntArrayList}. + */ + IntArrayList() { + array = new int[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code IntArrayList} containing the same elements as {@code other}. + */ + IntArrayList(List other) { + if (other instanceof IntArrayList) { + IntArrayList list = (IntArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new int[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Integer get(int index) { + return getInt(index); + } + + @Override + public int getInt(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Integer set(int index, Integer element) { + return setInt(index, element); + } + + @Override + public int setInt(int index, int element) { + ensureIsMutable(); + ensureIndexInRange(index); + int previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Integer element) { + addInt(index, element); + } + + /** + * Like {@link #add(Integer)} but more efficient in that it doesn't box the element. + */ + @Override + public void addInt(int element) { + addInt(size, element); + } + + /** + * Like {@link #add(int, Integer)} but more efficient in that it doesn't box the element. + */ + private void addInt(int index, int element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + int[] newArray = new int[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another IntArrayList to avoid boxing elements. + if (!(collection instanceof IntArrayList)) { + return super.addAll(collection); + } + + IntArrayList list = (IntArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Integer remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + int value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java index 74bf44c0..20054b79 100644 --- a/java/src/main/java/com/google/protobuf/Internal.java +++ b/java/src/main/java/com/google/protobuf/Internal.java @@ -30,6 +30,7 @@ package com.google.protobuf; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.AbstractList; @@ -532,4 +533,132 @@ public class Internal { } } } + + /** + * Extends {@link List} to add the capability to make the list immutable and inspect if it is + * modifiable. + */ + public static interface ProtobufList extends List { + + /** + * Makes this list immutable. All subsequent modifications will throw an + * {@link UnsupportedOperationException}. + */ + void makeImmutable(); + + /** + * Returns whether this list can be modified via the publicly accessible {@link List} methods. + */ + boolean isModifiable(); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Integers if + * possible. Does not support null elements. + */ + public static interface IntList extends ProtobufList { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + int getInt(int index); + + /** + * Like {@link #add(Integer)} but more efficient in that it doesn't box the element. + */ + void addInt(int element); + + /** + * Like {@link #set(int, Integer)} but more efficient in that it doesn't box the element. + */ + int setInt(int index, int element); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Booleans if + * possible. Does not support null elements. + */ + public static interface BooleanList extends ProtobufList { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + boolean getBoolean(int index); + + /** + * Like {@link #add(Boolean)} but more efficient in that it doesn't box the element. + */ + void addBoolean(boolean element); + + /** + * Like {@link #set(int, Boolean)} but more efficient in that it doesn't box the element. + */ + boolean setBoolean(int index, boolean element); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Longs if + * possible. Does not support null elements. + */ + public static interface LongList extends ProtobufList { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + long getLong(int index); + + /** + * Like {@link #add(Long)} but more efficient in that it doesn't box the element. + */ + void addLong(long element); + + /** + * Like {@link #set(int, Long)} but more efficient in that it doesn't box the element. + */ + long setLong(int index, long element); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Doubles if + * possible. Does not support null elements. + */ + public static interface DoubleList extends ProtobufList { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + double getDouble(int index); + + /** + * Like {@link #add(Double)} but more efficient in that it doesn't box the element. + */ + void addDouble(double element); + + /** + * Like {@link #set(int, Double)} but more efficient in that it doesn't box the element. + */ + double setDouble(int index, double element); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Floats if + * possible. Does not support null elements. + */ + public static interface FloatList extends ProtobufList { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + float getFloat(int index); + + /** + * Like {@link #add(Float)} but more efficient in that it doesn't box the element. + */ + void addFloat(float element); + + /** + * Like {@link #set(int, Float)} but more efficient in that it doesn't box the element. + */ + float setFloat(int index, float element); + } } diff --git a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java index 2d40a51f..a2997e1c 100644 --- a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java +++ b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java @@ -62,11 +62,20 @@ import java.util.RandomAccess; * * @author jonp@google.com (Jon Perlow) */ -public class LazyStringArrayList extends AbstractList +public class LazyStringArrayList extends AbstractProtobufList implements LazyStringList, RandomAccess { + + private static final LazyStringArrayList EMPTY_LIST = new LazyStringArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + static LazyStringArrayList emptyList() { + return EMPTY_LIST; + } - public static final LazyStringList EMPTY = - new LazyStringArrayList().getUnmodifiableView(); + // For compatibility with older runtimes. + public static final LazyStringList EMPTY = EMPTY_LIST; private final List list; @@ -116,12 +125,26 @@ public class LazyStringArrayList extends AbstractList @Override public String set(int index, String s) { + ensureIsMutable(); Object o = list.set(index, s); return asString(o); } @Override public void add(int index, String element) { + ensureIsMutable(); + list.add(index, element); + modCount++; + } + + private void add(int index, ByteString element) { + ensureIsMutable(); + list.add(index, element); + modCount++; + } + + private void add(int index, byte[] element) { + ensureIsMutable(); list.add(index, element); modCount++; } @@ -137,6 +160,7 @@ public class LazyStringArrayList extends AbstractList @Override public boolean addAll(int index, Collection c) { + ensureIsMutable(); // When copying from another LazyStringList, directly copy the underlying // elements rather than forcing each element to be decoded to a String. Collection collection = c instanceof LazyStringList @@ -148,6 +172,7 @@ public class LazyStringArrayList extends AbstractList // @Override public boolean addAllByteString(Collection values) { + ensureIsMutable(); boolean ret = list.addAll(values); modCount++; return ret; @@ -155,6 +180,7 @@ public class LazyStringArrayList extends AbstractList // @Override public boolean addAllByteArray(Collection c) { + ensureIsMutable(); boolean ret = list.addAll(c); modCount++; return ret; @@ -162,6 +188,7 @@ public class LazyStringArrayList extends AbstractList @Override public String remove(int index) { + ensureIsMutable(); Object o = list.remove(index); modCount++; return asString(o); @@ -169,18 +196,21 @@ public class LazyStringArrayList extends AbstractList @Override public void clear() { + ensureIsMutable(); list.clear(); modCount++; } // @Override public void add(ByteString element) { + ensureIsMutable(); list.add(element); modCount++; } // @Override public void add(byte[] element) { + ensureIsMutable(); list.add(element); modCount++; } @@ -207,14 +237,23 @@ public class LazyStringArrayList extends AbstractList // @Override public void set(int index, ByteString s) { - list.set(index, s); + setAndReturn(index, s); + } + + private Object setAndReturn(int index, ByteString s) { + ensureIsMutable(); + return list.set(index, s); } // @Override public void set(int index, byte[] s) { - list.set(index, s); + setAndReturn(index, s); + } + + private Object setAndReturn(int index, byte[] s) { + ensureIsMutable(); + return list.set(index, s); } - private static String asString(Object o) { if (o instanceof String) { @@ -253,6 +292,7 @@ public class LazyStringArrayList extends AbstractList // @Override public void mergeFrom(LazyStringList other) { + ensureIsMutable(); for (Object o : other.getUnderlyingElements()) { if (o instanceof byte[]) { byte[] b = (byte[]) o; @@ -267,20 +307,15 @@ public class LazyStringArrayList extends AbstractList private static class ByteArrayListView extends AbstractList implements RandomAccess { - private final List list; + private final LazyStringArrayList list; - ByteArrayListView(List list) { + ByteArrayListView(LazyStringArrayList list) { this.list = list; } @Override public byte[] get(int index) { - Object o = list.get(index); - byte[] b = asByteArray(o); - if (b != o) { - list.set(index, b); - } - return b; + return list.getByteArray(index); } @Override @@ -290,7 +325,7 @@ public class LazyStringArrayList extends AbstractList @Override public byte[] set(int index, byte[] s) { - Object o = list.set(index, s); + Object o = list.setAndReturn(index, s); modCount++; return asByteArray(o); } @@ -311,25 +346,20 @@ public class LazyStringArrayList extends AbstractList // @Override public List asByteArrayList() { - return new ByteArrayListView(list); + return new ByteArrayListView(this); } private static class ByteStringListView extends AbstractList implements RandomAccess { - private final List list; + private final LazyStringArrayList list; - ByteStringListView(List list) { + ByteStringListView(LazyStringArrayList list) { this.list = list; } @Override public ByteString get(int index) { - Object o = list.get(index); - ByteString b = asByteString(o); - if (b != o) { - list.set(index, b); - } - return b; + return list.getByteString(index); } @Override @@ -339,7 +369,7 @@ public class LazyStringArrayList extends AbstractList @Override public ByteString set(int index, ByteString s) { - Object o = list.set(index, s); + Object o = list.setAndReturn(index, s); modCount++; return asByteString(o); } @@ -360,12 +390,15 @@ public class LazyStringArrayList extends AbstractList // @Override public List asByteStringList() { - return new ByteStringListView(list); + return new ByteStringListView(this); } // @Override public LazyStringList getUnmodifiableView() { - return new UnmodifiableLazyStringList(this); + if (isModifiable()) { + return new UnmodifiableLazyStringList(this); + } + return this; } } diff --git a/java/src/main/java/com/google/protobuf/LongArrayList.java b/java/src/main/java/com/google/protobuf/LongArrayList.java new file mode 100644 index 00000000..298617ff --- /dev/null +++ b/java/src/main/java/com/google/protobuf/LongArrayList.java @@ -0,0 +1,242 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.LongList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link LongList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class LongArrayList extends AbstractProtobufList implements LongList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final LongArrayList EMPTY_LIST = new LongArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static LongArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private long[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code LongArrayList}. + */ + LongArrayList() { + array = new long[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code LongArrayList} containing the same elements as {@code other}. + */ + LongArrayList(List other) { + if (other instanceof LongArrayList) { + LongArrayList list = (LongArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new long[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Long get(int index) { + return getLong(index); + } + + @Override + public long getLong(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Long set(int index, Long element) { + return setLong(index, element); + } + + @Override + public long setLong(int index, long element) { + ensureIsMutable(); + ensureIndexInRange(index); + long previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Long element) { + addLong(index, element); + } + + /** + * Like {@link #add(Long)} but more efficient in that it doesn't box the element. + */ + @Override + public void addLong(long element) { + addLong(size, element); + } + + /** + * Like {@link #add(int, Long)} but more efficient in that it doesn't box the element. + */ + private void addLong(int index, long element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + long[] newArray = new long[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another LongArrayList to avoid boxing elements. + if (!(collection instanceof LongArrayList)) { + return super.addAll(collection); + } + + LongArrayList list = (LongArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Long remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + long value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/MapField.java b/java/src/main/java/com/google/protobuf/MapField.java index 82906d37..b290993c 100644 --- a/java/src/main/java/com/google/protobuf/MapField.java +++ b/java/src/main/java/com/google/protobuf/MapField.java @@ -30,9 +30,11 @@ package com.google.protobuf; +import com.google.protobuf.MapFieldLite.MutatabilityAwareMap; + import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -51,7 +53,7 @@ import java.util.Map; * and getList() concurrently in multiple threads. If write-access is needed, * all access must be synchronized. */ -public class MapField { +public class MapField implements MutabilityOracle { /** * Indicates where the data of this map field is currently stored. * @@ -72,8 +74,9 @@ public class MapField { */ private enum StorageMode {MAP, LIST, BOTH} + private volatile boolean isMutable; private volatile StorageMode mode; - private Map mapData; + private MutatabilityAwareMap mapData; private List listData; // Convert between a map entry Message and a key-value pair. @@ -110,20 +113,19 @@ public class MapField { private MapField( Converter converter, StorageMode mode, - Map mapData, - List listData) { + Map mapData) { this.converter = converter; + this.isMutable = true; this.mode = mode; - this.mapData = mapData; - this.listData = listData; + this.mapData = new MutatabilityAwareMap(this, mapData); + this.listData = null; } private MapField( MapEntry defaultEntry, StorageMode mode, - Map mapData, - List listData) { - this(new ImmutableMessageConverter(defaultEntry), mode, mapData, listData); + Map mapData) { + this(new ImmutableMessageConverter(defaultEntry), mode, mapData); } @@ -131,14 +133,14 @@ public class MapField { public static MapField emptyMapField( MapEntry defaultEntry) { return new MapField( - defaultEntry, StorageMode.MAP, Collections.emptyMap(), null); + defaultEntry, StorageMode.MAP, Collections.emptyMap()); } /** Creates a new mutable empty MapField. */ public static MapField newMapField(MapEntry defaultEntry) { return new MapField( - defaultEntry, StorageMode.MAP, new HashMap(), null); + defaultEntry, StorageMode.MAP, new LinkedHashMap()); } @@ -151,7 +153,7 @@ public class MapField { converter.convertMessageToKeyAndValue(message, map); } - private List convertMapToList(Map mapData) { + private List convertMapToList(MutatabilityAwareMap mapData) { List listData = new ArrayList(); for (Map.Entry entry : mapData.entrySet()) { listData.add( @@ -161,12 +163,12 @@ public class MapField { return listData; } - private Map convertListToMap(List listData) { - Map mapData = new HashMap(); + private MutatabilityAwareMap convertListToMap(List listData) { + Map mapData = new LinkedHashMap(); for (Message item : listData) { convertMessageToKeyAndValue(item, mapData); } - return mapData; + return new MutatabilityAwareMap(this, mapData); } /** Returns the content of this MapField as a read-only Map. */ @@ -199,7 +201,7 @@ public class MapField { } public void clear() { - mapData = new HashMap(); + mapData = new MutatabilityAwareMap(this, new LinkedHashMap()); mode = StorageMode.MAP; } @@ -221,7 +223,7 @@ public class MapField { /** Returns a deep copy of this MapField. */ public MapField copy() { return new MapField( - converter, StorageMode.MAP, MapFieldLite.copy(getMap()), null); + converter, StorageMode.MAP, MapFieldLite.copy(getMap())); } /** Gets the content of this MapField as a read-only List. */ @@ -256,4 +258,29 @@ public class MapField { Message getMapEntryMessageDefaultInstance() { return converter.getMessageDefaultInstance(); } + + /** + * Makes this list immutable. All subsequent modifications will throw an + * {@link UnsupportedOperationException}. + */ + public void makeImmutable() { + isMutable = false; + } + + /** + * Returns whether this field can be modified. + */ + public boolean isMutable() { + return isMutable; + } + + /* (non-Javadoc) + * @see com.google.protobuf.MutabilityOracle#ensureMutable() + */ + @Override + public void ensureMutable() { + if (!isMutable()) { + throw new UnsupportedOperationException(); + } + } } diff --git a/java/src/main/java/com/google/protobuf/MapFieldLite.java b/java/src/main/java/com/google/protobuf/MapFieldLite.java index 7f94c690..c17fa7b1 100644 --- a/java/src/main/java/com/google/protobuf/MapFieldLite.java +++ b/java/src/main/java/com/google/protobuf/MapFieldLite.java @@ -31,9 +31,12 @@ package com.google.protobuf; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; -import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; /** * Internal representation of map fields in generated lite-runtime messages. @@ -41,16 +44,21 @@ import java.util.Map; * This class is a protobuf implementation detail. Users shouldn't use this * class directly. */ -public class MapFieldLite { - private Map mapData; +public class MapFieldLite implements MutabilityOracle { + private MutatabilityAwareMap mapData; + private boolean isMutable; private MapFieldLite(Map mapData) { - this.mapData = mapData; + this.mapData = new MutatabilityAwareMap(this, mapData); + this.isMutable = true; } @SuppressWarnings({"rawtypes", "unchecked"}) private static final MapFieldLite EMPTY_MAP_FIELD = new MapFieldLite(Collections.emptyMap()); + static { + EMPTY_MAP_FIELD.makeImmutable(); + } /** Returns an singleton immutable empty MapFieldLite instance. */ @SuppressWarnings({"unchecked", "cast"}) @@ -60,7 +68,7 @@ public class MapFieldLite { /** Creates a new MapFieldLite instance. */ public static MapFieldLite newMapField() { - return new MapFieldLite(new HashMap()); + return new MapFieldLite(new LinkedHashMap()); } /** Gets the content of this MapField as a read-only Map. */ @@ -168,7 +176,7 @@ public class MapFieldLite { */ @SuppressWarnings("unchecked") static Map copy(Map map) { - Map result = new HashMap(); + Map result = new LinkedHashMap(); for (Map.Entry entry : map.entrySet()) { result.put(entry.getKey(), (V) copy(entry.getValue())); } @@ -179,4 +187,360 @@ public class MapFieldLite { public MapFieldLite copy() { return new MapFieldLite(copy(mapData)); } + + /** + * Makes this field immutable. All subsequent modifications will throw an + * {@link UnsupportedOperationException}. + */ + public void makeImmutable() { + isMutable = false; + } + + /** + * Returns whether this field can be modified. + */ + public boolean isMutable() { + return isMutable; + } + + @Override + public void ensureMutable() { + if (!isMutable()) { + throw new UnsupportedOperationException(); + } + } + + /** + * An internal map that checks for mutability before delegating. + */ + static class MutatabilityAwareMap implements Map { + private final MutabilityOracle mutabilityOracle; + private final Map delegate; + + MutatabilityAwareMap(MutabilityOracle mutabilityOracle, Map delegate) { + this.mutabilityOracle = mutabilityOracle; + this.delegate = delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return delegate.containsValue(value); + } + + @Override + public V get(Object key) { + return delegate.get(key); + } + + @Override + public V put(K key, V value) { + mutabilityOracle.ensureMutable(); + return delegate.put(key, value); + } + + @Override + public V remove(Object key) { + mutabilityOracle.ensureMutable(); + return delegate.remove(key); + } + + @Override + public void putAll(Map m) { + mutabilityOracle.ensureMutable(); + delegate.putAll(m); + } + + @Override + public void clear() { + mutabilityOracle.ensureMutable(); + delegate.clear(); + } + + @Override + public Set keySet() { + return new MutatabilityAwareSet(mutabilityOracle, delegate.keySet()); + } + + @Override + public Collection values() { + return new MutatabilityAwareCollection(mutabilityOracle, delegate.values()); + } + + @Override + public Set> entrySet() { + return new MutatabilityAwareSet>(mutabilityOracle, delegate.entrySet()); + } + + @Override + public boolean equals(Object o) { + return delegate.equals(o); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + } + + /** + * An internal collection that checks for mutability before delegating. + */ + private static class MutatabilityAwareCollection implements Collection { + private final MutabilityOracle mutabilityOracle; + private final Collection delegate; + + MutatabilityAwareCollection(MutabilityOracle mutabilityOracle, Collection delegate) { + this.mutabilityOracle = mutabilityOracle; + this.delegate = delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return delegate.contains(o); + } + + @Override + public Iterator iterator() { + return new MutatabilityAwareIterator(mutabilityOracle, delegate.iterator()); + } + + @Override + public Object[] toArray() { + return delegate.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return delegate.toArray(a); + } + + @Override + public boolean add(E e) { + // Unsupported operation in the delegate. + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + mutabilityOracle.ensureMutable(); + return delegate.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return delegate.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + // Unsupported operation in the delegate. + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection c) { + mutabilityOracle.ensureMutable(); + return delegate.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + mutabilityOracle.ensureMutable(); + return delegate.retainAll(c); + } + + @Override + public void clear() { + mutabilityOracle.ensureMutable(); + delegate.clear(); + } + + @Override + public boolean equals(Object o) { + return delegate.equals(o); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + } + + /** + * An internal set that checks for mutability before delegating. + */ + private static class MutatabilityAwareSet implements Set { + private final MutabilityOracle mutabilityOracle; + private final Set delegate; + + MutatabilityAwareSet(MutabilityOracle mutabilityOracle, Set delegate) { + this.mutabilityOracle = mutabilityOracle; + this.delegate = delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return delegate.contains(o); + } + + @Override + public Iterator iterator() { + return new MutatabilityAwareIterator(mutabilityOracle, delegate.iterator()); + } + + @Override + public Object[] toArray() { + return delegate.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return delegate.toArray(a); + } + + @Override + public boolean add(E e) { + mutabilityOracle.ensureMutable(); + return delegate.add(e); + } + + @Override + public boolean remove(Object o) { + mutabilityOracle.ensureMutable(); + return delegate.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return delegate.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + mutabilityOracle.ensureMutable(); + return delegate.addAll(c); + } + + @Override + public boolean retainAll(Collection c) { + mutabilityOracle.ensureMutable(); + return delegate.retainAll(c); + } + + @Override + public boolean removeAll(Collection c) { + mutabilityOracle.ensureMutable(); + return delegate.removeAll(c); + } + + @Override + public void clear() { + mutabilityOracle.ensureMutable(); + delegate.clear(); + } + + @Override + public boolean equals(Object o) { + return delegate.equals(o); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + } + + /** + * An internal iterator that checks for mutability before delegating. + */ + private static class MutatabilityAwareIterator implements Iterator { + private final MutabilityOracle mutabilityOracle; + private final Iterator delegate; + + MutatabilityAwareIterator(MutabilityOracle mutabilityOracle, Iterator delegate) { + this.mutabilityOracle = mutabilityOracle; + this.delegate = delegate; + } + + @Override + public boolean hasNext() { + return delegate.hasNext(); + } + + @Override + public E next() { + return delegate.next(); + } + + @Override + public void remove() { + mutabilityOracle.ensureMutable(); + delegate.remove(); + } + + @Override + public boolean equals(Object obj) { + return delegate.equals(obj); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + } } diff --git a/java/src/main/java/com/google/protobuf/MutabilityOracle.java b/java/src/main/java/com/google/protobuf/MutabilityOracle.java new file mode 100644 index 00000000..82b723c9 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/MutabilityOracle.java @@ -0,0 +1,48 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +/** + * Verifies that an object is mutable, throwing if not. + */ +interface MutabilityOracle { + static final MutabilityOracle IMMUTABLE = new MutabilityOracle() { + @Override + public void ensureMutable() { + throw new UnsupportedOperationException(); + } + }; + + /** + * Throws an {@link UnsupportedOperationException} if not mutable. + */ + void ensureMutable(); +} diff --git a/java/src/main/java/com/google/protobuf/ProtobufArrayList.java b/java/src/main/java/com/google/protobuf/ProtobufArrayList.java new file mode 100644 index 00000000..759368c9 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/ProtobufArrayList.java @@ -0,0 +1,95 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.ProtobufList; + +import java.util.ArrayList; +import java.util.List; + +/** + * Implements {@link ProtobufList} for non-primitive and {@link String} types. + */ +class ProtobufArrayList extends AbstractProtobufList { + + private static final ProtobufArrayList EMPTY_LIST = new ProtobufArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + @SuppressWarnings("unchecked") // Guaranteed safe by runtime. + public static ProtobufArrayList emptyList() { + return (ProtobufArrayList) EMPTY_LIST; + } + + private final List list; + + ProtobufArrayList() { + list = new ArrayList(); + } + + ProtobufArrayList(List toCopy) { + list = new ArrayList(toCopy); + } + + @Override + public void add(int index, E element) { + ensureIsMutable(); + list.add(index, element); + modCount++; + } + + @Override + public E get(int index) { + return list.get(index); + } + + @Override + public E remove(int index) { + ensureIsMutable(); + E toReturn = list.remove(index); + modCount++; + return toReturn; + } + + @Override + public E set(int index, E element) { + ensureIsMutable(); + E toReturn = list.set(index, element); + modCount++; + return toReturn; + } + + @Override + public int size() { + return list.size(); + } +} diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java index dd2b4600..a79ce559 100644 --- a/java/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -1725,7 +1725,7 @@ public final class TextFormat { * {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with * "\x") are also recognized. */ - static ByteString unescapeBytes(final CharSequence charString) + public static ByteString unescapeBytes(final CharSequence charString) throws InvalidEscapeSequenceException { // First convert the Java character sequence to UTF-8 bytes. ByteString input = ByteString.copyFromUtf8(charString.toString()); @@ -1808,7 +1808,7 @@ public final class TextFormat { * Thrown by {@link TextFormat#unescapeBytes} and * {@link TextFormat#unescapeText} when an invalid escape sequence is seen. */ - static class InvalidEscapeSequenceException extends IOException { + public static class InvalidEscapeSequenceException extends IOException { private static final long serialVersionUID = -8164033650142593304L; InvalidEscapeSequenceException(final String description) { diff --git a/java/src/test/java/com/google/protobuf/BooleanArrayListTest.java b/java/src/test/java/com/google/protobuf/BooleanArrayListTest.java new file mode 100644 index 00000000..df89c263 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/BooleanArrayListTest.java @@ -0,0 +1,473 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static java.util.Arrays.asList; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; + +/** + * Tests for {@link BooleanArrayList}. + * + * @author dweis@google.com (Daniel Weis) + */ +public class BooleanArrayListTest extends TestCase { + + private static final BooleanArrayList UNARY_LIST = newImmutableBooleanArrayList(true); + private static final BooleanArrayList TERTIARY_LIST = + newImmutableBooleanArrayList(true, true, false); + + private BooleanArrayList list; + + @Override + protected void setUp() throws Exception { + list = new BooleanArrayList(); + } + + public void testEmptyListReturnsSameInstance() { + assertSame(BooleanArrayList.emptyList(), BooleanArrayList.emptyList()); + } + + public void testEmptyListIsImmutable() { + assertImmutable(BooleanArrayList.emptyList()); + } + + public void testMakeImmutable() { + list.addBoolean(true); + list.addBoolean(false); + list.addBoolean(true); + list.addBoolean(true); + list.makeImmutable(); + assertImmutable(list); + } + + public void testCopyConstructor() { + BooleanArrayList copy = new BooleanArrayList(TERTIARY_LIST); + assertEquals(TERTIARY_LIST, copy); + + copy = new BooleanArrayList(BooleanArrayList.emptyList()); + assertEquals(BooleanArrayList.emptyList(), copy); + + copy = new BooleanArrayList(asList(false, false, true)); + assertEquals(asList(false, false, true), copy); + + copy = new BooleanArrayList(Collections.emptyList()); + assertEquals(BooleanArrayList.emptyList(), copy); + } + + public void testModificationWithIteration() { + list.addAll(asList(true, false, false, true)); + Iterator iterator = list.iterator(); + assertEquals(4, list.size()); + assertEquals(true, (boolean) list.get(0)); + assertEquals(true, (boolean) iterator.next()); + list.set(0, true); + assertEquals(false, (boolean) iterator.next()); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, false); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testGet() { + assertEquals(true, (boolean) TERTIARY_LIST.get(0)); + assertEquals(true, (boolean) TERTIARY_LIST.get(1)); + assertEquals(false, (boolean) TERTIARY_LIST.get(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testGetInt() { + assertEquals(true, TERTIARY_LIST.getBoolean(0)); + assertEquals(true, TERTIARY_LIST.getBoolean(1)); + assertEquals(false, TERTIARY_LIST.getBoolean(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSize() { + assertEquals(0, BooleanArrayList.emptyList().size()); + assertEquals(1, UNARY_LIST.size()); + assertEquals(3, TERTIARY_LIST.size()); + + list.addBoolean(true); + list.addBoolean(false); + list.addBoolean(false); + list.addBoolean(false); + assertEquals(4, list.size()); + + list.remove(0); + assertEquals(3, list.size()); + + list.add(true); + assertEquals(4, list.size()); + } + + public void testSet() { + list.addBoolean(false); + list.addBoolean(false); + + assertEquals(false, (boolean) list.set(0, true)); + assertEquals(true, list.getBoolean(0)); + + assertEquals(false, (boolean) list.set(1, false)); + assertEquals(false, list.getBoolean(1)); + + try { + list.set(-1, true); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.set(2, false); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSetInt() { + list.addBoolean(true); + list.addBoolean(true); + + assertEquals(true, list.setBoolean(0, false)); + assertEquals(false, list.getBoolean(0)); + + assertEquals(true, list.setBoolean(1, false)); + assertEquals(false, list.getBoolean(1)); + + try { + list.setBoolean(-1, false); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.setBoolean(2, true); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAdd() { + assertEquals(0, list.size()); + + assertTrue(list.add(true)); + assertEquals(asList(true), list); + + assertTrue(list.add(false)); + list.add(0, false); + assertEquals(asList(false, true, false), list); + + list.add(0, false); + list.add(0, true); + // Force a resize by getting up to 11 elements. + for (int i = 0; i < 6; i++) { + list.add(true); + } + assertEquals(asList(true, false, false, true, false, true, true, true, true, true, true), list); + + try { + list.add(-1, false); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.add(4, true); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAddInt() { + assertEquals(0, list.size()); + + list.addBoolean(true); + assertEquals(asList(true), list); + + list.addBoolean(false); + assertEquals(asList(true, false), list); + } + + public void testAddAll() { + assertEquals(0, list.size()); + + assertTrue(list.addAll(Collections.singleton(false))); + assertEquals(1, list.size()); + assertEquals(false, (boolean) list.get(0)); + assertEquals(false, list.getBoolean(0)); + + assertTrue(list.addAll(asList(true, false, false, false, true))); + assertEquals(asList(false, true, false, false, false, true), list); + + assertTrue(list.addAll(TERTIARY_LIST)); + assertEquals(asList(false, true, false, false, false, true, true, true, false), list); + + assertFalse(list.addAll(Collections.emptyList())); + assertFalse(list.addAll(BooleanArrayList.emptyList())); + } + + public void testRemove() { + list.addAll(TERTIARY_LIST); + assertEquals(true, (boolean) list.remove(0)); + assertEquals(asList(true, false), list); + + assertTrue(list.remove(Boolean.TRUE)); + assertEquals(asList(false), list); + + assertFalse(list.remove(Boolean.TRUE)); + assertEquals(asList(false), list); + + assertEquals(false, (boolean) list.remove(0)); + assertEquals(asList(), list); + + try { + list.remove(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.remove(0); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + private void assertImmutable(BooleanArrayList list) { + if (list.contains(1)) { + throw new RuntimeException("Cannot test the immutability of lists that contain 1."); + } + + try { + list.add(false); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, true); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.singletonList(false)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(new BooleanArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.singleton(true)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addBoolean(true); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, true); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.setBoolean(0, false); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + private static BooleanArrayList newImmutableBooleanArrayList(boolean... elements) { + BooleanArrayList list = new BooleanArrayList(); + for (boolean element : elements) { + list.addBoolean(element); + } + list.makeImmutable(); + return list; + } +} diff --git a/java/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/src/test/java/com/google/protobuf/DescriptorsTest.java index 6cfa18d5..edd7fc46 100644 --- a/java/src/test/java/com/google/protobuf/DescriptorsTest.java +++ b/java/src/test/java/com/google/protobuf/DescriptorsTest.java @@ -56,6 +56,7 @@ import protobuf_unittest.UnittestProto.TestAllTypes; import protobuf_unittest.UnittestProto.TestExtremeDefaultValues; import protobuf_unittest.UnittestProto.TestMultipleExtensionRanges; import protobuf_unittest.UnittestProto.TestRequired; +import protobuf_unittest.UnittestProto.TestReservedFields; import protobuf_unittest.UnittestProto.TestService; import junit.framework.TestCase; @@ -687,6 +688,9 @@ public class DescriptorsTest extends TestCase { assertEquals(4, oneofDescriptor.getFieldCount()); assertSame(oneofDescriptor.getField(1), field); + + assertEquals(4, oneofDescriptor.getFields().size()); + assertEquals(oneofDescriptor.getFields().get(1), field); } public void testMessageDescriptorExtensions() throws Exception { @@ -702,6 +706,19 @@ public class DescriptorsTest extends TestCase { assertTrue(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(4143)); } + public void testReservedFields() { + Descriptor d = TestReservedFields.getDescriptor(); + assertTrue(d.isReservedNumber(2)); + assertFalse(d.isReservedNumber(8)); + assertTrue(d.isReservedNumber(9)); + assertTrue(d.isReservedNumber(10)); + assertTrue(d.isReservedNumber(11)); + assertFalse(d.isReservedNumber(12)); + assertFalse(d.isReservedName("foo")); + assertTrue(d.isReservedName("bar")); + assertTrue(d.isReservedName("baz")); + } + public void testToString() { assertEquals("protobuf_unittest.TestAllTypes.optional_uint64", UnittestProto.TestAllTypes.getDescriptor().findFieldByNumber( diff --git a/java/src/test/java/com/google/protobuf/DoubleArrayListTest.java b/java/src/test/java/com/google/protobuf/DoubleArrayListTest.java new file mode 100644 index 00000000..e7a73d70 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/DoubleArrayListTest.java @@ -0,0 +1,473 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static java.util.Arrays.asList; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; + +/** + * Tests for {@link DoubleArrayList}. + * + * @author dweis@google.com (Daniel Weis) + */ +public class DoubleArrayListTest extends TestCase { + + private static final DoubleArrayList UNARY_LIST = newImmutableDoubleArrayList(1); + private static final DoubleArrayList TERTIARY_LIST = + newImmutableDoubleArrayList(1, 2, 3); + + private DoubleArrayList list; + + @Override + protected void setUp() throws Exception { + list = new DoubleArrayList(); + } + + public void testEmptyListReturnsSameInstance() { + assertSame(DoubleArrayList.emptyList(), DoubleArrayList.emptyList()); + } + + public void testEmptyListIsImmutable() { + assertImmutable(DoubleArrayList.emptyList()); + } + + public void testMakeImmutable() { + list.addDouble(2); + list.addDouble(4); + list.addDouble(6); + list.addDouble(8); + list.makeImmutable(); + assertImmutable(list); + } + + public void testCopyConstructor() { + DoubleArrayList copy = new DoubleArrayList(TERTIARY_LIST); + assertEquals(TERTIARY_LIST, copy); + + copy = new DoubleArrayList(DoubleArrayList.emptyList()); + assertEquals(DoubleArrayList.emptyList(), copy); + + copy = new DoubleArrayList(asList(1D, 2D, 3D)); + assertEquals(asList(1D, 2D, 3D), copy); + + copy = new DoubleArrayList(Collections.emptyList()); + assertEquals(DoubleArrayList.emptyList(), copy); + } + + public void testModificationWithIteration() { + list.addAll(asList(1D, 2D, 3D, 4D)); + Iterator iterator = list.iterator(); + assertEquals(4, list.size()); + assertEquals(1D, (double) list.get(0)); + assertEquals(1D, (double) iterator.next()); + list.set(0, 1D); + assertEquals(2D, (double) iterator.next()); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, 0D); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testGet() { + assertEquals(1D, (double) TERTIARY_LIST.get(0)); + assertEquals(2D, (double) TERTIARY_LIST.get(1)); + assertEquals(3D, (double) TERTIARY_LIST.get(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testGetInt() { + assertEquals(1D, TERTIARY_LIST.getDouble(0)); + assertEquals(2D, TERTIARY_LIST.getDouble(1)); + assertEquals(3D, TERTIARY_LIST.getDouble(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSize() { + assertEquals(0, DoubleArrayList.emptyList().size()); + assertEquals(1, UNARY_LIST.size()); + assertEquals(3, TERTIARY_LIST.size()); + + list.addDouble(2); + list.addDouble(4); + list.addDouble(6); + list.addDouble(8); + assertEquals(4, list.size()); + + list.remove(0); + assertEquals(3, list.size()); + + list.add(16D); + assertEquals(4, list.size()); + } + + public void testSet() { + list.addDouble(2); + list.addDouble(4); + + assertEquals(2D, (double) list.set(0, 0D)); + assertEquals(0D, list.getDouble(0)); + + assertEquals(4D, (double) list.set(1, 0D)); + assertEquals(0D, list.getDouble(1)); + + try { + list.set(-1, 0D); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.set(2, 0D); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSetInt() { + list.addDouble(2); + list.addDouble(4); + + assertEquals(2D, list.setDouble(0, 0)); + assertEquals(0D, list.getDouble(0)); + + assertEquals(4D, list.setDouble(1, 0)); + assertEquals(0D, list.getDouble(1)); + + try { + list.setDouble(-1, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.setDouble(2, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAdd() { + assertEquals(0, list.size()); + + assertTrue(list.add(2D)); + assertEquals(asList(2D), list); + + assertTrue(list.add(3D)); + list.add(0, 4D); + assertEquals(asList(4D, 2D, 3D), list); + + list.add(0, 1D); + list.add(0, 0D); + // Force a resize by getting up to 11 elements. + for (int i = 0; i < 6; i++) { + list.add(Double.valueOf(5 + i)); + } + assertEquals(asList(0D, 1D, 4D, 2D, 3D, 5D, 6D, 7D, 8D, 9D, 10D), list); + + try { + list.add(-1, 5D); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.add(4, 5D); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAddInt() { + assertEquals(0, list.size()); + + list.addDouble(2); + assertEquals(asList(2D), list); + + list.addDouble(3); + assertEquals(asList(2D, 3D), list); + } + + public void testAddAll() { + assertEquals(0, list.size()); + + assertTrue(list.addAll(Collections.singleton(1D))); + assertEquals(1, list.size()); + assertEquals(1D, (double) list.get(0)); + assertEquals(1D, list.getDouble(0)); + + assertTrue(list.addAll(asList(2D, 3D, 4D, 5D, 6D))); + assertEquals(asList(1D, 2D, 3D, 4D, 5D, 6D), list); + + assertTrue(list.addAll(TERTIARY_LIST)); + assertEquals(asList(1D, 2D, 3D, 4D, 5D, 6D, 1D, 2D, 3D), list); + + assertFalse(list.addAll(Collections.emptyList())); + assertFalse(list.addAll(DoubleArrayList.emptyList())); + } + + public void testRemove() { + list.addAll(TERTIARY_LIST); + assertEquals(1D, (double) list.remove(0)); + assertEquals(asList(2D, 3D), list); + + assertTrue(list.remove(Double.valueOf(3))); + assertEquals(asList(2D), list); + + assertFalse(list.remove(Double.valueOf(3))); + assertEquals(asList(2D), list); + + assertEquals(2D, (double) list.remove(0)); + assertEquals(asList(), list); + + try { + list.remove(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.remove(0); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + private void assertImmutable(DoubleArrayList list) { + if (list.contains(1)) { + throw new RuntimeException("Cannot test the immutability of lists that contain 1."); + } + + try { + list.add(1D); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, 1D); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.singletonList(1D)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(new DoubleArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.singleton(1D)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addDouble(0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, 0D); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.setDouble(0, 0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + private static DoubleArrayList newImmutableDoubleArrayList(double... elements) { + DoubleArrayList list = new DoubleArrayList(); + for (double element : elements) { + list.addDouble(element); + } + list.makeImmutable(); + return list; + } +} diff --git a/java/src/test/java/com/google/protobuf/FloatArrayListTest.java b/java/src/test/java/com/google/protobuf/FloatArrayListTest.java new file mode 100644 index 00000000..8f3e93dc --- /dev/null +++ b/java/src/test/java/com/google/protobuf/FloatArrayListTest.java @@ -0,0 +1,473 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static java.util.Arrays.asList; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; + +/** + * Tests for {@link FloatArrayList}. + * + * @author dweis@google.com (Daniel Weis) + */ +public class FloatArrayListTest extends TestCase { + + private static final FloatArrayList UNARY_LIST = newImmutableFloatArrayList(1); + private static final FloatArrayList TERTIARY_LIST = + newImmutableFloatArrayList(1, 2, 3); + + private FloatArrayList list; + + @Override + protected void setUp() throws Exception { + list = new FloatArrayList(); + } + + public void testEmptyListReturnsSameInstance() { + assertSame(FloatArrayList.emptyList(), FloatArrayList.emptyList()); + } + + public void testEmptyListIsImmutable() { + assertImmutable(FloatArrayList.emptyList()); + } + + public void testMakeImmutable() { + list.addFloat(2); + list.addFloat(4); + list.addFloat(6); + list.addFloat(8); + list.makeImmutable(); + assertImmutable(list); + } + + public void testCopyConstructor() { + FloatArrayList copy = new FloatArrayList(TERTIARY_LIST); + assertEquals(TERTIARY_LIST, copy); + + copy = new FloatArrayList(FloatArrayList.emptyList()); + assertEquals(FloatArrayList.emptyList(), copy); + + copy = new FloatArrayList(asList(1F, 2F, 3F)); + assertEquals(asList(1F, 2F, 3F), copy); + + copy = new FloatArrayList(Collections.emptyList()); + assertEquals(FloatArrayList.emptyList(), copy); + } + + public void testModificationWithIteration() { + list.addAll(asList(1F, 2F, 3F, 4F)); + Iterator iterator = list.iterator(); + assertEquals(4, list.size()); + assertEquals(1F, (float) list.get(0)); + assertEquals(1F, (float) iterator.next()); + list.set(0, 1F); + assertEquals(2F, (float) iterator.next()); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, 0F); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testGet() { + assertEquals(1F, (float) TERTIARY_LIST.get(0)); + assertEquals(2F, (float) TERTIARY_LIST.get(1)); + assertEquals(3F, (float) TERTIARY_LIST.get(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testGetFloat() { + assertEquals(1F, TERTIARY_LIST.getFloat(0)); + assertEquals(2F, TERTIARY_LIST.getFloat(1)); + assertEquals(3F, TERTIARY_LIST.getFloat(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSize() { + assertEquals(0, FloatArrayList.emptyList().size()); + assertEquals(1, UNARY_LIST.size()); + assertEquals(3, TERTIARY_LIST.size()); + + list.addFloat(2); + list.addFloat(4); + list.addFloat(6); + list.addFloat(8); + assertEquals(4, list.size()); + + list.remove(0); + assertEquals(3, list.size()); + + list.add(16F); + assertEquals(4, list.size()); + } + + public void testSet() { + list.addFloat(2); + list.addFloat(4); + + assertEquals(2F, (float) list.set(0, 0F)); + assertEquals(0F, list.getFloat(0)); + + assertEquals(4F, (float) list.set(1, 0F)); + assertEquals(0F, list.getFloat(1)); + + try { + list.set(-1, 0F); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.set(2, 0F); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSetFloat() { + list.addFloat(2); + list.addFloat(4); + + assertEquals(2F, list.setFloat(0, 0)); + assertEquals(0F, list.getFloat(0)); + + assertEquals(4F, list.setFloat(1, 0)); + assertEquals(0F, list.getFloat(1)); + + try { + list.setFloat(-1, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.setFloat(2, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAdd() { + assertEquals(0, list.size()); + + assertTrue(list.add(2F)); + assertEquals(asList(2F), list); + + assertTrue(list.add(3F)); + list.add(0, 4F); + assertEquals(asList(4F, 2F, 3F), list); + + list.add(0, 1F); + list.add(0, 0F); + // Force a resize by getting up to 11 elements. + for (int i = 0; i < 6; i++) { + list.add(Float.valueOf(5 + i)); + } + assertEquals(asList(0F, 1F, 4F, 2F, 3F, 5F, 6F, 7F, 8F, 9F, 10F), list); + + try { + list.add(-1, 5F); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.add(4, 5F); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAddFloat() { + assertEquals(0, list.size()); + + list.addFloat(2); + assertEquals(asList(2F), list); + + list.addFloat(3); + assertEquals(asList(2F, 3F), list); + } + + public void testAddAll() { + assertEquals(0, list.size()); + + assertTrue(list.addAll(Collections.singleton(1F))); + assertEquals(1, list.size()); + assertEquals(1F, (float) list.get(0)); + assertEquals(1F, list.getFloat(0)); + + assertTrue(list.addAll(asList(2F, 3F, 4F, 5F, 6F))); + assertEquals(asList(1F, 2F, 3F, 4F, 5F, 6F), list); + + assertTrue(list.addAll(TERTIARY_LIST)); + assertEquals(asList(1F, 2F, 3F, 4F, 5F, 6F, 1F, 2F, 3F), list); + + assertFalse(list.addAll(Collections.emptyList())); + assertFalse(list.addAll(FloatArrayList.emptyList())); + } + + public void testRemove() { + list.addAll(TERTIARY_LIST); + assertEquals(1F, (float) list.remove(0)); + assertEquals(asList(2F, 3F), list); + + assertTrue(list.remove(Float.valueOf(3))); + assertEquals(asList(2F), list); + + assertFalse(list.remove(Float.valueOf(3))); + assertEquals(asList(2F), list); + + assertEquals(2F, (float) list.remove(0)); + assertEquals(asList(), list); + + try { + list.remove(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.remove(0); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + private void assertImmutable(FloatArrayList list) { + if (list.contains(1)) { + throw new RuntimeException("Cannot test the immutability of lists that contain 1."); + } + + try { + list.add(1F); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, 1F); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.singletonList(1F)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(new FloatArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.singleton(1F)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addFloat(0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, 0F); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.setFloat(0, 0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + private static FloatArrayList newImmutableFloatArrayList(int... elements) { + FloatArrayList list = new FloatArrayList(); + for (int element : elements) { + list.addFloat(element); + } + list.makeImmutable(); + return list; + } +} diff --git a/java/src/test/java/com/google/protobuf/IntArrayListTest.java b/java/src/test/java/com/google/protobuf/IntArrayListTest.java new file mode 100644 index 00000000..3733eb30 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/IntArrayListTest.java @@ -0,0 +1,473 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static java.util.Arrays.asList; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; + +/** + * Tests for {@link IntArrayList}. + * + * @author dweis@google.com (Daniel Weis) + */ +public class IntArrayListTest extends TestCase { + + private static final IntArrayList UNARY_LIST = newImmutableIntArrayList(1); + private static final IntArrayList TERTIARY_LIST = + newImmutableIntArrayList(1, 2, 3); + + private IntArrayList list; + + @Override + protected void setUp() throws Exception { + list = new IntArrayList(); + } + + public void testEmptyListReturnsSameInstance() { + assertSame(IntArrayList.emptyList(), IntArrayList.emptyList()); + } + + public void testEmptyListIsImmutable() { + assertImmutable(IntArrayList.emptyList()); + } + + public void testMakeImmutable() { + list.addInt(2); + list.addInt(4); + list.addInt(6); + list.addInt(8); + list.makeImmutable(); + assertImmutable(list); + } + + public void testCopyConstructor() { + IntArrayList copy = new IntArrayList(TERTIARY_LIST); + assertEquals(TERTIARY_LIST, copy); + + copy = new IntArrayList(IntArrayList.emptyList()); + assertEquals(IntArrayList.emptyList(), copy); + + copy = new IntArrayList(asList(1, 2, 3)); + assertEquals(asList(1, 2, 3), copy); + + copy = new IntArrayList(Collections.emptyList()); + assertEquals(IntArrayList.emptyList(), copy); + } + + public void testModificationWithIteration() { + list.addAll(asList(1, 2, 3, 4)); + Iterator iterator = list.iterator(); + assertEquals(4, list.size()); + assertEquals(1, (int) list.get(0)); + assertEquals(1, (int) iterator.next()); + list.set(0, 1); + assertEquals(2, (int) iterator.next()); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, 0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testGet() { + assertEquals(1, (int) TERTIARY_LIST.get(0)); + assertEquals(2, (int) TERTIARY_LIST.get(1)); + assertEquals(3, (int) TERTIARY_LIST.get(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testGetInt() { + assertEquals(1, TERTIARY_LIST.getInt(0)); + assertEquals(2, TERTIARY_LIST.getInt(1)); + assertEquals(3, TERTIARY_LIST.getInt(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSize() { + assertEquals(0, IntArrayList.emptyList().size()); + assertEquals(1, UNARY_LIST.size()); + assertEquals(3, TERTIARY_LIST.size()); + + list.addInt(2); + list.addInt(4); + list.addInt(6); + list.addInt(8); + assertEquals(4, list.size()); + + list.remove(0); + assertEquals(3, list.size()); + + list.add(16); + assertEquals(4, list.size()); + } + + public void testSet() { + list.addInt(2); + list.addInt(4); + + assertEquals(2, (int) list.set(0, 0)); + assertEquals(0, list.getInt(0)); + + assertEquals(4, (int) list.set(1, 0)); + assertEquals(0, list.getInt(1)); + + try { + list.set(-1, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.set(2, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSetInt() { + list.addInt(2); + list.addInt(4); + + assertEquals(2, list.setInt(0, 0)); + assertEquals(0, list.getInt(0)); + + assertEquals(4, list.setInt(1, 0)); + assertEquals(0, list.getInt(1)); + + try { + list.setInt(-1, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.setInt(2, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAdd() { + assertEquals(0, list.size()); + + assertTrue(list.add(2)); + assertEquals(asList(2), list); + + assertTrue(list.add(3)); + list.add(0, 4); + assertEquals(asList(4, 2, 3), list); + + list.add(0, 1); + list.add(0, 0); + // Force a resize by getting up to 11 elements. + for (int i = 0; i < 6; i++) { + list.add(5 + i); + } + assertEquals(asList(0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10), list); + + try { + list.add(-1, 5); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.add(4, 5); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAddInt() { + assertEquals(0, list.size()); + + list.addInt(2); + assertEquals(asList(2), list); + + list.addInt(3); + assertEquals(asList(2, 3), list); + } + + public void testAddAll() { + assertEquals(0, list.size()); + + assertTrue(list.addAll(Collections.singleton(1))); + assertEquals(1, list.size()); + assertEquals(1, (int) list.get(0)); + assertEquals(1, list.getInt(0)); + + assertTrue(list.addAll(asList(2, 3, 4, 5, 6))); + assertEquals(asList(1, 2, 3, 4, 5, 6), list); + + assertTrue(list.addAll(TERTIARY_LIST)); + assertEquals(asList(1, 2, 3, 4, 5, 6, 1, 2, 3), list); + + assertFalse(list.addAll(Collections.emptyList())); + assertFalse(list.addAll(IntArrayList.emptyList())); + } + + public void testRemove() { + list.addAll(TERTIARY_LIST); + assertEquals(1, (int) list.remove(0)); + assertEquals(asList(2, 3), list); + + assertTrue(list.remove(Integer.valueOf(3))); + assertEquals(asList(2), list); + + assertFalse(list.remove(Integer.valueOf(3))); + assertEquals(asList(2), list); + + assertEquals(2, (int) list.remove(0)); + assertEquals(asList(), list); + + try { + list.remove(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.remove(0); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + private void assertImmutable(IntArrayList list) { + if (list.contains(1)) { + throw new RuntimeException("Cannot test the immutability of lists that contain 1."); + } + + try { + list.add(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, 1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.singletonList(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(new IntArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addInt(0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, 0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.setInt(0, 0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + private static IntArrayList newImmutableIntArrayList(int... elements) { + IntArrayList list = new IntArrayList(); + for (int element : elements) { + list.addInt(element); + } + list.makeImmutable(); + return list; + } +} diff --git a/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java b/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java index f3012b96..0f42ac50 100644 --- a/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java +++ b/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java @@ -30,9 +30,13 @@ package com.google.protobuf; +import static java.util.Arrays.asList; + import junit.framework.TestCase; import java.util.ArrayList; +import java.util.ConcurrentModificationException; +import java.util.Iterator; import java.util.List; /** @@ -171,4 +175,188 @@ public class LazyStringArrayListTest extends TestCase { assertSame(BYTE_STRING_B, list2.getByteString(1)); assertSame(BYTE_STRING_C, list2.getByteString(2)); } + + public void testModificationWithIteration() { + LazyStringArrayList list = new LazyStringArrayList(); + list.addAll(asList(STRING_A, STRING_B, STRING_C)); + Iterator iterator = list.iterator(); + assertEquals(3, list.size()); + assertEquals(STRING_A, list.get(0)); + assertEquals(STRING_A, iterator.next()); + + // Does not structurally modify. + iterator = list.iterator(); + list.set(0, STRING_B); + iterator.next(); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, STRING_C); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testMakeImmutable() { + LazyStringArrayList list = new LazyStringArrayList(); + list.add(STRING_A); + list.add(STRING_B); + list.add(STRING_C); + list.makeImmutable(); + assertGenericListImmutable(list, STRING_A); + + // LazyStringArrayList has extra methods not covered in the generic + // assertion. + + try { + list.add(BYTE_STRING_A.toByteArray()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(BYTE_STRING_A); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAllByteArray(asList(BYTE_STRING_A.toByteArray())); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAllByteString(asList(BYTE_STRING_A)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.mergeFrom(new LazyStringArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, BYTE_STRING_A.toByteArray()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, BYTE_STRING_A); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + public void testImmutabilityPropagation() { + LazyStringArrayList list = new LazyStringArrayList(); + list.add(STRING_A); + list.makeImmutable(); + + assertGenericListImmutable(list.asByteStringList(), BYTE_STRING_A); + + // Arrays use reference equality so need to retrieve the underlying value + // to properly test deep immutability. + List byteArrayList = list.asByteArrayList(); + assertGenericListImmutable(byteArrayList, byteArrayList.get(0)); + } + + private static void assertGenericListImmutable(List list, T value) { + try { + list.add(value); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, value); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(asList(value)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, asList(value)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(value); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(asList(value)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(asList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(asList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, value); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } } diff --git a/java/src/test/java/com/google/protobuf/LiteTest.java b/java/src/test/java/com/google/protobuf/LiteTest.java index 4d8037fc..8c3b5e5c 100644 --- a/java/src/test/java/com/google/protobuf/LiteTest.java +++ b/java/src/test/java/com/google/protobuf/LiteTest.java @@ -30,9 +30,18 @@ package com.google.protobuf; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + import com.google.protobuf.UnittestLite; -import com.google.protobuf.UnittestLite.TestAllTypesLite; +import com.google.protobuf.UnittestLite.ForeignEnumLite; +import com.google.protobuf.UnittestLite.ForeignMessageLite; import com.google.protobuf.UnittestLite.TestAllExtensionsLite; +import com.google.protobuf.UnittestLite.TestAllTypesLite; +import com.google.protobuf.UnittestLite.TestAllTypesLite.NestedMessage; +import com.google.protobuf.UnittestLite.TestAllTypesLite.OneofFieldCase; +import com.google.protobuf.UnittestLite.TestAllTypesLite.OptionalGroup; +import com.google.protobuf.UnittestLite.TestAllTypesLite.RepeatedGroup; import com.google.protobuf.UnittestLite.TestNestedExtensionLite; import junit.framework.TestCase; @@ -149,13 +158,1302 @@ public class LiteTest extends TestCase { public void testClone() { TestAllTypesLite.Builder expected = TestAllTypesLite.newBuilder() .setOptionalInt32(123); - assertEquals( - expected.getOptionalInt32(), expected.clone().getOptionalInt32()); + assertEquals( + expected.getOptionalInt32(), expected.clone().getOptionalInt32()); - TestAllExtensionsLite.Builder expected2 = TestAllExtensionsLite.newBuilder() - .setExtension(UnittestLite.optionalInt32ExtensionLite, 123); - assertEquals( - expected2.getExtension(UnittestLite.optionalInt32ExtensionLite), - expected2.clone().getExtension(UnittestLite.optionalInt32ExtensionLite)); + TestAllExtensionsLite.Builder expected2 = TestAllExtensionsLite.newBuilder() + .setExtension(UnittestLite.optionalInt32ExtensionLite, 123); + assertEquals( + expected2.getExtension(UnittestLite.optionalInt32ExtensionLite), + expected2.clone().getExtension(UnittestLite.optionalInt32ExtensionLite)); + } + + public void testAddAll() { + try { + TestAllTypesLite.newBuilder() + .addAllRepeatedBytes(null); + fail(); + } catch (NullPointerException e) { + // expected. + } + } + + public void testSanityCopyOnWrite() throws InvalidProtocolBufferException { + // Since builders are implemented as a thin wrapper around a message + // instance, we attempt to verify that we can't cause the builder to modify + // a produced message. + + TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder(); + TestAllTypesLite message = builder.build(); + TestAllTypesLite messageAfterBuild; + builder.setOptionalBool(true); + assertEquals(false, message.getOptionalBool()); + assertEquals(true, builder.getOptionalBool()); + messageAfterBuild = builder.build(); + assertEquals(true, messageAfterBuild.getOptionalBool()); + assertEquals(false, message.getOptionalBool()); + builder.clearOptionalBool(); + assertEquals(false, builder.getOptionalBool()); + assertEquals(true, messageAfterBuild.getOptionalBool()); + + message = builder.build(); + builder.setOptionalBytes(ByteString.copyFromUtf8("hi")); + assertEquals(ByteString.EMPTY, message.getOptionalBytes()); + assertEquals(ByteString.copyFromUtf8("hi"), builder.getOptionalBytes()); + messageAfterBuild = builder.build(); + assertEquals( + ByteString.copyFromUtf8("hi"), messageAfterBuild.getOptionalBytes()); + assertEquals(ByteString.EMPTY, message.getOptionalBytes()); + builder.clearOptionalBytes(); + assertEquals(ByteString.EMPTY, builder.getOptionalBytes()); + assertEquals( + ByteString.copyFromUtf8("hi"), messageAfterBuild.getOptionalBytes()); + + message = builder.build(); + builder.setOptionalCord("hi"); + assertEquals("", message.getOptionalCord()); + assertEquals("hi", builder.getOptionalCord()); + messageAfterBuild = builder.build(); + assertEquals("hi", messageAfterBuild.getOptionalCord()); + assertEquals("", message.getOptionalCord()); + builder.clearOptionalCord(); + assertEquals("", builder.getOptionalCord()); + assertEquals("hi", messageAfterBuild.getOptionalCord()); + + message = builder.build(); + builder.setOptionalCordBytes(ByteString.copyFromUtf8("no")); + assertEquals(ByteString.EMPTY, message.getOptionalCordBytes()); + assertEquals(ByteString.copyFromUtf8("no"), builder.getOptionalCordBytes()); + messageAfterBuild = builder.build(); + assertEquals( + ByteString.copyFromUtf8("no"), + messageAfterBuild.getOptionalCordBytes()); + assertEquals(ByteString.EMPTY, message.getOptionalCordBytes()); + builder.clearOptionalCord(); + assertEquals(ByteString.EMPTY, builder.getOptionalCordBytes()); + assertEquals( + ByteString.copyFromUtf8("no"), + messageAfterBuild.getOptionalCordBytes()); + + message = builder.build(); + builder.setOptionalDouble(1); + assertEquals(0D, message.getOptionalDouble()); + assertEquals(1D, builder.getOptionalDouble()); + messageAfterBuild = builder.build(); + assertEquals(1D, messageAfterBuild.getOptionalDouble()); + assertEquals(0D, message.getOptionalDouble()); + builder.clearOptionalDouble(); + assertEquals(0D, builder.getOptionalDouble()); + assertEquals(1D, messageAfterBuild.getOptionalDouble()); + + message = builder.build(); + builder.setOptionalFixed32(1); + assertEquals(0, message.getOptionalFixed32()); + assertEquals(1, builder.getOptionalFixed32()); + messageAfterBuild = builder.build(); + assertEquals(1, messageAfterBuild.getOptionalFixed32()); + assertEquals(0, message.getOptionalFixed32()); + builder.clearOptionalFixed32(); + assertEquals(0, builder.getOptionalFixed32()); + assertEquals(1, messageAfterBuild.getOptionalFixed32()); + + message = builder.build(); + builder.setOptionalFixed64(1); + assertEquals(0L, message.getOptionalFixed64()); + assertEquals(1L, builder.getOptionalFixed64()); + messageAfterBuild = builder.build(); + assertEquals(1L, messageAfterBuild.getOptionalFixed64()); + assertEquals(0L, message.getOptionalFixed64()); + builder.clearOptionalFixed64(); + assertEquals(0L, builder.getOptionalFixed64()); + assertEquals(1L, messageAfterBuild.getOptionalFixed64()); + + message = builder.build(); + builder.setOptionalFloat(1); + assertEquals(0F, message.getOptionalFloat()); + assertEquals(1F, builder.getOptionalFloat()); + messageAfterBuild = builder.build(); + assertEquals(1F, messageAfterBuild.getOptionalFloat()); + assertEquals(0F, message.getOptionalFloat()); + builder.clearOptionalFloat(); + assertEquals(0F, builder.getOptionalFloat()); + assertEquals(1F, messageAfterBuild.getOptionalFloat()); + + message = builder.build(); + builder.setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_FOO, message.getOptionalForeignEnum()); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_BAR, builder.getOptionalForeignEnum()); + messageAfterBuild = builder.build(); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_BAR, + messageAfterBuild.getOptionalForeignEnum()); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_FOO, message.getOptionalForeignEnum()); + builder.clearOptionalForeignEnum(); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_FOO, builder.getOptionalForeignEnum()); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_BAR, + messageAfterBuild.getOptionalForeignEnum()); + + message = builder.build(); + ForeignMessageLite foreignMessage = ForeignMessageLite.newBuilder() + .setC(1) + .build(); + builder.setOptionalForeignMessage(foreignMessage); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + message.getOptionalForeignMessage()); + assertEquals(foreignMessage, builder.getOptionalForeignMessage()); + messageAfterBuild = builder.build(); + assertEquals(foreignMessage, messageAfterBuild.getOptionalForeignMessage()); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + message.getOptionalForeignMessage()); + builder.clearOptionalForeignMessage(); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + builder.getOptionalForeignMessage()); + assertEquals(foreignMessage, messageAfterBuild.getOptionalForeignMessage()); + + message = builder.build(); + ForeignMessageLite.Builder foreignMessageBuilder = + ForeignMessageLite.newBuilder() + .setC(3); + builder.setOptionalForeignMessage(foreignMessageBuilder); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + message.getOptionalForeignMessage()); + // LITE_RUNTIME doesn't implement equals so we compare on a property and + // ensure the property isn't set on foreignMessage. + assertEquals(3, builder.getOptionalForeignMessage().getC()); + messageAfterBuild = builder.build(); + assertEquals(3, messageAfterBuild.getOptionalForeignMessage().getC()); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + message.getOptionalForeignMessage()); + builder.clearOptionalForeignMessage(); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + builder.getOptionalForeignMessage()); + assertEquals(3, messageAfterBuild.getOptionalForeignMessage().getC()); + + message = builder.build(); + OptionalGroup optionalGroup = OptionalGroup.newBuilder() + .setA(1) + .build(); + builder.setOptionalGroup(optionalGroup); + assertEquals( + OptionalGroup.getDefaultInstance(), message.getOptionalGroup()); + assertEquals(optionalGroup, builder.getOptionalGroup()); + messageAfterBuild = builder.build(); + assertEquals(optionalGroup, messageAfterBuild.getOptionalGroup()); + assertEquals( + OptionalGroup.getDefaultInstance(), message.getOptionalGroup()); + builder.clearOptionalGroup(); + assertEquals( + OptionalGroup.getDefaultInstance(), builder.getOptionalGroup()); + assertEquals(optionalGroup, messageAfterBuild.getOptionalGroup()); + + message = builder.build(); + OptionalGroup.Builder optionalGroupBuilder = OptionalGroup.newBuilder() + .setA(3); + builder.setOptionalGroup(optionalGroupBuilder); + assertEquals( + OptionalGroup.getDefaultInstance(), message.getOptionalGroup()); + // LITE_RUNTIME doesn't implement equals so we compare on a property and + // ensure the property isn't set on optionalGroup. + assertEquals(3, builder.getOptionalGroup().getA()); + messageAfterBuild = builder.build(); + assertEquals(3, messageAfterBuild.getOptionalGroup().getA()); + assertEquals( + OptionalGroup.getDefaultInstance(), message.getOptionalGroup()); + builder.clearOptionalGroup(); + assertEquals( + OptionalGroup.getDefaultInstance(), builder.getOptionalGroup()); + assertEquals(3, messageAfterBuild.getOptionalGroup().getA()); + + message = builder.build(); + builder.setOptionalInt32(1); + assertEquals(0, message.getOptionalInt32()); + assertEquals(1, builder.getOptionalInt32()); + messageAfterBuild = builder.build(); + assertEquals(1, messageAfterBuild.getOptionalInt32()); + assertEquals(0, message.getOptionalInt32()); + builder.clearOptionalInt32(); + assertEquals(0, builder.getOptionalInt32()); + assertEquals(1, messageAfterBuild.getOptionalInt32()); + + message = builder.build(); + builder.setOptionalInt64(1); + assertEquals(0L, message.getOptionalInt64()); + assertEquals(1L, builder.getOptionalInt64()); + messageAfterBuild = builder.build(); + assertEquals(1L, messageAfterBuild.getOptionalInt64()); + assertEquals(0L, message.getOptionalInt64()); + builder.clearOptionalInt64(); + assertEquals(0L, builder.getOptionalInt64()); + assertEquals(1L, messageAfterBuild.getOptionalInt64()); + + message = builder.build(); + NestedMessage nestedMessage = NestedMessage.newBuilder() + .setBb(1) + .build(); + builder.setOptionalLazyMessage(nestedMessage); + assertEquals( + NestedMessage.getDefaultInstance(), + message.getOptionalLazyMessage()); + assertEquals(nestedMessage, builder.getOptionalLazyMessage()); + messageAfterBuild = builder.build(); + assertEquals(nestedMessage, messageAfterBuild.getOptionalLazyMessage()); + assertEquals( + NestedMessage.getDefaultInstance(), + message.getOptionalLazyMessage()); + builder.clearOptionalLazyMessage(); + assertEquals( + NestedMessage.getDefaultInstance(), builder.getOptionalLazyMessage()); + assertEquals(nestedMessage, messageAfterBuild.getOptionalLazyMessage()); + + message = builder.build(); + NestedMessage.Builder nestedMessageBuilder = + NestedMessage.newBuilder() + .setBb(3); + builder.setOptionalLazyMessage(nestedMessageBuilder); + assertEquals( + NestedMessage.getDefaultInstance(), + message.getOptionalLazyMessage()); + // LITE_RUNTIME doesn't implement equals so we compare on a property. + assertEquals(3, builder.getOptionalLazyMessage().getBb()); + messageAfterBuild = builder.build(); + assertEquals(3, messageAfterBuild.getOptionalLazyMessage().getBb()); + assertEquals( + NestedMessage.getDefaultInstance(), + message.getOptionalLazyMessage()); + builder.clearOptionalLazyMessage(); + assertEquals( + NestedMessage.getDefaultInstance(), builder.getOptionalLazyMessage()); + assertEquals(3, messageAfterBuild.getOptionalLazyMessage().getBb()); + + message = builder.build(); + builder.setOptionalSfixed32(1); + assertEquals(0, message.getOptionalSfixed32()); + assertEquals(1, builder.getOptionalSfixed32()); + messageAfterBuild = builder.build(); + assertEquals(1, messageAfterBuild.getOptionalSfixed32()); + assertEquals(0, message.getOptionalSfixed32()); + builder.clearOptionalSfixed32(); + assertEquals(0, builder.getOptionalSfixed32()); + assertEquals(1, messageAfterBuild.getOptionalSfixed32()); + + message = builder.build(); + builder.setOptionalSfixed64(1); + assertEquals(0L, message.getOptionalSfixed64()); + assertEquals(1L, builder.getOptionalSfixed64()); + messageAfterBuild = builder.build(); + assertEquals(1L, messageAfterBuild.getOptionalSfixed64()); + assertEquals(0L, message.getOptionalSfixed64()); + builder.clearOptionalSfixed64(); + assertEquals(0L, builder.getOptionalSfixed64()); + assertEquals(1L, messageAfterBuild.getOptionalSfixed64()); + + message = builder.build(); + builder.setOptionalSint32(1); + assertEquals(0, message.getOptionalSint32()); + assertEquals(1, builder.getOptionalSint32()); + messageAfterBuild = builder.build(); + assertEquals(1, messageAfterBuild.getOptionalSint32()); + builder.clearOptionalSint32(); + assertEquals(0, builder.getOptionalSint32()); + assertEquals(1, messageAfterBuild.getOptionalSint32()); + + message = builder.build(); + builder.setOptionalSint64(1); + assertEquals(0L, message.getOptionalSint64()); + assertEquals(1L, builder.getOptionalSint64()); + messageAfterBuild = builder.build(); + assertEquals(1L, messageAfterBuild.getOptionalSint64()); + assertEquals(0L, message.getOptionalSint64()); + builder.clearOptionalSint64(); + assertEquals(0L, builder.getOptionalSint64()); + assertEquals(1L, messageAfterBuild.getOptionalSint64()); + + message = builder.build(); + builder.setOptionalString("hi"); + assertEquals("", message.getOptionalString()); + assertEquals("hi", builder.getOptionalString()); + messageAfterBuild = builder.build(); + assertEquals("hi", messageAfterBuild.getOptionalString()); + assertEquals("", message.getOptionalString()); + builder.clearOptionalString(); + assertEquals("", builder.getOptionalString()); + assertEquals("hi", messageAfterBuild.getOptionalString()); + + message = builder.build(); + builder.setOptionalStringBytes(ByteString.copyFromUtf8("no")); + assertEquals(ByteString.EMPTY, message.getOptionalStringBytes()); + assertEquals( + ByteString.copyFromUtf8("no"), builder.getOptionalStringBytes()); + messageAfterBuild = builder.build(); + assertEquals( + ByteString.copyFromUtf8("no"), + messageAfterBuild.getOptionalStringBytes()); + assertEquals(ByteString.EMPTY, message.getOptionalStringBytes()); + builder.clearOptionalString(); + assertEquals(ByteString.EMPTY, builder.getOptionalStringBytes()); + assertEquals( + ByteString.copyFromUtf8("no"), + messageAfterBuild.getOptionalStringBytes()); + + message = builder.build(); + builder.setOptionalStringPiece("hi"); + assertEquals("", message.getOptionalStringPiece()); + assertEquals("hi", builder.getOptionalStringPiece()); + messageAfterBuild = builder.build(); + assertEquals("hi", messageAfterBuild.getOptionalStringPiece()); + assertEquals("", message.getOptionalStringPiece()); + builder.clearOptionalStringPiece(); + assertEquals("", builder.getOptionalStringPiece()); + assertEquals("hi", messageAfterBuild.getOptionalStringPiece()); + + message = builder.build(); + builder.setOptionalStringPieceBytes(ByteString.copyFromUtf8("no")); + assertEquals(ByteString.EMPTY, message.getOptionalStringPieceBytes()); + assertEquals( + ByteString.copyFromUtf8("no"), builder.getOptionalStringPieceBytes()); + messageAfterBuild = builder.build(); + assertEquals( + ByteString.copyFromUtf8("no"), + messageAfterBuild.getOptionalStringPieceBytes()); + assertEquals(ByteString.EMPTY, message.getOptionalStringPieceBytes()); + builder.clearOptionalStringPiece(); + assertEquals(ByteString.EMPTY, builder.getOptionalStringPieceBytes()); + assertEquals( + ByteString.copyFromUtf8("no"), + messageAfterBuild.getOptionalStringPieceBytes()); + + message = builder.build(); + builder.setOptionalUint32(1); + assertEquals(0, message.getOptionalUint32()); + assertEquals(1, builder.getOptionalUint32()); + messageAfterBuild = builder.build(); + assertEquals(1, messageAfterBuild.getOptionalUint32()); + assertEquals(0, message.getOptionalUint32()); + builder.clearOptionalUint32(); + assertEquals(0, builder.getOptionalUint32()); + assertEquals(1, messageAfterBuild.getOptionalUint32()); + + message = builder.build(); + builder.setOptionalUint64(1); + assertEquals(0L, message.getOptionalUint64()); + assertEquals(1L, builder.getOptionalUint64()); + messageAfterBuild = builder.build(); + assertEquals(1L, messageAfterBuild.getOptionalUint64()); + assertEquals(0L, message.getOptionalUint64()); + builder.clearOptionalUint64(); + assertEquals(0L, builder.getOptionalUint64()); + assertEquals(1L, messageAfterBuild.getOptionalUint64()); + + message = builder.build(); + builder.addAllRepeatedBool(singletonList(true)); + assertEquals(emptyList(), message.getRepeatedBoolList()); + assertEquals(singletonList(true), builder.getRepeatedBoolList()); + assertEquals(emptyList(), message.getRepeatedBoolList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedBool(); + assertEquals(emptyList(), builder.getRepeatedBoolList()); + assertEquals(singletonList(true), messageAfterBuild.getRepeatedBoolList()); + + message = builder.build(); + builder.addAllRepeatedBytes(singletonList(ByteString.copyFromUtf8("hi"))); + assertEquals(emptyList(), message.getRepeatedBytesList()); + assertEquals( + singletonList(ByteString.copyFromUtf8("hi")), + builder.getRepeatedBytesList()); + assertEquals(emptyList(), message.getRepeatedBytesList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedBytes(); + assertEquals(emptyList(), builder.getRepeatedBytesList()); + assertEquals( + singletonList(ByteString.copyFromUtf8("hi")), + messageAfterBuild.getRepeatedBytesList()); + + message = builder.build(); + builder.addAllRepeatedCord(singletonList("hi")); + assertEquals(emptyList(), message.getRepeatedCordList()); + assertEquals(singletonList("hi"), builder.getRepeatedCordList()); + assertEquals(emptyList(), message.getRepeatedCordList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedCord(); + assertEquals(emptyList(), builder.getRepeatedCordList()); + assertEquals(singletonList("hi"), messageAfterBuild.getRepeatedCordList()); + + message = builder.build(); + builder.addAllRepeatedDouble(singletonList(1D)); + assertEquals(emptyList(), message.getRepeatedDoubleList()); + assertEquals(singletonList(1D), builder.getRepeatedDoubleList()); + assertEquals(emptyList(), message.getRepeatedDoubleList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedDouble(); + assertEquals(emptyList(), builder.getRepeatedDoubleList()); + assertEquals(singletonList(1D), messageAfterBuild.getRepeatedDoubleList()); + + message = builder.build(); + builder.addAllRepeatedFixed32(singletonList(1)); + assertEquals(emptyList(), message.getRepeatedFixed32List()); + assertEquals(singletonList(1), builder.getRepeatedFixed32List()); + assertEquals(emptyList(), message.getRepeatedFixed32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedFixed32(); + assertEquals(emptyList(), builder.getRepeatedFixed32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedFixed32List()); + + message = builder.build(); + builder.addAllRepeatedFixed64(singletonList(1L)); + assertEquals(emptyList(), message.getRepeatedFixed64List()); + assertEquals(singletonList(1L), builder.getRepeatedFixed64List()); + assertEquals(emptyList(), message.getRepeatedFixed64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedFixed64(); + assertEquals(emptyList(), builder.getRepeatedFixed64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedFixed64List()); + + message = builder.build(); + builder.addAllRepeatedFloat(singletonList(1F)); + assertEquals(emptyList(), message.getRepeatedFloatList()); + assertEquals(singletonList(1F), builder.getRepeatedFloatList()); + assertEquals(emptyList(), message.getRepeatedFloatList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedFloat(); + assertEquals(emptyList(), builder.getRepeatedFloatList()); + assertEquals(singletonList(1F), messageAfterBuild.getRepeatedFloatList()); + + message = builder.build(); + builder.addAllRepeatedForeignEnum( + singletonList(ForeignEnumLite.FOREIGN_LITE_BAR)); + assertEquals(emptyList(), message.getRepeatedForeignEnumList()); + assertEquals( + singletonList(ForeignEnumLite.FOREIGN_LITE_BAR), + builder.getRepeatedForeignEnumList()); + assertEquals(emptyList(), message.getRepeatedForeignEnumList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedForeignEnum(); + assertEquals(emptyList(), builder.getRepeatedForeignEnumList()); + assertEquals( + singletonList(ForeignEnumLite.FOREIGN_LITE_BAR), + messageAfterBuild.getRepeatedForeignEnumList()); + + message = builder.build(); + builder.addAllRepeatedForeignMessage(singletonList(foreignMessage)); + assertEquals(emptyList(), message.getRepeatedForeignMessageList()); + assertEquals( + singletonList(foreignMessage), builder.getRepeatedForeignMessageList()); + assertEquals(emptyList(), message.getRepeatedForeignMessageList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedForeignMessage(); + assertEquals(emptyList(), builder.getRepeatedForeignMessageList()); + assertEquals( + singletonList(foreignMessage), + messageAfterBuild.getRepeatedForeignMessageList()); + + message = builder.build(); + builder.addAllRepeatedGroup( + singletonList(RepeatedGroup.getDefaultInstance())); + assertEquals(emptyList(), message.getRepeatedGroupList()); + assertEquals( + singletonList(RepeatedGroup.getDefaultInstance()), + builder.getRepeatedGroupList()); + assertEquals(emptyList(), message.getRepeatedGroupList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedGroup(); + assertEquals(emptyList(), builder.getRepeatedGroupList()); + assertEquals( + singletonList(RepeatedGroup.getDefaultInstance()), + messageAfterBuild.getRepeatedGroupList()); + + message = builder.build(); + builder.addAllRepeatedInt32(singletonList(1)); + assertEquals(emptyList(), message.getRepeatedInt32List()); + assertEquals(singletonList(1), builder.getRepeatedInt32List()); + assertEquals(emptyList(), message.getRepeatedInt32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedInt32(); + assertEquals(emptyList(), builder.getRepeatedInt32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedInt32List()); + + message = builder.build(); + builder.addAllRepeatedInt64(singletonList(1L)); + assertEquals(emptyList(), message.getRepeatedInt64List()); + assertEquals(singletonList(1L), builder.getRepeatedInt64List()); + assertEquals(emptyList(), message.getRepeatedInt64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedInt64(); + assertEquals(emptyList(), builder.getRepeatedInt64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedInt64List()); + + message = builder.build(); + builder.addAllRepeatedLazyMessage(singletonList(nestedMessage)); + assertEquals(emptyList(), message.getRepeatedLazyMessageList()); + assertEquals( + singletonList(nestedMessage), builder.getRepeatedLazyMessageList()); + assertEquals(emptyList(), message.getRepeatedLazyMessageList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedLazyMessage(); + assertEquals(emptyList(), builder.getRepeatedLazyMessageList()); + assertEquals( + singletonList(nestedMessage), + messageAfterBuild.getRepeatedLazyMessageList()); + + message = builder.build(); + builder.addAllRepeatedSfixed32(singletonList(1)); + assertEquals(emptyList(), message.getRepeatedSfixed32List()); + assertEquals(singletonList(1), builder.getRepeatedSfixed32List()); + assertEquals(emptyList(), message.getRepeatedSfixed32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSfixed32(); + assertEquals(emptyList(), builder.getRepeatedSfixed32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedSfixed32List()); + + message = builder.build(); + builder.addAllRepeatedSfixed64(singletonList(1L)); + assertEquals(emptyList(), message.getRepeatedSfixed64List()); + assertEquals(singletonList(1L), builder.getRepeatedSfixed64List()); + assertEquals(emptyList(), message.getRepeatedSfixed64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSfixed64(); + assertEquals(emptyList(), builder.getRepeatedSfixed64List()); + assertEquals( + singletonList(1L), messageAfterBuild.getRepeatedSfixed64List()); + + message = builder.build(); + builder.addAllRepeatedSint32(singletonList(1)); + assertEquals(emptyList(), message.getRepeatedSint32List()); + assertEquals(singletonList(1), builder.getRepeatedSint32List()); + assertEquals(emptyList(), message.getRepeatedSint32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSint32(); + assertEquals(emptyList(), builder.getRepeatedSint32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedSint32List()); + + message = builder.build(); + builder.addAllRepeatedSint64(singletonList(1L)); + assertEquals(emptyList(), message.getRepeatedSint64List()); + assertEquals(singletonList(1L), builder.getRepeatedSint64List()); + assertEquals(emptyList(), message.getRepeatedSint64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSint64(); + assertEquals(emptyList(), builder.getRepeatedSint64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedSint64List()); + + message = builder.build(); + builder.addAllRepeatedString(singletonList("hi")); + assertEquals(emptyList(), message.getRepeatedStringList()); + assertEquals(singletonList("hi"), builder.getRepeatedStringList()); + assertEquals(emptyList(), message.getRepeatedStringList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedString(); + assertEquals(emptyList(), builder.getRepeatedStringList()); + assertEquals( + singletonList("hi"), messageAfterBuild.getRepeatedStringList()); + + message = builder.build(); + builder.addAllRepeatedStringPiece(singletonList("hi")); + assertEquals(emptyList(), message.getRepeatedStringPieceList()); + assertEquals(singletonList("hi"), builder.getRepeatedStringPieceList()); + assertEquals(emptyList(), message.getRepeatedStringPieceList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedStringPiece(); + assertEquals(emptyList(), builder.getRepeatedStringPieceList()); + assertEquals( + singletonList("hi"), messageAfterBuild.getRepeatedStringPieceList()); + + message = builder.build(); + builder.addAllRepeatedUint32(singletonList(1)); + assertEquals(emptyList(), message.getRepeatedUint32List()); + assertEquals(singletonList(1), builder.getRepeatedUint32List()); + assertEquals(emptyList(), message.getRepeatedUint32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedUint32(); + assertEquals(emptyList(), builder.getRepeatedUint32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedUint32List()); + + message = builder.build(); + builder.addAllRepeatedUint64(singletonList(1L)); + assertEquals(emptyList(), message.getRepeatedUint64List()); + assertEquals(singletonList(1L), builder.getRepeatedUint64List()); + assertEquals(emptyList(), message.getRepeatedUint64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedUint64(); + assertEquals(emptyList(), builder.getRepeatedUint64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedUint64List()); + + message = builder.build(); + builder.addRepeatedBool(true); + assertEquals(emptyList(), message.getRepeatedBoolList()); + assertEquals(singletonList(true), builder.getRepeatedBoolList()); + assertEquals(emptyList(), message.getRepeatedBoolList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedBool(); + assertEquals(emptyList(), builder.getRepeatedBoolList()); + assertEquals(singletonList(true), messageAfterBuild.getRepeatedBoolList()); + + message = builder.build(); + builder.addRepeatedBytes(ByteString.copyFromUtf8("hi")); + assertEquals(emptyList(), message.getRepeatedBytesList()); + assertEquals( + singletonList(ByteString.copyFromUtf8("hi")), + builder.getRepeatedBytesList()); + assertEquals(emptyList(), message.getRepeatedBytesList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedBytes(); + assertEquals(emptyList(), builder.getRepeatedBytesList()); + assertEquals( + singletonList(ByteString.copyFromUtf8("hi")), + messageAfterBuild.getRepeatedBytesList()); + + message = builder.build(); + builder.addRepeatedCord("hi"); + assertEquals(emptyList(), message.getRepeatedCordList()); + assertEquals(singletonList("hi"), builder.getRepeatedCordList()); + assertEquals(emptyList(), message.getRepeatedCordList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedCord(); + assertEquals(emptyList(), builder.getRepeatedCordList()); + assertEquals(singletonList("hi"), messageAfterBuild.getRepeatedCordList()); + + message = builder.build(); + builder.addRepeatedDouble(1D); + assertEquals(emptyList(), message.getRepeatedDoubleList()); + assertEquals(singletonList(1D), builder.getRepeatedDoubleList()); + assertEquals(emptyList(), message.getRepeatedDoubleList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedDouble(); + assertEquals(emptyList(), builder.getRepeatedDoubleList()); + assertEquals(singletonList(1D), messageAfterBuild.getRepeatedDoubleList()); + + message = builder.build(); + builder.addRepeatedFixed32(1); + assertEquals(emptyList(), message.getRepeatedFixed32List()); + assertEquals(singletonList(1), builder.getRepeatedFixed32List()); + assertEquals(emptyList(), message.getRepeatedFixed32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedFixed32(); + assertEquals(emptyList(), builder.getRepeatedFixed32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedFixed32List()); + + message = builder.build(); + builder.addRepeatedFixed64(1L); + assertEquals(emptyList(), message.getRepeatedFixed64List()); + assertEquals(singletonList(1L), builder.getRepeatedFixed64List()); + assertEquals(emptyList(), message.getRepeatedFixed64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedFixed64(); + assertEquals(emptyList(), builder.getRepeatedFixed64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedFixed64List()); + + message = builder.build(); + builder.addRepeatedFloat(1F); + assertEquals(emptyList(), message.getRepeatedFloatList()); + assertEquals(singletonList(1F), builder.getRepeatedFloatList()); + assertEquals(emptyList(), message.getRepeatedFloatList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedFloat(); + assertEquals(emptyList(), builder.getRepeatedFloatList()); + assertEquals(singletonList(1F), messageAfterBuild.getRepeatedFloatList()); + + message = builder.build(); + builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR); + assertEquals(emptyList(), message.getRepeatedForeignEnumList()); + assertEquals( + singletonList(ForeignEnumLite.FOREIGN_LITE_BAR), + builder.getRepeatedForeignEnumList()); + assertEquals(emptyList(), message.getRepeatedForeignEnumList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedForeignEnum(); + assertEquals(emptyList(), builder.getRepeatedForeignEnumList()); + assertEquals( + singletonList(ForeignEnumLite.FOREIGN_LITE_BAR), + messageAfterBuild.getRepeatedForeignEnumList()); + + message = builder.build(); + builder.addRepeatedForeignMessage(foreignMessage); + assertEquals(emptyList(), message.getRepeatedForeignMessageList()); + assertEquals( + singletonList(foreignMessage), builder.getRepeatedForeignMessageList()); + assertEquals(emptyList(), message.getRepeatedForeignMessageList()); + messageAfterBuild = builder.build(); + builder.removeRepeatedForeignMessage(0); + assertEquals(emptyList(), builder.getRepeatedForeignMessageList()); + assertEquals( + singletonList(foreignMessage), + messageAfterBuild.getRepeatedForeignMessageList()); + + message = builder.build(); + builder.addRepeatedGroup(RepeatedGroup.getDefaultInstance()); + assertEquals(emptyList(), message.getRepeatedGroupList()); + assertEquals( + singletonList(RepeatedGroup.getDefaultInstance()), + builder.getRepeatedGroupList()); + assertEquals(emptyList(), message.getRepeatedGroupList()); + messageAfterBuild = builder.build(); + builder.removeRepeatedGroup(0); + assertEquals(emptyList(), builder.getRepeatedGroupList()); + assertEquals( + singletonList(RepeatedGroup.getDefaultInstance()), + messageAfterBuild.getRepeatedGroupList()); + + message = builder.build(); + builder.addRepeatedInt32(1); + assertEquals(emptyList(), message.getRepeatedInt32List()); + assertEquals(singletonList(1), builder.getRepeatedInt32List()); + assertEquals(emptyList(), message.getRepeatedInt32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedInt32(); + assertEquals(emptyList(), builder.getRepeatedInt32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedInt32List()); + + message = builder.build(); + builder.addRepeatedInt64(1L); + assertEquals(emptyList(), message.getRepeatedInt64List()); + assertEquals(singletonList(1L), builder.getRepeatedInt64List()); + assertEquals(emptyList(), message.getRepeatedInt64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedInt64(); + assertEquals(emptyList(), builder.getRepeatedInt64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedInt64List()); + + message = builder.build(); + builder.addRepeatedLazyMessage(nestedMessage); + assertEquals(emptyList(), message.getRepeatedLazyMessageList()); + assertEquals( + singletonList(nestedMessage), builder.getRepeatedLazyMessageList()); + assertEquals(emptyList(), message.getRepeatedLazyMessageList()); + messageAfterBuild = builder.build(); + builder.removeRepeatedLazyMessage(0); + assertEquals(emptyList(), builder.getRepeatedLazyMessageList()); + assertEquals( + singletonList(nestedMessage), + messageAfterBuild.getRepeatedLazyMessageList()); + + message = builder.build(); + builder.addRepeatedSfixed32(1); + assertEquals(emptyList(), message.getRepeatedSfixed32List()); + assertEquals(singletonList(1), builder.getRepeatedSfixed32List()); + assertEquals(emptyList(), message.getRepeatedSfixed32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSfixed32(); + assertEquals(emptyList(), builder.getRepeatedSfixed32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedSfixed32List()); + + message = builder.build(); + builder.addRepeatedSfixed64(1L); + assertEquals(emptyList(), message.getRepeatedSfixed64List()); + assertEquals(singletonList(1L), builder.getRepeatedSfixed64List()); + assertEquals(emptyList(), message.getRepeatedSfixed64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSfixed64(); + assertEquals(emptyList(), builder.getRepeatedSfixed64List()); + assertEquals( + singletonList(1L), messageAfterBuild.getRepeatedSfixed64List()); + + message = builder.build(); + builder.addRepeatedSint32(1); + assertEquals(emptyList(), message.getRepeatedSint32List()); + assertEquals(singletonList(1), builder.getRepeatedSint32List()); + assertEquals(emptyList(), message.getRepeatedSint32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSint32(); + assertEquals(emptyList(), builder.getRepeatedSint32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedSint32List()); + + message = builder.build(); + builder.addRepeatedSint64(1L); + assertEquals(emptyList(), message.getRepeatedSint64List()); + assertEquals(singletonList(1L), builder.getRepeatedSint64List()); + assertEquals(emptyList(), message.getRepeatedSint64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSint64(); + assertEquals(emptyList(), builder.getRepeatedSint64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedSint64List()); + + message = builder.build(); + builder.addRepeatedString("hi"); + assertEquals(emptyList(), message.getRepeatedStringList()); + assertEquals(singletonList("hi"), builder.getRepeatedStringList()); + assertEquals(emptyList(), message.getRepeatedStringList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedString(); + assertEquals(emptyList(), builder.getRepeatedStringList()); + assertEquals( + singletonList("hi"), messageAfterBuild.getRepeatedStringList()); + + message = builder.build(); + builder.addRepeatedStringPiece("hi"); + assertEquals(emptyList(), message.getRepeatedStringPieceList()); + assertEquals(singletonList("hi"), builder.getRepeatedStringPieceList()); + assertEquals(emptyList(), message.getRepeatedStringPieceList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedStringPiece(); + assertEquals(emptyList(), builder.getRepeatedStringPieceList()); + assertEquals( + singletonList("hi"), messageAfterBuild.getRepeatedStringPieceList()); + + message = builder.build(); + builder.addRepeatedUint32(1); + assertEquals(emptyList(), message.getRepeatedUint32List()); + assertEquals(singletonList(1), builder.getRepeatedUint32List()); + assertEquals(emptyList(), message.getRepeatedUint32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedUint32(); + assertEquals(emptyList(), builder.getRepeatedUint32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedUint32List()); + + message = builder.build(); + builder.addRepeatedUint64(1L); + assertEquals(emptyList(), message.getRepeatedUint64List()); + assertEquals(singletonList(1L), builder.getRepeatedUint64List()); + assertEquals(emptyList(), message.getRepeatedUint64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedUint64(); + assertEquals(emptyList(), builder.getRepeatedUint64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedUint64List()); + + message = builder.build(); + builder.addRepeatedBool(true); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedBoolCount()); + builder.setRepeatedBool(0, false); + assertEquals(true, messageAfterBuild.getRepeatedBool(0)); + assertEquals(false, builder.getRepeatedBool(0)); + builder.clearRepeatedBool(); + + message = builder.build(); + builder.addRepeatedBytes(ByteString.copyFromUtf8("hi")); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedBytesCount()); + builder.setRepeatedBytes(0, ByteString.EMPTY); + assertEquals( + ByteString.copyFromUtf8("hi"), messageAfterBuild.getRepeatedBytes(0)); + assertEquals(ByteString.EMPTY, builder.getRepeatedBytes(0)); + builder.clearRepeatedBytes(); + + message = builder.build(); + builder.addRepeatedCord("hi"); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedCordCount()); + builder.setRepeatedCord(0, ""); + assertEquals("hi", messageAfterBuild.getRepeatedCord(0)); + assertEquals("", builder.getRepeatedCord(0)); + builder.clearRepeatedCord(); + message = builder.build(); + + builder.addRepeatedCordBytes(ByteString.copyFromUtf8("hi")); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedCordCount()); + builder.setRepeatedCord(0, ""); + assertEquals( + ByteString.copyFromUtf8("hi"), messageAfterBuild.getRepeatedCordBytes(0)); + assertEquals(ByteString.EMPTY, builder.getRepeatedCordBytes(0)); + builder.clearRepeatedCord(); + + message = builder.build(); + builder.addRepeatedDouble(1D); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedDoubleCount()); + builder.setRepeatedDouble(0, 0D); + assertEquals(1D, messageAfterBuild.getRepeatedDouble(0)); + assertEquals(0D, builder.getRepeatedDouble(0)); + builder.clearRepeatedDouble(); + + message = builder.build(); + builder.addRepeatedFixed32(1); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedFixed32Count()); + builder.setRepeatedFixed32(0, 0); + assertEquals(1, messageAfterBuild.getRepeatedFixed32(0)); + assertEquals(0, builder.getRepeatedFixed32(0)); + builder.clearRepeatedFixed32(); + + message = builder.build(); + builder.addRepeatedFixed64(1L); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedFixed64Count()); + builder.setRepeatedFixed64(0, 0L); + assertEquals(1L, messageAfterBuild.getRepeatedFixed64(0)); + assertEquals(0L, builder.getRepeatedFixed64(0)); + builder.clearRepeatedFixed64(); + + message = builder.build(); + builder.addRepeatedFloat(1F); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedFloatCount()); + builder.setRepeatedFloat(0, 0F); + assertEquals(1F, messageAfterBuild.getRepeatedFloat(0)); + assertEquals(0F, builder.getRepeatedFloat(0)); + builder.clearRepeatedFloat(); + + message = builder.build(); + builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedForeignEnumCount()); + builder.setRepeatedForeignEnum(0, ForeignEnumLite.FOREIGN_LITE_FOO); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_BAR, + messageAfterBuild.getRepeatedForeignEnum(0)); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_FOO, builder.getRepeatedForeignEnum(0)); + builder.clearRepeatedForeignEnum(); + + message = builder.build(); + builder.addRepeatedForeignMessage(foreignMessage); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedForeignMessageCount()); + builder.setRepeatedForeignMessage( + 0, ForeignMessageLite.getDefaultInstance()); + assertEquals( + foreignMessage, messageAfterBuild.getRepeatedForeignMessage(0)); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + builder.getRepeatedForeignMessage(0)); + builder.clearRepeatedForeignMessage(); + + message = builder.build(); + builder.addRepeatedForeignMessage(foreignMessageBuilder); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedForeignMessageCount()); + builder.setRepeatedForeignMessage( + 0, ForeignMessageLite.getDefaultInstance()); + // LITE_RUNTIME doesn't implement equals so we compare on a property. + assertEquals(3, messageAfterBuild.getRepeatedForeignMessage(0).getC()); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + builder.getRepeatedForeignMessage(0)); + builder.clearRepeatedForeignMessage(); + + message = builder.build(); + builder.addRepeatedForeignMessage(0, foreignMessage); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedForeignMessageCount()); + builder.setRepeatedForeignMessage(0, foreignMessageBuilder); + assertEquals( + foreignMessage, messageAfterBuild.getRepeatedForeignMessage(0)); + // LITE_RUNTIME doesn't implement equals so we compare on a property. + assertEquals(3, builder.getRepeatedForeignMessage(0).getC()); + builder.clearRepeatedForeignMessage(); + + message = builder.build(); + RepeatedGroup repeatedGroup = RepeatedGroup.newBuilder() + .setA(1) + .build(); + builder.addRepeatedGroup(repeatedGroup); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedGroupCount()); + builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance()); + assertEquals(repeatedGroup, messageAfterBuild.getRepeatedGroup(0)); + assertEquals( + RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0)); + builder.clearRepeatedGroup(); + + message = builder.build(); + builder.addRepeatedGroup(0, repeatedGroup); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedGroupCount()); + builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance()); + assertEquals(repeatedGroup, messageAfterBuild.getRepeatedGroup(0)); + assertEquals( + RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0)); + builder.clearRepeatedGroup(); + + message = builder.build(); + RepeatedGroup.Builder repeatedGroupBuilder = RepeatedGroup.newBuilder() + .setA(3); + builder.addRepeatedGroup(repeatedGroupBuilder); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedGroupCount()); + builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance()); + // LITE_RUNTIME doesn't implement equals so we compare on a property and + // ensure the property isn't set on repeatedGroup. + assertEquals(3, messageAfterBuild.getRepeatedGroup(0).getA()); + assertEquals( + RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0)); + builder.clearRepeatedGroup(); + + message = builder.build(); + builder.addRepeatedGroup(0, repeatedGroupBuilder); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedGroupCount()); + builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance()); + // LITE_RUNTIME doesn't implement equals so we compare on a property and + // ensure the property isn't set on repeatedGroup. + assertEquals(3, messageAfterBuild.getRepeatedGroup(0).getA()); + assertEquals( + RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0)); + builder.clearRepeatedGroup(); + + message = builder.build(); + builder.addRepeatedInt32(1); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedInt32Count()); + builder.setRepeatedInt32(0, 0); + assertEquals(1, messageAfterBuild.getRepeatedInt32(0)); + assertEquals(0, builder.getRepeatedInt32(0)); + builder.clearRepeatedInt32(); + + message = builder.build(); + builder.addRepeatedInt64(1L); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedInt64Count()); + builder.setRepeatedInt64(0, 0L); + assertEquals(1L, messageAfterBuild.getRepeatedInt64(0)); + assertEquals(0L, builder.getRepeatedInt64(0)); + builder.clearRepeatedInt64(); + + message = builder.build(); + builder.addRepeatedLazyMessage(nestedMessage); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedLazyMessageCount()); + builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance()); + assertEquals(nestedMessage, messageAfterBuild.getRepeatedLazyMessage(0)); + assertEquals( + NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0)); + builder.clearRepeatedLazyMessage(); + + message = builder.build(); + builder.addRepeatedLazyMessage(0, nestedMessage); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedLazyMessageCount()); + builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance()); + assertEquals(nestedMessage, messageAfterBuild.getRepeatedLazyMessage(0)); + assertEquals( + NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0)); + builder.clearRepeatedLazyMessage(); + + message = builder.build(); + builder.addRepeatedLazyMessage(nestedMessageBuilder); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedLazyMessageCount()); + builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance()); + // LITE_RUNTIME doesn't implement equals so we compare on a property and + // ensure the property isn't set on repeatedGroup. + assertEquals(3, messageAfterBuild.getRepeatedLazyMessage(0).getBb()); + assertEquals( + NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0)); + builder.clearRepeatedLazyMessage(); + + message = builder.build(); + builder.addRepeatedLazyMessage(0, nestedMessageBuilder); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedLazyMessageCount()); + builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance()); + // LITE_RUNTIME doesn't implement equals so we compare on a property and + // ensure the property isn't set on repeatedGroup. + assertEquals(3, messageAfterBuild.getRepeatedLazyMessage(0).getBb()); + assertEquals( + NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0)); + builder.clearRepeatedLazyMessage(); + + message = builder.build(); + builder.addRepeatedSfixed32(1); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedSfixed32Count()); + builder.setRepeatedSfixed32(0, 0); + assertEquals(1, messageAfterBuild.getRepeatedSfixed32(0)); + assertEquals(0, builder.getRepeatedSfixed32(0)); + builder.clearRepeatedSfixed32(); + + message = builder.build(); + builder.addRepeatedSfixed64(1L); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedSfixed64Count()); + builder.setRepeatedSfixed64(0, 0L); + assertEquals(1L, messageAfterBuild.getRepeatedSfixed64(0)); + assertEquals(0L, builder.getRepeatedSfixed64(0)); + builder.clearRepeatedSfixed64(); + + message = builder.build(); + builder.addRepeatedSint32(1); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedSint32Count()); + builder.setRepeatedSint32(0, 0); + assertEquals(1, messageAfterBuild.getRepeatedSint32(0)); + assertEquals(0, builder.getRepeatedSint32(0)); + builder.clearRepeatedSint32(); + + message = builder.build(); + builder.addRepeatedSint64(1L); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedSint64Count()); + builder.setRepeatedSint64(0, 0L); + assertEquals(1L, messageAfterBuild.getRepeatedSint64(0)); + assertEquals(0L, builder.getRepeatedSint64(0)); + builder.clearRepeatedSint64(); + + message = builder.build(); + builder.addRepeatedString("hi"); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedStringCount()); + builder.setRepeatedString(0, ""); + assertEquals("hi", messageAfterBuild.getRepeatedString(0)); + assertEquals("", builder.getRepeatedString(0)); + builder.clearRepeatedString(); + + message = builder.build(); + builder.addRepeatedStringBytes(ByteString.copyFromUtf8("hi")); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedStringCount()); + builder.setRepeatedString(0, ""); + assertEquals( + ByteString.copyFromUtf8("hi"), + messageAfterBuild.getRepeatedStringBytes(0)); + assertEquals(ByteString.EMPTY, builder.getRepeatedStringBytes(0)); + builder.clearRepeatedString(); + + message = builder.build(); + builder.addRepeatedStringPiece("hi"); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedStringPieceCount()); + builder.setRepeatedStringPiece(0, ""); + assertEquals("hi", messageAfterBuild.getRepeatedStringPiece(0)); + assertEquals("", builder.getRepeatedStringPiece(0)); + builder.clearRepeatedStringPiece(); + + message = builder.build(); + builder.addRepeatedStringPieceBytes(ByteString.copyFromUtf8("hi")); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedStringPieceCount()); + builder.setRepeatedStringPiece(0, ""); + assertEquals( + ByteString.copyFromUtf8("hi"), + messageAfterBuild.getRepeatedStringPieceBytes(0)); + assertEquals(ByteString.EMPTY, builder.getRepeatedStringPieceBytes(0)); + builder.clearRepeatedStringPiece(); + + message = builder.build(); + builder.addRepeatedUint32(1); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedUint32Count()); + builder.setRepeatedUint32(0, 0); + assertEquals(1, messageAfterBuild.getRepeatedUint32(0)); + assertEquals(0, builder.getRepeatedUint32(0)); + builder.clearRepeatedUint32(); + + message = builder.build(); + builder.addRepeatedUint64(1L); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedUint64Count()); + builder.setRepeatedUint64(0, 0L); + assertEquals(1L, messageAfterBuild.getRepeatedUint64(0)); + assertEquals(0L, builder.getRepeatedUint64(0)); + builder.clearRepeatedUint64(); + + message = builder.build(); + assertEquals(0, message.getSerializedSize()); + builder.mergeFrom(TestAllTypesLite.newBuilder() + .setOptionalBool(true) + .build()); + assertEquals(0, message.getSerializedSize()); + assertEquals(true, builder.build().getOptionalBool()); + builder.clearOptionalBool(); + + message = builder.build(); + assertEquals(0, message.getSerializedSize()); + builder.mergeFrom(TestAllTypesLite.newBuilder() + .setOptionalBool(true) + .build()); + assertEquals(0, message.getSerializedSize()); + assertEquals(true, builder.build().getOptionalBool()); + builder.clear(); + assertEquals(0, builder.build().getSerializedSize()); + + message = builder.build(); + assertEquals(0, message.getSerializedSize()); + builder.mergeOptionalForeignMessage(foreignMessage); + assertEquals(0, message.getSerializedSize()); + assertEquals( + foreignMessage.getC(), + builder.build().getOptionalForeignMessage().getC()); + builder.clearOptionalForeignMessage(); + + message = builder.build(); + assertEquals(0, message.getSerializedSize()); + builder.mergeOptionalLazyMessage(nestedMessage); + assertEquals(0, message.getSerializedSize()); + assertEquals( + nestedMessage.getBb(), + builder.build().getOptionalLazyMessage().getBb()); + builder.clearOptionalLazyMessage(); + + message = builder.build(); + builder.setOneofString("hi"); + assertEquals( + OneofFieldCase.ONEOFFIELD_NOT_SET, message.getOneofFieldCase()); + assertEquals(OneofFieldCase.ONEOF_STRING, builder.getOneofFieldCase()); + assertEquals("hi", builder.getOneofString()); + messageAfterBuild = builder.build(); + assertEquals( + OneofFieldCase.ONEOF_STRING, messageAfterBuild.getOneofFieldCase()); + assertEquals("hi", messageAfterBuild.getOneofString()); + builder.setOneofUint32(1); + assertEquals( + OneofFieldCase.ONEOF_STRING, messageAfterBuild.getOneofFieldCase()); + assertEquals("hi", messageAfterBuild.getOneofString()); + assertEquals(OneofFieldCase.ONEOF_UINT32, builder.getOneofFieldCase()); + assertEquals(1, builder.getOneofUint32()); + + TestAllExtensionsLite.Builder extendableMessageBuilder = + TestAllExtensionsLite.newBuilder(); + TestAllExtensionsLite extendableMessage = extendableMessageBuilder.build(); + extendableMessageBuilder.setExtension( + UnittestLite.optionalInt32ExtensionLite, 1); + assertFalse(extendableMessage.hasExtension( + UnittestLite.optionalInt32ExtensionLite)); + extendableMessage = extendableMessageBuilder.build(); + assertEquals( + 1, (int) extendableMessageBuilder.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + assertEquals( + 1, (int) extendableMessage.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + extendableMessageBuilder.setExtension( + UnittestLite.optionalInt32ExtensionLite, 3); + assertEquals( + 3, (int) extendableMessageBuilder.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + assertEquals( + 1, (int) extendableMessage.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + extendableMessage = extendableMessageBuilder.build(); + assertEquals( + 3, (int) extendableMessageBuilder.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + assertEquals( + 3, (int) extendableMessage.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + + // No extension registry, so it should be in unknown fields. + extendableMessage = + TestAllExtensionsLite.parseFrom(extendableMessage.toByteArray()); + assertFalse(extendableMessage.hasExtension( + UnittestLite.optionalInt32ExtensionLite)); + + extendableMessageBuilder = extendableMessage.toBuilder(); + extendableMessageBuilder.mergeFrom(TestAllExtensionsLite.newBuilder() + .setExtension(UnittestLite.optionalFixed32ExtensionLite, 11) + .build()); + + extendableMessage = extendableMessageBuilder.build(); + ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance(); + UnittestLite.registerAllExtensions(registry); + extendableMessage = TestAllExtensionsLite.parseFrom( + extendableMessage.toByteArray(), registry); + + // The unknown field was preserved. + assertEquals( + 3, (int) extendableMessage.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + assertEquals( + 11, (int) extendableMessage.getExtension( + UnittestLite.optionalFixed32ExtensionLite)); } } diff --git a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java b/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java index 7b201a9d..958b6a7e 100644 --- a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java +++ b/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java @@ -307,7 +307,8 @@ public class LiteralByteStringTest extends TestCase { public void testToString_returnsCanonicalEmptyString() throws UnsupportedEncodingException{ assertSame(classUnderTest + " must be the same string references", - ByteString.EMPTY.toString(Internal.UTF_8), new LiteralByteString(new byte[]{}).toString(Internal.UTF_8)); + ByteString.EMPTY.toString(Internal.UTF_8), + new LiteralByteString(new byte[]{}).toString(Internal.UTF_8)); } public void testToString_raisesException() throws UnsupportedEncodingException{ diff --git a/java/src/test/java/com/google/protobuf/LongArrayListTest.java b/java/src/test/java/com/google/protobuf/LongArrayListTest.java new file mode 100644 index 00000000..3a52ec7f --- /dev/null +++ b/java/src/test/java/com/google/protobuf/LongArrayListTest.java @@ -0,0 +1,473 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static java.util.Arrays.asList; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; + +/** + * Tests for {@link LongArrayList}. + * + * @author dweis@google.com (Daniel Weis) + */ +public class LongArrayListTest extends TestCase { + + private static final LongArrayList UNARY_LIST = newImmutableLongArrayList(1); + private static final LongArrayList TERTIARY_LIST = + newImmutableLongArrayList(1, 2, 3); + + private LongArrayList list; + + @Override + protected void setUp() throws Exception { + list = new LongArrayList(); + } + + public void testEmptyListReturnsSameInstance() { + assertSame(LongArrayList.emptyList(), LongArrayList.emptyList()); + } + + public void testEmptyListIsImmutable() { + assertImmutable(LongArrayList.emptyList()); + } + + public void testMakeImmutable() { + list.addLong(2); + list.addLong(4); + list.addLong(6); + list.addLong(8); + list.makeImmutable(); + assertImmutable(list); + } + + public void testCopyConstructor() { + LongArrayList copy = new LongArrayList(TERTIARY_LIST); + assertEquals(TERTIARY_LIST, copy); + + copy = new LongArrayList(LongArrayList.emptyList()); + assertEquals(LongArrayList.emptyList(), copy); + + copy = new LongArrayList(asList(1L, 2L, 3L)); + assertEquals(asList(1L, 2L, 3L), copy); + + copy = new LongArrayList(Collections.emptyList()); + assertEquals(LongArrayList.emptyList(), copy); + } + + public void testModificationWithIteration() { + list.addAll(asList(1L, 2L, 3L, 4L)); + Iterator iterator = list.iterator(); + assertEquals(4, list.size()); + assertEquals(1, (long) list.get(0)); + assertEquals(1, (long) iterator.next()); + list.set(0, 1L); + assertEquals(2, (long) iterator.next()); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, 0L); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testGet() { + assertEquals(1, (long) TERTIARY_LIST.get(0)); + assertEquals(2, (long) TERTIARY_LIST.get(1)); + assertEquals(3, (long) TERTIARY_LIST.get(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testGetLong() { + assertEquals(1, TERTIARY_LIST.getLong(0)); + assertEquals(2, TERTIARY_LIST.getLong(1)); + assertEquals(3, TERTIARY_LIST.getLong(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSize() { + assertEquals(0, LongArrayList.emptyList().size()); + assertEquals(1, UNARY_LIST.size()); + assertEquals(3, TERTIARY_LIST.size()); + + list.addLong(2); + list.addLong(4); + list.addLong(6); + list.addLong(8); + assertEquals(4, list.size()); + + list.remove(0); + assertEquals(3, list.size()); + + list.add(16L); + assertEquals(4, list.size()); + } + + public void testSet() { + list.addLong(2); + list.addLong(4); + + assertEquals(2, (long) list.set(0, 0L)); + assertEquals(0, list.getLong(0)); + + assertEquals(4, (long) list.set(1, 0L)); + assertEquals(0, list.getLong(1)); + + try { + list.set(-1, 0L); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.set(2, 0L); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSetLong() { + list.addLong(2); + list.addLong(4); + + assertEquals(2, list.setLong(0, 0)); + assertEquals(0, list.getLong(0)); + + assertEquals(4, list.setLong(1, 0)); + assertEquals(0, list.getLong(1)); + + try { + list.setLong(-1, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.setLong(2, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAdd() { + assertEquals(0, list.size()); + + assertTrue(list.add(2L)); + assertEquals(asList(2L), list); + + assertTrue(list.add(3L)); + list.add(0, 4L); + assertEquals(asList(4L, 2L, 3L), list); + + list.add(0, 1L); + list.add(0, 0L); + // Force a resize by getting up to 11 elements. + for (int i = 0; i < 6; i++) { + list.add(Long.valueOf(5 + i)); + } + assertEquals(asList(0L, 1L, 4L, 2L, 3L, 5L, 6L, 7L, 8L, 9L, 10L), list); + + try { + list.add(-1, 5L); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.add(4, 5L); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAddLong() { + assertEquals(0, list.size()); + + list.addLong(2); + assertEquals(asList(2L), list); + + list.addLong(3); + assertEquals(asList(2L, 3L), list); + } + + public void testAddAll() { + assertEquals(0, list.size()); + + assertTrue(list.addAll(Collections.singleton(1L))); + assertEquals(1, list.size()); + assertEquals(1, (long) list.get(0)); + assertEquals(1, list.getLong(0)); + + assertTrue(list.addAll(asList(2L, 3L, 4L, 5L, 6L))); + assertEquals(asList(1L, 2L, 3L, 4L, 5L, 6L), list); + + assertTrue(list.addAll(TERTIARY_LIST)); + assertEquals(asList(1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L), list); + + assertFalse(list.addAll(Collections.emptyList())); + assertFalse(list.addAll(LongArrayList.emptyList())); + } + + public void testRemove() { + list.addAll(TERTIARY_LIST); + assertEquals(1, (long) list.remove(0)); + assertEquals(asList(2L, 3L), list); + + assertTrue(list.remove(3L)); + assertEquals(asList(2L), list); + + assertFalse(list.remove(3L)); + assertEquals(asList(2L), list); + + assertEquals(2, (long) list.remove(0)); + assertEquals(asList(), list); + + try { + list.remove(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.remove(0); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + private void assertImmutable(LongArrayList list) { + if (list.contains(1)) { + throw new RuntimeException("Cannot test the immutability of lists that contain 1."); + } + + try { + list.add(1L); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, 1L); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.singletonList(1L)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(new LongArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.singleton(1L)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addLong(0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, 0L); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.setLong(0, 0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + private static LongArrayList newImmutableLongArrayList(long... elements) { + LongArrayList list = new LongArrayList(); + for (long element : elements) { + list.addLong(element); + } + list.makeImmutable(); + return list; + } +} diff --git a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java index 22dbeb76..6cff689f 100644 --- a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java +++ b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java @@ -36,6 +36,11 @@ import map_lite_test.MapForProto2TestProto.TestUnknownEnumValue; import junit.framework.TestCase; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + /** * Unit tests for map fields. */ @@ -170,6 +175,140 @@ public class MapForProto2LiteTest extends TestCase { assertEquals(0, message.getStringToInt32Field().size()); } + public void testSanityCopyOnWrite() throws InvalidProtocolBufferException { + // Since builders are implemented as a thin wrapper around a message + // instance, we attempt to verify that we can't cause the builder to modify + // a produced message. + + TestMap.Builder builder = TestMap.newBuilder(); + TestMap message = builder.build(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertTrue(message.getInt32ToInt32Field().isEmpty()); + message = builder.build(); + try { + intMap.put(2, 3); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), message.getInt32ToInt32Field()); + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + builder.getMutableInt32ToInt32Field().put(2, 3); + assertEquals(newMap(1, 2), message.getInt32ToInt32Field()); + assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field()); + } + + public void testMutableMapLifecycle() { + TestMap.Builder builder = TestMap.newBuilder(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + try { + intMap.put(2, 3); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + builder.getMutableInt32ToInt32Field().put(2, 3); + assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field()); + + Map enumMap = builder.getMutableInt32ToEnumField(); + enumMap.put(1, TestMap.EnumValue.BAR); + assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField()); + try { + enumMap.put(2, TestMap.EnumValue.FOO); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField()); + builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO); + assertEquals( + newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO), + builder.getInt32ToEnumField()); + + Map stringMap = builder.getMutableInt32ToStringField(); + stringMap.put(1, "1"); + assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField()); + try { + stringMap.put(2, "2"); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, "1"), builder.getInt32ToStringField()); + builder.getMutableInt32ToStringField().put(2, "2"); + assertEquals( + newMap(1, "1", 2, "2"), + builder.getInt32ToStringField()); + + Map messageMap = builder.getMutableInt32ToMessageField(); + messageMap.put(1, TestMap.MessageValue.getDefaultInstance()); + assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), + builder.build().getInt32ToMessageField()); + try { + messageMap.put(2, TestMap.MessageValue.getDefaultInstance()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), + builder.getInt32ToMessageField()); + builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance()); + assertEquals( + newMap(1, TestMap.MessageValue.getDefaultInstance(), + 2, TestMap.MessageValue.getDefaultInstance()), + builder.getInt32ToMessageField()); + } + + public void testMutableMapLifecycle_collections() { + TestMap.Builder builder = TestMap.newBuilder(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + try { + intMap.remove(2); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.entrySet().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.entrySet().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.keySet().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.values().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.values().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), intMap); + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + } + public void testGettersAndSetters() throws Exception { TestMap.Builder builder = TestMap.newBuilder(); TestMap message = builder.build(); @@ -274,4 +413,26 @@ public class MapForProto2LiteTest extends TestCase { assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue()); } + + public void testIterationOrder() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + assertEquals(Arrays.asList("1", "2", "3"), + new ArrayList(message.getStringToInt32Field().keySet())); + } + + private static Map newMap(K key1, V value1) { + Map map = new HashMap(); + map.put(key1, value1); + return map; + } + + private static Map newMap(K key1, V value1, K key2, V value2) { + Map map = new HashMap(); + map.put(key1, value1); + map.put(key2, value2); + return map; + } } diff --git a/java/src/test/java/com/google/protobuf/MapForProto2Test.java b/java/src/test/java/com/google/protobuf/MapForProto2Test.java index 78cba1b4..7e984040 100644 --- a/java/src/test/java/com/google/protobuf/MapForProto2Test.java +++ b/java/src/test/java/com/google/protobuf/MapForProto2Test.java @@ -40,6 +40,7 @@ import map_test.MapForProto2TestProto.TestUnknownEnumValue; import junit.framework.TestCase; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -177,6 +178,116 @@ public class MapForProto2Test extends TestCase { assertEquals(0, message.getInt32ToMessageField().size()); assertEquals(0, message.getStringToInt32Field().size()); } + + public void testMutableMapLifecycle() { + TestMap.Builder builder = TestMap.newBuilder(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + try { + intMap.put(2, 3); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + builder.getMutableInt32ToInt32Field().put(2, 3); + assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field()); + + Map enumMap = builder.getMutableInt32ToEnumField(); + enumMap.put(1, TestMap.EnumValue.BAR); + assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField()); + try { + enumMap.put(2, TestMap.EnumValue.FOO); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField()); + builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO); + assertEquals( + newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO), + builder.getInt32ToEnumField()); + + Map stringMap = builder.getMutableInt32ToStringField(); + stringMap.put(1, "1"); + assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField()); + try { + stringMap.put(2, "2"); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, "1"), builder.getInt32ToStringField()); + builder.getMutableInt32ToStringField().put(2, "2"); + assertEquals( + newMap(1, "1", 2, "2"), + builder.getInt32ToStringField()); + + Map messageMap = builder.getMutableInt32ToMessageField(); + messageMap.put(1, TestMap.MessageValue.getDefaultInstance()); + assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), + builder.build().getInt32ToMessageField()); + try { + messageMap.put(2, TestMap.MessageValue.getDefaultInstance()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), + builder.getInt32ToMessageField()); + builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance()); + assertEquals( + newMap(1, TestMap.MessageValue.getDefaultInstance(), + 2, TestMap.MessageValue.getDefaultInstance()), + builder.getInt32ToMessageField()); + } + + public void testMutableMapLifecycle_collections() { + TestMap.Builder builder = TestMap.newBuilder(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + try { + intMap.remove(2); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.entrySet().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.entrySet().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.keySet().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.values().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.values().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), intMap); + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + } public void testGettersAndSetters() throws Exception { TestMap.Builder builder = TestMap.newBuilder(); @@ -513,4 +624,41 @@ public class MapForProto2Test extends TestCase { assertEquals(2, message.getRecursiveMapField().get(1).getValue()); assertEquals(4, message.getRecursiveMapField().get(3).getValue()); } + + public void testIterationOrder() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + assertEquals(Arrays.asList("1", "2", "3"), + new ArrayList(message.getStringToInt32Field().keySet())); + } + + // Regression test for b/20494788 + public void testMapInitializationOrder() throws Exception { + assertEquals("RedactAllTypes", map_test.RedactAllTypes + .getDefaultInstance().getDescriptorForType().getName()); + + map_test.Message1.Builder builder = + map_test.Message1.newBuilder(); + builder.getMutableMapField().put("key", true); + map_test.Message1 message = builder.build(); + Message mapEntry = (Message) message.getRepeatedField( + message.getDescriptorForType().findFieldByName("map_field"), 0); + assertEquals(2, mapEntry.getAllFields().size()); + } + + private static Map newMap(K key1, V value1) { + Map map = new HashMap(); + map.put(key1, value1); + return map; + } + + private static Map newMap(K key1, V value1, K key2, V value2) { + Map map = new HashMap(); + map.put(key1, value1); + map.put(key2, value2); + return map; + } } + diff --git a/java/src/test/java/com/google/protobuf/MapTest.java b/java/src/test/java/com/google/protobuf/MapTest.java index b8e67b7c..0509be15 100644 --- a/java/src/test/java/com/google/protobuf/MapTest.java +++ b/java/src/test/java/com/google/protobuf/MapTest.java @@ -41,6 +41,7 @@ import map_test.MapTestProto.TestOnChangeEventPropagation; import junit.framework.TestCase; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -178,7 +179,117 @@ public class MapTest extends TestCase { assertEquals(0, message.getInt32ToMessageField().size()); assertEquals(0, message.getStringToInt32Field().size()); } + + public void testMutableMapLifecycle() { + TestMap.Builder builder = TestMap.newBuilder(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + try { + intMap.put(2, 3); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + builder.getMutableInt32ToInt32Field().put(2, 3); + assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field()); + + Map enumMap = builder.getMutableInt32ToEnumField(); + enumMap.put(1, TestMap.EnumValue.BAR); + assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField()); + try { + enumMap.put(2, TestMap.EnumValue.FOO); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField()); + builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO); + assertEquals( + newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO), + builder.getInt32ToEnumField()); + + Map stringMap = builder.getMutableInt32ToStringField(); + stringMap.put(1, "1"); + assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField()); + try { + stringMap.put(2, "2"); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, "1"), builder.getInt32ToStringField()); + builder.getMutableInt32ToStringField().put(2, "2"); + assertEquals( + newMap(1, "1", 2, "2"), + builder.getInt32ToStringField()); + + Map messageMap = builder.getMutableInt32ToMessageField(); + messageMap.put(1, TestMap.MessageValue.getDefaultInstance()); + assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), + builder.build().getInt32ToMessageField()); + try { + messageMap.put(2, TestMap.MessageValue.getDefaultInstance()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), + builder.getInt32ToMessageField()); + builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance()); + assertEquals( + newMap(1, TestMap.MessageValue.getDefaultInstance(), + 2, TestMap.MessageValue.getDefaultInstance()), + builder.getInt32ToMessageField()); + } + public void testMutableMapLifecycle_collections() { + TestMap.Builder builder = TestMap.newBuilder(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + try { + intMap.remove(2); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.entrySet().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.entrySet().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.keySet().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.values().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.values().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), intMap); + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + } + public void testGettersAndSetters() throws Exception { TestMap.Builder builder = TestMap.newBuilder(); TestMap message = builder.build(); @@ -611,4 +722,26 @@ public class MapTest extends TestCase { assertEquals(data.get(entry.getKey()) + 1, entry.getValue().intValue()); } } + + public void testIterationOrder() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + assertEquals(Arrays.asList("1", "2", "3"), + new ArrayList(message.getStringToInt32Field().keySet())); + } + + private static Map newMap(K key1, V value1) { + Map map = new HashMap(); + map.put(key1, value1); + return map; + } + + private static Map newMap(K key1, V value1, K key2, V value2) { + Map map = new HashMap(); + map.put(key1, value1); + map.put(key2, value2); + return map; + } } diff --git a/java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java b/java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java new file mode 100644 index 00000000..f9f5e9c7 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java @@ -0,0 +1,303 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static java.util.Arrays.asList; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; + +/** + * Tests for {@link ProtobufArrayList}. + */ +public class ProtobufArrayListTest extends TestCase { + + private static final ProtobufArrayList UNARY_LIST = newImmutableProtoArrayList(1); + private static final ProtobufArrayList TERTIARY_LIST = + newImmutableProtoArrayList(1, 2, 3); + + private ProtobufArrayList list; + + @Override + protected void setUp() throws Exception { + list = new ProtobufArrayList(); + } + + public void testEmptyListReturnsSameInstance() { + assertSame(ProtobufArrayList.emptyList(), ProtobufArrayList.emptyList()); + } + + public void testEmptyListIsImmutable() { + assertImmutable(ProtobufArrayList.emptyList()); + } + + public void testCopyConstructor() { + ProtobufArrayList copy = new ProtobufArrayList(TERTIARY_LIST); + assertEquals(TERTIARY_LIST, copy); + + copy = new ProtobufArrayList(IntArrayList.emptyList()); + assertEquals(ProtobufArrayList.emptyList(), copy); + + copy = new ProtobufArrayList(asList(1, 2, 3)); + assertEquals(asList(1, 2, 3), copy); + + copy = new ProtobufArrayList(Collections.emptyList()); + assertEquals(ProtobufArrayList.emptyList(), copy); + } + + public void testModificationWithIteration() { + list.addAll(asList(1, 2, 3, 4)); + Iterator iterator = list.iterator(); + assertEquals(4, list.size()); + assertEquals(1, (int) list.get(0)); + assertEquals(1, (int) iterator.next()); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.set(0, 1); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, 0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testMakeImmutable() { + list.add(2); + list.add(4); + list.add(6); + list.add(8); + list.makeImmutable(); + assertImmutable(list); + } + + public void testRemove() { + list.add(2); + list.add(4); + list.add(6); + + list.remove(1); + assertEquals(asList(2, 6), list); + + list.remove(1); + assertEquals(asList(2), list); + + list.remove(0); + assertEquals(asList(), list); + } + + public void testGet() { + list.add(2); + list.add(6); + + assertEquals(2, (int) list.get(0)); + assertEquals(6, (int) list.get(1)); + } + + public void testSet() { + list.add(2); + list.add(6); + + list.set(0, 1); + assertEquals(1, (int) list.get(0)); + list.set(1, 2); + assertEquals(2, (int) list.get(1)); + } + + private void assertImmutable(List list) { + if (list.contains(1)) { + throw new RuntimeException("Cannot test the immutability of lists that contain 1."); + } + + try { + list.add(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, 1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.singletonList(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(new ProtobufArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, 0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + private static ProtobufArrayList newImmutableProtoArrayList(int... elements) { + ProtobufArrayList list = new ProtobufArrayList(); + for (int element : elements) { + list.add(element); + } + list.makeImmutable(); + return list; + } +} diff --git a/java/src/test/java/com/google/protobuf/field_presence_test.proto b/java/src/test/java/com/google/protobuf/field_presence_test.proto index 8d8ea8ef..8f3ca8c6 100644 --- a/java/src/test/java/com/google/protobuf/field_presence_test.proto +++ b/java/src/test/java/com/google/protobuf/field_presence_test.proto @@ -45,15 +45,15 @@ message TestAllTypes { BAZ = 2; } message NestedMessage { - optional int32 value = 1; + int32 value = 1; } - optional int32 optional_int32 = 1; - optional string optional_string = 2; - optional bytes optional_bytes = 3; - optional NestedEnum optional_nested_enum = 4; - optional NestedMessage optional_nested_message = 5; - optional protobuf_unittest.TestRequired optional_proto2_message = 6; + int32 optional_int32 = 1; + string optional_string = 2; + bytes optional_bytes = 3; + NestedEnum optional_nested_enum = 4; + NestedMessage optional_nested_message = 5; + protobuf_unittest.TestRequired optional_proto2_message = 6; oneof oneof_field { int32 oneof_int32 = 11; @@ -75,12 +75,12 @@ message TestAllTypes { } message TestOptionalFieldsOnly { - optional int32 optional_int32 = 1; - optional string optional_string = 2; - optional bytes optional_bytes = 3; - optional TestAllTypes.NestedEnum optional_nested_enum = 4; - optional TestAllTypes.NestedMessage optional_nested_message = 5; - optional protobuf_unittest.TestRequired optional_proto2_message = 6; + int32 optional_int32 = 1; + string optional_string = 2; + bytes optional_bytes = 3; + TestAllTypes.NestedEnum optional_nested_enum = 4; + TestAllTypes.NestedMessage optional_nested_message = 5; + protobuf_unittest.TestRequired optional_proto2_message = 6; } message TestRepeatedFieldsOnly { diff --git a/java/src/test/java/com/google/protobuf/map_initialization_order_test.proto b/java/src/test/java/com/google/protobuf/map_initialization_order_test.proto new file mode 100644 index 00000000..b02ac599 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/map_initialization_order_test.proto @@ -0,0 +1,61 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Regression test for a map initilaization order bug. The bug only manifests +// when: +// 1. A message contains map fields and is also extendable. +// 2. There is a file-level extension defined in the same file referencing +// the above message as the extension type. +// 3. The program executes in the following order: +// a. getDescriptor() is called on another message in the same file. +// b. use protobuf reflection to access the map field. +// The symptom is a NullPointerException being thrown. +syntax = "proto2"; + +package map_test; + +option java_package = "map_test"; +option java_outer_classname = "MapInitializationOrderTest"; +option java_multiple_files = true; + +// Mirrors the structure of +// javatests/com/google/cloud/common/logging/logging_test.proto. + +message Message1 { + map map_field = 1; + extensions 1000 to max; +} + +extend Message1 { + optional Message1 recursive_extension = 1001; +} + +message RedactAllTypes { +} diff --git a/java/src/test/java/com/google/protobuf/map_test.proto b/java/src/test/java/com/google/protobuf/map_test.proto index bf692c22..2f7709be 100644 --- a/java/src/test/java/com/google/protobuf/map_test.proto +++ b/java/src/test/java/com/google/protobuf/map_test.proto @@ -39,7 +39,7 @@ option java_generate_equals_and_hash = true; message TestMap { message MessageValue { - optional int32 value = 1; + int32 value = 1; } enum EnumValue { FOO = 0; @@ -60,5 +60,5 @@ message TestMap { // propagate the onChange event and mark its parent dirty when a change // is made to a map field. message TestOnChangeEventPropagation { - optional TestMap optional_message = 1; + TestMap optional_message = 1; } diff --git a/python/google/protobuf/descriptor.py b/python/google/protobuf/descriptor.py index f7a58ca0..970b1a88 100755 --- a/python/google/protobuf/descriptor.py +++ b/python/google/protobuf/descriptor.py @@ -245,9 +245,6 @@ class Descriptor(_NestedDescriptorBase): is_extendable: Does this type define any extension ranges? - options: (descriptor_pb2.MessageOptions) Protocol message options or None - to use default message options. - oneofs: (list of OneofDescriptor) The list of descriptors for oneof fields in this message. oneofs_by_name: (dict str -> OneofDescriptor) Same objects as in |oneofs|, @@ -265,7 +262,7 @@ class Descriptor(_NestedDescriptorBase): file=None, serialized_start=None, serialized_end=None, syntax=None): _message.Message._CheckCalledFromGeneratedFile() - return _message.Message._GetMessageDescriptor(full_name) + return _message.default_pool.FindMessageTypeByName(full_name) # NOTE(tmarek): The file argument redefining a builtin is nothing we can # fix right now since we don't know how many clients already rely on the @@ -495,9 +492,9 @@ class FieldDescriptor(DescriptorBase): has_default_value=True, containing_oneof=None): _message.Message._CheckCalledFromGeneratedFile() if is_extension: - return _message.Message._GetExtensionDescriptor(full_name) + return _message.default_pool.FindExtensionByName(full_name) else: - return _message.Message._GetFieldDescriptor(full_name) + return _message.default_pool.FindFieldByName(full_name) def __init__(self, name, full_name, index, number, type, cpp_type, label, default_value, message_type, enum_type, containing_type, @@ -528,14 +525,9 @@ class FieldDescriptor(DescriptorBase): self.containing_oneof = containing_oneof if api_implementation.Type() == 'cpp': if is_extension: - # pylint: disable=protected-access - self._cdescriptor = ( - _message.Message._GetExtensionDescriptor(full_name)) - # pylint: enable=protected-access + self._cdescriptor = _message.default_pool.FindExtensionByName(full_name) else: - # pylint: disable=protected-access - self._cdescriptor = _message.Message._GetFieldDescriptor(full_name) - # pylint: enable=protected-access + self._cdescriptor = _message.default_pool.FindFieldByName(full_name) else: self._cdescriptor = None @@ -592,7 +584,7 @@ class EnumDescriptor(_NestedDescriptorBase): containing_type=None, options=None, file=None, serialized_start=None, serialized_end=None): _message.Message._CheckCalledFromGeneratedFile() - return _message.Message._GetEnumDescriptor(full_name) + return _message.default_pool.FindEnumTypeByName(full_name) def __init__(self, name, full_name, filename, values, containing_type=None, options=None, file=None, @@ -677,7 +669,7 @@ class OneofDescriptor(object): def __new__(cls, name, full_name, index, containing_type, fields): _message.Message._CheckCalledFromGeneratedFile() - return _message.Message._GetOneofDescriptor(full_name) + return _message.default_pool.FindOneofByName(full_name) def __init__(self, name, full_name, index, containing_type, fields): """Arguments are as described in the attribute description above.""" @@ -788,12 +780,8 @@ class FileDescriptor(DescriptorBase): dependencies=None, syntax=None): # FileDescriptor() is called from various places, not only from generated # files, to register dynamic proto files and messages. - # TODO(amauryfa): Expose BuildFile() as a public function and make this - # constructor an implementation detail. if serialized_pb: - # pylint: disable=protected-access2 - return _message.Message._BuildFile(serialized_pb) - # pylint: enable=protected-access + return _message.default_pool.AddSerializedFile(serialized_pb) else: return super(FileDescriptor, cls).__new__(cls) @@ -814,9 +802,7 @@ class FileDescriptor(DescriptorBase): if (api_implementation.Type() == 'cpp' and self.serialized_pb is not None): - # pylint: disable=protected-access - _message.Message._BuildFile(self.serialized_pb) - # pylint: enable=protected-access + _message.default_pool.AddSerializedFile(self.serialized_pb) def CopyToProto(self, proto): """Copies this to a descriptor_pb2.FileDescriptorProto. @@ -864,10 +850,10 @@ def MakeDescriptor(desc_proto, package='', build_file_if_cpp=True, file_descriptor_proto = descriptor_pb2.FileDescriptorProto() file_descriptor_proto.message_type.add().MergeFrom(desc_proto) - # Generate a random name for this proto file to prevent conflicts with - # any imported ones. We need to specify a file name so BuildFile accepts - # our FileDescriptorProto, but it is not important what that file name - # is actually set to. + # Generate a random name for this proto file to prevent conflicts with any + # imported ones. We need to specify a file name so the descriptor pool + # accepts our FileDescriptorProto, but it is not important what that file + # name is actually set to. proto_name = str(uuid.uuid4()) if package: @@ -877,10 +863,8 @@ def MakeDescriptor(desc_proto, package='', build_file_if_cpp=True, else: file_descriptor_proto.name = proto_name + '.proto' - # pylint: disable=protected-access - result = _message.Message._BuildFile( - file_descriptor_proto.SerializeToString()) - # pylint: enable=protected-access + _message.default_pool.Add(file_descriptor_proto) + result = _message.default_pool.FindFileByName(file_descriptor_proto.name) if _USE_C_DESCRIPTORS: return result.message_types_by_name[desc_proto.name] diff --git a/python/google/protobuf/descriptor_pool.py b/python/google/protobuf/descriptor_pool.py index 7e7701f8..1244ba7c 100644 --- a/python/google/protobuf/descriptor_pool.py +++ b/python/google/protobuf/descriptor_pool.py @@ -113,6 +113,20 @@ class DescriptorPool(object): self._internal_db.Add(file_desc_proto) + def AddSerializedFile(self, serialized_file_desc_proto): + """Adds the FileDescriptorProto and its types to this pool. + + Args: + serialized_file_desc_proto: A bytes string, serialization of the + FileDescriptorProto to add. + """ + + # pylint: disable=g-import-not-at-top + from google.protobuf import descriptor_pb2 + file_desc_proto = descriptor_pb2.FileDescriptorProto.FromString( + serialized_file_desc_proto) + self.Add(file_desc_proto) + def AddDescriptor(self, desc): """Adds a Descriptor to the pool, non-recursively. @@ -320,17 +334,17 @@ class DescriptorPool(object): file_descriptor, None, scope)) for index, extension_proto in enumerate(file_proto.extension): - extension_desc = self.MakeFieldDescriptor( + extension_desc = self._MakeFieldDescriptor( extension_proto, file_proto.package, index, is_extension=True) extension_desc.containing_type = self._GetTypeFromScope( file_descriptor.package, extension_proto.extendee, scope) - self.SetFieldType(extension_proto, extension_desc, + self._SetFieldType(extension_proto, extension_desc, file_descriptor.package, scope) file_descriptor.extensions_by_name[extension_desc.name] = ( extension_desc) for desc_proto in file_proto.message_type: - self.SetAllFieldTypes(file_proto.package, desc_proto, scope) + self._SetAllFieldTypes(file_proto.package, desc_proto, scope) if file_proto.package: desc_proto_prefix = _PrefixWithDot(file_proto.package) @@ -381,10 +395,11 @@ class DescriptorPool(object): enums = [ self._ConvertEnumDescriptor(enum, desc_name, file_desc, None, scope) for enum in desc_proto.enum_type] - fields = [self.MakeFieldDescriptor(field, desc_name, index) + fields = [self._MakeFieldDescriptor(field, desc_name, index) for index, field in enumerate(desc_proto.field)] extensions = [ - self.MakeFieldDescriptor(extension, desc_name, index, is_extension=True) + self._MakeFieldDescriptor(extension, desc_name, index, + is_extension=True) for index, extension in enumerate(desc_proto.extension)] oneofs = [ descriptor.OneofDescriptor(desc.name, '.'.join((desc_name, desc.name)), @@ -464,8 +479,8 @@ class DescriptorPool(object): self._enum_descriptors[enum_name] = desc return desc - def MakeFieldDescriptor(self, field_proto, message_name, index, - is_extension=False): + def _MakeFieldDescriptor(self, field_proto, message_name, index, + is_extension=False): """Creates a field descriptor from a FieldDescriptorProto. For message and enum type fields, this method will do a look up @@ -506,7 +521,7 @@ class DescriptorPool(object): extension_scope=None, options=field_proto.options) - def SetAllFieldTypes(self, package, desc_proto, scope): + def _SetAllFieldTypes(self, package, desc_proto, scope): """Sets all the descriptor's fields's types. This method also sets the containing types on any extensions. @@ -527,18 +542,18 @@ class DescriptorPool(object): nested_package = '.'.join([package, desc_proto.name]) for field_proto, field_desc in zip(desc_proto.field, main_desc.fields): - self.SetFieldType(field_proto, field_desc, nested_package, scope) + self._SetFieldType(field_proto, field_desc, nested_package, scope) for extension_proto, extension_desc in ( zip(desc_proto.extension, main_desc.extensions)): extension_desc.containing_type = self._GetTypeFromScope( nested_package, extension_proto.extendee, scope) - self.SetFieldType(extension_proto, extension_desc, nested_package, scope) + self._SetFieldType(extension_proto, extension_desc, nested_package, scope) for nested_type in desc_proto.nested_type: - self.SetAllFieldTypes(nested_package, nested_type, scope) + self._SetAllFieldTypes(nested_package, nested_type, scope) - def SetFieldType(self, field_proto, field_desc, package, scope): + def _SetFieldType(self, field_proto, field_desc, package, scope): """Sets the field's type, cpp_type, message_type and enum_type. Args: diff --git a/python/google/protobuf/internal/containers.py b/python/google/protobuf/internal/containers.py index d976f9e1..72c2fa01 100755 --- a/python/google/protobuf/internal/containers.py +++ b/python/google/protobuf/internal/containers.py @@ -41,6 +41,146 @@ are: __author__ = 'petar@google.com (Petar Petrov)' +import sys + +if sys.version_info[0] < 3: + # We would use collections.MutableMapping all the time, but in Python 2 it + # doesn't define __slots__. This causes two significant problems: + # + # 1. we can't disallow arbitrary attribute assignment, even if our derived + # classes *do* define __slots__. + # + # 2. we can't safely derive a C type from it without __slots__ defined (the + # interpreter expects to find a dict at tp_dictoffset, which we can't + # robustly provide. And we don't want an instance dict anyway. + # + # So this is the Python 2.7 definition of Mapping/MutableMapping functions + # verbatim, except that: + # 1. We declare __slots__. + # 2. We don't declare this as a virtual base class. The classes defined + # in collections are the interesting base classes, not us. + # + # Note: deriving from object is critical. It is the only thing that makes + # this a true type, allowing us to derive from it in C++ cleanly and making + # __slots__ properly disallow arbitrary element assignment. + from collections import Mapping as _Mapping + + class Mapping(object): + __slots__ = () + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + def __contains__(self, key): + try: + self[key] + except KeyError: + return False + else: + return True + + def iterkeys(self): + return iter(self) + + def itervalues(self): + for key in self: + yield self[key] + + def iteritems(self): + for key in self: + yield (key, self[key]) + + def keys(self): + return list(self) + + def items(self): + return [(key, self[key]) for key in self] + + def values(self): + return [self[key] for key in self] + + # Mappings are not hashable by default, but subclasses can change this + __hash__ = None + + def __eq__(self, other): + if not isinstance(other, _Mapping): + return NotImplemented + return dict(self.items()) == dict(other.items()) + + def __ne__(self, other): + return not (self == other) + + class MutableMapping(Mapping): + __slots__ = () + + __marker = object() + + def pop(self, key, default=__marker): + try: + value = self[key] + except KeyError: + if default is self.__marker: + raise + return default + else: + del self[key] + return value + + def popitem(self): + try: + key = next(iter(self)) + except StopIteration: + raise KeyError + value = self[key] + del self[key] + return key, value + + def clear(self): + try: + while True: + self.popitem() + except KeyError: + pass + + def update(*args, **kwds): + if len(args) > 2: + raise TypeError("update() takes at most 2 positional " + "arguments ({} given)".format(len(args))) + elif not args: + raise TypeError("update() takes at least 1 argument (0 given)") + self = args[0] + other = args[1] if len(args) >= 2 else () + + if isinstance(other, Mapping): + for key in other: + self[key] = other[key] + elif hasattr(other, "keys"): + for key in other.keys(): + self[key] = other[key] + else: + for key, value in other: + self[key] = value + for key, value in kwds.items(): + self[key] = value + + def setdefault(self, key, default=None): + try: + return self[key] + except KeyError: + self[key] = default + return default + + _Mapping.register(Mapping) + +else: + # In Python 3 we can just use MutableMapping directly, because it defines + # __slots__. + from collections import MutableMapping + + class BaseContainer(object): """Base container class.""" @@ -286,3 +426,160 @@ class RepeatedCompositeFieldContainer(BaseContainer): raise TypeError('Can only compare repeated composite fields against ' 'other repeated composite fields.') return self._values == other._values + + +class ScalarMap(MutableMapping): + + """Simple, type-checked, dict-like container for holding repeated scalars.""" + + # Disallows assignment to other attributes. + __slots__ = ['_key_checker', '_value_checker', '_values', '_message_listener'] + + def __init__(self, message_listener, key_checker, value_checker): + """ + Args: + message_listener: A MessageListener implementation. + The ScalarMap will call this object's Modified() method when it + is modified. + key_checker: A type_checkers.ValueChecker instance to run on keys + inserted into this container. + value_checker: A type_checkers.ValueChecker instance to run on values + inserted into this container. + """ + self._message_listener = message_listener + self._key_checker = key_checker + self._value_checker = value_checker + self._values = {} + + def __getitem__(self, key): + try: + return self._values[key] + except KeyError: + key = self._key_checker.CheckValue(key) + val = self._value_checker.DefaultValue() + self._values[key] = val + return val + + def __contains__(self, item): + return item in self._values + + # We need to override this explicitly, because our defaultdict-like behavior + # will make the default implementation (from our base class) always insert + # the key. + def get(self, key, default=None): + if key in self: + return self[key] + else: + return default + + def __setitem__(self, key, value): + checked_key = self._key_checker.CheckValue(key) + checked_value = self._value_checker.CheckValue(value) + self._values[checked_key] = checked_value + self._message_listener.Modified() + + def __delitem__(self, key): + del self._values[key] + self._message_listener.Modified() + + def __len__(self): + return len(self._values) + + def __iter__(self): + return iter(self._values) + + def MergeFrom(self, other): + self._values.update(other._values) + self._message_listener.Modified() + + # This is defined in the abstract base, but we can do it much more cheaply. + def clear(self): + self._values.clear() + self._message_listener.Modified() + + +class MessageMap(MutableMapping): + + """Simple, type-checked, dict-like container for with submessage values.""" + + # Disallows assignment to other attributes. + __slots__ = ['_key_checker', '_values', '_message_listener', + '_message_descriptor'] + + def __init__(self, message_listener, message_descriptor, key_checker): + """ + Args: + message_listener: A MessageListener implementation. + The ScalarMap will call this object's Modified() method when it + is modified. + key_checker: A type_checkers.ValueChecker instance to run on keys + inserted into this container. + value_checker: A type_checkers.ValueChecker instance to run on values + inserted into this container. + """ + self._message_listener = message_listener + self._message_descriptor = message_descriptor + self._key_checker = key_checker + self._values = {} + + def __getitem__(self, key): + try: + return self._values[key] + except KeyError: + key = self._key_checker.CheckValue(key) + new_element = self._message_descriptor._concrete_class() + new_element._SetListener(self._message_listener) + self._values[key] = new_element + self._message_listener.Modified() + + return new_element + + def get_or_create(self, key): + """get_or_create() is an alias for getitem (ie. map[key]). + + Args: + key: The key to get or create in the map. + + This is useful in cases where you want to be explicit that the call is + mutating the map. This can avoid lint errors for statements like this + that otherwise would appear to be pointless statements: + + msg.my_map[key] + """ + return self[key] + + # We need to override this explicitly, because our defaultdict-like behavior + # will make the default implementation (from our base class) always insert + # the key. + def get(self, key, default=None): + if key in self: + return self[key] + else: + return default + + def __contains__(self, item): + return item in self._values + + def __setitem__(self, key, value): + raise ValueError('May not set values directly, call my_map[key].foo = 5') + + def __delitem__(self, key): + del self._values[key] + self._message_listener.Modified() + + def __len__(self): + return len(self._values) + + def __iter__(self): + return iter(self._values) + + def MergeFrom(self, other): + for key in other: + self[key].MergeFrom(other[key]) + # self._message_listener.Modified() not required here, because + # mutations to submessages already propagate. + + # This is defined in the abstract base, but we can do it much more cheaply. + def clear(self): + self._values.clear() + self._message_listener.Modified() diff --git a/python/google/protobuf/internal/decoder.py b/python/google/protobuf/internal/decoder.py index 0f500606..3837eaea 100755 --- a/python/google/protobuf/internal/decoder.py +++ b/python/google/protobuf/internal/decoder.py @@ -732,6 +732,50 @@ def MessageSetItemDecoder(extensions_by_number): return DecodeItem +# -------------------------------------------------------------------- + +def MapDecoder(field_descriptor, new_default, is_message_map): + """Returns a decoder for a map field.""" + + key = field_descriptor + tag_bytes = encoder.TagBytes(field_descriptor.number, + wire_format.WIRETYPE_LENGTH_DELIMITED) + tag_len = len(tag_bytes) + local_DecodeVarint = _DecodeVarint + # Can't read _concrete_class yet; might not be initialized. + message_type = field_descriptor.message_type + + def DecodeMap(buffer, pos, end, message, field_dict): + submsg = message_type._concrete_class() + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + # Read length. + (size, pos) = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated message.') + # Read sub-message. + submsg.Clear() + if submsg._InternalParse(buffer, pos, new_pos) != new_pos: + # The only reason _InternalParse would return early is if it + # encountered an end-group tag. + raise _DecodeError('Unexpected end-group tag.') + + if is_message_map: + value[submsg.key].MergeFrom(submsg.value) + else: + value[submsg.key] = submsg.value + + # Predict that the next tag is another copy of the same repeated field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos == end: + # Prediction failed. Return. + return new_pos + + return DecodeMap + # -------------------------------------------------------------------- # Optimization is not as heavy here because calls to SkipField() are rare, # except for handling end-group tags. diff --git a/python/google/protobuf/internal/descriptor_database_test.py b/python/google/protobuf/internal/descriptor_database_test.py index 56fe14e9..8416e157 100644 --- a/python/google/protobuf/internal/descriptor_database_test.py +++ b/python/google/protobuf/internal/descriptor_database_test.py @@ -35,7 +35,6 @@ __author__ = 'matthewtoia@google.com (Matt Toia)' import unittest - from google.protobuf import descriptor_pb2 from google.protobuf.internal import factory_test2_pb2 from google.protobuf import descriptor_database diff --git a/python/google/protobuf/internal/descriptor_pool_test.py b/python/google/protobuf/internal/descriptor_pool_test.py index 7d145f42..d159cc62 100644 --- a/python/google/protobuf/internal/descriptor_pool_test.py +++ b/python/google/protobuf/internal/descriptor_pool_test.py @@ -37,6 +37,7 @@ __author__ = 'matthewtoia@google.com (Matt Toia)' import os import unittest +import unittest from google.protobuf import unittest_pb2 from google.protobuf import descriptor_pb2 from google.protobuf.internal import api_implementation @@ -226,6 +227,13 @@ class DescriptorPoolTest(unittest.TestCase): db.Add(self.factory_test2_fd) self.testFindMessageTypeByName() + def testAddSerializedFile(self): + db = descriptor_database.DescriptorDatabase() + self.pool = descriptor_pool.DescriptorPool(db) + self.pool.AddSerializedFile(self.factory_test1_fd.SerializeToString()) + self.pool.AddSerializedFile(self.factory_test2_fd.SerializeToString()) + self.testFindMessageTypeByName() + def testComplexNesting(self): test1_desc = descriptor_pb2.FileDescriptorProto.FromString( descriptor_pool_test1_pb2.DESCRIPTOR.serialized_pb) @@ -510,6 +518,43 @@ class AddDescriptorTest(unittest.TestCase): 'protobuf_unittest.TestAllTypes') +@unittest.skipIf( + api_implementation.Type() != 'cpp', + 'default_pool is only supported by the C++ implementation') +class DefaultPoolTest(unittest.TestCase): + + def testFindMethods(self): + # pylint: disable=g-import-not-at-top + from google.protobuf.pyext import _message + pool = _message.default_pool + self.assertIs( + pool.FindFileByName('google/protobuf/unittest.proto'), + unittest_pb2.DESCRIPTOR) + self.assertIs( + pool.FindMessageTypeByName('protobuf_unittest.TestAllTypes'), + unittest_pb2.TestAllTypes.DESCRIPTOR) + self.assertIs( + pool.FindFieldByName('protobuf_unittest.TestAllTypes.optional_int32'), + unittest_pb2.TestAllTypes.DESCRIPTOR.fields_by_name['optional_int32']) + self.assertIs( + pool.FindExtensionByName('protobuf_unittest.optional_int32_extension'), + unittest_pb2.DESCRIPTOR.extensions_by_name['optional_int32_extension']) + self.assertIs( + pool.FindEnumTypeByName('protobuf_unittest.ForeignEnum'), + unittest_pb2.ForeignEnum.DESCRIPTOR) + self.assertIs( + pool.FindOneofByName('protobuf_unittest.TestAllTypes.oneof_field'), + unittest_pb2.TestAllTypes.DESCRIPTOR.oneofs_by_name['oneof_field']) + + def testAddFileDescriptor(self): + # pylint: disable=g-import-not-at-top + from google.protobuf.pyext import _message + pool = _message.default_pool + file_desc = descriptor_pb2.FileDescriptorProto(name='some/file.proto') + pool.Add(file_desc) + pool.AddSerializedFile(file_desc.SerializeToString()) + + TEST1_FILE = ProtoFile( 'google/protobuf/internal/descriptor_pool_test1.proto', 'google.protobuf.python.internal', diff --git a/python/google/protobuf/internal/descriptor_test.py b/python/google/protobuf/internal/descriptor_test.py index 335caee6..26866f3a 100755 --- a/python/google/protobuf/internal/descriptor_test.py +++ b/python/google/protobuf/internal/descriptor_test.py @@ -35,8 +35,8 @@ __author__ = 'robinson@google.com (Will Robinson)' import sys -import unittest +import unittest from google.protobuf import unittest_custom_options_pb2 from google.protobuf import unittest_import_pb2 from google.protobuf import unittest_pb2 diff --git a/python/google/protobuf/internal/encoder.py b/python/google/protobuf/internal/encoder.py index 38a5138a..752f4eab 100755 --- a/python/google/protobuf/internal/encoder.py +++ b/python/google/protobuf/internal/encoder.py @@ -314,7 +314,7 @@ def MessageSizer(field_number, is_repeated, is_packed): # -------------------------------------------------------------------- -# MessageSet is special. +# MessageSet is special: it needs custom logic to compute its size properly. def MessageSetItemSizer(field_number): @@ -339,6 +339,32 @@ def MessageSetItemSizer(field_number): return FieldSize +# -------------------------------------------------------------------- +# Map is special: it needs custom logic to compute its size properly. + + +def MapSizer(field_descriptor): + """Returns a sizer for a map field.""" + + # Can't look at field_descriptor.message_type._concrete_class because it may + # not have been initialized yet. + message_type = field_descriptor.message_type + message_sizer = MessageSizer(field_descriptor.number, False, False) + + def FieldSize(map_value): + total = 0 + for key in map_value: + value = map_value[key] + # It's wasteful to create the messages and throw them away one second + # later since we'll do the same for the actual encode. But there's not an + # obvious way to avoid this within the current design without tons of code + # duplication. + entry_msg = message_type._concrete_class(key=key, value=value) + total += message_sizer(entry_msg) + return total + + return FieldSize + # ==================================================================== # Encoders! @@ -786,3 +812,30 @@ def MessageSetItemEncoder(field_number): return write(end_bytes) return EncodeField + + +# -------------------------------------------------------------------- +# As before, Map is special. + + +def MapEncoder(field_descriptor): + """Encoder for extensions of MessageSet. + + Maps always have a wire format like this: + message MapEntry { + key_type key = 1; + value_type value = 2; + } + repeated MapEntry map = N; + """ + # Can't look at field_descriptor.message_type._concrete_class because it may + # not have been initialized yet. + message_type = field_descriptor.message_type + encode_message = MessageEncoder(field_descriptor.number, False, False) + + def EncodeField(write, value): + for key in value: + entry_msg = message_type._concrete_class(key=key, value=value[key]) + encode_message(write, entry_msg) + + return EncodeField diff --git a/python/google/protobuf/internal/generator_test.py b/python/google/protobuf/internal/generator_test.py index ccc5860b..5c07cbe6 100755 --- a/python/google/protobuf/internal/generator_test.py +++ b/python/google/protobuf/internal/generator_test.py @@ -42,7 +42,6 @@ further ensures that we can use Python protocol message objects as we expect. __author__ = 'robinson@google.com (Will Robinson)' import unittest - from google.protobuf.internal import test_bad_identifiers_pb2 from google.protobuf import unittest_custom_options_pb2 from google.protobuf import unittest_import_pb2 diff --git a/python/google/protobuf/internal/message_factory_test.py b/python/google/protobuf/internal/message_factory_test.py index 626c3fc9..b8694f96 100644 --- a/python/google/protobuf/internal/message_factory_test.py +++ b/python/google/protobuf/internal/message_factory_test.py @@ -35,7 +35,6 @@ __author__ = 'matthewtoia@google.com (Matt Toia)' import unittest - from google.protobuf import descriptor_pb2 from google.protobuf.internal import factory_test1_pb2 from google.protobuf.internal import factory_test2_pb2 diff --git a/python/google/protobuf/internal/message_test.py b/python/google/protobuf/internal/message_test.py index 4ecaa1c7..320ff0d2 100755 --- a/python/google/protobuf/internal/message_test.py +++ b/python/google/protobuf/internal/message_test.py @@ -50,7 +50,9 @@ import pickle import sys import unittest +import unittest from google.protobuf.internal import _parameterized +from google.protobuf import map_unittest_pb2 from google.protobuf import unittest_pb2 from google.protobuf import unittest_proto3_arena_pb2 from google.protobuf.internal import api_implementation @@ -125,10 +127,17 @@ class MessageTest(unittest.TestCase): self.assertEqual(unpickled_message, golden_message) def testPositiveInfinity(self, message_module): - golden_data = (b'\x5D\x00\x00\x80\x7F' - b'\x61\x00\x00\x00\x00\x00\x00\xF0\x7F' - b'\xCD\x02\x00\x00\x80\x7F' - b'\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\x7F') + if message_module is unittest_pb2: + golden_data = (b'\x5D\x00\x00\x80\x7F' + b'\x61\x00\x00\x00\x00\x00\x00\xF0\x7F' + b'\xCD\x02\x00\x00\x80\x7F' + b'\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\x7F') + else: + golden_data = (b'\x5D\x00\x00\x80\x7F' + b'\x61\x00\x00\x00\x00\x00\x00\xF0\x7F' + b'\xCA\x02\x04\x00\x00\x80\x7F' + b'\xD2\x02\x08\x00\x00\x00\x00\x00\x00\xF0\x7F') + golden_message = message_module.TestAllTypes() golden_message.ParseFromString(golden_data) self.assertTrue(IsPosInf(golden_message.optional_float)) @@ -138,10 +147,17 @@ class MessageTest(unittest.TestCase): self.assertEqual(golden_data, golden_message.SerializeToString()) def testNegativeInfinity(self, message_module): - golden_data = (b'\x5D\x00\x00\x80\xFF' - b'\x61\x00\x00\x00\x00\x00\x00\xF0\xFF' - b'\xCD\x02\x00\x00\x80\xFF' - b'\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\xFF') + if message_module is unittest_pb2: + golden_data = (b'\x5D\x00\x00\x80\xFF' + b'\x61\x00\x00\x00\x00\x00\x00\xF0\xFF' + b'\xCD\x02\x00\x00\x80\xFF' + b'\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\xFF') + else: + golden_data = (b'\x5D\x00\x00\x80\xFF' + b'\x61\x00\x00\x00\x00\x00\x00\xF0\xFF' + b'\xCA\x02\x04\x00\x00\x80\xFF' + b'\xD2\x02\x08\x00\x00\x00\x00\x00\x00\xF0\xFF') + golden_message = message_module.TestAllTypes() golden_message.ParseFromString(golden_data) self.assertTrue(IsNegInf(golden_message.optional_float)) @@ -1034,64 +1050,132 @@ class Proto2Test(unittest.TestCase): self.assertEqual(len(parsing_merge.Extensions[ unittest_pb2.TestParsingMerge.repeated_ext]), 3) + def testPythonicInit(self): + message = unittest_pb2.TestAllTypes( + optional_int32=100, + optional_fixed32=200, + optional_float=300.5, + optional_bytes=b'x', + optionalgroup={'a': 400}, + optional_nested_message={'bb': 500}, + optional_nested_enum='BAZ', + repeatedgroup=[{'a': 600}, + {'a': 700}], + repeated_nested_enum=['FOO', unittest_pb2.TestAllTypes.BAR], + default_int32=800, + oneof_string='y') + self.assertTrue(isinstance(message, unittest_pb2.TestAllTypes)) + self.assertEqual(100, message.optional_int32) + self.assertEqual(200, message.optional_fixed32) + self.assertEqual(300.5, message.optional_float) + self.assertEqual(b'x', message.optional_bytes) + self.assertEqual(400, message.optionalgroup.a) + self.assertTrue(isinstance(message.optional_nested_message, + unittest_pb2.TestAllTypes.NestedMessage)) + self.assertEqual(500, message.optional_nested_message.bb) + self.assertEqual(unittest_pb2.TestAllTypes.BAZ, + message.optional_nested_enum) + self.assertEqual(2, len(message.repeatedgroup)) + self.assertEqual(600, message.repeatedgroup[0].a) + self.assertEqual(700, message.repeatedgroup[1].a) + self.assertEqual(2, len(message.repeated_nested_enum)) + self.assertEqual(unittest_pb2.TestAllTypes.FOO, + message.repeated_nested_enum[0]) + self.assertEqual(unittest_pb2.TestAllTypes.BAR, + message.repeated_nested_enum[1]) + self.assertEqual(800, message.default_int32) + self.assertEqual('y', message.oneof_string) + self.assertFalse(message.HasField('optional_int64')) + self.assertEqual(0, len(message.repeated_float)) + self.assertEqual(42, message.default_int64) + + message = unittest_pb2.TestAllTypes(optional_nested_enum=u'BAZ') + self.assertEqual(unittest_pb2.TestAllTypes.BAZ, + message.optional_nested_enum) + + with self.assertRaises(ValueError): + unittest_pb2.TestAllTypes( + optional_nested_message={'INVALID_NESTED_FIELD': 17}) + + with self.assertRaises(TypeError): + unittest_pb2.TestAllTypes( + optional_nested_message={'bb': 'INVALID_VALUE_TYPE'}) + + with self.assertRaises(ValueError): + unittest_pb2.TestAllTypes(optional_nested_enum='INVALID_LABEL') + + with self.assertRaises(ValueError): + unittest_pb2.TestAllTypes(repeated_nested_enum='FOO') + # Class to test proto3-only features/behavior (updated field presence & enums) class Proto3Test(unittest.TestCase): + # Utility method for comparing equality with a map. + def assertMapIterEquals(self, map_iter, dict_value): + # Avoid mutating caller's copy. + dict_value = dict(dict_value) + + for k, v in map_iter: + self.assertEqual(v, dict_value[k]) + del dict_value[k] + + self.assertEqual({}, dict_value) + def testFieldPresence(self): message = unittest_proto3_arena_pb2.TestAllTypes() # We can't test presence of non-repeated, non-submessage fields. with self.assertRaises(ValueError): - message.HasField("optional_int32") + message.HasField('optional_int32') with self.assertRaises(ValueError): - message.HasField("optional_float") + message.HasField('optional_float') with self.assertRaises(ValueError): - message.HasField("optional_string") + message.HasField('optional_string') with self.assertRaises(ValueError): - message.HasField("optional_bool") + message.HasField('optional_bool') # But we can still test presence of submessage fields. - self.assertFalse(message.HasField("optional_nested_message")) + self.assertFalse(message.HasField('optional_nested_message')) # As with proto2, we can't test presence of fields that don't exist, or # repeated fields. with self.assertRaises(ValueError): - message.HasField("field_doesnt_exist") + message.HasField('field_doesnt_exist') with self.assertRaises(ValueError): - message.HasField("repeated_int32") + message.HasField('repeated_int32') with self.assertRaises(ValueError): - message.HasField("repeated_nested_message") + message.HasField('repeated_nested_message') # Fields should default to their type-specific default. self.assertEqual(0, message.optional_int32) self.assertEqual(0, message.optional_float) - self.assertEqual("", message.optional_string) + self.assertEqual('', message.optional_string) self.assertEqual(False, message.optional_bool) self.assertEqual(0, message.optional_nested_message.bb) # Setting a submessage should still return proper presence information. message.optional_nested_message.bb = 0 - self.assertTrue(message.HasField("optional_nested_message")) + self.assertTrue(message.HasField('optional_nested_message')) # Set the fields to non-default values. message.optional_int32 = 5 message.optional_float = 1.1 - message.optional_string = "abc" + message.optional_string = 'abc' message.optional_bool = True message.optional_nested_message.bb = 15 # Clearing the fields unsets them and resets their value to default. - message.ClearField("optional_int32") - message.ClearField("optional_float") - message.ClearField("optional_string") - message.ClearField("optional_bool") - message.ClearField("optional_nested_message") + message.ClearField('optional_int32') + message.ClearField('optional_float') + message.ClearField('optional_string') + message.ClearField('optional_bool') + message.ClearField('optional_nested_message') self.assertEqual(0, message.optional_int32) self.assertEqual(0, message.optional_float) - self.assertEqual("", message.optional_string) + self.assertEqual('', message.optional_string) self.assertEqual(False, message.optional_bool) self.assertEqual(0, message.optional_nested_message.bb) @@ -1113,6 +1197,393 @@ class Proto3Test(unittest.TestCase): self.assertEqual(1234567, m2.optional_nested_enum) self.assertEqual(7654321, m2.repeated_nested_enum[0]) + # Map isn't really a proto3-only feature. But there is no proto2 equivalent + # of google/protobuf/map_unittest.proto right now, so it's not easy to + # test both with the same test like we do for the other proto2/proto3 tests. + # (google/protobuf/map_protobuf_unittest.proto is very different in the set + # of messages and fields it contains). + def testScalarMapDefaults(self): + msg = map_unittest_pb2.TestMap() + + # Scalars start out unset. + self.assertFalse(-123 in msg.map_int32_int32) + self.assertFalse(-2**33 in msg.map_int64_int64) + self.assertFalse(123 in msg.map_uint32_uint32) + self.assertFalse(2**33 in msg.map_uint64_uint64) + self.assertFalse('abc' in msg.map_string_string) + self.assertFalse(888 in msg.map_int32_enum) + + # Accessing an unset key returns the default. + self.assertEqual(0, msg.map_int32_int32[-123]) + self.assertEqual(0, msg.map_int64_int64[-2**33]) + self.assertEqual(0, msg.map_uint32_uint32[123]) + self.assertEqual(0, msg.map_uint64_uint64[2**33]) + self.assertEqual('', msg.map_string_string['abc']) + self.assertEqual(0, msg.map_int32_enum[888]) + + # It also sets the value in the map + self.assertTrue(-123 in msg.map_int32_int32) + self.assertTrue(-2**33 in msg.map_int64_int64) + self.assertTrue(123 in msg.map_uint32_uint32) + self.assertTrue(2**33 in msg.map_uint64_uint64) + self.assertTrue('abc' in msg.map_string_string) + self.assertTrue(888 in msg.map_int32_enum) + + self.assertTrue(isinstance(msg.map_string_string['abc'], unicode)) + + # Accessing an unset key still throws TypeError of the type of the key + # is incorrect. + with self.assertRaises(TypeError): + msg.map_string_string[123] + + self.assertFalse(123 in msg.map_string_string) + + def testMapGet(self): + # Need to test that get() properly returns the default, even though the dict + # has defaultdict-like semantics. + msg = map_unittest_pb2.TestMap() + + self.assertIsNone(msg.map_int32_int32.get(5)) + self.assertEquals(10, msg.map_int32_int32.get(5, 10)) + self.assertIsNone(msg.map_int32_int32.get(5)) + + msg.map_int32_int32[5] = 15 + self.assertEquals(15, msg.map_int32_int32.get(5)) + + self.assertIsNone(msg.map_int32_foreign_message.get(5)) + self.assertEquals(10, msg.map_int32_foreign_message.get(5, 10)) + + submsg = msg.map_int32_foreign_message[5] + self.assertIs(submsg, msg.map_int32_foreign_message.get(5)) + + def testScalarMap(self): + msg = map_unittest_pb2.TestMap() + + self.assertEqual(0, len(msg.map_int32_int32)) + self.assertFalse(5 in msg.map_int32_int32) + + msg.map_int32_int32[-123] = -456 + msg.map_int64_int64[-2**33] = -2**34 + msg.map_uint32_uint32[123] = 456 + msg.map_uint64_uint64[2**33] = 2**34 + msg.map_string_string['abc'] = '123' + msg.map_int32_enum[888] = 2 + + self.assertEqual([], msg.FindInitializationErrors()) + + self.assertEqual(1, len(msg.map_string_string)) + + # Bad key. + with self.assertRaises(TypeError): + msg.map_string_string[123] = '123' + + # Verify that trying to assign a bad key doesn't actually add a member to + # the map. + self.assertEqual(1, len(msg.map_string_string)) + + # Bad value. + with self.assertRaises(TypeError): + msg.map_string_string['123'] = 123 + + serialized = msg.SerializeToString() + msg2 = map_unittest_pb2.TestMap() + msg2.ParseFromString(serialized) + + # Bad key. + with self.assertRaises(TypeError): + msg2.map_string_string[123] = '123' + + # Bad value. + with self.assertRaises(TypeError): + msg2.map_string_string['123'] = 123 + + self.assertEqual(-456, msg2.map_int32_int32[-123]) + self.assertEqual(-2**34, msg2.map_int64_int64[-2**33]) + self.assertEqual(456, msg2.map_uint32_uint32[123]) + self.assertEqual(2**34, msg2.map_uint64_uint64[2**33]) + self.assertEqual('123', msg2.map_string_string['abc']) + self.assertEqual(2, msg2.map_int32_enum[888]) + + def testStringUnicodeConversionInMap(self): + msg = map_unittest_pb2.TestMap() + + unicode_obj = u'\u1234' + bytes_obj = unicode_obj.encode('utf8') + + msg.map_string_string[bytes_obj] = bytes_obj + + (key, value) = msg.map_string_string.items()[0] + + self.assertEqual(key, unicode_obj) + self.assertEqual(value, unicode_obj) + + self.assertTrue(isinstance(key, unicode)) + self.assertTrue(isinstance(value, unicode)) + + def testMessageMap(self): + msg = map_unittest_pb2.TestMap() + + self.assertEqual(0, len(msg.map_int32_foreign_message)) + self.assertFalse(5 in msg.map_int32_foreign_message) + + msg.map_int32_foreign_message[123] + # get_or_create() is an alias for getitem. + msg.map_int32_foreign_message.get_or_create(-456) + + self.assertEqual(2, len(msg.map_int32_foreign_message)) + self.assertIn(123, msg.map_int32_foreign_message) + self.assertIn(-456, msg.map_int32_foreign_message) + self.assertEqual(2, len(msg.map_int32_foreign_message)) + + # Bad key. + with self.assertRaises(TypeError): + msg.map_int32_foreign_message['123'] + + # Can't assign directly to submessage. + with self.assertRaises(ValueError): + msg.map_int32_foreign_message[999] = msg.map_int32_foreign_message[123] + + # Verify that trying to assign a bad key doesn't actually add a member to + # the map. + self.assertEqual(2, len(msg.map_int32_foreign_message)) + + serialized = msg.SerializeToString() + msg2 = map_unittest_pb2.TestMap() + msg2.ParseFromString(serialized) + + self.assertEqual(2, len(msg2.map_int32_foreign_message)) + self.assertIn(123, msg2.map_int32_foreign_message) + self.assertIn(-456, msg2.map_int32_foreign_message) + self.assertEqual(2, len(msg2.map_int32_foreign_message)) + + def testMergeFrom(self): + msg = map_unittest_pb2.TestMap() + msg.map_int32_int32[12] = 34 + msg.map_int32_int32[56] = 78 + msg.map_int64_int64[22] = 33 + msg.map_int32_foreign_message[111].c = 5 + msg.map_int32_foreign_message[222].c = 10 + + msg2 = map_unittest_pb2.TestMap() + msg2.map_int32_int32[12] = 55 + msg2.map_int64_int64[88] = 99 + msg2.map_int32_foreign_message[222].c = 15 + + msg2.MergeFrom(msg) + + self.assertEqual(34, msg2.map_int32_int32[12]) + self.assertEqual(78, msg2.map_int32_int32[56]) + self.assertEqual(33, msg2.map_int64_int64[22]) + self.assertEqual(99, msg2.map_int64_int64[88]) + self.assertEqual(5, msg2.map_int32_foreign_message[111].c) + self.assertEqual(10, msg2.map_int32_foreign_message[222].c) + + # Verify that there is only one entry per key, even though the MergeFrom + # may have internally created multiple entries for a single key in the + # list representation. + as_dict = {} + for key in msg2.map_int32_foreign_message: + self.assertFalse(key in as_dict) + as_dict[key] = msg2.map_int32_foreign_message[key].c + + self.assertEqual({111: 5, 222: 10}, as_dict) + + # Special case: test that delete of item really removes the item, even if + # there might have physically been duplicate keys due to the previous merge. + # This is only a special case for the C++ implementation which stores the + # map as an array. + del msg2.map_int32_int32[12] + self.assertFalse(12 in msg2.map_int32_int32) + + del msg2.map_int32_foreign_message[222] + self.assertFalse(222 in msg2.map_int32_foreign_message) + + def testIntegerMapWithLongs(self): + msg = map_unittest_pb2.TestMap() + msg.map_int32_int32[long(-123)] = long(-456) + msg.map_int64_int64[long(-2**33)] = long(-2**34) + msg.map_uint32_uint32[long(123)] = long(456) + msg.map_uint64_uint64[long(2**33)] = long(2**34) + + serialized = msg.SerializeToString() + msg2 = map_unittest_pb2.TestMap() + msg2.ParseFromString(serialized) + + self.assertEqual(-456, msg2.map_int32_int32[-123]) + self.assertEqual(-2**34, msg2.map_int64_int64[-2**33]) + self.assertEqual(456, msg2.map_uint32_uint32[123]) + self.assertEqual(2**34, msg2.map_uint64_uint64[2**33]) + + def testMapAssignmentCausesPresence(self): + msg = map_unittest_pb2.TestMapSubmessage() + msg.test_map.map_int32_int32[123] = 456 + + serialized = msg.SerializeToString() + msg2 = map_unittest_pb2.TestMapSubmessage() + msg2.ParseFromString(serialized) + + self.assertEqual(msg, msg2) + + # Now test that various mutations of the map properly invalidate the + # cached size of the submessage. + msg.test_map.map_int32_int32[888] = 999 + serialized = msg.SerializeToString() + msg2.ParseFromString(serialized) + self.assertEqual(msg, msg2) + + msg.test_map.map_int32_int32.clear() + serialized = msg.SerializeToString() + msg2.ParseFromString(serialized) + self.assertEqual(msg, msg2) + + def testMapAssignmentCausesPresenceForSubmessages(self): + msg = map_unittest_pb2.TestMapSubmessage() + msg.test_map.map_int32_foreign_message[123].c = 5 + + serialized = msg.SerializeToString() + msg2 = map_unittest_pb2.TestMapSubmessage() + msg2.ParseFromString(serialized) + + self.assertEqual(msg, msg2) + + # Now test that various mutations of the map properly invalidate the + # cached size of the submessage. + msg.test_map.map_int32_foreign_message[888].c = 7 + serialized = msg.SerializeToString() + msg2.ParseFromString(serialized) + self.assertEqual(msg, msg2) + + msg.test_map.map_int32_foreign_message[888].MergeFrom( + msg.test_map.map_int32_foreign_message[123]) + serialized = msg.SerializeToString() + msg2.ParseFromString(serialized) + self.assertEqual(msg, msg2) + + msg.test_map.map_int32_foreign_message.clear() + serialized = msg.SerializeToString() + msg2.ParseFromString(serialized) + self.assertEqual(msg, msg2) + + def testModifyMapWhileIterating(self): + msg = map_unittest_pb2.TestMap() + + string_string_iter = iter(msg.map_string_string) + int32_foreign_iter = iter(msg.map_int32_foreign_message) + + msg.map_string_string['abc'] = '123' + msg.map_int32_foreign_message[5].c = 5 + + with self.assertRaises(RuntimeError): + for key in string_string_iter: + pass + + with self.assertRaises(RuntimeError): + for key in int32_foreign_iter: + pass + + def testSubmessageMap(self): + msg = map_unittest_pb2.TestMap() + + submsg = msg.map_int32_foreign_message[111] + self.assertIs(submsg, msg.map_int32_foreign_message[111]) + self.assertTrue(isinstance(submsg, unittest_pb2.ForeignMessage)) + + submsg.c = 5 + + serialized = msg.SerializeToString() + msg2 = map_unittest_pb2.TestMap() + msg2.ParseFromString(serialized) + + self.assertEqual(5, msg2.map_int32_foreign_message[111].c) + + # Doesn't allow direct submessage assignment. + with self.assertRaises(ValueError): + msg.map_int32_foreign_message[88] = unittest_pb2.ForeignMessage() + + def testMapIteration(self): + msg = map_unittest_pb2.TestMap() + + for k, v in msg.map_int32_int32.iteritems(): + # Should not be reached. + self.assertTrue(False) + + msg.map_int32_int32[2] = 4 + msg.map_int32_int32[3] = 6 + msg.map_int32_int32[4] = 8 + self.assertEqual(3, len(msg.map_int32_int32)) + + matching_dict = {2: 4, 3: 6, 4: 8} + self.assertMapIterEquals(msg.map_int32_int32.iteritems(), matching_dict) + + def testMapIterationClearMessage(self): + # Iterator needs to work even if message and map are deleted. + msg = map_unittest_pb2.TestMap() + + msg.map_int32_int32[2] = 4 + msg.map_int32_int32[3] = 6 + msg.map_int32_int32[4] = 8 + + it = msg.map_int32_int32.iteritems() + del msg + + matching_dict = {2: 4, 3: 6, 4: 8} + self.assertMapIterEquals(it, matching_dict) + + def testMapConstruction(self): + msg = map_unittest_pb2.TestMap(map_int32_int32={1: 2, 3: 4}) + self.assertEqual(2, msg.map_int32_int32[1]) + self.assertEqual(4, msg.map_int32_int32[3]) + + msg = map_unittest_pb2.TestMap( + map_int32_foreign_message={3: unittest_pb2.ForeignMessage(c=5)}) + self.assertEqual(5, msg.map_int32_foreign_message[3].c) + + def testMapValidAfterFieldCleared(self): + # Map needs to work even if field is cleared. + # For the C++ implementation this tests the correctness of + # ScalarMapContainer::Release() + msg = map_unittest_pb2.TestMap() + map = msg.map_int32_int32 + + map[2] = 4 + map[3] = 6 + map[4] = 8 + + msg.ClearField('map_int32_int32') + matching_dict = {2: 4, 3: 6, 4: 8} + self.assertMapIterEquals(map.iteritems(), matching_dict) + + def testMapIterValidAfterFieldCleared(self): + # Map iterator needs to work even if field is cleared. + # For the C++ implementation this tests the correctness of + # ScalarMapContainer::Release() + msg = map_unittest_pb2.TestMap() + + msg.map_int32_int32[2] = 4 + msg.map_int32_int32[3] = 6 + msg.map_int32_int32[4] = 8 + + it = msg.map_int32_int32.iteritems() + + msg.ClearField('map_int32_int32') + matching_dict = {2: 4, 3: 6, 4: 8} + self.assertMapIterEquals(it, matching_dict) + + def testMapDelete(self): + msg = map_unittest_pb2.TestMap() + + self.assertEqual(0, len(msg.map_int32_int32)) + + msg.map_int32_int32[4] = 6 + self.assertEqual(1, len(msg.map_int32_int32)) + + with self.assertRaises(KeyError): + del msg.map_int32_int32[88] + + del msg.map_int32_int32[4] + self.assertEqual(0, len(msg.map_int32_int32)) + + class ValidTypeNamesTest(unittest.TestCase): diff --git a/python/google/protobuf/internal/proto_builder_test.py b/python/google/protobuf/internal/proto_builder_test.py index b1e57f35..edaf3fa3 100644 --- a/python/google/protobuf/internal/proto_builder_test.py +++ b/python/google/protobuf/internal/proto_builder_test.py @@ -32,6 +32,7 @@ """Tests for google.protobuf.proto_builder.""" +import collections import unittest from google.protobuf import descriptor_pb2 @@ -43,10 +44,11 @@ from google.protobuf import text_format class ProtoBuilderTest(unittest.TestCase): def setUp(self): - self._fields = { - 'foo': descriptor_pb2.FieldDescriptorProto.TYPE_INT64, - 'bar': descriptor_pb2.FieldDescriptorProto.TYPE_STRING, - } + self.ordered_fields = collections.OrderedDict([ + ('foo', descriptor_pb2.FieldDescriptorProto.TYPE_INT64), + ('bar', descriptor_pb2.FieldDescriptorProto.TYPE_STRING), + ]) + self._fields = dict(self.ordered_fields) def testMakeSimpleProtoClass(self): """Test that we can create a proto class.""" @@ -59,6 +61,17 @@ class ProtoBuilderTest(unittest.TestCase): self.assertMultiLineEqual( 'bar: "asdf"\nfoo: 12345\n', text_format.MessageToString(proto)) + def testOrderedFields(self): + """Test that the field order is maintained when given an OrderedDict.""" + proto_cls = proto_builder.MakeSimpleProtoClass( + self.ordered_fields, + full_name='net.proto2.python.public.proto_builder_test.OrderedTest') + proto = proto_cls() + proto.foo = 12345 + proto.bar = 'asdf' + self.assertMultiLineEqual( + 'foo: 12345\nbar: "asdf"\n', text_format.MessageToString(proto)) + def testMakeSameProtoClassTwice(self): """Test that the DescriptorPool is used.""" pool = descriptor_pool.DescriptorPool() diff --git a/python/google/protobuf/internal/python_message.py b/python/google/protobuf/internal/python_message.py index 54f584ae..ca9f7675 100755 --- a/python/google/protobuf/internal/python_message.py +++ b/python/google/protobuf/internal/python_message.py @@ -61,9 +61,11 @@ if sys.version_info[0] < 3: except ImportError: from StringIO import StringIO as BytesIO import copy_reg as copyreg + _basestring = basestring else: from io import BytesIO import copyreg + _basestring = str import struct import weakref @@ -77,6 +79,7 @@ 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 symbol_database from google.protobuf import text_format _FieldDescriptor = descriptor_mod.FieldDescriptor @@ -101,6 +104,7 @@ def InitMessage(descriptor, cls): for field in descriptor.fields: _AttachFieldHelpers(cls, field) + descriptor._concrete_class = cls # pylint: disable=protected-access _AddEnumValues(descriptor, cls) _AddInitMethod(descriptor, cls) _AddPropertiesForFields(descriptor, cls) @@ -198,12 +202,37 @@ def _IsMessageSetExtension(field): field.label == _FieldDescriptor.LABEL_OPTIONAL) +def _IsMapField(field): + return (field.type == _FieldDescriptor.TYPE_MESSAGE and + field.message_type.has_options and + field.message_type.GetOptions().map_entry) + + +def _IsMessageMapField(field): + value_type = field.message_type.fields_by_name["value"] + return value_type.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE + + 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): + is_packable = (is_repeated and + wire_format.IsTypePackable(field_descriptor.type)) + if not is_packable: + is_packed = False + elif field_descriptor.containing_type.syntax == "proto2": + is_packed = (field_descriptor.has_options and + field_descriptor.GetOptions().packed) + else: + has_packed_false = (field_descriptor.has_options and + field_descriptor.GetOptions().HasField("packed") and + field_descriptor.GetOptions().packed == False) + is_packed = not has_packed_false + is_map_entry = _IsMapField(field_descriptor) + + if is_map_entry: + field_encoder = encoder.MapEncoder(field_descriptor) + sizer = encoder.MapSizer(field_descriptor) + elif _IsMessageSetExtension(field_descriptor): field_encoder = encoder.MessageSetItemEncoder(field_descriptor.number) sizer = encoder.MessageSetItemSizer(field_descriptor.number) else: @@ -228,9 +257,16 @@ def _AttachFieldHelpers(cls, field_descriptor): if field_descriptor.containing_oneof is not None: oneof_descriptor = field_descriptor - field_decoder = type_checkers.TYPE_TO_DECODER[decode_type]( - field_descriptor.number, is_repeated, is_packed, - field_descriptor, field_descriptor._default_constructor) + if is_map_entry: + is_message_map = _IsMessageMapField(field_descriptor) + + field_decoder = decoder.MapDecoder( + field_descriptor, _GetInitializeDefaultForMap(field_descriptor), + is_message_map) + else: + field_decoder = type_checkers.TYPE_TO_DECODER[decode_type]( + field_descriptor.number, is_repeated, is_packed, + field_descriptor, field_descriptor._default_constructor) cls._decoders_by_tag[tag_bytes] = (field_decoder, oneof_descriptor) @@ -265,6 +301,26 @@ def _AddEnumValues(descriptor, cls): setattr(cls, enum_value.name, enum_value.number) +def _GetInitializeDefaultForMap(field): + if field.label != _FieldDescriptor.LABEL_REPEATED: + raise ValueError('map_entry set on non-repeated field %s' % ( + field.name)) + fields_by_name = field.message_type.fields_by_name + key_checker = type_checkers.GetTypeChecker(fields_by_name['key']) + + value_field = fields_by_name['value'] + if _IsMessageMapField(field): + def MakeMessageMapDefault(message): + return containers.MessageMap( + message._listener_for_children, value_field.message_type, key_checker) + return MakeMessageMapDefault + else: + value_checker = type_checkers.GetTypeChecker(value_field) + def MakePrimitiveMapDefault(message): + return containers.ScalarMap( + message._listener_for_children, key_checker, value_checker) + return MakePrimitiveMapDefault + def _DefaultValueConstructorForField(field): """Returns a function which returns a default value for a field. @@ -279,6 +335,9 @@ def _DefaultValueConstructorForField(field): value may refer back to |message| via a weak reference. """ + if _IsMapField(field): + return _GetInitializeDefaultForMap(field) + if field.label == _FieldDescriptor.LABEL_REPEATED: if field.has_default_value and field.default_value != []: raise ValueError('Repeated field default value not empty list: %s' % ( @@ -329,7 +388,22 @@ def _ReraiseTypeErrorWithFieldName(message_name, field_name): def _AddInitMethod(message_descriptor, cls): """Adds an __init__ method to cls.""" - fields = message_descriptor.fields + + def _GetIntegerEnumValue(enum_type, value): + """Convert a string or integer enum value to an integer. + + If the value is a string, it is converted to the enum value in + enum_type with the same name. If the value is not a string, it's + returned as-is. (No conversion or bounds-checking is done.) + """ + if isinstance(value, _basestring): + try: + return enum_type.values_by_name[value].number + except KeyError: + raise ValueError('Enum type %s: unknown label "%s"' % ( + enum_type.full_name, value)) + return value + def init(self, **kwargs): self._cached_byte_size = 0 self._cached_byte_size_dirty = len(kwargs) > 0 @@ -352,19 +426,37 @@ def _AddInitMethod(message_descriptor, cls): 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) + if _IsMapField(field): + if _IsMessageMapField(field): + for key in field_value: + copy[key].MergeFrom(field_value[key]) + else: + copy.update(field_value) + else: + for val in field_value: + if isinstance(val, dict): + copy.add(**val) + else: + copy.add().MergeFrom(val) else: # Scalar + if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM: + field_value = [_GetIntegerEnumValue(field.enum_type, val) + for val in field_value] copy.extend(field_value) self._fields[field] = copy elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: copy = field._default_constructor(self) + new_val = field_value + if isinstance(field_value, dict): + new_val = field.message_type._concrete_class(**field_value) try: - copy.MergeFrom(field_value) + copy.MergeFrom(new_val) except TypeError: _ReraiseTypeErrorWithFieldName(message_descriptor.name, field_name) self._fields[field] = copy else: + if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM: + field_value = _GetIntegerEnumValue(field.enum_type, field_value) try: setattr(self, field_name, field_value) except TypeError: @@ -758,6 +850,26 @@ def _AddHasExtensionMethod(cls): return extension_handle in self._fields cls.HasExtension = HasExtension +def _UnpackAny(msg): + type_url = msg.type_url + db = symbol_database.Default() + + if not type_url: + return None + + # TODO(haberman): For now we just strip the hostname. Better logic will be + # required. + type_name = type_url.split("/")[-1] + descriptor = db.pool.FindMessageTypeByName(type_name) + + if descriptor is None: + return None + + message_class = db.GetPrototype(descriptor) + message = message_class() + + message.ParseFromString(msg.value) + return message def _AddEqualsMethod(message_descriptor, cls): """Helper for _AddMessageMethods().""" @@ -769,6 +881,12 @@ def _AddEqualsMethod(message_descriptor, cls): if self is other: return True + if self.DESCRIPTOR.full_name == "google.protobuf.Any": + any_a = _UnpackAny(self) + any_b = _UnpackAny(other) + if any_a and any_b: + return any_a == any_b + if not self.ListFields() == other.ListFields(): return False @@ -961,6 +1079,9 @@ def _AddIsInitializedMethod(message_descriptor, cls): for field, value in list(self._fields.items()): # dict can change size! if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: if field.label == _FieldDescriptor.LABEL_REPEATED: + if (field.message_type.has_options and + field.message_type.GetOptions().map_entry): + continue for element in value: if not element.IsInitialized(): if errors is not None: @@ -996,16 +1117,26 @@ def _AddIsInitializedMethod(message_descriptor, cls): else: name = field.name - if field.label == _FieldDescriptor.LABEL_REPEATED: + if _IsMapField(field): + if _IsMessageMapField(field): + for key in value: + element = value[key] + prefix = "%s[%d]." % (name, key) + sub_errors = element.FindInitializationErrors() + errors += [prefix + error for error in sub_errors] + else: + # ScalarMaps can't have any initialization errors. + pass + elif 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 ] + errors += [prefix + error for error in sub_errors] else: prefix = name + "." sub_errors = value.FindInitializationErrors() - errors += [ prefix + error for error in sub_errors ] + errors += [prefix + error for error in sub_errors] return errors diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py index ae79c78b..4eca4989 100755 --- a/python/google/protobuf/internal/reflection_test.py +++ b/python/google/protobuf/internal/reflection_test.py @@ -39,8 +39,8 @@ import copy import gc import operator import struct -import unittest +import unittest from google.protobuf import unittest_import_pb2 from google.protobuf import unittest_mset_pb2 from google.protobuf import unittest_pb2 @@ -1798,8 +1798,8 @@ class ReflectionTest(unittest.TestCase): def testBadArguments(self): # Some of these assertions used to segfault. from google.protobuf.pyext import _message - self.assertRaises(TypeError, _message.Message._GetFieldDescriptor, 3) - self.assertRaises(TypeError, _message.Message._GetExtensionDescriptor, 42) + self.assertRaises(TypeError, _message.default_pool.FindFieldByName, 3) + self.assertRaises(TypeError, _message.default_pool.FindExtensionByName, 42) self.assertRaises(TypeError, unittest_pb2.TestAllTypes().__getattribute__, 42) diff --git a/python/google/protobuf/internal/service_reflection_test.py b/python/google/protobuf/internal/service_reflection_test.py index de462124..9967255a 100755 --- a/python/google/protobuf/internal/service_reflection_test.py +++ b/python/google/protobuf/internal/service_reflection_test.py @@ -35,7 +35,6 @@ __author__ = 'petar@google.com (Petar Petrov)' import unittest - from google.protobuf import unittest_pb2 from google.protobuf import service_reflection from google.protobuf import service @@ -81,7 +80,7 @@ class FooUnitTest(unittest.TestCase): self.assertEqual('Method Bar not implemented.', rpc_controller.failure_message) self.assertEqual(None, self.callback_response) - + class MyServiceImpl(unittest_pb2.TestService): def Foo(self, rpc_controller, request, done): self.foo_called = True diff --git a/python/google/protobuf/internal/symbol_database_test.py b/python/google/protobuf/internal/symbol_database_test.py index c888aff7..80b83bc2 100644 --- a/python/google/protobuf/internal/symbol_database_test.py +++ b/python/google/protobuf/internal/symbol_database_test.py @@ -33,7 +33,6 @@ """Tests for google.protobuf.symbol_database.""" import unittest - from google.protobuf import unittest_pb2 from google.protobuf import symbol_database diff --git a/python/google/protobuf/internal/test_util.py b/python/google/protobuf/internal/test_util.py index d84e3836..0cbdbad9 100755 --- a/python/google/protobuf/internal/test_util.py +++ b/python/google/protobuf/internal/test_util.py @@ -75,7 +75,8 @@ def SetAllNonLazyFields(message): message.optional_string = u'115' message.optional_bytes = b'116' - message.optionalgroup.a = 117 + if IsProto2(message): + message.optionalgroup.a = 117 message.optional_nested_message.bb = 118 message.optional_foreign_message.c = 119 message.optional_import_message.d = 120 @@ -109,7 +110,8 @@ def SetAllNonLazyFields(message): message.repeated_string.append(u'215') message.repeated_bytes.append(b'216') - message.repeatedgroup.add().a = 217 + if IsProto2(message): + message.repeatedgroup.add().a = 217 message.repeated_nested_message.add().bb = 218 message.repeated_foreign_message.add().c = 219 message.repeated_import_message.add().d = 220 @@ -140,7 +142,8 @@ def SetAllNonLazyFields(message): message.repeated_string.append(u'315') message.repeated_bytes.append(b'316') - message.repeatedgroup.add().a = 317 + if IsProto2(message): + message.repeatedgroup.add().a = 317 message.repeated_nested_message.add().bb = 318 message.repeated_foreign_message.add().c = 319 message.repeated_import_message.add().d = 320 @@ -396,7 +399,8 @@ def ExpectAllFieldsSet(test_case, message): test_case.assertTrue(message.HasField('optional_string')) test_case.assertTrue(message.HasField('optional_bytes')) - test_case.assertTrue(message.HasField('optionalgroup')) + if IsProto2(message): + test_case.assertTrue(message.HasField('optionalgroup')) test_case.assertTrue(message.HasField('optional_nested_message')) test_case.assertTrue(message.HasField('optional_foreign_message')) test_case.assertTrue(message.HasField('optional_import_message')) @@ -430,7 +434,8 @@ def ExpectAllFieldsSet(test_case, message): test_case.assertEqual('115', message.optional_string) test_case.assertEqual(b'116', message.optional_bytes) - test_case.assertEqual(117, message.optionalgroup.a) + if IsProto2(message): + test_case.assertEqual(117, message.optionalgroup.a) test_case.assertEqual(118, message.optional_nested_message.bb) test_case.assertEqual(119, message.optional_foreign_message.c) test_case.assertEqual(120, message.optional_import_message.d) @@ -463,7 +468,8 @@ def ExpectAllFieldsSet(test_case, message): test_case.assertEqual(2, len(message.repeated_string)) test_case.assertEqual(2, len(message.repeated_bytes)) - test_case.assertEqual(2, len(message.repeatedgroup)) + if IsProto2(message): + test_case.assertEqual(2, len(message.repeatedgroup)) test_case.assertEqual(2, len(message.repeated_nested_message)) test_case.assertEqual(2, len(message.repeated_foreign_message)) test_case.assertEqual(2, len(message.repeated_import_message)) @@ -491,7 +497,8 @@ def ExpectAllFieldsSet(test_case, message): test_case.assertEqual('215', message.repeated_string[0]) test_case.assertEqual(b'216', message.repeated_bytes[0]) - test_case.assertEqual(217, message.repeatedgroup[0].a) + if IsProto2(message): + test_case.assertEqual(217, message.repeatedgroup[0].a) test_case.assertEqual(218, message.repeated_nested_message[0].bb) test_case.assertEqual(219, message.repeated_foreign_message[0].c) test_case.assertEqual(220, message.repeated_import_message[0].d) @@ -521,7 +528,8 @@ def ExpectAllFieldsSet(test_case, message): test_case.assertEqual('315', message.repeated_string[1]) test_case.assertEqual(b'316', message.repeated_bytes[1]) - test_case.assertEqual(317, message.repeatedgroup[1].a) + if IsProto2(message): + test_case.assertEqual(317, message.repeatedgroup[1].a) test_case.assertEqual(318, message.repeated_nested_message[1].bb) test_case.assertEqual(319, message.repeated_foreign_message[1].c) test_case.assertEqual(320, message.repeated_import_message[1].d) diff --git a/python/google/protobuf/internal/text_encoding_test.py b/python/google/protobuf/internal/text_encoding_test.py index 27896b94..5df13b78 100755 --- a/python/google/protobuf/internal/text_encoding_test.py +++ b/python/google/protobuf/internal/text_encoding_test.py @@ -33,7 +33,6 @@ """Tests for google.protobuf.text_encoding.""" import unittest - from google.protobuf import text_encoding TEST_VALUES = [ diff --git a/python/google/protobuf/internal/text_format_test.py b/python/google/protobuf/internal/text_format_test.py index bf7e06ee..06bd1ee5 100755 --- a/python/google/protobuf/internal/text_format_test.py +++ b/python/google/protobuf/internal/text_format_test.py @@ -37,8 +37,10 @@ __author__ = 'kenton@google.com (Kenton Varda)' import re import unittest +import unittest from google.protobuf.internal import _parameterized +from google.protobuf import map_unittest_pb2 from google.protobuf import unittest_mset_pb2 from google.protobuf import unittest_pb2 from google.protobuf import unittest_proto3_arena_pb2 @@ -309,31 +311,6 @@ class TextFormatTest(TextFormatBase): r'"unknown_field".'), text_format.Parse, text, message) - def testParseGroupNotClosed(self, message_module): - message = message_module.TestAllTypes() - text = 'RepeatedGroup: <' - self.assertRaisesRegexp( - text_format.ParseError, '1:16 : Expected ">".', - text_format.Parse, text, message) - - text = 'RepeatedGroup: {' - self.assertRaisesRegexp( - text_format.ParseError, '1:16 : Expected "}".', - text_format.Parse, text, message) - - def testParseEmptyGroup(self, message_module): - message = message_module.TestAllTypes() - text = 'OptionalGroup: {}' - text_format.Parse(text, message) - self.assertTrue(message.HasField('optionalgroup')) - - message.Clear() - - message = message_module.TestAllTypes() - text = 'OptionalGroup: <>' - text_format.Parse(text, message) - self.assertTrue(message.HasField('optionalgroup')) - def testParseBadEnumValue(self, message_module): message = message_module.TestAllTypes() text = 'optional_nested_enum: BARR' @@ -408,6 +385,14 @@ class TextFormatTest(TextFormatBase): # Ideally the schemas would be made more similar so these tests could pass. class OnlyWorksWithProto2RightNowTests(TextFormatBase): + def testPrintAllFieldsPointy(self, message_module): + message = unittest_pb2.TestAllTypes() + test_util.SetAllFields(message) + self.CompareToGoldenFile( + self.RemoveRedundantZeros( + text_format.MessageToString(message, pointy_brackets=True)), + 'text_format_unittest_data_pointy_oneof.txt') + def testParseGolden(self): golden_text = '\n'.join(self.ReadGolden('text_format_unittest_data.txt')) parsed_message = unittest_pb2.TestAllTypes() @@ -471,8 +456,49 @@ class OnlyWorksWithProto2RightNowTests(TextFormatBase): test_util.SetAllFields(message) self.assertEquals(message, parsed_message) + def testPrintMap(self): + message = map_unittest_pb2.TestMap() -# Tests of proto2-only features (MessageSet and extensions). + message.map_int32_int32[-123] = -456 + message.map_int64_int64[-2**33] = -2**34 + message.map_uint32_uint32[123] = 456 + message.map_uint64_uint64[2**33] = 2**34 + message.map_string_string["abc"] = "123" + message.map_int32_foreign_message[111].c = 5 + + # Maps are serialized to text format using their underlying repeated + # representation. + self.CompareToGoldenText( + text_format.MessageToString(message), + 'map_int32_int32 {\n' + ' key: -123\n' + ' value: -456\n' + '}\n' + 'map_int64_int64 {\n' + ' key: -8589934592\n' + ' value: -17179869184\n' + '}\n' + 'map_uint32_uint32 {\n' + ' key: 123\n' + ' value: 456\n' + '}\n' + 'map_uint64_uint64 {\n' + ' key: 8589934592\n' + ' value: 17179869184\n' + '}\n' + 'map_string_string {\n' + ' key: "abc"\n' + ' value: "123"\n' + '}\n' + 'map_int32_foreign_message {\n' + ' key: 111\n' + ' value {\n' + ' c: 5\n' + ' }\n' + '}\n') + + +# Tests of proto2-only features (MessageSet, extensions, etc.). class Proto2Tests(TextFormatBase): def testPrintMessageSet(self): @@ -620,6 +646,69 @@ class Proto2Tests(TextFormatBase): 'have multiple "optional_int32" fields.'), text_format.Parse, text, message) + def testParseGroupNotClosed(self): + message = unittest_pb2.TestAllTypes() + text = 'RepeatedGroup: <' + self.assertRaisesRegexp( + text_format.ParseError, '1:16 : Expected ">".', + text_format.Parse, text, message) + text = 'RepeatedGroup: {' + self.assertRaisesRegexp( + text_format.ParseError, '1:16 : Expected "}".', + text_format.Parse, text, message) + + def testParseEmptyGroup(self): + message = unittest_pb2.TestAllTypes() + text = 'OptionalGroup: {}' + text_format.Parse(text, message) + self.assertTrue(message.HasField('optionalgroup')) + + message.Clear() + + message = unittest_pb2.TestAllTypes() + text = 'OptionalGroup: <>' + text_format.Parse(text, message) + self.assertTrue(message.HasField('optionalgroup')) + + # Maps aren't really proto2-only, but our test schema only has maps for + # proto2. + def testParseMap(self): + text = ('map_int32_int32 {\n' + ' key: -123\n' + ' value: -456\n' + '}\n' + 'map_int64_int64 {\n' + ' key: -8589934592\n' + ' value: -17179869184\n' + '}\n' + 'map_uint32_uint32 {\n' + ' key: 123\n' + ' value: 456\n' + '}\n' + 'map_uint64_uint64 {\n' + ' key: 8589934592\n' + ' value: 17179869184\n' + '}\n' + 'map_string_string {\n' + ' key: "abc"\n' + ' value: "123"\n' + '}\n' + 'map_int32_foreign_message {\n' + ' key: 111\n' + ' value {\n' + ' c: 5\n' + ' }\n' + '}\n') + message = map_unittest_pb2.TestMap() + text_format.Parse(text, message) + + self.assertEqual(-456, message.map_int32_int32[-123]) + self.assertEqual(-2**34, message.map_int64_int64[-2**33]) + self.assertEqual(456, message.map_uint32_uint32[123]) + self.assertEqual(2**34, message.map_uint64_uint64[2**33]) + self.assertEqual("123", message.map_string_string["abc"]) + self.assertEqual(5, message.map_int32_foreign_message[111].c) + class TokenizerTest(unittest.TestCase): diff --git a/python/google/protobuf/internal/type_checkers.py b/python/google/protobuf/internal/type_checkers.py index 76c056c4..f20e526a 100755 --- a/python/google/protobuf/internal/type_checkers.py +++ b/python/google/protobuf/internal/type_checkers.py @@ -129,6 +129,9 @@ class IntValueChecker(object): proposed_value = self._TYPE(proposed_value) return proposed_value + def DefaultValue(self): + return 0 + class EnumValueChecker(object): @@ -146,6 +149,9 @@ class EnumValueChecker(object): raise ValueError('Unknown enum value: %d' % proposed_value) return proposed_value + def DefaultValue(self): + return self._enum_type.values[0].number + class UnicodeValueChecker(object): @@ -171,6 +177,9 @@ class UnicodeValueChecker(object): (proposed_value)) return proposed_value + def DefaultValue(self): + return u"" + class Int32ValueChecker(IntValueChecker): # We're sure to use ints instead of longs here since comparison may be more diff --git a/python/google/protobuf/internal/unknown_fields_test.py b/python/google/protobuf/internal/unknown_fields_test.py index 9337ae8a..1b81ae79 100755 --- a/python/google/protobuf/internal/unknown_fields_test.py +++ b/python/google/protobuf/internal/unknown_fields_test.py @@ -36,7 +36,6 @@ __author__ = 'bohdank@google.com (Bohdan Koval)' import unittest - from google.protobuf import unittest_mset_pb2 from google.protobuf import unittest_pb2 from google.protobuf import unittest_proto3_arena_pb2 diff --git a/python/google/protobuf/internal/wire_format_test.py b/python/google/protobuf/internal/wire_format_test.py index 5cd7fcb9..78dc1167 100755 --- a/python/google/protobuf/internal/wire_format_test.py +++ b/python/google/protobuf/internal/wire_format_test.py @@ -35,7 +35,6 @@ __author__ = 'robinson@google.com (Will Robinson)' import unittest - from google.protobuf import message from google.protobuf.internal import wire_format diff --git a/python/google/protobuf/proto_builder.py b/python/google/protobuf/proto_builder.py index 1fa28f1a..7489cf63 100644 --- a/python/google/protobuf/proto_builder.py +++ b/python/google/protobuf/proto_builder.py @@ -30,6 +30,7 @@ """Dynamic Protobuf class creator.""" +import collections import hashlib import os @@ -59,7 +60,9 @@ def MakeSimpleProtoClass(fields, full_name, pool=None): Note: this doesn't validate field names! Args: - fields: dict of {name: field_type} mappings for each field in the proto. + fields: dict of {name: field_type} mappings for each field in the proto. If + this is an OrderedDict the order will be maintained, otherwise the + fields will be sorted by name. full_name: str, the fully-qualified name of the proto type. pool: optional DescriptorPool instance. Returns: @@ -73,12 +76,19 @@ def MakeSimpleProtoClass(fields, full_name, pool=None): # The factory's DescriptorPool doesn't know about this class yet. pass + # Get a list of (name, field_type) tuples from the fields dict. If fields was + # an OrderedDict we keep the order, but otherwise we sort the field to ensure + # consistent ordering. + field_items = fields.items() + if not isinstance(fields, collections.OrderedDict): + field_items = sorted(field_items) + # Use a consistent file name that is unlikely to conflict with any imported # proto files. fields_hash = hashlib.sha1() - for f_name, f_type in sorted(fields.items()): - fields_hash.update(f_name.encode('utf8')) - fields_hash.update(str(f_type).encode('utf8')) + for f_name, f_type in field_items: + fields_hash.update(f_name.encode('utf-8')) + fields_hash.update(str(f_type).encode('utf-8')) proto_file_name = fields_hash.hexdigest() + '.proto' package, name = full_name.rsplit('.', 1) @@ -87,7 +97,7 @@ def MakeSimpleProtoClass(fields, full_name, pool=None): file_proto.package = package desc_proto = file_proto.message_type.add() desc_proto.name = name - for f_number, (f_name, f_type) in enumerate(sorted(fields.items()), 1): + for f_number, (f_name, f_type) in enumerate(field_items, 1): field_proto = desc_proto.field.add() field_proto.name = f_name field_proto.number = f_number diff --git a/python/google/protobuf/pyext/descriptor.cc b/python/google/protobuf/pyext/descriptor.cc index e77d0bb9..2160757b 100644 --- a/python/google/protobuf/pyext/descriptor.cc +++ b/python/google/protobuf/pyext/descriptor.cc @@ -43,8 +43,6 @@ #include #include -#define C(str) const_cast(str) - #if PY_MAJOR_VERSION >= 3 #define PyString_FromStringAndSize PyUnicode_FromStringAndSize #define PyString_Check PyUnicode_Check @@ -257,8 +255,14 @@ namespace descriptor { // Creates or retrieve a Python descriptor of the specified type. // Objects are interned: the same descriptor will return the same object if it // was kept alive. +// 'was_created' is an optional pointer to a bool, and is set to true if a new +// object was allocated. // Always return a new reference. -PyObject* NewInternedDescriptor(PyTypeObject* type, const void* descriptor) { +PyObject* NewInternedDescriptor(PyTypeObject* type, const void* descriptor, + bool* was_created) { + if (was_created) { + *was_created = false; + } if (descriptor == NULL) { PyErr_BadInternalCall(); return NULL; @@ -283,6 +287,9 @@ PyObject* NewInternedDescriptor(PyTypeObject* type, const void* descriptor) { GetDescriptorPool()->interned_descriptors->insert( std::make_pair(descriptor, reinterpret_cast(py_descriptor))); + if (was_created) { + *was_created = true; + } return reinterpret_cast(py_descriptor); } @@ -298,9 +305,7 @@ static PyGetSetDef Getters[] = { PyTypeObject PyBaseDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - // Keep the fully qualified _message symbol in a line for opensource. - "google.protobuf.internal._message." - "DescriptorBase", // tp_name + FULL_MODULE_NAME ".DescriptorBase", // tp_name sizeof(PyBaseDescriptor), // tp_basicsize 0, // tp_itemsize (destructor)Dealloc, // tp_dealloc @@ -357,7 +362,7 @@ static PyObject* GetFullName(PyBaseDescriptor* self, void *closure) { } static PyObject* GetFile(PyBaseDescriptor *self, void *closure) { - return PyFileDescriptor_New(_GetDescriptor(self)->file()); + return PyFileDescriptor_FromDescriptor(_GetDescriptor(self)->file()); } static PyObject* GetConcreteClass(PyBaseDescriptor* self, void *closure) { @@ -367,17 +372,6 @@ static PyObject* GetConcreteClass(PyBaseDescriptor* self, void *closure) { return concrete_class; } -static int SetConcreteClass(PyBaseDescriptor *self, PyObject *value, - void *closure) { - // This attribute is also set from reflection.py. Check that it's actually a - // no-op. - if (value != cdescriptor_pool::GetMessageClass( - GetDescriptorPool(), _GetDescriptor(self))) { - PyErr_SetString(PyExc_AttributeError, "Cannot change _concrete_class"); - } - return 0; -} - static PyObject* GetFieldsByName(PyBaseDescriptor* self, void *closure) { return NewMessageFieldsByName(_GetDescriptor(self)); } @@ -452,7 +446,7 @@ static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) { const Descriptor* containing_type = _GetDescriptor(self)->containing_type(); if (containing_type) { - return PyMessageDescriptor_New(containing_type); + return PyMessageDescriptor_FromDescriptor(containing_type); } else { Py_RETURN_NONE; } @@ -515,29 +509,34 @@ static PyObject* GetSyntax(PyBaseDescriptor *self, void *closure) { } static PyGetSetDef Getters[] = { - { C("name"), (getter)GetName, NULL, "Last name", NULL}, - { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL}, - { C("_concrete_class"), (getter)GetConcreteClass, (setter)SetConcreteClass, "concrete class", NULL}, - { C("file"), (getter)GetFile, NULL, "File descriptor", NULL}, - - { C("fields"), (getter)GetFieldsSeq, NULL, "Fields sequence", NULL}, - { C("fields_by_name"), (getter)GetFieldsByName, NULL, "Fields by name", NULL}, - { C("fields_by_number"), (getter)GetFieldsByNumber, NULL, "Fields by number", NULL}, - { C("nested_types"), (getter)GetNestedTypesSeq, NULL, "Nested types sequence", NULL}, - { C("nested_types_by_name"), (getter)GetNestedTypesByName, NULL, "Nested types by name", NULL}, - { C("extensions"), (getter)GetExtensions, NULL, "Extensions Sequence", NULL}, - { C("extensions_by_name"), (getter)GetExtensionsByName, NULL, "Extensions by name", NULL}, - { C("extension_ranges"), (getter)GetExtensionRanges, NULL, "Extension ranges", NULL}, - { C("enum_types"), (getter)GetEnumsSeq, NULL, "Enum sequence", NULL}, - { C("enum_types_by_name"), (getter)GetEnumTypesByName, NULL, "Enum types by name", NULL}, - { C("enum_values_by_name"), (getter)GetEnumValuesByName, NULL, "Enum values by name", NULL}, - { C("oneofs_by_name"), (getter)GetOneofsByName, NULL, "Oneofs by name", NULL}, - { C("oneofs"), (getter)GetOneofsSeq, NULL, "Oneofs by name", NULL}, - { C("containing_type"), (getter)GetContainingType, (setter)SetContainingType, "Containing type", NULL}, - { C("is_extendable"), (getter)IsExtendable, (setter)NULL, NULL, NULL}, - { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL}, - { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL}, - { C("syntax"), (getter)GetSyntax, (setter)NULL, "Syntax", NULL}, + { "name", (getter)GetName, NULL, "Last name"}, + { "full_name", (getter)GetFullName, NULL, "Full name"}, + { "_concrete_class", (getter)GetConcreteClass, NULL, "concrete class"}, + { "file", (getter)GetFile, NULL, "File descriptor"}, + + { "fields", (getter)GetFieldsSeq, NULL, "Fields sequence"}, + { "fields_by_name", (getter)GetFieldsByName, NULL, "Fields by name"}, + { "fields_by_number", (getter)GetFieldsByNumber, NULL, "Fields by number"}, + { "nested_types", (getter)GetNestedTypesSeq, NULL, "Nested types sequence"}, + { "nested_types_by_name", (getter)GetNestedTypesByName, NULL, + "Nested types by name"}, + { "extensions", (getter)GetExtensions, NULL, "Extensions Sequence"}, + { "extensions_by_name", (getter)GetExtensionsByName, NULL, + "Extensions by name"}, + { "extension_ranges", (getter)GetExtensionRanges, NULL, "Extension ranges"}, + { "enum_types", (getter)GetEnumsSeq, NULL, "Enum sequence"}, + { "enum_types_by_name", (getter)GetEnumTypesByName, NULL, + "Enum types by name"}, + { "enum_values_by_name", (getter)GetEnumValuesByName, NULL, + "Enum values by name"}, + { "oneofs_by_name", (getter)GetOneofsByName, NULL, "Oneofs by name"}, + { "oneofs", (getter)GetOneofsSeq, NULL, "Oneofs by name"}, + { "containing_type", (getter)GetContainingType, (setter)SetContainingType, + "Containing type"}, + { "is_extendable", (getter)IsExtendable, (setter)NULL}, + { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, + { "_options", (getter)NULL, (setter)SetOptions, "Options"}, + { "syntax", (getter)GetSyntax, (setter)NULL, "Syntax"}, {NULL} }; @@ -552,9 +551,7 @@ static PyMethodDef Methods[] = { PyTypeObject PyMessageDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - // Keep the fully qualified _message symbol in a line for opensource. - C("google.protobuf.internal._message." - "MessageDescriptor"), // tp_name + FULL_MODULE_NAME ".MessageDescriptor", // tp_name sizeof(PyBaseDescriptor), // tp_basicsize 0, // tp_itemsize 0, // tp_dealloc @@ -573,7 +570,7 @@ PyTypeObject PyMessageDescriptor_Type = { 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - C("A Message Descriptor"), // tp_doc + "A Message Descriptor", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare @@ -586,10 +583,10 @@ PyTypeObject PyMessageDescriptor_Type = { &descriptor::PyBaseDescriptor_Type, // tp_base }; -PyObject* PyMessageDescriptor_New( +PyObject* PyMessageDescriptor_FromDescriptor( const Descriptor* message_descriptor) { return descriptor::NewInternedDescriptor( - &PyMessageDescriptor_Type, message_descriptor); + &PyMessageDescriptor_Type, message_descriptor, NULL); } const Descriptor* PyMessageDescriptor_AsDescriptor(PyObject* obj) { @@ -715,7 +712,7 @@ static PyObject* GetCDescriptor(PyObject *self, void *closure) { static PyObject *GetEnumType(PyBaseDescriptor *self, void *closure) { const EnumDescriptor* enum_type = _GetDescriptor(self)->enum_type(); if (enum_type) { - return PyEnumDescriptor_New(enum_type); + return PyEnumDescriptor_FromDescriptor(enum_type); } else { Py_RETURN_NONE; } @@ -728,7 +725,7 @@ static int SetEnumType(PyBaseDescriptor *self, PyObject *value, void *closure) { static PyObject *GetMessageType(PyBaseDescriptor *self, void *closure) { const Descriptor* message_type = _GetDescriptor(self)->message_type(); if (message_type) { - return PyMessageDescriptor_New(message_type); + return PyMessageDescriptor_FromDescriptor(message_type); } else { Py_RETURN_NONE; } @@ -743,7 +740,7 @@ static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) { const Descriptor* containing_type = _GetDescriptor(self)->containing_type(); if (containing_type) { - return PyMessageDescriptor_New(containing_type); + return PyMessageDescriptor_FromDescriptor(containing_type); } else { Py_RETURN_NONE; } @@ -758,7 +755,7 @@ static PyObject* GetExtensionScope(PyBaseDescriptor *self, void *closure) { const Descriptor* extension_scope = _GetDescriptor(self)->extension_scope(); if (extension_scope) { - return PyMessageDescriptor_New(extension_scope); + return PyMessageDescriptor_FromDescriptor(extension_scope); } else { Py_RETURN_NONE; } @@ -768,7 +765,7 @@ static PyObject* GetContainingOneof(PyBaseDescriptor *self, void *closure) { const OneofDescriptor* containing_oneof = _GetDescriptor(self)->containing_oneof(); if (containing_oneof) { - return PyOneofDescriptor_New(containing_oneof); + return PyOneofDescriptor_FromDescriptor(containing_oneof); } else { Py_RETURN_NONE; } @@ -803,26 +800,30 @@ static int SetOptions(PyBaseDescriptor *self, PyObject *value, static PyGetSetDef Getters[] = { - { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL}, - { C("name"), (getter)GetName, NULL, "Unqualified name", NULL}, - { C("type"), (getter)GetType, NULL, "C++ Type", NULL}, - { C("cpp_type"), (getter)GetCppType, NULL, "C++ Type", NULL}, - { C("label"), (getter)GetLabel, NULL, "Label", NULL}, - { C("number"), (getter)GetNumber, NULL, "Number", NULL}, - { C("index"), (getter)GetIndex, NULL, "Index", NULL}, - { C("default_value"), (getter)GetDefaultValue, NULL, "Default Value", NULL}, - { C("has_default_value"), (getter)HasDefaultValue, NULL, NULL, NULL}, - { C("is_extension"), (getter)IsExtension, NULL, "ID", NULL}, - { C("id"), (getter)GetID, NULL, "ID", NULL}, - { C("_cdescriptor"), (getter)GetCDescriptor, NULL, "HAACK REMOVE ME", NULL}, - - { C("message_type"), (getter)GetMessageType, (setter)SetMessageType, "Message type", NULL}, - { C("enum_type"), (getter)GetEnumType, (setter)SetEnumType, "Enum type", NULL}, - { C("containing_type"), (getter)GetContainingType, (setter)SetContainingType, "Containing type", NULL}, - { C("extension_scope"), (getter)GetExtensionScope, (setter)NULL, "Extension scope", NULL}, - { C("containing_oneof"), (getter)GetContainingOneof, (setter)SetContainingOneof, "Containing oneof", NULL}, - { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL}, - { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL}, + { "full_name", (getter)GetFullName, NULL, "Full name"}, + { "name", (getter)GetName, NULL, "Unqualified name"}, + { "type", (getter)GetType, NULL, "C++ Type"}, + { "cpp_type", (getter)GetCppType, NULL, "C++ Type"}, + { "label", (getter)GetLabel, NULL, "Label"}, + { "number", (getter)GetNumber, NULL, "Number"}, + { "index", (getter)GetIndex, NULL, "Index"}, + { "default_value", (getter)GetDefaultValue, NULL, "Default Value"}, + { "has_default_value", (getter)HasDefaultValue}, + { "is_extension", (getter)IsExtension, NULL, "ID"}, + { "id", (getter)GetID, NULL, "ID"}, + { "_cdescriptor", (getter)GetCDescriptor, NULL, "HAACK REMOVE ME"}, + + { "message_type", (getter)GetMessageType, (setter)SetMessageType, + "Message type"}, + { "enum_type", (getter)GetEnumType, (setter)SetEnumType, "Enum type"}, + { "containing_type", (getter)GetContainingType, (setter)SetContainingType, + "Containing type"}, + { "extension_scope", (getter)GetExtensionScope, (setter)NULL, + "Extension scope"}, + { "containing_oneof", (getter)GetContainingOneof, (setter)SetContainingOneof, + "Containing oneof"}, + { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, + { "_options", (getter)NULL, (setter)SetOptions, "Options"}, {NULL} }; @@ -835,8 +836,7 @@ static PyMethodDef Methods[] = { PyTypeObject PyFieldDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - C("google.protobuf.internal." - "_message.FieldDescriptor"), // tp_name + FULL_MODULE_NAME ".FieldDescriptor", // tp_name sizeof(PyBaseDescriptor), // tp_basicsize 0, // tp_itemsize 0, // tp_dealloc @@ -855,7 +855,7 @@ PyTypeObject PyFieldDescriptor_Type = { 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - C("A Field Descriptor"), // tp_doc + "A Field Descriptor", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare @@ -868,10 +868,10 @@ PyTypeObject PyFieldDescriptor_Type = { &descriptor::PyBaseDescriptor_Type, // tp_base }; -PyObject* PyFieldDescriptor_New( +PyObject* PyFieldDescriptor_FromDescriptor( const FieldDescriptor* field_descriptor) { return descriptor::NewInternedDescriptor( - &PyFieldDescriptor_Type, field_descriptor); + &PyFieldDescriptor_Type, field_descriptor, NULL); } const FieldDescriptor* PyFieldDescriptor_AsDescriptor(PyObject* obj) { @@ -900,7 +900,7 @@ static PyObject* GetName(PyBaseDescriptor *self, void *closure) { } static PyObject* GetFile(PyBaseDescriptor *self, void *closure) { - return PyFileDescriptor_New(_GetDescriptor(self)->file()); + return PyFileDescriptor_FromDescriptor(_GetDescriptor(self)->file()); } static PyObject* GetEnumvaluesByName(PyBaseDescriptor* self, void *closure) { @@ -919,7 +919,7 @@ static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) { const Descriptor* containing_type = _GetDescriptor(self)->containing_type(); if (containing_type) { - return PyMessageDescriptor_New(containing_type); + return PyMessageDescriptor_FromDescriptor(containing_type); } else { Py_RETURN_NONE; } @@ -964,16 +964,19 @@ static PyMethodDef Methods[] = { }; static PyGetSetDef Getters[] = { - { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL}, - { C("name"), (getter)GetName, NULL, "last name", NULL}, - { C("file"), (getter)GetFile, NULL, "File descriptor", NULL}, - { C("values"), (getter)GetEnumvaluesSeq, NULL, "values", NULL}, - { C("values_by_name"), (getter)GetEnumvaluesByName, NULL, "Enumvalues by name", NULL}, - { C("values_by_number"), (getter)GetEnumvaluesByNumber, NULL, "Enumvalues by number", NULL}, - - { C("containing_type"), (getter)GetContainingType, (setter)SetContainingType, "Containing type", NULL}, - { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL}, - { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL}, + { "full_name", (getter)GetFullName, NULL, "Full name"}, + { "name", (getter)GetName, NULL, "last name"}, + { "file", (getter)GetFile, NULL, "File descriptor"}, + { "values", (getter)GetEnumvaluesSeq, NULL, "values"}, + { "values_by_name", (getter)GetEnumvaluesByName, NULL, + "Enum values by name"}, + { "values_by_number", (getter)GetEnumvaluesByNumber, NULL, + "Enum values by number"}, + + { "containing_type", (getter)GetContainingType, (setter)SetContainingType, + "Containing type"}, + { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, + { "_options", (getter)NULL, (setter)SetOptions, "Options"}, {NULL} }; @@ -981,9 +984,7 @@ static PyGetSetDef Getters[] = { PyTypeObject PyEnumDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - // Keep the fully qualified _message symbol in a line for opensource. - C("google.protobuf.internal._message." - "EnumDescriptor"), // tp_name + FULL_MODULE_NAME ".EnumDescriptor", // tp_name sizeof(PyBaseDescriptor), // tp_basicsize 0, // tp_itemsize 0, // tp_dealloc @@ -1002,7 +1003,7 @@ PyTypeObject PyEnumDescriptor_Type = { 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - C("A Enum Descriptor"), // tp_doc + "A Enum Descriptor", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare @@ -1015,10 +1016,10 @@ PyTypeObject PyEnumDescriptor_Type = { &descriptor::PyBaseDescriptor_Type, // tp_base }; -PyObject* PyEnumDescriptor_New( +PyObject* PyEnumDescriptor_FromDescriptor( const EnumDescriptor* enum_descriptor) { return descriptor::NewInternedDescriptor( - &PyEnumDescriptor_Type, enum_descriptor); + &PyEnumDescriptor_Type, enum_descriptor, NULL); } namespace enumvalue_descriptor { @@ -1042,7 +1043,7 @@ static PyObject* GetIndex(PyBaseDescriptor *self, void *closure) { } static PyObject* GetType(PyBaseDescriptor *self, void *closure) { - return PyEnumDescriptor_New(_GetDescriptor(self)->type()); + return PyEnumDescriptor_FromDescriptor(_GetDescriptor(self)->type()); } static PyObject* GetHasOptions(PyBaseDescriptor *self, void *closure) { @@ -1069,13 +1070,13 @@ static int SetOptions(PyBaseDescriptor *self, PyObject *value, static PyGetSetDef Getters[] = { - { C("name"), (getter)GetName, NULL, "name", NULL}, - { C("number"), (getter)GetNumber, NULL, "number", NULL}, - { C("index"), (getter)GetIndex, NULL, "index", NULL}, - { C("type"), (getter)GetType, NULL, "index", NULL}, + { "name", (getter)GetName, NULL, "name"}, + { "number", (getter)GetNumber, NULL, "number"}, + { "index", (getter)GetIndex, NULL, "index"}, + { "type", (getter)GetType, NULL, "index"}, - { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL}, - { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL}, + { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, + { "_options", (getter)NULL, (setter)SetOptions, "Options"}, {NULL} }; @@ -1088,8 +1089,7 @@ static PyMethodDef Methods[] = { PyTypeObject PyEnumValueDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - C("google.protobuf.internal." - "_message.EnumValueDescriptor"), // tp_name + FULL_MODULE_NAME ".EnumValueDescriptor", // tp_name sizeof(PyBaseDescriptor), // tp_basicsize 0, // tp_itemsize 0, // tp_dealloc @@ -1108,7 +1108,7 @@ PyTypeObject PyEnumValueDescriptor_Type = { 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - C("A EnumValue Descriptor"), // tp_doc + "A EnumValue Descriptor", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare @@ -1121,10 +1121,10 @@ PyTypeObject PyEnumValueDescriptor_Type = { &descriptor::PyBaseDescriptor_Type, // tp_base }; -PyObject* PyEnumValueDescriptor_New( +PyObject* PyEnumValueDescriptor_FromDescriptor( const EnumValueDescriptor* enumvalue_descriptor) { return descriptor::NewInternedDescriptor( - &PyEnumValueDescriptor_Type, enumvalue_descriptor); + &PyEnumValueDescriptor_Type, enumvalue_descriptor, NULL); } namespace file_descriptor { @@ -1218,18 +1218,20 @@ static PyObject* CopyToProto(PyFileDescriptor *self, PyObject *target) { } static PyGetSetDef Getters[] = { - { C("name"), (getter)GetName, NULL, "name", NULL}, - { C("package"), (getter)GetPackage, NULL, "package", NULL}, - { C("serialized_pb"), (getter)GetSerializedPb, NULL, NULL, NULL}, - { C("message_types_by_name"), (getter)GetMessageTypesByName, NULL, "Messages by name", NULL}, - { C("enum_types_by_name"), (getter)GetEnumTypesByName, NULL, "Enums by name", NULL}, - { C("extensions_by_name"), (getter)GetExtensionsByName, NULL, "Extensions by name", NULL}, - { C("dependencies"), (getter)GetDependencies, NULL, "Dependencies", NULL}, - { C("public_dependencies"), (getter)GetPublicDependencies, NULL, "Dependencies", NULL}, - - { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL}, - { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL}, - { C("syntax"), (getter)GetSyntax, (setter)NULL, "Syntax", NULL}, + { "name", (getter)GetName, NULL, "name"}, + { "package", (getter)GetPackage, NULL, "package"}, + { "serialized_pb", (getter)GetSerializedPb}, + { "message_types_by_name", (getter)GetMessageTypesByName, NULL, + "Messages by name"}, + { "enum_types_by_name", (getter)GetEnumTypesByName, NULL, "Enums by name"}, + { "extensions_by_name", (getter)GetExtensionsByName, NULL, + "Extensions by name"}, + { "dependencies", (getter)GetDependencies, NULL, "Dependencies"}, + { "public_dependencies", (getter)GetPublicDependencies, NULL, "Dependencies"}, + + { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, + { "_options", (getter)NULL, (setter)SetOptions, "Options"}, + { "syntax", (getter)GetSyntax, (setter)NULL, "Syntax"}, {NULL} }; @@ -1243,11 +1245,10 @@ static PyMethodDef Methods[] = { PyTypeObject PyFileDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - C("google.protobuf.internal." - "_message.FileDescriptor"), // tp_name + FULL_MODULE_NAME ".FileDescriptor", // tp_name sizeof(PyFileDescriptor), // tp_basicsize 0, // tp_itemsize - (destructor)file_descriptor::Dealloc, // tp_dealloc + (destructor)file_descriptor::Dealloc, // tp_dealloc 0, // tp_print 0, // tp_getattr 0, // tp_setattr @@ -1263,7 +1264,7 @@ PyTypeObject PyFileDescriptor_Type = { 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - C("A File Descriptor"), // tp_doc + "A File Descriptor", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare @@ -1284,23 +1285,28 @@ PyTypeObject PyFileDescriptor_Type = { PyObject_Del, // tp_free }; -PyObject* PyFileDescriptor_New(const FileDescriptor* file_descriptor) { - return descriptor::NewInternedDescriptor( - &PyFileDescriptor_Type, file_descriptor); +PyObject* PyFileDescriptor_FromDescriptor( + const FileDescriptor* file_descriptor) { + return PyFileDescriptor_FromDescriptorWithSerializedPb(file_descriptor, + NULL); } -PyObject* PyFileDescriptor_NewWithPb( +PyObject* PyFileDescriptor_FromDescriptorWithSerializedPb( const FileDescriptor* file_descriptor, PyObject *serialized_pb) { - PyObject* py_descriptor = PyFileDescriptor_New(file_descriptor); + bool was_created; + PyObject* py_descriptor = descriptor::NewInternedDescriptor( + &PyFileDescriptor_Type, file_descriptor, &was_created); if (py_descriptor == NULL) { return NULL; } - if (serialized_pb != NULL) { + if (was_created) { PyFileDescriptor* cfile_descriptor = reinterpret_cast(py_descriptor); Py_XINCREF(serialized_pb); cfile_descriptor->serialized_pb = serialized_pb; } + // TODO(amauryfa): In the case of a cached object, check that serialized_pb + // is the same as before. return py_descriptor; } @@ -1333,19 +1339,19 @@ static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) { const Descriptor* containing_type = _GetDescriptor(self)->containing_type(); if (containing_type) { - return PyMessageDescriptor_New(containing_type); + return PyMessageDescriptor_FromDescriptor(containing_type); } else { Py_RETURN_NONE; } } static PyGetSetDef Getters[] = { - { C("name"), (getter)GetName, NULL, "Name", NULL}, - { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL}, - { C("index"), (getter)GetIndex, NULL, "Index", NULL}, + { "name", (getter)GetName, NULL, "Name"}, + { "full_name", (getter)GetFullName, NULL, "Full name"}, + { "index", (getter)GetIndex, NULL, "Index"}, - { C("containing_type"), (getter)GetContainingType, NULL, "Containing type", NULL}, - { C("fields"), (getter)GetFields, NULL, "Fields", NULL}, + { "containing_type", (getter)GetContainingType, NULL, "Containing type"}, + { "fields", (getter)GetFields, NULL, "Fields"}, {NULL} }; @@ -1353,8 +1359,7 @@ static PyGetSetDef Getters[] = { PyTypeObject PyOneofDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - C("google.protobuf.internal." - "_message.OneofDescriptor"), // tp_name + FULL_MODULE_NAME ".OneofDescriptor", // tp_name sizeof(PyBaseDescriptor), // tp_basicsize 0, // tp_itemsize 0, // tp_dealloc @@ -1373,7 +1378,7 @@ PyTypeObject PyOneofDescriptor_Type = { 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - C("A Oneof Descriptor"), // tp_doc + "A Oneof Descriptor", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare @@ -1386,10 +1391,10 @@ PyTypeObject PyOneofDescriptor_Type = { &descriptor::PyBaseDescriptor_Type, // tp_base }; -PyObject* PyOneofDescriptor_New( +PyObject* PyOneofDescriptor_FromDescriptor( const OneofDescriptor* oneof_descriptor) { return descriptor::NewInternedDescriptor( - &PyOneofDescriptor_Type, oneof_descriptor); + &PyOneofDescriptor_Type, oneof_descriptor, NULL); } // Add a enum values to a type dictionary. diff --git a/python/google/protobuf/pyext/descriptor.h b/python/google/protobuf/pyext/descriptor.h index ba6e7298..b2550406 100644 --- a/python/google/protobuf/pyext/descriptor.h +++ b/python/google/protobuf/pyext/descriptor.h @@ -48,21 +48,24 @@ extern PyTypeObject PyEnumValueDescriptor_Type; extern PyTypeObject PyFileDescriptor_Type; extern PyTypeObject PyOneofDescriptor_Type; -// Return a new reference to a Descriptor object. +// Wraps a Descriptor in a Python object. // The C++ pointer is usually borrowed from the global DescriptorPool. // In any case, it must stay alive as long as the Python object. -PyObject* PyMessageDescriptor_New(const Descriptor* descriptor); -PyObject* PyFieldDescriptor_New(const FieldDescriptor* descriptor); -PyObject* PyEnumDescriptor_New(const EnumDescriptor* descriptor); -PyObject* PyEnumValueDescriptor_New(const EnumValueDescriptor* descriptor); -PyObject* PyOneofDescriptor_New(const OneofDescriptor* descriptor); -PyObject* PyFileDescriptor_New(const FileDescriptor* file_descriptor); +// Returns a new reference. +PyObject* PyMessageDescriptor_FromDescriptor(const Descriptor* descriptor); +PyObject* PyFieldDescriptor_FromDescriptor(const FieldDescriptor* descriptor); +PyObject* PyEnumDescriptor_FromDescriptor(const EnumDescriptor* descriptor); +PyObject* PyEnumValueDescriptor_FromDescriptor( + const EnumValueDescriptor* descriptor); +PyObject* PyOneofDescriptor_FromDescriptor(const OneofDescriptor* descriptor); +PyObject* PyFileDescriptor_FromDescriptor( + const FileDescriptor* file_descriptor); // Alternate constructor of PyFileDescriptor, used when we already have a // serialized FileDescriptorProto that can be cached. // Returns a new reference. -PyObject* PyFileDescriptor_NewWithPb(const FileDescriptor* file_descriptor, - PyObject* serialized_pb); +PyObject* PyFileDescriptor_FromDescriptorWithSerializedPb( + const FileDescriptor* file_descriptor, PyObject* serialized_pb); // Return the C++ descriptor pointer. // This function checks the parameter type; on error, return NULL with a Python diff --git a/python/google/protobuf/pyext/descriptor_containers.cc b/python/google/protobuf/pyext/descriptor_containers.cc index 06edebf8..92e11e31 100644 --- a/python/google/protobuf/pyext/descriptor_containers.cc +++ b/python/google/protobuf/pyext/descriptor_containers.cc @@ -898,7 +898,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyFieldDescriptor_New(item); + return PyFieldDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -956,7 +956,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyMessageDescriptor_New(item); + return PyMessageDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1006,7 +1006,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyEnumDescriptor_New(item); + return PyEnumDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1082,7 +1082,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyEnumValueDescriptor_New(item); + return PyEnumValueDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1124,7 +1124,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyFieldDescriptor_New(item); + return PyFieldDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1174,7 +1174,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyOneofDescriptor_New(item); + return PyOneofDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1238,7 +1238,7 @@ static ItemDescriptor GetByNumber(PyContainer* self, int number) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyEnumValueDescriptor_New(item); + return PyEnumValueDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1302,7 +1302,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyFieldDescriptor_New(item); + return PyFieldDescriptor_FromDescriptor(item); } static int GetItemIndex(ItemDescriptor item) { @@ -1354,7 +1354,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyMessageDescriptor_New(item); + return PyMessageDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1400,7 +1400,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyEnumDescriptor_New(item); + return PyEnumDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1446,7 +1446,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyFieldDescriptor_New(item); + return PyFieldDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1488,7 +1488,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyFileDescriptor_New(item); + return PyFileDescriptor_FromDescriptor(item); } static DescriptorContainerDef ContainerDef = { @@ -1522,7 +1522,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyFileDescriptor_New(item); + return PyFileDescriptor_FromDescriptor(item); } static DescriptorContainerDef ContainerDef = { diff --git a/python/google/protobuf/pyext/descriptor_containers.h b/python/google/protobuf/pyext/descriptor_containers.h index d81537de..8fbdaff9 100644 --- a/python/google/protobuf/pyext/descriptor_containers.h +++ b/python/google/protobuf/pyext/descriptor_containers.h @@ -28,6 +28,9 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_CONTAINERS_H__ +#define GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_CONTAINERS_H__ + // Mappings and Sequences of descriptors. // They implement containers like fields_by_name, EnumDescriptor.values... // See descriptor_containers.cc for more description. @@ -92,4 +95,6 @@ PyObject* NewFilePublicDependencies(const FileDescriptor* descriptor); } // namespace python } // namespace protobuf + } // namespace google +#endif // GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_CONTAINERS_H__ diff --git a/python/google/protobuf/pyext/descriptor_pool.cc b/python/google/protobuf/pyext/descriptor_pool.cc index bc3077bc..ecd90847 100644 --- a/python/google/protobuf/pyext/descriptor_pool.cc +++ b/python/google/protobuf/pyext/descriptor_pool.cc @@ -35,10 +35,9 @@ #include #include #include +#include #include -#define C(str) const_cast(str) - #if PY_MAJOR_VERSION >= 3 #define PyString_FromStringAndSize PyUnicode_FromStringAndSize #if PY_VERSION_HEX < 0x03030000 @@ -108,11 +107,11 @@ PyObject* FindMessageByName(PyDescriptorPool* self, PyObject* arg) { self->pool->FindMessageTypeByName(string(name, name_size)); if (message_descriptor == NULL) { - PyErr_Format(PyExc_TypeError, "Couldn't find message %.200s", name); + PyErr_Format(PyExc_KeyError, "Couldn't find message %.200s", name); return NULL; } - return PyMessageDescriptor_New(message_descriptor); + return PyMessageDescriptor_FromDescriptor(message_descriptor); } // Add a message class to our database. @@ -158,6 +157,24 @@ PyObject *GetMessageClass(PyDescriptorPool* self, } } +PyObject* FindFileByName(PyDescriptorPool* self, PyObject* arg) { + Py_ssize_t name_size; + char* name; + if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) { + return NULL; + } + + const FileDescriptor* file_descriptor = + self->pool->FindFileByName(string(name, name_size)); + if (file_descriptor == NULL) { + PyErr_Format(PyExc_KeyError, "Couldn't find file %.200s", + name); + return NULL; + } + + return PyFileDescriptor_FromDescriptor(file_descriptor); +} + PyObject* FindFieldByName(PyDescriptorPool* self, PyObject* arg) { Py_ssize_t name_size; char* name; @@ -168,12 +185,12 @@ PyObject* FindFieldByName(PyDescriptorPool* self, PyObject* arg) { const FieldDescriptor* field_descriptor = self->pool->FindFieldByName(string(name, name_size)); if (field_descriptor == NULL) { - PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s", + PyErr_Format(PyExc_KeyError, "Couldn't find field %.200s", name); return NULL; } - return PyFieldDescriptor_New(field_descriptor); + return PyFieldDescriptor_FromDescriptor(field_descriptor); } PyObject* FindExtensionByName(PyDescriptorPool* self, PyObject* arg) { @@ -186,11 +203,11 @@ PyObject* FindExtensionByName(PyDescriptorPool* self, PyObject* arg) { const FieldDescriptor* field_descriptor = self->pool->FindExtensionByName(string(name, name_size)); if (field_descriptor == NULL) { - PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s", name); + PyErr_Format(PyExc_KeyError, "Couldn't find extension field %.200s", name); return NULL; } - return PyFieldDescriptor_New(field_descriptor); + return PyFieldDescriptor_FromDescriptor(field_descriptor); } PyObject* FindEnumTypeByName(PyDescriptorPool* self, PyObject* arg) { @@ -203,11 +220,11 @@ PyObject* FindEnumTypeByName(PyDescriptorPool* self, PyObject* arg) { const EnumDescriptor* enum_descriptor = self->pool->FindEnumTypeByName(string(name, name_size)); if (enum_descriptor == NULL) { - PyErr_Format(PyExc_TypeError, "Couldn't find enum %.200s", name); + PyErr_Format(PyExc_KeyError, "Couldn't find enum %.200s", name); return NULL; } - return PyEnumDescriptor_New(enum_descriptor); + return PyEnumDescriptor_FromDescriptor(enum_descriptor); } PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg) { @@ -220,70 +237,13 @@ PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg) { const OneofDescriptor* oneof_descriptor = self->pool->FindOneofByName(string(name, name_size)); if (oneof_descriptor == NULL) { - PyErr_Format(PyExc_TypeError, "Couldn't find oneof %.200s", name); + PyErr_Format(PyExc_KeyError, "Couldn't find oneof %.200s", name); return NULL; } - return PyOneofDescriptor_New(oneof_descriptor); + return PyOneofDescriptor_FromDescriptor(oneof_descriptor); } -static PyMethodDef Methods[] = { - { C("FindFieldByName"), - (PyCFunction)FindFieldByName, - METH_O, - C("Searches for a field descriptor by full name.") }, - { C("FindExtensionByName"), - (PyCFunction)FindExtensionByName, - METH_O, - C("Searches for extension descriptor by full name.") }, - {NULL} -}; - -} // namespace cdescriptor_pool - -PyTypeObject PyDescriptorPool_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - C("google.protobuf.internal." - "_message.DescriptorPool"), // tp_name - sizeof(PyDescriptorPool), // tp_basicsize - 0, // tp_itemsize - (destructor)cdescriptor_pool::Dealloc, // 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 - cdescriptor_pool::Methods, // 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 - 0, // tp_alloc - 0, // tp_new - PyObject_Del, // tp_free -}; - // The code below loads new Descriptors from a serialized FileDescriptorProto. @@ -301,6 +261,7 @@ class BuildFileErrorCollector : public DescriptorPool::ErrorCollector { if (!had_errors) { error_message += ("Invalid proto descriptor for file \"" + filename + "\":\n"); + had_errors = true; } // As this only happens on failure and will result in the program not // running at all, no effort is made to optimize this string manipulation. @@ -311,7 +272,7 @@ class BuildFileErrorCollector : public DescriptorPool::ErrorCollector { bool had_errors; }; -PyObject* Python_BuildFile(PyObject* ignored, PyObject* serialized_pb) { +PyObject* AddSerializedFile(PyDescriptorPool* self, PyObject* serialized_pb) { char* message_type; Py_ssize_t message_len; @@ -330,13 +291,14 @@ PyObject* Python_BuildFile(PyObject* ignored, PyObject* serialized_pb) { const FileDescriptor* generated_file = DescriptorPool::generated_pool()->FindFileByName(file_proto.name()); if (generated_file != NULL) { - return PyFileDescriptor_NewWithPb(generated_file, serialized_pb); + return PyFileDescriptor_FromDescriptorWithSerializedPb( + generated_file, serialized_pb); } BuildFileErrorCollector error_collector; const FileDescriptor* descriptor = - GetDescriptorPool()->pool->BuildFileCollectingErrors(file_proto, - &error_collector); + self->pool->BuildFileCollectingErrors(file_proto, + &error_collector); if (descriptor == NULL) { PyErr_Format(PyExc_TypeError, "Couldn't build proto file into descriptor pool!\n%s", @@ -344,9 +306,84 @@ PyObject* Python_BuildFile(PyObject* ignored, PyObject* serialized_pb) { return NULL; } - return PyFileDescriptor_NewWithPb(descriptor, serialized_pb); + return PyFileDescriptor_FromDescriptorWithSerializedPb( + descriptor, serialized_pb); +} + +PyObject* Add(PyDescriptorPool* self, PyObject* file_descriptor_proto) { + ScopedPyObjectPtr serialized_pb( + PyObject_CallMethod(file_descriptor_proto, "SerializeToString", NULL)); + if (serialized_pb == NULL) { + return NULL; + } + return AddSerializedFile(self, serialized_pb); } +static PyMethodDef Methods[] = { + { "Add", (PyCFunction)Add, METH_O, + "Adds the FileDescriptorProto and its types to this pool." }, + { "AddSerializedFile", (PyCFunction)AddSerializedFile, METH_O, + "Adds a serialized FileDescriptorProto to this pool." }, + + { "FindFileByName", (PyCFunction)FindFileByName, METH_O, + "Searches for a file descriptor by its .proto name." }, + { "FindMessageTypeByName", (PyCFunction)FindMessageByName, METH_O, + "Searches for a message descriptor by full name." }, + { "FindFieldByName", (PyCFunction)FindFieldByName, METH_O, + "Searches for a field descriptor by full name." }, + { "FindExtensionByName", (PyCFunction)FindExtensionByName, METH_O, + "Searches for extension descriptor by full name." }, + { "FindEnumTypeByName", (PyCFunction)FindEnumTypeByName, METH_O, + "Searches for enum type descriptor by full name." }, + { "FindOneofByName", (PyCFunction)FindOneofByName, METH_O, + "Searches for oneof descriptor by full name." }, + {NULL} +}; + +} // namespace cdescriptor_pool + +PyTypeObject PyDescriptorPool_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + FULL_MODULE_NAME ".DescriptorPool", // tp_name + sizeof(PyDescriptorPool), // tp_basicsize + 0, // tp_itemsize + (destructor)cdescriptor_pool::Dealloc, // 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 + "A Descriptor Pool", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + cdescriptor_pool::Methods, // 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 + 0, // tp_alloc + 0, // tp_new + PyObject_Del, // tp_free +}; + static PyDescriptorPool* global_cdescriptor_pool = NULL; bool InitDescriptorPool() { diff --git a/python/google/protobuf/pyext/descriptor_pool.h b/python/google/protobuf/pyext/descriptor_pool.h index 4e494b89..efb1abeb 100644 --- a/python/google/protobuf/pyext/descriptor_pool.h +++ b/python/google/protobuf/pyext/descriptor_pool.h @@ -95,6 +95,8 @@ const Descriptor* FindMessageTypeByName(PyDescriptorPool* self, const Descriptor* RegisterMessageClass( PyDescriptorPool* self, PyObject* message_class, PyObject* descriptor); +// The function below are also exposed as methods of the DescriptorPool type. + // Retrieves the Python class registered with the given message descriptor. // // Returns a *borrowed* reference if found, otherwise returns NULL with an @@ -134,12 +136,8 @@ PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg); } // namespace cdescriptor_pool -// Implement the Python "_BuildFile" method, it takes a serialized -// FileDescriptorProto, and adds it to the C++ DescriptorPool. -// It returns a new FileDescriptor object, or NULL when an exception is raised. -PyObject* Python_BuildFile(PyObject* ignored, PyObject* args); - // Retrieve the global descriptor pool owned by the _message module. +// Returns a *borrowed* reference. PyDescriptorPool* GetDescriptorPool(); // Initialize objects used by this module. diff --git a/python/google/protobuf/pyext/extension_dict.cc b/python/google/protobuf/pyext/extension_dict.cc index 8e38fc42..b8d18f8d 100644 --- a/python/google/protobuf/pyext/extension_dict.cc +++ b/python/google/protobuf/pyext/extension_dict.cc @@ -51,16 +51,6 @@ namespace python { namespace extension_dict { -// TODO(tibell): Always use self->message for clarity, just like in -// RepeatedCompositeContainer. -static Message* GetMessage(ExtensionDict* self) { - if (self->parent != NULL) { - return self->parent->message; - } else { - return self->message; - } -} - PyObject* len(ExtensionDict* self) { #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(PyDict_Size(self->values)); @@ -89,7 +79,7 @@ int ReleaseExtension(ExtensionDict* self, } } else if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { if (cmessage::ReleaseSubMessage( - GetMessage(self), descriptor, + self->parent, descriptor, reinterpret_cast(extension)) < 0) { return -1; } @@ -109,7 +99,7 @@ PyObject* subscript(ExtensionDict* self, PyObject* key) { if (descriptor->label() != FieldDescriptor::LABEL_REPEATED && descriptor->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { - return cmessage::InternalGetScalar(self->parent, descriptor); + return cmessage::InternalGetScalar(self->parent->message, descriptor); } PyObject* value = PyDict_GetItem(self->values, key); @@ -266,8 +256,7 @@ static PyMethodDef Methods[] = { PyTypeObject ExtensionDict_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - "google.protobuf.internal." - "cpp._message.ExtensionDict", // tp_name + FULL_MODULE_NAME ".ExtensionDict", // tp_name sizeof(ExtensionDict), // tp_basicsize 0, // tp_itemsize (destructor)extension_dict::dealloc, // tp_dealloc diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc index a2b357b2..a4843e8d 100644 --- a/python/google/protobuf/pyext/message.cc +++ b/python/google/protobuf/pyext/message.cc @@ -59,6 +59,8 @@ #include #include #include +#include +#include #include #include @@ -93,9 +95,9 @@ static const FieldDescriptor* GetFieldDescriptor( static const Descriptor* GetMessageDescriptor(PyTypeObject* cls); static string GetMessageName(CMessage* self); int InternalReleaseFieldByDescriptor( + CMessage* self, const FieldDescriptor* field_descriptor, - PyObject* composite_field, - Message* parent_message); + PyObject* composite_field); } // namespace cmessage // --------------------------------------------------------------------- @@ -127,10 +129,29 @@ static int VisitCompositeField(const FieldDescriptor* descriptor, Visitor visitor) { if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) { if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - RepeatedCompositeContainer* container = - reinterpret_cast(child); - if (visitor.VisitRepeatedCompositeContainer(container) == -1) - return -1; + if (descriptor->is_map()) { + const Descriptor* entry_type = descriptor->message_type(); + const FieldDescriptor* value_type = + entry_type->FindFieldByName("value"); + if (value_type->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + MessageMapContainer* container = + reinterpret_cast(child); + if (visitor.VisitMessageMapContainer(container) == -1) { + return -1; + } + } else { + ScalarMapContainer* container = + reinterpret_cast(child); + if (visitor.VisitScalarMapContainer(container) == -1) { + return -1; + } + } + } else { + RepeatedCompositeContainer* container = + reinterpret_cast(child); + if (visitor.VisitRepeatedCompositeContainer(container) == -1) + return -1; + } } else { RepeatedScalarContainer* container = reinterpret_cast(child); @@ -444,7 +465,7 @@ static int MaybeReleaseOverlappingOneofField( } if (InternalReleaseFieldByDescriptor( - existing_field, child_message, message) < 0) { + cmessage, existing_field, child_message) < 0) { return -1; } return PyDict_DelItemString(cmessage->composite_fields, field_name); @@ -483,6 +504,16 @@ struct FixupMessageReference : public ChildVisitor { return 0; } + int VisitScalarMapContainer(ScalarMapContainer* container) { + container->message = message_; + return 0; + } + + int VisitMessageMapContainer(MessageMapContainer* container) { + container->message = message_; + return 0; + } + private: Message* message_; }; @@ -500,6 +531,9 @@ int AssureWritable(CMessage* self) { self->message->GetDescriptor()); self->message = prototype->New(); self->owner.reset(self->message); + // Cascade the new owner to eventual children: even if this message is + // empty, some submessages or repeated containers might exist already. + SetOwner(self, self->owner); } else { // Otherwise, we need a mutable child message. if (AssureWritable(self->parent) == -1) @@ -520,8 +554,9 @@ int AssureWritable(CMessage* self) { // When a CMessage is made writable its Message pointer is updated // to point to a new mutable Message. When that happens we need to // update any references to the old, read-only CMessage. There are - // three places such references occur: RepeatedScalarContainer, - // RepeatedCompositeContainer, and ExtensionDict. + // five places such references occur: RepeatedScalarContainer, + // RepeatedCompositeContainer, ScalarMapContainer, MessageMapContainer, + // and ExtensionDict. if (self->extensions != NULL) self->extensions->message = self->message; if (ForEachCompositeField(self, FixupMessageReference(self->message)) == -1) @@ -583,15 +618,43 @@ const FieldDescriptor* GetExtensionDescriptor(PyObject* extension) { return PyFieldDescriptor_AsDescriptor(extension); } +// If value is a string, convert it into an enum value based on the labels in +// descriptor, otherwise simply return value. Always returns a new reference. +static PyObject* GetIntegerEnumValue(const FieldDescriptor& descriptor, + PyObject* value) { + if (PyString_Check(value) || PyUnicode_Check(value)) { + const EnumDescriptor* enum_descriptor = descriptor.enum_type(); + if (enum_descriptor == NULL) { + PyErr_SetString(PyExc_TypeError, "not an enum field"); + return NULL; + } + char* enum_label; + Py_ssize_t size; + if (PyString_AsStringAndSize(value, &enum_label, &size) < 0) { + return NULL; + } + const EnumValueDescriptor* enum_value_descriptor = + enum_descriptor->FindValueByName(string(enum_label, size)); + if (enum_value_descriptor == NULL) { + PyErr_SetString(PyExc_ValueError, "unknown enum label"); + return NULL; + } + return PyInt_FromLong(enum_value_descriptor->number()); + } + Py_INCREF(value); + return value; +} + // If cmessage_list is not NULL, this function releases values into the // container CMessages instead of just removing. Repeated composite container // needs to do this to make sure CMessages stay alive if they're still // referenced after deletion. Repeated scalar container doesn't need to worry. int InternalDeleteRepeatedField( - Message* message, + CMessage* self, const FieldDescriptor* field_descriptor, PyObject* slice, PyObject* cmessage_list) { + Message* message = self->message; Py_ssize_t length, from, to, step, slice_length; const Reflection* reflection = message->GetReflection(); int min, max; @@ -665,7 +728,7 @@ int InternalDeleteRepeatedField( CMessage* last_cmessage = reinterpret_cast( PyList_GET_ITEM(cmessage_list, PyList_GET_SIZE(cmessage_list) - 1)); repeated_composite_container::ReleaseLastTo( - field_descriptor, message, last_cmessage); + self, field_descriptor, last_cmessage); if (PySequence_DelItem(cmessage_list, -1) < 0) { return -1; } @@ -696,16 +759,90 @@ int InitAttributes(CMessage* self, PyObject* kwargs) { PyString_AsString(name)); return -1; } - if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) { + if (descriptor->is_map()) { + ScopedPyObjectPtr map(GetAttr(self, name)); + const FieldDescriptor* value_descriptor = + descriptor->message_type()->FindFieldByName("value"); + if (value_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + Py_ssize_t map_pos = 0; + PyObject* map_key; + PyObject* map_value; + while (PyDict_Next(value, &map_pos, &map_key, &map_value)) { + ScopedPyObjectPtr function_return; + function_return.reset(PyObject_GetItem(map.get(), map_key)); + if (function_return.get() == NULL) { + return -1; + } + ScopedPyObjectPtr ok(PyObject_CallMethod( + function_return.get(), "MergeFrom", "O", map_value)); + if (ok.get() == NULL) { + return -1; + } + } + } else { + ScopedPyObjectPtr function_return; + function_return.reset( + PyObject_CallMethod(map.get(), "update", "O", value)); + if (function_return.get() == NULL) { + return -1; + } + } + } else if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) { ScopedPyObjectPtr container(GetAttr(self, name)); if (container == NULL) { return -1; } if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - if (repeated_composite_container::Extend( - reinterpret_cast(container.get()), - value) - == NULL) { + RepeatedCompositeContainer* rc_container = + reinterpret_cast(container.get()); + ScopedPyObjectPtr iter(PyObject_GetIter(value)); + if (iter == NULL) { + PyErr_SetString(PyExc_TypeError, "Value must be iterable"); + return -1; + } + ScopedPyObjectPtr next; + while ((next.reset(PyIter_Next(iter))) != NULL) { + PyObject* kwargs = (PyDict_Check(next) ? next.get() : NULL); + ScopedPyObjectPtr new_msg( + repeated_composite_container::Add(rc_container, NULL, kwargs)); + if (new_msg == NULL) { + return -1; + } + if (kwargs == NULL) { + // next was not a dict, it's a message we need to merge + ScopedPyObjectPtr merged( + MergeFrom(reinterpret_cast(new_msg.get()), next)); + if (merged == NULL) { + return -1; + } + } + } + if (PyErr_Occurred()) { + // Check to see how PyIter_Next() exited. + return -1; + } + } else if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + RepeatedScalarContainer* rs_container = + reinterpret_cast(container.get()); + ScopedPyObjectPtr iter(PyObject_GetIter(value)); + if (iter == NULL) { + PyErr_SetString(PyExc_TypeError, "Value must be iterable"); + return -1; + } + ScopedPyObjectPtr next; + while ((next.reset(PyIter_Next(iter))) != NULL) { + ScopedPyObjectPtr enum_value(GetIntegerEnumValue(*descriptor, next)); + if (enum_value == NULL) { + return -1; + } + ScopedPyObjectPtr new_msg( + repeated_scalar_container::Append(rs_container, enum_value)); + if (new_msg == NULL) { + return -1; + } + } + if (PyErr_Occurred()) { + // Check to see how PyIter_Next() exited. return -1; } } else { @@ -721,12 +858,26 @@ int InitAttributes(CMessage* self, PyObject* kwargs) { if (message == NULL) { return -1; } - if (MergeFrom(reinterpret_cast(message.get()), - value) == NULL) { - return -1; + CMessage* cmessage = reinterpret_cast(message.get()); + if (PyDict_Check(value)) { + if (InitAttributes(cmessage, value) < 0) { + return -1; + } + } else { + ScopedPyObjectPtr merged(MergeFrom(cmessage, value)); + if (merged == NULL) { + return -1; + } } } else { - if (SetAttr(self, name, value) < 0) { + ScopedPyObjectPtr new_val; + if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + new_val.reset(GetIntegerEnumValue(*descriptor, value)); + if (new_val == NULL) { + return -1; + } + } + if (SetAttr(self, name, (new_val == NULL) ? value : new_val) < 0) { return -1; } } @@ -789,7 +940,6 @@ static PyObject* New(PyTypeObject* type, } self->message = default_message->New(); self->owner.reset(self->message); - return reinterpret_cast(self); } @@ -830,6 +980,16 @@ struct ClearWeakReferences : public ChildVisitor { return 0; } + int VisitScalarMapContainer(ScalarMapContainer* container) { + container->parent = NULL; + return 0; + } + + int VisitMessageMapContainer(MessageMapContainer* container) { + container->parent = NULL; + return 0; + } + int VisitCMessage(CMessage* cmessage, const FieldDescriptor* field_descriptor) { cmessage->parent = NULL; @@ -1064,6 +1224,16 @@ struct SetOwnerVisitor : public ChildVisitor { return 0; } + int VisitScalarMapContainer(ScalarMapContainer* container) { + scalar_map_container::SetOwner(container, new_owner_); + return 0; + } + + int VisitMessageMapContainer(MessageMapContainer* container) { + message_map_container::SetOwner(container, new_owner_); + return 0; + } + int VisitCMessage(CMessage* cmessage, const FieldDescriptor* field_descriptor) { return SetOwner(cmessage, new_owner_); @@ -1084,11 +1254,11 @@ int SetOwner(CMessage* self, const shared_ptr& new_owner) { // Releases the message specified by 'field' and returns the // pointer. If the field does not exist a new message is created using // 'descriptor'. The caller takes ownership of the returned pointer. -Message* ReleaseMessage(Message* message, +Message* ReleaseMessage(CMessage* self, const Descriptor* descriptor, const FieldDescriptor* field_descriptor) { - Message* released_message = message->GetReflection()->ReleaseMessage( - message, field_descriptor, message_factory); + Message* released_message = self->message->GetReflection()->ReleaseMessage( + self->message, field_descriptor, message_factory); // ReleaseMessage will return NULL which differs from // child_cmessage->message, if the field does not exist. In this case, // the latter points to the default instance via a const_cast<>, so we @@ -1102,12 +1272,12 @@ Message* ReleaseMessage(Message* message, return released_message; } -int ReleaseSubMessage(Message* message, +int ReleaseSubMessage(CMessage* self, const FieldDescriptor* field_descriptor, CMessage* child_cmessage) { // Release the Message shared_ptr released_message(ReleaseMessage( - message, child_cmessage->message->GetDescriptor(), field_descriptor)); + self, child_cmessage->message->GetDescriptor(), field_descriptor)); child_cmessage->message = released_message.get(); child_cmessage->owner.swap(released_message); child_cmessage->parent = NULL; @@ -1119,8 +1289,8 @@ int ReleaseSubMessage(Message* message, struct ReleaseChild : public ChildVisitor { // message must outlive this object. - explicit ReleaseChild(Message* parent_message) : - parent_message_(parent_message) {} + explicit ReleaseChild(CMessage* parent) : + parent_(parent) {} int VisitRepeatedCompositeContainer(RepeatedCompositeContainer* container) { return repeated_composite_container::Release( @@ -1132,23 +1302,33 @@ struct ReleaseChild : public ChildVisitor { reinterpret_cast(container)); } + int VisitScalarMapContainer(ScalarMapContainer* container) { + return scalar_map_container::Release( + reinterpret_cast(container)); + } + + int VisitMessageMapContainer(MessageMapContainer* container) { + return message_map_container::Release( + reinterpret_cast(container)); + } + int VisitCMessage(CMessage* cmessage, const FieldDescriptor* field_descriptor) { - return ReleaseSubMessage(parent_message_, field_descriptor, + return ReleaseSubMessage(parent_, field_descriptor, reinterpret_cast(cmessage)); } - Message* parent_message_; + CMessage* parent_; }; int InternalReleaseFieldByDescriptor( + CMessage* self, const FieldDescriptor* field_descriptor, - PyObject* composite_field, - Message* parent_message) { + PyObject* composite_field) { return VisitCompositeField( field_descriptor, composite_field, - ReleaseChild(parent_message)); + ReleaseChild(self)); } PyObject* ClearFieldByDescriptor( @@ -1200,8 +1380,8 @@ PyObject* ClearField(CMessage* self, PyObject* arg) { // Only release the field if there's a possibility that there are // references to it. if (composite_field != NULL) { - if (InternalReleaseFieldByDescriptor(field_descriptor, - composite_field, message) < 0) { + if (InternalReleaseFieldByDescriptor(self, field_descriptor, + composite_field) < 0) { return NULL; } PyDict_DelItem(self->composite_fields, arg); @@ -1219,7 +1399,7 @@ PyObject* ClearField(CMessage* self, PyObject* arg) { PyObject* Clear(CMessage* self) { AssureWritable(self); - if (ForEachCompositeField(self, ReleaseChild(self->message)) == -1) + if (ForEachCompositeField(self, ReleaseChild(self)) == -1) return NULL; // The old ExtensionDict still aliases this CMessage, but all its @@ -1582,7 +1762,8 @@ static PyObject* ListFields(CMessage* self) { } if (fields[i]->is_extension()) { - ScopedPyObjectPtr extension_field(PyFieldDescriptor_New(fields[i])); + ScopedPyObjectPtr extension_field( + PyFieldDescriptor_FromDescriptor(fields[i])); if (extension_field == NULL) { return NULL; } @@ -1616,7 +1797,8 @@ static PyObject* ListFields(CMessage* self) { PyErr_SetString(PyExc_ValueError, "bad string"); return NULL; } - ScopedPyObjectPtr field_descriptor(PyFieldDescriptor_New(fields[i])); + ScopedPyObjectPtr field_descriptor( + PyFieldDescriptor_FromDescriptor(fields[i])); if (field_descriptor == NULL) { return NULL; } @@ -1683,10 +1865,8 @@ static PyObject* RichCompare(CMessage* self, PyObject* other, int opid) { } } -PyObject* InternalGetScalar( - CMessage* self, - const FieldDescriptor* field_descriptor) { - Message* message = self->message; +PyObject* InternalGetScalar(const Message* message, + const FieldDescriptor* field_descriptor) { const Reflection* reflection = message->GetReflection(); if (!CheckFieldBelongsToMessage(field_descriptor, message)) { @@ -1739,12 +1919,12 @@ PyObject* InternalGetScalar( if (!message->GetReflection()->SupportsUnknownEnumValues() && !message->GetReflection()->HasField(*message, field_descriptor)) { // Look for the value in the unknown fields. - 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() == + const UnknownFieldSet& unknown_field_set = + message->GetReflection()->GetUnknownFields(*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()); + result = PyInt_FromLong(unknown_field_set.field(i).varint()); break; } } @@ -1793,21 +1973,16 @@ PyObject* InternalGetSubMessage( return reinterpret_cast(cmsg); } -int InternalSetScalar( - CMessage* self, +int InternalSetNonOneofScalar( + Message* message, const FieldDescriptor* field_descriptor, PyObject* arg) { - Message* message = self->message; const Reflection* reflection = message->GetReflection(); if (!CheckFieldBelongsToMessage(field_descriptor, message)) { return -1; } - if (MaybeReleaseOverlappingOneofField(self, field_descriptor) < 0) { - return -1; - } - switch (field_descriptor->cpp_type()) { case FieldDescriptor::CPPTYPE_INT32: { GOOGLE_CHECK_GET_INT32(arg, value, -1); @@ -1878,6 +2053,21 @@ int InternalSetScalar( return 0; } +int InternalSetScalar( + CMessage* self, + const FieldDescriptor* field_descriptor, + PyObject* arg) { + if (!CheckFieldBelongsToMessage(field_descriptor, self->message)) { + return -1; + } + + if (MaybeReleaseOverlappingOneofField(self, field_descriptor) < 0) { + return -1; + } + + return InternalSetNonOneofScalar(self->message, field_descriptor, arg); +} + PyObject* FromString(PyTypeObject* cls, PyObject* serialized) { PyObject* py_cmsg = PyObject_CallObject( reinterpret_cast(cls), NULL); @@ -1955,7 +2145,8 @@ static PyObject* AddDescriptors(PyObject* cls, PyObject* descriptor) { // which was built previously. for (int i = 0; i < message_descriptor->enum_type_count(); ++i) { const EnumDescriptor* enum_descriptor = message_descriptor->enum_type(i); - ScopedPyObjectPtr enum_type(PyEnumDescriptor_New(enum_descriptor)); + ScopedPyObjectPtr enum_type( + PyEnumDescriptor_FromDescriptor(enum_descriptor)); if (enum_type == NULL) { return NULL; } @@ -1993,7 +2184,7 @@ static PyObject* AddDescriptors(PyObject* cls, PyObject* descriptor) { // which was defined previously. for (int i = 0; i < message_descriptor->extension_count(); ++i) { const google::protobuf::FieldDescriptor* field = message_descriptor->extension(i); - ScopedPyObjectPtr extension_field(PyFieldDescriptor_New(field)); + ScopedPyObjectPtr extension_field(PyFieldDescriptor_FromDescriptor(field)); if (extension_field == NULL) { return NULL; } @@ -2097,26 +2288,6 @@ PyObject* SetState(CMessage* self, PyObject* state) { } // CMessage static methods: -PyObject* _GetMessageDescriptor(PyObject* unused, PyObject* arg) { - return cdescriptor_pool::FindMessageByName(GetDescriptorPool(), arg); -} - -PyObject* _GetFieldDescriptor(PyObject* unused, PyObject* arg) { - return cdescriptor_pool::FindFieldByName(GetDescriptorPool(), arg); -} - -PyObject* _GetExtensionDescriptor(PyObject* unused, PyObject* arg) { - return cdescriptor_pool::FindExtensionByName(GetDescriptorPool(), arg); -} - -PyObject* _GetEnumDescriptor(PyObject* unused, PyObject* arg) { - return cdescriptor_pool::FindEnumTypeByName(GetDescriptorPool(), arg); -} - -PyObject* _GetOneofDescriptor(PyObject* unused, PyObject* arg) { - return cdescriptor_pool::FindOneofByName(GetDescriptorPool(), arg); -} - PyObject* _CheckCalledFromGeneratedFile(PyObject* unused, PyObject* unused_arg) { if (!_CalledFromGeneratedFile(1)) { @@ -2188,21 +2359,6 @@ static PyMethodDef Methods[] = { "or None if no field is set." }, // Static Methods. - { "_BuildFile", (PyCFunction)Python_BuildFile, METH_O | METH_STATIC, - "Registers a new protocol buffer file in the global C++ descriptor pool." }, - { "_GetMessageDescriptor", (PyCFunction)_GetMessageDescriptor, - METH_O | METH_STATIC, "Finds a message descriptor in the message pool." }, - { "_GetFieldDescriptor", (PyCFunction)_GetFieldDescriptor, - METH_O | METH_STATIC, "Finds a field descriptor in the message pool." }, - { "_GetExtensionDescriptor", (PyCFunction)_GetExtensionDescriptor, - METH_O | METH_STATIC, - "Finds a extension descriptor in the message pool." }, - { "_GetEnumDescriptor", (PyCFunction)_GetEnumDescriptor, - METH_O | METH_STATIC, - "Finds an enum descriptor in the message pool." }, - { "_GetOneofDescriptor", (PyCFunction)_GetOneofDescriptor, - METH_O | METH_STATIC, - "Finds an oneof descriptor in the message pool." }, { "_CheckCalledFromGeneratedFile", (PyCFunction)_CheckCalledFromGeneratedFile, METH_NOARGS | METH_STATIC, "Raises TypeError if the caller is not in a _pb2.py file."}, @@ -2234,6 +2390,31 @@ PyObject* GetAttr(CMessage* self, PyObject* name) { reinterpret_cast(self), name); } + if (field_descriptor->is_map()) { + PyObject* py_container = NULL; + const Descriptor* entry_type = field_descriptor->message_type(); + const FieldDescriptor* value_type = entry_type->FindFieldByName("value"); + if (value_type->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + PyObject* value_class = cdescriptor_pool::GetMessageClass( + GetDescriptorPool(), value_type->message_type()); + if (value_class == NULL) { + return NULL; + } + py_container = message_map_container::NewContainer(self, field_descriptor, + value_class); + } else { + py_container = scalar_map_container::NewContainer(self, field_descriptor); + } + if (py_container == NULL) { + return NULL; + } + if (!SetCompositeField(self, name, py_container)) { + Py_DECREF(py_container); + return NULL; + } + return py_container; + } + if (field_descriptor->label() == FieldDescriptor::LABEL_REPEATED) { PyObject* py_container = NULL; if (field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { @@ -2267,7 +2448,7 @@ PyObject* GetAttr(CMessage* self, PyObject* name) { return sub_message; } - return InternalGetScalar(self, field_descriptor); + return InternalGetScalar(self->message, field_descriptor); } int SetAttr(CMessage* self, PyObject* name, PyObject* value) { @@ -2304,9 +2485,7 @@ int SetAttr(CMessage* self, PyObject* name, PyObject* value) { PyTypeObject CMessage_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - // Keep the fully qualified _message symbol in a line for opensource. - "google.protobuf.pyext._message." - "CMessage", // tp_name + FULL_MODULE_NAME ".CMessage", // tp_name sizeof(CMessage), // tp_basicsize 0, // tp_itemsize (destructor)cmessage::Dealloc, // tp_dealloc @@ -2401,7 +2580,7 @@ void InitGlobals() { k_extensions_by_name = PyString_FromString("_extensions_by_name"); k_extensions_by_number = PyString_FromString("_extensions_by_number"); - message_factory = new DynamicMessageFactory(GetDescriptorPool()->pool); + message_factory = new DynamicMessageFactory(); message_factory->SetDelegateToGeneratedFactory(true); } @@ -2469,6 +2648,61 @@ bool InitProto2MessageModule(PyObject *m) { reinterpret_cast( &RepeatedCompositeContainer_Type)); + // ScalarMapContainer_Type derives from our MutableMapping type. + PyObject* containers = + PyImport_ImportModule("google.protobuf.internal.containers"); + if (containers == NULL) { + return false; + } + + PyObject* mutable_mapping = + PyObject_GetAttrString(containers, "MutableMapping"); + Py_DECREF(containers); + + if (mutable_mapping == NULL) { + return false; + } + + if (!PyObject_TypeCheck(mutable_mapping, &PyType_Type)) { + Py_DECREF(mutable_mapping); + return false; + } + + ScalarMapContainer_Type.tp_base = + reinterpret_cast(mutable_mapping); + + if (PyType_Ready(&ScalarMapContainer_Type) < 0) { + return false; + } + + PyModule_AddObject(m, "ScalarMapContainer", + reinterpret_cast(&ScalarMapContainer_Type)); + + if (PyType_Ready(&ScalarMapIterator_Type) < 0) { + return false; + } + + PyModule_AddObject(m, "ScalarMapIterator", + reinterpret_cast(&ScalarMapIterator_Type)); + + Py_INCREF(mutable_mapping); + MessageMapContainer_Type.tp_base = + reinterpret_cast(mutable_mapping); + + if (PyType_Ready(&MessageMapContainer_Type) < 0) { + return false; + } + + PyModule_AddObject(m, "MessageMapContainer", + reinterpret_cast(&MessageMapContainer_Type)); + + if (PyType_Ready(&MessageMapIterator_Type) < 0) { + return false; + } + + PyModule_AddObject(m, "MessageMapIterator", + reinterpret_cast(&MessageMapIterator_Type)); + ExtensionDict_Type.tp_hash = PyObject_HashNotImplemented; if (PyType_Ready(&ExtensionDict_Type) < 0) { return false; @@ -2478,6 +2712,12 @@ bool InitProto2MessageModule(PyObject *m) { m, "ExtensionDict", reinterpret_cast(&ExtensionDict_Type)); + // Expose the DescriptorPool used to hold all descriptors added from generated + // pb2.py files. + Py_INCREF(GetDescriptorPool()); // PyModule_AddObject steals a reference. + PyModule_AddObject( + m, "default_pool", reinterpret_cast(GetDescriptorPool())); + // This implementation provides full Descriptor types, we advertise it so that // descriptor.py can use them in replacement of the Python classes. PyModule_AddIntConstant(m, "_USE_C_DESCRIPTORS", 1); diff --git a/python/google/protobuf/pyext/message.h b/python/google/protobuf/pyext/message.h index 2f2da795..7360b207 100644 --- a/python/google/protobuf/pyext/message.h +++ b/python/google/protobuf/pyext/message.h @@ -120,7 +120,7 @@ CMessage* NewEmptyMessage(PyObject* type, const Descriptor* descriptor); // A new message will be created if this is a read-only default instance. // // Corresponds to reflection api method ReleaseMessage. -int ReleaseSubMessage(Message* message, +int ReleaseSubMessage(CMessage* self, const FieldDescriptor* field_descriptor, CMessage* child_cmessage); @@ -144,7 +144,7 @@ PyObject* InternalGetSubMessage( // by slice will be removed from cmessage_list by this function. // // Corresponds to reflection api method RemoveLast. -int InternalDeleteRepeatedField(Message* message, +int InternalDeleteRepeatedField(CMessage* self, const FieldDescriptor* field_descriptor, PyObject* slice, PyObject* cmessage_list); @@ -153,10 +153,15 @@ int InternalSetScalar(CMessage* self, const FieldDescriptor* field_descriptor, PyObject* value); +// Sets the specified scalar value to the message. Requires it is not a Oneof. +int InternalSetNonOneofScalar(Message* message, + const FieldDescriptor* field_descriptor, + PyObject* arg); + // Retrieves the specified scalar value from the message. // // Returns a new python reference. -PyObject* InternalGetScalar(CMessage* self, +PyObject* InternalGetScalar(const Message* message, const FieldDescriptor* field_descriptor); // Clears the message, removing all contained data. Extension dictionary and @@ -279,7 +284,7 @@ extern PyObject* kint64min_py; extern PyObject* kint64max_py; extern PyObject* kuint64max_py; -#define C(str) const_cast(str) +#define FULL_MODULE_NAME "google.protobuf.pyext._message" void FormatTypeError(PyObject* arg, char* expected_types); template diff --git a/python/google/protobuf/pyext/message_map_container.cc b/python/google/protobuf/pyext/message_map_container.cc new file mode 100644 index 00000000..ab8d8fb9 --- /dev/null +++ b/python/google/protobuf/pyext/message_map_container.cc @@ -0,0 +1,540 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: haberman@google.com (Josh Haberman) + +#include + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace python { + +struct MessageMapIterator { + PyObject_HEAD; + + // This dict contains the full contents of what we want to iterate over. + // There's no way to avoid building this, because the list representation + // (which is canonical) can contain duplicate keys. So at the very least we + // need a set that lets us skip duplicate keys. And at the point that we're + // doing that, we might as well just build the actual dict we're iterating + // over and use dict's built-in iterator. + PyObject* dict; + + // An iterator on dict. + PyObject* iter; + + // A pointer back to the container, so we can notice changes to the version. + MessageMapContainer* container; + + // The version of the map when we took the iterator to it. + // + // We store this so that if the map is modified during iteration we can throw + // an error. + uint64 version; +}; + +static MessageMapIterator* GetIter(PyObject* obj) { + return reinterpret_cast(obj); +} + +namespace message_map_container { + +static MessageMapContainer* GetMap(PyObject* obj) { + return reinterpret_cast(obj); +} + +// The private constructor of MessageMapContainer objects. +PyObject* NewContainer(CMessage* parent, + const google::protobuf::FieldDescriptor* parent_field_descriptor, + PyObject* concrete_class) { + if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) { + return NULL; + } + + PyObject* obj = PyType_GenericAlloc(&MessageMapContainer_Type, 0); + if (obj == NULL) { + return PyErr_Format(PyExc_RuntimeError, + "Could not allocate new container."); + } + + MessageMapContainer* self = GetMap(obj); + + self->message = parent->message; + self->parent = parent; + self->parent_field_descriptor = parent_field_descriptor; + self->owner = parent->owner; + self->version = 0; + + self->key_field_descriptor = + parent_field_descriptor->message_type()->FindFieldByName("key"); + self->value_field_descriptor = + parent_field_descriptor->message_type()->FindFieldByName("value"); + + self->message_dict = PyDict_New(); + if (self->message_dict == NULL) { + return PyErr_Format(PyExc_RuntimeError, + "Could not allocate message dict."); + } + + Py_INCREF(concrete_class); + self->subclass_init = concrete_class; + + if (self->key_field_descriptor == NULL || + self->value_field_descriptor == NULL) { + Py_DECREF(obj); + return PyErr_Format(PyExc_KeyError, + "Map entry descriptor did not have key/value fields"); + } + + return obj; +} + +// Initializes the underlying Message object of "to" so it becomes a new parent +// repeated scalar, and copies all the values from "from" to it. A child scalar +// container can be released by passing it as both from and to (e.g. making it +// the recipient of the new parent message and copying the values from itself). +static int InitializeAndCopyToParentContainer( + MessageMapContainer* from, + MessageMapContainer* to) { + // For now we require from == to, re-evaluate if we want to support deep copy + // as in repeated_composite_container.cc. + GOOGLE_DCHECK(from == to); + Message* old_message = from->message; + Message* new_message = old_message->New(); + to->parent = NULL; + to->parent_field_descriptor = from->parent_field_descriptor; + to->message = new_message; + to->owner.reset(new_message); + + vector fields; + fields.push_back(from->parent_field_descriptor); + old_message->GetReflection()->SwapFields(old_message, new_message, fields); + return 0; +} + +static PyObject* GetCMessage(MessageMapContainer* self, Message* entry) { + // Get or create the CMessage object corresponding to this message. + Message* message = entry->GetReflection()->MutableMessage( + entry, self->value_field_descriptor); + ScopedPyObjectPtr key(PyLong_FromVoidPtr(message)); + PyObject* ret = PyDict_GetItem(self->message_dict, key); + + if (ret == NULL) { + CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init, + message->GetDescriptor()); + ret = reinterpret_cast(cmsg); + + if (cmsg == NULL) { + return NULL; + } + cmsg->owner = self->owner; + cmsg->message = message; + cmsg->parent = self->parent; + + if (PyDict_SetItem(self->message_dict, key, ret) < 0) { + Py_DECREF(ret); + return NULL; + } + } else { + Py_INCREF(ret); + } + + return ret; +} + +int Release(MessageMapContainer* self) { + InitializeAndCopyToParentContainer(self, self); + return 0; +} + +void SetOwner(MessageMapContainer* self, + const shared_ptr& new_owner) { + self->owner = new_owner; +} + +Py_ssize_t Length(PyObject* _self) { + MessageMapContainer* self = GetMap(_self); + google::protobuf::Message* message = self->message; + return message->GetReflection()->FieldSize(*message, + self->parent_field_descriptor); +} + +int MapKeyMatches(MessageMapContainer* self, const Message* entry, + PyObject* key) { + // TODO(haberman): do we need more strict type checking? + ScopedPyObjectPtr entry_key( + cmessage::InternalGetScalar(entry, self->key_field_descriptor)); + int ret = PyObject_RichCompareBool(key, entry_key, Py_EQ); + return ret; +} + +int SetItem(PyObject *_self, PyObject *key, PyObject *v) { + if (v) { + PyErr_Format(PyExc_ValueError, + "Direct assignment of submessage not allowed"); + return -1; + } + + // Now we know that this is a delete, not a set. + + MessageMapContainer* self = GetMap(_self); + cmessage::AssureWritable(self->parent); + + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + size_t size = + reflection->FieldSize(*message, self->parent_field_descriptor); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. We need to search from the end because the underlying + // representation can have duplicates if a user calls MergeFrom(); the last + // one needs to win. + // + // TODO(haberman): add lookup API to Reflection API. + bool found = false; + for (int i = size - 1; i >= 0; i--) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, entry, key); + if (matches < 0) return -1; + if (matches) { + found = true; + if (i != size - 1) { + reflection->SwapElements(message, self->parent_field_descriptor, i, + size - 1); + } + reflection->RemoveLast(message, self->parent_field_descriptor); + + // Can't exit now, the repeated field representation of maps allows + // duplicate keys, and we have to be sure to remove all of them. + } + } + + if (!found) { + PyErr_Format(PyExc_KeyError, "Key not present in map"); + return -1; + } + + self->version++; + + return 0; +} + +PyObject* GetIterator(PyObject *_self) { + MessageMapContainer* self = GetMap(_self); + + ScopedPyObjectPtr obj(PyType_GenericAlloc(&MessageMapIterator_Type, 0)); + if (obj == NULL) { + return PyErr_Format(PyExc_KeyError, "Could not allocate iterator"); + } + + MessageMapIterator* iter = GetIter(obj); + + Py_INCREF(self); + iter->container = self; + iter->version = self->version; + iter->dict = PyDict_New(); + if (iter->dict == NULL) { + return PyErr_Format(PyExc_RuntimeError, + "Could not allocate dict for iterator."); + } + + // Build the entire map into a dict right now. Start from the beginning so + // that later entries win in the case of duplicates. + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. We need to search from the end because the underlying + // representation can have duplicates if a user calls MergeFrom(); the last + // one needs to win. + // + // TODO(haberman): add lookup API to Reflection API. + size_t size = + reflection->FieldSize(*message, self->parent_field_descriptor); + for (int i = size - 1; i >= 0; i--) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + ScopedPyObjectPtr key( + cmessage::InternalGetScalar(entry, self->key_field_descriptor)); + if (PyDict_SetItem(iter->dict, key.get(), GetCMessage(self, entry)) < 0) { + return PyErr_Format(PyExc_RuntimeError, + "SetItem failed in iterator construction."); + } + } + + iter->iter = PyObject_GetIter(iter->dict); + + return obj.release(); +} + +PyObject* GetItem(PyObject* _self, PyObject* key) { + MessageMapContainer* self = GetMap(_self); + cmessage::AssureWritable(self->parent); + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. We need to search from the end because the underlying + // representation can have duplicates if a user calls MergeFrom(); the last + // one needs to win. + // + // TODO(haberman): add lookup API to Reflection API. + size_t size = + reflection->FieldSize(*message, self->parent_field_descriptor); + for (int i = size - 1; i >= 0; i--) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, entry, key); + if (matches < 0) return NULL; + if (matches) { + return GetCMessage(self, entry); + } + } + + // Key is not already present; insert a new entry. + Message* entry = + reflection->AddMessage(message, self->parent_field_descriptor); + + self->version++; + + if (cmessage::InternalSetNonOneofScalar(entry, self->key_field_descriptor, + key) < 0) { + reflection->RemoveLast(message, self->parent_field_descriptor); + return NULL; + } + + return GetCMessage(self, entry); +} + +PyObject* Contains(PyObject* _self, PyObject* key) { + MessageMapContainer* self = GetMap(_self); + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. + // + // TODO(haberman): add lookup API to Reflection API. + size_t size = + reflection->FieldSize(*message, self->parent_field_descriptor); + for (int i = 0; i < size; i++) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, entry, key); + if (matches < 0) return NULL; + if (matches) { + Py_RETURN_TRUE; + } + } + + Py_RETURN_FALSE; +} + +PyObject* Clear(PyObject* _self) { + MessageMapContainer* self = GetMap(_self); + cmessage::AssureWritable(self->parent); + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + self->version++; + reflection->ClearField(message, self->parent_field_descriptor); + + Py_RETURN_NONE; +} + +PyObject* Get(PyObject* self, PyObject* args) { + PyObject* key; + PyObject* default_value = NULL; + if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) { + return NULL; + } + + ScopedPyObjectPtr is_present(Contains(self, key)); + if (is_present.get() == NULL) { + return NULL; + } + + if (PyObject_IsTrue(is_present.get())) { + return GetItem(self, key); + } else { + if (default_value != NULL) { + Py_INCREF(default_value); + return default_value; + } else { + Py_RETURN_NONE; + } + } +} + +static PyMappingMethods MpMethods = { + Length, // mp_length + GetItem, // mp_subscript + SetItem, // mp_ass_subscript +}; + +static void Dealloc(PyObject* _self) { + MessageMapContainer* self = GetMap(_self); + self->owner.reset(); + Py_DECREF(self->message_dict); + Py_TYPE(_self)->tp_free(_self); +} + +static PyMethodDef Methods[] = { + { "__contains__", (PyCFunction)Contains, METH_O, + "Tests whether the map contains this element."}, + { "clear", (PyCFunction)Clear, METH_NOARGS, + "Removes all elements from the map."}, + { "get", Get, METH_VARARGS, + "Gets the value for the given key if present, or otherwise a default" }, + { "get_or_create", GetItem, METH_O, + "Alias for getitem, useful to make explicit that the map is mutated." }, + /* + { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS, + "Makes a deep copy of the class." }, + { "__reduce__", (PyCFunction)Reduce, METH_NOARGS, + "Outputs picklable representation of the repeated field." }, + */ + {NULL, NULL}, +}; + +} // namespace message_map_container + +namespace message_map_iterator { + +static void Dealloc(PyObject* _self) { + MessageMapIterator* self = GetIter(_self); + Py_DECREF(self->dict); + Py_DECREF(self->iter); + Py_DECREF(self->container); + Py_TYPE(_self)->tp_free(_self); +} + +PyObject* IterNext(PyObject* _self) { + MessageMapIterator* self = GetIter(_self); + + // This won't catch mutations to the map performed by MergeFrom(); no easy way + // to address that. + if (self->version != self->container->version) { + return PyErr_Format(PyExc_RuntimeError, + "Map modified during iteration."); + } + + return PyIter_Next(self->iter); +} + +} // namespace message_map_iterator + +PyTypeObject MessageMapContainer_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + FULL_MODULE_NAME ".MessageMapContainer", // tp_name + sizeof(MessageMapContainer), // tp_basicsize + 0, // tp_itemsize + message_map_container::Dealloc, // 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 + &message_map_container::MpMethods, // 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 + "A map container for message", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + message_map_container::GetIterator, // tp_iter + 0, // tp_iternext + message_map_container::Methods, // 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 +}; + +PyTypeObject MessageMapIterator_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + FULL_MODULE_NAME ".MessageMapIterator", // tp_name + sizeof(MessageMapIterator), // tp_basicsize + 0, // tp_itemsize + message_map_iterator::Dealloc, // 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 + "A scalar map iterator", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + PyObject_SelfIter, // tp_iter + message_map_iterator::IterNext, // tp_iternext + 0, // 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 +}; + +} // namespace python +} // namespace protobuf +} // namespace google diff --git a/python/google/protobuf/pyext/message_map_container.h b/python/google/protobuf/pyext/message_map_container.h new file mode 100644 index 00000000..4ca0aecc --- /dev/null +++ b/python/google/protobuf/pyext/message_map_container.h @@ -0,0 +1,117 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_MAP_CONTAINER_H__ +#define GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_MAP_CONTAINER_H__ + +#include + +#include +#ifndef _SHARED_PTR_H +#include +#endif + +#include + +namespace google { +namespace protobuf { + +class Message; + +using internal::shared_ptr; + +namespace python { + +struct CMessage; + +struct MessageMapContainer { + PyObject_HEAD; + + // This is the top-level C++ Message object that owns the whole + // proto tree. Every Python MessageMapContainer holds a + // reference to it in order to keep it alive as long as there's a + // Python object that references any part of the tree. + shared_ptr owner; + + // Pointer to the C++ Message that contains this container. The + // MessageMapContainer does not own this pointer. + Message* message; + + // Weak reference to a parent CMessage object (i.e. may be NULL.) + // + // Used to make sure all ancestors are also mutable when first + // modifying the container. + CMessage* parent; + + // Pointer to the parent's descriptor that describes this + // field. Used together with the parent's message when making a + // default message instance mutable. + // The pointer is owned by the global DescriptorPool. + const FieldDescriptor* parent_field_descriptor; + const FieldDescriptor* key_field_descriptor; + const FieldDescriptor* value_field_descriptor; + + // A callable that is used to create new child messages. + PyObject* subclass_init; + + // A dict mapping Message* -> CMessage. + PyObject* message_dict; + + // We bump this whenever we perform a mutation, to invalidate existing + // iterators. + uint64 version; +}; + +extern PyTypeObject MessageMapContainer_Type; +extern PyTypeObject MessageMapIterator_Type; + +namespace message_map_container { + +// Builds a MessageMapContainer object, from a parent message and a +// field descriptor. +extern PyObject* NewContainer(CMessage* parent, + const FieldDescriptor* parent_field_descriptor, + PyObject* concrete_class); + +// Releases the messages in the container to a new message. +// +// Returns 0 on success, -1 on failure. +int Release(MessageMapContainer* self); + +// Set the owner field of self and any children of self. +void SetOwner(MessageMapContainer* self, + const shared_ptr& new_owner); + +} // namespace message_map_container +} // namespace python +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_MAP_CONTAINER_H__ diff --git a/python/google/protobuf/pyext/repeated_composite_container.cc b/python/google/protobuf/pyext/repeated_composite_container.cc index 0fe98e73..86b75d0f 100644 --- a/python/google/protobuf/pyext/repeated_composite_container.cc +++ b/python/google/protobuf/pyext/repeated_composite_container.cc @@ -367,8 +367,8 @@ int AssignSubscript(RepeatedCompositeContainer* self, } // Delete from the underlying Message, if any. - if (self->message != NULL) { - if (cmessage::InternalDeleteRepeatedField(self->message, + if (self->parent != NULL) { + if (cmessage::InternalDeleteRepeatedField(self->parent, self->parent_field_descriptor, slice, self->child_messages) < 0) { @@ -572,47 +572,35 @@ static PyObject* Pop(RepeatedCompositeContainer* self, return item; } -// The caller takes ownership of the returned Message. -Message* ReleaseLast(const FieldDescriptor* field, - const Descriptor* type, - Message* message) { +// Release field of parent message and transfer the ownership to target. +void ReleaseLastTo(CMessage* parent, + const FieldDescriptor* field, + CMessage* target) { + GOOGLE_CHECK_NOTNULL(parent); GOOGLE_CHECK_NOTNULL(field); - GOOGLE_CHECK_NOTNULL(type); - GOOGLE_CHECK_NOTNULL(message); + GOOGLE_CHECK_NOTNULL(target); - Message* released_message = message->GetReflection()->ReleaseLast( - message, field); + shared_ptr released_message( + parent->message->GetReflection()->ReleaseLast(parent->message, field)); // TODO(tibell): Deal with proto1. // ReleaseMessage will return NULL which differs from // child_cmessage->message, if the field does not exist. In this case, // the latter points to the default instance via a const_cast<>, so we // have to reset it to a new mutable object since we are taking ownership. - if (released_message == NULL) { + if (released_message.get() == NULL) { const Message* prototype = - cmessage::GetMessageFactory()->GetPrototype(type); + cmessage::GetMessageFactory()->GetPrototype( + target->message->GetDescriptor()); GOOGLE_CHECK_NOTNULL(prototype); - return prototype->New(); - } else { - return released_message; + released_message.reset(prototype->New()); } -} -// Release field of message and transfer the ownership to cmessage. -void ReleaseLastTo(const FieldDescriptor* field, - Message* message, - CMessage* cmessage) { - GOOGLE_CHECK_NOTNULL(field); - GOOGLE_CHECK_NOTNULL(message); - GOOGLE_CHECK_NOTNULL(cmessage); - - shared_ptr released_message( - ReleaseLast(field, cmessage->message->GetDescriptor(), message)); - cmessage->parent = NULL; - cmessage->parent_field_descriptor = NULL; - cmessage->message = released_message.get(); - cmessage->read_only = false; - cmessage::SetOwner(cmessage, released_message); + target->parent = NULL; + target->parent_field_descriptor = NULL; + target->message = released_message.get(); + target->read_only = false; + cmessage::SetOwner(target, released_message); } // Called to release a container using @@ -635,7 +623,7 @@ int Release(RepeatedCompositeContainer* self) { for (Py_ssize_t i = size - 1; i >= 0; --i) { CMessage* child_cmessage = reinterpret_cast( PyList_GET_ITEM(self->child_messages, i)); - ReleaseLastTo(field, message, child_cmessage); + ReleaseLastTo(self->parent, field, child_cmessage); } // Detach from containing message. @@ -732,9 +720,7 @@ static PyMethodDef Methods[] = { PyTypeObject RepeatedCompositeContainer_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - // Keep the fully qualified _message symbol in a line for opensource. - "google.protobuf.pyext._message." - "RepeatedCompositeContainer", // tp_name + FULL_MODULE_NAME ".RepeatedCompositeContainer", // tp_name sizeof(RepeatedCompositeContainer), // tp_basicsize 0, // tp_itemsize (destructor)repeated_composite_container::Dealloc, // tp_dealloc diff --git a/python/google/protobuf/pyext/repeated_composite_container.h b/python/google/protobuf/pyext/repeated_composite_container.h index ce7cee0f..e0f21360 100644 --- a/python/google/protobuf/pyext/repeated_composite_container.h +++ b/python/google/protobuf/pyext/repeated_composite_container.h @@ -161,13 +161,13 @@ int SetOwner(RepeatedCompositeContainer* self, const shared_ptr& new_owner); // Removes the last element of the repeated message field 'field' on -// the Message 'message', and transfers the ownership of the released -// Message to 'cmessage'. +// the Message 'parent', and transfers the ownership of the released +// Message to 'target'. // // Corresponds to reflection api method ReleaseMessage. -void ReleaseLastTo(const FieldDescriptor* field, - Message* message, - CMessage* cmessage); +void ReleaseLastTo(CMessage* parent, + const FieldDescriptor* field, + CMessage* target); } // namespace repeated_composite_container } // namespace python diff --git a/python/google/protobuf/pyext/repeated_scalar_container.cc b/python/google/protobuf/pyext/repeated_scalar_container.cc index 110a4c85..fd196836 100644 --- a/python/google/protobuf/pyext/repeated_scalar_container.cc +++ b/python/google/protobuf/pyext/repeated_scalar_container.cc @@ -102,7 +102,7 @@ static int AssignItem(RepeatedScalarContainer* self, if (arg == NULL) { ScopedPyObjectPtr py_index(PyLong_FromLong(index)); - return cmessage::InternalDeleteRepeatedField(message, field_descriptor, + return cmessage::InternalDeleteRepeatedField(self->parent, field_descriptor, py_index, NULL); } @@ -470,7 +470,7 @@ static int AssSubscript(RepeatedScalarContainer* self, if (value == NULL) { return cmessage::InternalDeleteRepeatedField( - message, field_descriptor, slice, NULL); + self->parent, field_descriptor, slice, NULL); } if (!create_list) { @@ -769,9 +769,7 @@ static PyMethodDef Methods[] = { PyTypeObject RepeatedScalarContainer_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - // Keep the fully qualified _message symbol in a line for opensource. - "google.protobuf.pyext._message." - "RepeatedScalarContainer", // tp_name + FULL_MODULE_NAME ".RepeatedScalarContainer", // tp_name sizeof(RepeatedScalarContainer), // tp_basicsize 0, // tp_itemsize (destructor)repeated_scalar_container::Dealloc, // tp_dealloc diff --git a/python/google/protobuf/pyext/scalar_map_container.cc b/python/google/protobuf/pyext/scalar_map_container.cc new file mode 100644 index 00000000..6f731d27 --- /dev/null +++ b/python/google/protobuf/pyext/scalar_map_container.cc @@ -0,0 +1,514 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: haberman@google.com (Josh Haberman) + +#include + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace python { + +struct ScalarMapIterator { + PyObject_HEAD; + + // This dict contains the full contents of what we want to iterate over. + // There's no way to avoid building this, because the list representation + // (which is canonical) can contain duplicate keys. So at the very least we + // need a set that lets us skip duplicate keys. And at the point that we're + // doing that, we might as well just build the actual dict we're iterating + // over and use dict's built-in iterator. + PyObject* dict; + + // An iterator on dict. + PyObject* iter; + + // A pointer back to the container, so we can notice changes to the version. + ScalarMapContainer* container; + + // The version of the map when we took the iterator to it. + // + // We store this so that if the map is modified during iteration we can throw + // an error. + uint64 version; +}; + +static ScalarMapIterator* GetIter(PyObject* obj) { + return reinterpret_cast(obj); +} + +namespace scalar_map_container { + +static ScalarMapContainer* GetMap(PyObject* obj) { + return reinterpret_cast(obj); +} + +// The private constructor of ScalarMapContainer objects. +PyObject *NewContainer( + CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor) { + if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) { + return NULL; + } + + ScopedPyObjectPtr obj(PyType_GenericAlloc(&ScalarMapContainer_Type, 0)); + if (obj.get() == NULL) { + return PyErr_Format(PyExc_RuntimeError, + "Could not allocate new container."); + } + + ScalarMapContainer* self = GetMap(obj); + + self->message = parent->message; + self->parent = parent; + self->parent_field_descriptor = parent_field_descriptor; + self->owner = parent->owner; + self->version = 0; + + self->key_field_descriptor = + parent_field_descriptor->message_type()->FindFieldByName("key"); + self->value_field_descriptor = + parent_field_descriptor->message_type()->FindFieldByName("value"); + + if (self->key_field_descriptor == NULL || + self->value_field_descriptor == NULL) { + return PyErr_Format(PyExc_KeyError, + "Map entry descriptor did not have key/value fields"); + } + + return obj.release(); +} + +// Initializes the underlying Message object of "to" so it becomes a new parent +// repeated scalar, and copies all the values from "from" to it. A child scalar +// container can be released by passing it as both from and to (e.g. making it +// the recipient of the new parent message and copying the values from itself). +static int InitializeAndCopyToParentContainer( + ScalarMapContainer* from, + ScalarMapContainer* to) { + // For now we require from == to, re-evaluate if we want to support deep copy + // as in repeated_scalar_container.cc. + GOOGLE_DCHECK(from == to); + Message* old_message = from->message; + Message* new_message = old_message->New(); + to->parent = NULL; + to->parent_field_descriptor = from->parent_field_descriptor; + to->message = new_message; + to->owner.reset(new_message); + + vector fields; + fields.push_back(from->parent_field_descriptor); + old_message->GetReflection()->SwapFields(old_message, new_message, fields); + return 0; +} + +int Release(ScalarMapContainer* self) { + return InitializeAndCopyToParentContainer(self, self); +} + +void SetOwner(ScalarMapContainer* self, + const shared_ptr& new_owner) { + self->owner = new_owner; +} + +Py_ssize_t Length(PyObject* _self) { + ScalarMapContainer* self = GetMap(_self); + google::protobuf::Message* message = self->message; + return message->GetReflection()->FieldSize(*message, + self->parent_field_descriptor); +} + +int MapKeyMatches(ScalarMapContainer* self, const Message* entry, + PyObject* key) { + // TODO(haberman): do we need more strict type checking? + ScopedPyObjectPtr entry_key( + cmessage::InternalGetScalar(entry, self->key_field_descriptor)); + int ret = PyObject_RichCompareBool(key, entry_key, Py_EQ); + return ret; +} + +PyObject* GetItem(PyObject* _self, PyObject* key) { + ScalarMapContainer* self = GetMap(_self); + + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. + // + // TODO(haberman): add lookup API to Reflection API. + size_t size = reflection->FieldSize(*message, self->parent_field_descriptor); + for (int i = size - 1; i >= 0; i--) { + const Message& entry = reflection->GetRepeatedMessage( + *message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, &entry, key); + if (matches < 0) return NULL; + if (matches) { + return cmessage::InternalGetScalar(&entry, self->value_field_descriptor); + } + } + + // Need to add a new entry. + Message* entry = + reflection->AddMessage(message, self->parent_field_descriptor); + PyObject* ret = NULL; + + if (cmessage::InternalSetNonOneofScalar(entry, self->key_field_descriptor, + key) >= 0) { + ret = cmessage::InternalGetScalar(entry, self->value_field_descriptor); + } + + self->version++; + + // If there was a type error above, it set the Python exception. + return ret; +} + +int SetItem(PyObject *_self, PyObject *key, PyObject *v) { + ScalarMapContainer* self = GetMap(_self); + cmessage::AssureWritable(self->parent); + + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + size_t size = + reflection->FieldSize(*message, self->parent_field_descriptor); + self->version++; + + if (v) { + // Set item. + // + // Right now the Reflection API doesn't support map lookup, so we implement + // it via linear search. + // + // TODO(haberman): add lookup API to Reflection API. + for (int i = size - 1; i >= 0; i--) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, entry, key); + if (matches < 0) return -1; + if (matches) { + return cmessage::InternalSetNonOneofScalar( + entry, self->value_field_descriptor, v); + } + } + + // Key is not already present; insert a new entry. + Message* entry = + reflection->AddMessage(message, self->parent_field_descriptor); + + if (cmessage::InternalSetNonOneofScalar(entry, self->key_field_descriptor, + key) < 0 || + cmessage::InternalSetNonOneofScalar(entry, self->value_field_descriptor, + v) < 0) { + reflection->RemoveLast(message, self->parent_field_descriptor); + return -1; + } + + return 0; + } else { + bool found = false; + for (int i = size - 1; i >= 0; i--) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, entry, key); + if (matches < 0) return -1; + if (matches) { + found = true; + if (i != size - 1) { + reflection->SwapElements(message, self->parent_field_descriptor, i, + size - 1); + } + reflection->RemoveLast(message, self->parent_field_descriptor); + + // Can't exit now, the repeated field representation of maps allows + // duplicate keys, and we have to be sure to remove all of them. + } + } + + if (found) { + return 0; + } else { + PyErr_Format(PyExc_KeyError, "Key not present in map"); + return -1; + } + } +} + +PyObject* GetIterator(PyObject *_self) { + ScalarMapContainer* self = GetMap(_self); + + ScopedPyObjectPtr obj(PyType_GenericAlloc(&ScalarMapIterator_Type, 0)); + if (obj == NULL) { + return PyErr_Format(PyExc_KeyError, "Could not allocate iterator"); + } + + ScalarMapIterator* iter = GetIter(obj.get()); + + Py_INCREF(self); + iter->container = self; + iter->version = self->version; + iter->dict = PyDict_New(); + if (iter->dict == NULL) { + return PyErr_Format(PyExc_RuntimeError, + "Could not allocate dict for iterator."); + } + + // Build the entire map into a dict right now. Start from the beginning so + // that later entries win in the case of duplicates. + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. We need to search from the end because the underlying + // representation can have duplicates if a user calls MergeFrom(); the last + // one needs to win. + // + // TODO(haberman): add lookup API to Reflection API. + size_t size = + reflection->FieldSize(*message, self->parent_field_descriptor); + for (int i = 0; i < size; i++) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + ScopedPyObjectPtr key( + cmessage::InternalGetScalar(entry, self->key_field_descriptor)); + ScopedPyObjectPtr val( + cmessage::InternalGetScalar(entry, self->value_field_descriptor)); + if (PyDict_SetItem(iter->dict, key.get(), val.get()) < 0) { + return PyErr_Format(PyExc_RuntimeError, + "SetItem failed in iterator construction."); + } + } + + + iter->iter = PyObject_GetIter(iter->dict); + + + return obj.release(); +} + +PyObject* Clear(PyObject* _self) { + ScalarMapContainer* self = GetMap(_self); + cmessage::AssureWritable(self->parent); + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + reflection->ClearField(message, self->parent_field_descriptor); + + Py_RETURN_NONE; +} + +PyObject* Contains(PyObject* _self, PyObject* key) { + ScalarMapContainer* self = GetMap(_self); + + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. + // + // TODO(haberman): add lookup API to Reflection API. + size_t size = reflection->FieldSize(*message, self->parent_field_descriptor); + for (int i = size - 1; i >= 0; i--) { + const Message& entry = reflection->GetRepeatedMessage( + *message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, &entry, key); + if (matches < 0) return NULL; + if (matches) { + Py_RETURN_TRUE; + } + } + + Py_RETURN_FALSE; +} + +PyObject* Get(PyObject* self, PyObject* args) { + PyObject* key; + PyObject* default_value = NULL; + if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) { + return NULL; + } + + ScopedPyObjectPtr is_present(Contains(self, key)); + if (is_present.get() == NULL) { + return NULL; + } + + if (PyObject_IsTrue(is_present.get())) { + return GetItem(self, key); + } else { + if (default_value != NULL) { + Py_INCREF(default_value); + return default_value; + } else { + Py_RETURN_NONE; + } + } +} + +static PyMappingMethods MpMethods = { + Length, // mp_length + GetItem, // mp_subscript + SetItem, // mp_ass_subscript +}; + +static void Dealloc(PyObject* _self) { + ScalarMapContainer* self = GetMap(_self); + self->owner.reset(); + Py_TYPE(_self)->tp_free(_self); +} + +static PyMethodDef Methods[] = { + { "__contains__", Contains, METH_O, + "Tests whether a key is a member of the map." }, + { "clear", (PyCFunction)Clear, METH_NOARGS, + "Removes all elements from the map." }, + { "get", Get, METH_VARARGS, + "Gets the value for the given key if present, or otherwise a default" }, + /* + { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS, + "Makes a deep copy of the class." }, + { "__reduce__", (PyCFunction)Reduce, METH_NOARGS, + "Outputs picklable representation of the repeated field." }, + */ + {NULL, NULL}, +}; + +} // namespace scalar_map_container + +namespace scalar_map_iterator { + +static void Dealloc(PyObject* _self) { + ScalarMapIterator* self = GetIter(_self); + Py_DECREF(self->dict); + Py_DECREF(self->iter); + Py_DECREF(self->container); + Py_TYPE(_self)->tp_free(_self); +} + +PyObject* IterNext(PyObject* _self) { + ScalarMapIterator* self = GetIter(_self); + + // This won't catch mutations to the map performed by MergeFrom(); no easy way + // to address that. + if (self->version != self->container->version) { + return PyErr_Format(PyExc_RuntimeError, + "Map modified during iteration."); + } + + return PyIter_Next(self->iter); +} + +} // namespace scalar_map_iterator + +PyTypeObject ScalarMapContainer_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + FULL_MODULE_NAME ".ScalarMapContainer", // tp_name + sizeof(ScalarMapContainer), // tp_basicsize + 0, // tp_itemsize + scalar_map_container::Dealloc, // 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 + &scalar_map_container::MpMethods, // 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 + "A scalar map container", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + scalar_map_container::GetIterator, // tp_iter + 0, // tp_iternext + scalar_map_container::Methods, // 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 +}; + +PyTypeObject ScalarMapIterator_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + FULL_MODULE_NAME ".ScalarMapIterator", // tp_name + sizeof(ScalarMapIterator), // tp_basicsize + 0, // tp_itemsize + scalar_map_iterator::Dealloc, // 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 + "A scalar map iterator", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + PyObject_SelfIter, // tp_iter + scalar_map_iterator::IterNext, // tp_iternext + 0, // 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 +}; + +} // namespace python +} // namespace protobuf +} // namespace google diff --git a/python/google/protobuf/pyext/scalar_map_container.h b/python/google/protobuf/pyext/scalar_map_container.h new file mode 100644 index 00000000..254e6e98 --- /dev/null +++ b/python/google/protobuf/pyext/scalar_map_container.h @@ -0,0 +1,110 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_SCALAR_MAP_CONTAINER_H__ +#define GOOGLE_PROTOBUF_PYTHON_CPP_SCALAR_MAP_CONTAINER_H__ + +#include + +#include +#ifndef _SHARED_PTR_H +#include +#endif + +#include + +namespace google { +namespace protobuf { + +class Message; + +using internal::shared_ptr; + +namespace python { + +struct CMessage; + +struct ScalarMapContainer { + PyObject_HEAD; + + // This is the top-level C++ Message object that owns the whole + // proto tree. Every Python ScalarMapContainer holds a + // reference to it in order to keep it alive as long as there's a + // Python object that references any part of the tree. + shared_ptr owner; + + // Pointer to the C++ Message that contains this container. The + // ScalarMapContainer does not own this pointer. + Message* message; + + // Weak reference to a parent CMessage object (i.e. may be NULL.) + // + // Used to make sure all ancestors are also mutable when first + // modifying the container. + CMessage* parent; + + // Pointer to the parent's descriptor that describes this + // field. Used together with the parent's message when making a + // default message instance mutable. + // The pointer is owned by the global DescriptorPool. + const FieldDescriptor* parent_field_descriptor; + const FieldDescriptor* key_field_descriptor; + const FieldDescriptor* value_field_descriptor; + + // We bump this whenever we perform a mutation, to invalidate existing + // iterators. + uint64 version; +}; + +extern PyTypeObject ScalarMapContainer_Type; +extern PyTypeObject ScalarMapIterator_Type; + +namespace scalar_map_container { + +// Builds a ScalarMapContainer object, from a parent message and a +// field descriptor. +extern PyObject *NewContainer( + CMessage* parent, const FieldDescriptor* parent_field_descriptor); + +// Releases the messages in the container to a new message. +// +// Returns 0 on success, -1 on failure. +int Release(ScalarMapContainer* self); + +// Set the owner field of self and any children of self. +void SetOwner(ScalarMapContainer* self, + const shared_ptr& new_owner); + +} // namespace scalar_map_container +} // namespace python +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_PYTHON_CPP_SCALAR_MAP_CONTAINER_H__ diff --git a/python/google/protobuf/reflection.py b/python/google/protobuf/reflection.py index 55e653a0..82fca661 100755 --- a/python/google/protobuf/reflection.py +++ b/python/google/protobuf/reflection.py @@ -144,7 +144,6 @@ class GeneratedProtocolMessageType(type): _InitMessage(descriptor, cls) superclass = super(GeneratedProtocolMessageType, cls) superclass.__init__(name, bases, dictionary) - setattr(descriptor, '_concrete_class', cls) def ParseMessage(descriptor, byte_str): diff --git a/python/google/protobuf/text_format.py b/python/google/protobuf/text_format.py index a47ce3e3..8cbd6822 100755 --- a/python/google/protobuf/text_format.py +++ b/python/google/protobuf/text_format.py @@ -100,6 +100,10 @@ def MessageToString(message, as_utf8=False, as_one_line=False, return result.rstrip() return result +def _IsMapEntry(field): + return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and + field.message_type.has_options and + field.message_type.GetOptions().map_entry) def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False, pointy_brackets=False, use_index_order=False, @@ -108,7 +112,19 @@ def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False, if use_index_order: fields.sort(key=lambda x: x[0].index) for field, value in fields: - if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + if _IsMapEntry(field): + for key in value: + # This is slow for maps with submessage entires because it copies the + # entire tree. Unfortunately this would take significant refactoring + # of this file to work around. + # + # TODO(haberman): refactor and optimize if this becomes an issue. + entry_submsg = field.message_type._concrete_class( + key=key, value=value[key]) + PrintField(field, entry_submsg, out, indent, as_utf8, as_one_line, + pointy_brackets=pointy_brackets, + use_index_order=use_index_order, float_format=float_format) + elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: for element in value: PrintField(field, element, out, indent, as_utf8, as_one_line, pointy_brackets=pointy_brackets, @@ -367,6 +383,7 @@ def _MergeField(tokenizer, message, allow_multiple_scalars): message_descriptor.full_name, name)) if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + is_map_entry = _IsMapEntry(field) tokenizer.TryConsume(':') if tokenizer.TryConsume('<'): @@ -378,6 +395,8 @@ def _MergeField(tokenizer, message, allow_multiple_scalars): if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: if field.is_extension: sub_message = message.Extensions[field].add() + elif is_map_entry: + sub_message = field.message_type._concrete_class() else: sub_message = getattr(message, field.name).add() else: @@ -391,6 +410,14 @@ def _MergeField(tokenizer, message, allow_multiple_scalars): if tokenizer.AtEnd(): raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % (end_token)) _MergeField(tokenizer, sub_message, allow_multiple_scalars) + + if is_map_entry: + value_cpptype = field.message_type.fields_by_name['value'].cpp_type + if value_cpptype == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + value = getattr(message, field.name)[sub_message.key] + value.MergeFrom(sub_message.value) + else: + getattr(message, field.name)[sub_message.key] = sub_message.value else: _MergeScalarField(tokenizer, message, field, allow_multiple_scalars) @@ -701,13 +728,16 @@ class _Tokenizer(object): String literals (whether bytes or text) can come in multiple adjacent tokens which are automatically concatenated, like in C or Python. This method only consumes one token. + + Raises: + ParseError: When the wrong format data is found. """ text = self.token if len(text) < 1 or text[0] not in ('\'', '"'): - raise self._ParseError('Expected string but found: "%r"' % text) + raise self._ParseError('Expected string but found: %r' % (text,)) if len(text) < 2 or text[-1] != text[0]: - raise self._ParseError('String missing ending quote.') + raise self._ParseError('String missing ending quote: %r' % (text,)) try: result = text_encoding.CUnescape(text[1:-1]) diff --git a/python/setup.py b/python/setup.py index a1365fba..5c321f50 100755 --- a/python/setup.py +++ b/python/setup.py @@ -91,6 +91,7 @@ def GenerateUnittestProtos(): if not os.path.exists("../.git"): return + generate_proto("../src/google/protobuf/map_unittest.proto") generate_proto("../src/google/protobuf/unittest.proto") generate_proto("../src/google/protobuf/unittest_custom_options.proto") generate_proto("../src/google/protobuf/unittest_import.proto") diff --git a/ruby/tests/generated_code.proto b/ruby/tests/generated_code.proto index b1d63232..42d82a6b 100644 --- a/ruby/tests/generated_code.proto +++ b/ruby/tests/generated_code.proto @@ -3,17 +3,17 @@ syntax = "proto3"; package A.B.C; message TestMessage { - optional int32 optional_int32 = 1; - optional int64 optional_int64 = 2; - optional uint32 optional_uint32 = 3; - optional uint64 optional_uint64 = 4; - optional bool optional_bool = 5; - optional double optional_double = 6; - optional float optional_float = 7; - optional string optional_string = 8; - optional bytes optional_bytes = 9; - optional TestEnum optional_enum = 10; - optional TestMessage optional_msg = 11; + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + bool optional_bool = 5; + double optional_double = 6; + float optional_float = 7; + string optional_string = 8; + bytes optional_bytes = 9; + TestEnum optional_enum = 10; + TestMessage optional_msg = 11; repeated int32 repeated_int32 = 21; repeated int64 repeated_int64 = 22; @@ -53,10 +53,10 @@ message TestMessage { map map_string_bool = 70; message NestedMessage { - optional int32 foo = 1; + int32 foo = 1; } - optional NestedMessage nested_message = 80; + NestedMessage nested_message = 80; } enum TestEnum { diff --git a/src/Makefile.am b/src/Makefile.am index fd10b789..2ecf6028 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -88,6 +88,7 @@ nobase_include_HEADERS = \ google/protobuf/stubs/type_traits.h \ google/protobuf/any.pb.h \ google/protobuf/api.pb.h \ + google/protobuf/any.h \ google/protobuf/arena.h \ google/protobuf/arenastring.h \ google/protobuf/descriptor_database.h \ @@ -186,6 +187,7 @@ libprotobuf_la_SOURCES = \ $(libprotobuf_lite_la_SOURCES) \ google/protobuf/any.pb.cc \ google/protobuf/api.pb.cc \ + google/protobuf/any.cc \ google/protobuf/descriptor.cc \ google/protobuf/descriptor_database.cc \ google/protobuf/descriptor.pb.cc \ @@ -264,6 +266,8 @@ libprotoc_la_SOURCES = \ google/protobuf/compiler/java/java_enum.cc \ google/protobuf/compiler/java/java_enum_field.cc \ google/protobuf/compiler/java/java_enum_field.h \ + google/protobuf/compiler/java/java_enum_field_lite.cc \ + google/protobuf/compiler/java/java_enum_field_lite.h \ google/protobuf/compiler/java/java_enum.h \ google/protobuf/compiler/java/java_extension.cc \ google/protobuf/compiler/java/java_extension.h \ @@ -278,22 +282,38 @@ libprotoc_la_SOURCES = \ google/protobuf/compiler/java/java_helpers.h \ google/protobuf/compiler/java/java_lazy_message_field.cc \ google/protobuf/compiler/java/java_lazy_message_field.h \ + google/protobuf/compiler/java/java_lazy_message_field_lite.cc\ + google/protobuf/compiler/java/java_lazy_message_field_lite.h \ google/protobuf/compiler/java/java_map_field.cc \ google/protobuf/compiler/java/java_map_field.h \ + google/protobuf/compiler/java/java_map_field_lite.cc \ + google/protobuf/compiler/java/java_map_field_lite.h \ google/protobuf/compiler/java/java_message.cc \ + google/protobuf/compiler/java/java_message_lite.cc \ + google/protobuf/compiler/java/java_message_builder.cc \ + google/protobuf/compiler/java/java_message_builder_lite.cc \ google/protobuf/compiler/java/java_message_field.cc \ google/protobuf/compiler/java/java_message_field.h \ + google/protobuf/compiler/java/java_message_field_lite.cc \ + google/protobuf/compiler/java/java_message_field_lite.h \ google/protobuf/compiler/java/java_message.h \ + google/protobuf/compiler/java/java_message_lite.h \ + google/protobuf/compiler/java/java_message_builder.h \ + google/protobuf/compiler/java/java_message_builder_lite.h \ google/protobuf/compiler/java/java_name_resolver.cc \ google/protobuf/compiler/java/java_name_resolver.h \ google/protobuf/compiler/java/java_primitive_field.cc \ google/protobuf/compiler/java/java_primitive_field.h \ + google/protobuf/compiler/java/java_primitive_field_lite.cc \ + google/protobuf/compiler/java/java_primitive_field_lite.h \ google/protobuf/compiler/java/java_shared_code_generator.cc \ google/protobuf/compiler/java/java_shared_code_generator.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/java/java_string_field_lite.cc \ + google/protobuf/compiler/java/java_string_field_lite.h \ google/protobuf/compiler/java/java_doc_comment.cc \ google/protobuf/compiler/java/java_doc_comment.h \ google/protobuf/compiler/javanano/javanano_enum.cc \ @@ -381,6 +401,7 @@ protoc_SOURCES = google/protobuf/compiler/main.cc # Tests ============================================================== protoc_inputs = \ + google/protobuf/any_test.proto \ google/protobuf/map_lite_unittest.proto \ google/protobuf/map_proto2_unittest.proto \ google/protobuf/map_unittest.proto \ @@ -419,6 +440,7 @@ EXTRA_DIST = \ google/protobuf/testdata/golden_message_proto3 \ google/protobuf/testdata/golden_packed_fields_message \ google/protobuf/testdata/bad_utf8_string \ + google/protobuf/testdata/map_test_data.txt \ google/protobuf/testdata/text_format_unittest_data.txt \ google/protobuf/testdata/text_format_unittest_data_oneof_implemented.txt \ google/protobuf/testdata/text_format_unittest_data_pointy.txt \ @@ -443,6 +465,8 @@ protoc_lite_outputs = \ protoc_outputs = \ $(protoc_lite_outputs) \ + google/protobuf/any_test.pb.cc \ + google/protobuf/any_test.pb.h \ google/protobuf/map_proto2_unittest.pb.cc \ google/protobuf/map_proto2_unittest.pb.h \ google/protobuf/map_unittest.pb.cc \ @@ -543,6 +567,7 @@ protobuf_test_SOURCES = \ google/protobuf/stubs/stringprintf_unittest.cc \ google/protobuf/stubs/template_util_unittest.cc \ google/protobuf/stubs/type_traits_unittest.cc \ + google/protobuf/any_test.cc \ google/protobuf/arenastring_unittest.cc \ google/protobuf/arena_unittest.cc \ google/protobuf/descriptor_database_unittest.cc \ @@ -604,6 +629,8 @@ nodist_protobuf_lazy_descriptor_test_SOURCES = $(protoc_outputs) protobuf_lite_test_LDADD = $(PTHREAD_LIBS) libprotobuf-lite.la protobuf_lite_test_CXXFLAGS = $(NO_OPT_CXXFLAGS) protobuf_lite_test_SOURCES = \ + google/protobuf/arena_test_util.cc \ + google/protobuf/arena_test_util.h \ google/protobuf/lite_unittest.cc \ google/protobuf/map_lite_test_util.cc \ google/protobuf/map_lite_test_util.h \ diff --git a/src/google/protobuf/any.cc b/src/google/protobuf/any.cc new file mode 100644 index 00000000..c66fdfad --- /dev/null +++ b/src/google/protobuf/any.cc @@ -0,0 +1,100 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +namespace google { +namespace protobuf { +namespace internal { + +namespace { +string GetTypeUrl(const Descriptor* message) { + return string(kTypeGoogleApisComPrefix) + message->full_name(); +} + +} // namespace + +const char kAnyFullTypeName[] = "google.protobuf.Any"; +const char kTypeGoogleApisComPrefix[] = "type.googleapis.com/"; + +AnyMetadata::AnyMetadata(UrlType* type_url, ValueType* value) + : type_url_(type_url), value_(value) { +} + +void AnyMetadata::PackFrom(const Message& message) { + type_url_->SetNoArena(&::google::protobuf::internal::GetEmptyString(), + GetTypeUrl(message.GetDescriptor())); + message.SerializeToString(value_->MutableNoArena( + &::google::protobuf::internal::GetEmptyStringAlreadyInited())); +} + +bool AnyMetadata::UnpackTo(Message* message) const { + if (!InternalIs(message->GetDescriptor())) { + return false; + } + return message->ParseFromString( + value_->GetNoArena(&::google::protobuf::internal::GetEmptyString())); +} + +bool AnyMetadata::InternalIs(const Descriptor* descriptor) const { + return type_url_->GetNoArena( + &::google::protobuf::internal::GetEmptyString()) == + GetTypeUrl(descriptor); +} + +bool ParseAnyTypeUrl(const string& type_url, string* full_type_name) { + const int prefix_len = strlen(kTypeGoogleApisComPrefix); + if (strncmp(type_url.c_str(), kTypeGoogleApisComPrefix, prefix_len) == 0) { + full_type_name->assign(type_url.data() + prefix_len, + type_url.size() - prefix_len); + return true; + } + return true; +} + + +bool GetAnyFieldDescriptors(const Message& message, + const FieldDescriptor** type_url_field, + const FieldDescriptor** value_field) { + const Descriptor* descriptor = message.GetDescriptor(); + if (descriptor->full_name() != kAnyFullTypeName) { + return false; + } + *type_url_field = descriptor->FindFieldByNumber(1); + *value_field = descriptor->FindFieldByNumber(2); + return (*type_url_field != NULL && + (*type_url_field)->type() == FieldDescriptor::TYPE_STRING && + *value_field != NULL && + (*value_field)->type() == FieldDescriptor::TYPE_BYTES); +} + +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/any.h b/src/google/protobuf/any.h new file mode 100644 index 00000000..757b45aa --- /dev/null +++ b/src/google/protobuf/any.h @@ -0,0 +1,90 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_ANY_H__ +#define GOOGLE_PROTOBUF_ANY_H__ + +#include + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { + +// Helper class used to implement google::protobuf::Any. +class AnyMetadata { + typedef ArenaStringPtr UrlType; + typedef ArenaStringPtr ValueType; + public: + // AnyMetadata does not take ownership of "type_url" and "value". + AnyMetadata(UrlType* type_url, ValueType* value); + + void PackFrom(const Message& message); + + bool UnpackTo(Message* message) const; + + template + bool Is() const { + return InternalIs(T::default_instance().GetDescriptor()); + } + + private: + bool InternalIs(const Descriptor* message) const; + + UrlType* type_url_; + ValueType* value_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(AnyMetadata); +}; + +extern const char kAnyFullTypeName[]; // "google.protobuf.Any". +extern const char kTypeGoogleApisComPrefix[]; // "type.googleapis.com/". + +// Get the proto type name from Any::type_url value. For example, passing +// "type.googleapis.com/rpc.QueryOrigin" will return "rpc.QueryOrigin" in +// *full_type_name. Returns false if type_url does not start with +// "type.googleapis.com". +bool ParseAnyTypeUrl(const string& type_url, string* full_type_name); + +// See if message is of type google.protobuf.Any, if so, return the descriptors +// for "type_url" and "value" fields. +bool GetAnyFieldDescriptors(const Message& message, + const FieldDescriptor** type_url_field, + const FieldDescriptor** value_field); + +} // namespace internal +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_ANY_H__ diff --git a/src/google/protobuf/any.pb.cc b/src/google/protobuf/any.pb.cc index 75cc8754..2c492b04 100644 --- a/src/google/protobuf/any.pb.cc +++ b/src/google/protobuf/any.pb.cc @@ -111,13 +111,21 @@ static void MergeFromFail(int line) { // =================================================================== +void Any::PackFrom(const ::google::protobuf::Message& message) { + _any_metadata_.PackFrom(message); +} + +bool Any::UnpackTo(::google::protobuf::Message* message) const { + return _any_metadata_.UnpackTo(message); +} + #ifndef _MSC_VER const int Any::kTypeUrlFieldNumber; const int Any::kValueFieldNumber; #endif // !_MSC_VER Any::Any() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL), _any_metadata_(&type_url_, &value_) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Any) } @@ -128,7 +136,8 @@ void Any::InitAsDefaultInstance() { Any::Any(const Any& from) : ::google::protobuf::Message(), - _internal_metadata_(NULL) { + _internal_metadata_(NULL), + _any_metadata_(&type_url_, &value_) { SharedCtor(); MergeFrom(from); // @@protoc_insertion_point(copy_constructor:google.protobuf.Any) @@ -316,9 +325,9 @@ int Any::ByteSize() const { void Any::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Any* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Any* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -378,7 +387,7 @@ void Any::InternalSwap(Any* other) { // Any // optional string type_url = 1; - void Any::clear_type_url() { +void Any::clear_type_url() { type_url_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Any::type_url() const { @@ -421,7 +430,7 @@ void Any::InternalSwap(Any* other) { } // optional bytes value = 2; - void Any::clear_value() { +void Any::clear_value() { value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Any::value() const { diff --git a/src/google/protobuf/any.pb.h b/src/google/protobuf/any.pb.h index b2c238c1..c324c4af 100644 --- a/src/google/protobuf/any.pb.h +++ b/src/google/protobuf/any.pb.h @@ -27,6 +27,7 @@ #include #include #include +#include "google/protobuf/any.h" // @@protoc_insertion_point(includes) namespace google { @@ -56,6 +57,14 @@ class LIBPROTOBUF_EXPORT Any : public ::google::protobuf::Message { static const ::google::protobuf::Descriptor* descriptor(); static const Any& default_instance(); + // implements Any ----------------------------------------------- + + void PackFrom(const ::google::protobuf::Message& message); + bool UnpackTo(::google::protobuf::Message* message) const; + template bool Is() const { + return _any_metadata_.Is(); + } + void Swap(Any* other); // implements Message ---------------------------------------------- @@ -127,6 +136,7 @@ class LIBPROTOBUF_EXPORT Any : public ::google::protobuf::Message { ::google::protobuf::internal::ArenaStringPtr type_url_; ::google::protobuf::internal::ArenaStringPtr value_; mutable int _cached_size_; + ::google::protobuf::internal::AnyMetadata _any_metadata_; friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fany_2eproto(); friend void protobuf_AssignDesc_google_2fprotobuf_2fany_2eproto(); friend void protobuf_ShutdownFile_google_2fprotobuf_2fany_2eproto(); diff --git a/src/google/protobuf/any_test.cc b/src/google/protobuf/any_test.cc new file mode 100644 index 00000000..1bfaa63d --- /dev/null +++ b/src/google/protobuf/any_test.cc @@ -0,0 +1,89 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +namespace google { +namespace protobuf { +namespace { + +TEST(AnyTest, TestPackAndUnpack) { + protobuf_unittest::TestAny submessage; + submessage.set_int32_value(12345); + protobuf_unittest::TestAny message; + message.mutable_any_value()->PackFrom(submessage); + + string data = message.SerializeAsString(); + + ASSERT_TRUE(message.ParseFromString(data)); + EXPECT_TRUE(message.has_any_value()); + ASSERT_TRUE(message.any_value().UnpackTo(&submessage)); + EXPECT_EQ(12345, submessage.int32_value()); +} + +TEST(AnyTest, TestPackAndUnpackAny) { + // We can pack a Any message inside another Any message. + protobuf_unittest::TestAny submessage; + submessage.set_int32_value(12345); + google::protobuf::Any any; + any.PackFrom(submessage); + protobuf_unittest::TestAny message; + message.mutable_any_value()->PackFrom(any); + + string data = message.SerializeAsString(); + + ASSERT_TRUE(message.ParseFromString(data)); + EXPECT_TRUE(message.has_any_value()); + ASSERT_TRUE(message.any_value().UnpackTo(&any)); + ASSERT_TRUE(any.UnpackTo(&submessage)); + EXPECT_EQ(12345, submessage.int32_value()); +} + +TEST(AnyTest, TestIs) { + protobuf_unittest::TestAny submessage; + submessage.set_int32_value(12345); + google::protobuf::Any any; + any.PackFrom(submessage); + ASSERT_TRUE(any.ParseFromString(any.SerializeAsString())); + EXPECT_TRUE(any.Is()); + EXPECT_FALSE(any.Is()); + + protobuf_unittest::TestAny message; + message.mutable_any_value()->PackFrom(any); + ASSERT_TRUE(message.ParseFromString(message.SerializeAsString())); + EXPECT_FALSE(message.any_value().Is()); + EXPECT_TRUE(message.any_value().Is()); +} + +} // namespace +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/any_test.proto b/src/google/protobuf/any_test.proto new file mode 100644 index 00000000..0c5b30ba --- /dev/null +++ b/src/google/protobuf/any_test.proto @@ -0,0 +1,41 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package protobuf_unittest; + +import "google/protobuf/any.proto"; + +message TestAny { + int32 int32_value = 1; + google.protobuf.Any any_value = 2; + repeated google.protobuf.Any repeated_any_value = 3; +} diff --git a/src/google/protobuf/api.pb.cc b/src/google/protobuf/api.pb.cc index b34ce803..434320dc 100644 --- a/src/google/protobuf/api.pb.cc +++ b/src/google/protobuf/api.pb.cc @@ -162,7 +162,7 @@ const int Api::kSourceContextFieldNumber; #endif // !_MSC_VER Api::Api() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Api) } @@ -230,7 +230,7 @@ Api* Api::New(::google::protobuf::Arena* arena) const { void Api::Clear() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); version_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); - if (source_context_ != NULL) delete source_context_; + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; methods_.Clear(); options_.Clear(); @@ -266,26 +266,31 @@ bool Api::MergePartialFromCodedStream( case 2: { if (tag == 18) { parse_methods: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_methods: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_methods())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_methods; - if (input->ExpectTag(26)) goto parse_options; + if (input->ExpectTag(18)) goto parse_loop_methods; + if (input->ExpectTag(26)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.Option options = 3; case 3: { if (tag == 26) { - parse_options: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_options: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_options())); } else { goto handle_unusual; } - if (input->ExpectTag(26)) goto parse_options; + if (input->ExpectTag(26)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(34)) goto parse_version; break; } @@ -483,9 +488,9 @@ int Api::ByteSize() const { void Api::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Api* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Api* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -553,7 +558,7 @@ void Api::InternalSwap(Api* other) { // Api // optional string name = 1; - void Api::clear_name() { +void Api::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Api::name() const { @@ -596,10 +601,10 @@ void Api::InternalSwap(Api* other) { } // repeated .google.protobuf.Method methods = 2; - int Api::methods_size() const { +int Api::methods_size() const { return methods_.size(); } - void Api::clear_methods() { +void Api::clear_methods() { methods_.Clear(); } const ::google::protobuf::Method& Api::methods(int index) const { @@ -626,10 +631,10 @@ Api::mutable_methods() { } // repeated .google.protobuf.Option options = 3; - int Api::options_size() const { +int Api::options_size() const { return options_.size(); } - void Api::clear_options() { +void Api::clear_options() { options_.Clear(); } const ::google::protobuf::Option& Api::options(int index) const { @@ -656,7 +661,7 @@ Api::mutable_options() { } // optional string version = 4; - void Api::clear_version() { +void Api::clear_version() { version_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Api::version() const { @@ -699,11 +704,11 @@ Api::mutable_options() { } // optional .google.protobuf.SourceContext source_context = 5; - bool Api::has_source_context() const { +bool Api::has_source_context() const { return !_is_default_instance_ && source_context_ != NULL; } - void Api::clear_source_context() { - if (source_context_ != NULL) delete source_context_; +void Api::clear_source_context() { + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; } const ::google::protobuf::SourceContext& Api::source_context() const { @@ -749,7 +754,7 @@ const int Method::kOptionsFieldNumber; #endif // !_MSC_VER Method::Method() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Method) } @@ -929,12 +934,15 @@ bool Method::MergePartialFromCodedStream( case 6: { if (tag == 50) { parse_options: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_options: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_options())); } else { goto handle_unusual; } - if (input->ExpectTag(50)) goto parse_options; + if (input->ExpectTag(50)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -1119,9 +1127,9 @@ int Method::ByteSize() const { void Method::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Method* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Method* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1196,7 +1204,7 @@ void Method::InternalSwap(Method* other) { // Method // optional string name = 1; - void Method::clear_name() { +void Method::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Method::name() const { @@ -1239,7 +1247,7 @@ void Method::InternalSwap(Method* other) { } // optional string request_type_url = 2; - void Method::clear_request_type_url() { +void Method::clear_request_type_url() { request_type_url_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Method::request_type_url() const { @@ -1282,7 +1290,7 @@ void Method::InternalSwap(Method* other) { } // optional bool request_streaming = 3; - void Method::clear_request_streaming() { +void Method::clear_request_streaming() { request_streaming_ = false; } bool Method::request_streaming() const { @@ -1296,7 +1304,7 @@ void Method::InternalSwap(Method* other) { } // optional string response_type_url = 4; - void Method::clear_response_type_url() { +void Method::clear_response_type_url() { response_type_url_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Method::response_type_url() const { @@ -1339,7 +1347,7 @@ void Method::InternalSwap(Method* other) { } // optional bool response_streaming = 5; - void Method::clear_response_streaming() { +void Method::clear_response_streaming() { response_streaming_ = false; } bool Method::response_streaming() const { @@ -1353,10 +1361,10 @@ void Method::InternalSwap(Method* other) { } // repeated .google.protobuf.Option options = 6; - int Method::options_size() const { +int Method::options_size() const { return options_.size(); } - void Method::clear_options() { +void Method::clear_options() { options_.Clear(); } const ::google::protobuf::Option& Method::options(int index) const { diff --git a/src/google/protobuf/api.pb.h b/src/google/protobuf/api.pb.h index f459af0f..985adc5d 100644 --- a/src/google/protobuf/api.pb.h +++ b/src/google/protobuf/api.pb.h @@ -466,7 +466,7 @@ inline bool Api::has_source_context() const { return !_is_default_instance_ && source_context_ != NULL; } inline void Api::clear_source_context() { - if (source_context_ != NULL) delete source_context_; + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; } inline const ::google::protobuf::SourceContext& Api::source_context() const { @@ -690,6 +690,8 @@ Method::mutable_options() { } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) diff --git a/src/google/protobuf/arena.cc b/src/google/protobuf/arena.cc index f7059d26..96009645 100755 --- a/src/google/protobuf/arena.cc +++ b/src/google/protobuf/arena.cc @@ -29,7 +29,6 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include #ifdef ADDRESS_SANITIZER #include @@ -155,10 +154,16 @@ void Arena::AddListNode(void* elem, void (*cleanup)(void*)) { reinterpret_cast(node))); } -void* Arena::AllocateAligned(size_t n) { +void* Arena::AllocateAligned(const std::type_info* allocated, size_t n) { // Align n to next multiple of 8 (from Hacker's Delight, Chapter 3.) n = (n + 7) & -8; + // Monitor allocation if needed. + if (GOOGLE_PREDICT_FALSE(hooks_cookie_ != NULL) && + options_.on_arena_allocation != NULL) { + options_.on_arena_allocation(allocated, n, hooks_cookie_); + } + // If this thread already owns a block in this arena then try to use that. // This fast path optimizes the case where multiple threads allocate from the // same arena. diff --git a/src/google/protobuf/arena.h b/src/google/protobuf/arena.h index b48bef92..4adcd677 100644 --- a/src/google/protobuf/arena.h +++ b/src/google/protobuf/arena.h @@ -28,12 +28,12 @@ // (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 header is logically internal, but is made public because it is used -// from protocol-compiler-generated code, which may reside in other components. - #ifndef GOOGLE_PROTOBUF_ARENA_H__ #define GOOGLE_PROTOBUF_ARENA_H__ +#if __cplusplus >= 201103L +#include +#endif #include #include @@ -113,10 +113,13 @@ struct ArenaOptions { void (*on_arena_reset)(Arena* arena, void* cookie, uint64 space_used); void (*on_arena_destruction)(Arena* arena, void* cookie, uint64 space_used); - // type_name is promised to be a static string - its lifetime extends to - // match program's lifetime. - void (*on_arena_allocation)(const char* type_name, uint64 alloc_size, - Arena* arena, void* cookie); + // type_info is promised to be static - its lifetime extends to + // match program's lifetime (It is given by typeid operator). + // Note: typeid(void) will be passed as allocated_type every time we + // intentionally want to avoid monitoring an allocation. (i.e. internal + // allocations for managing the arena) + void (*on_arena_allocation)(const std::type_info* allocated_type, + uint64 alloc_size, void* cookie); ArenaOptions() : start_block_size(kDefaultStartBlockSize), @@ -137,6 +140,14 @@ struct ArenaOptions { static const size_t kDefaultMaxBlockSize = 8192; }; +// Support for non-RTTI environments. (The metrics hooks API uses type +// information.) +#ifndef GOOGLE_PROTOBUF_NO_RTTI +#define RTTI_TYPE_ID(type) (&typeid(type)) +#else +#define RTTI_TYPE_ID(type) (NULL) +#endif + // Arena allocator. Arena allocation replaces ordinary (heap-based) allocation // with new/delete, and improves performance by aggregating allocations into // larger blocks and freeing allocations all at once. Protocol messages are @@ -146,6 +157,44 @@ struct ArenaOptions { // This is a thread-safe implementation: multiple threads may allocate from the // arena concurrently. Destruction is not thread-safe and the destructing // thread must synchronize with users of the arena first. +// +// An arena provides two allocation interfaces: CreateMessage, which works +// for arena-enabled proto2 message types as well as other types that satisfy +// the appropriate protocol (described below), and Create, which works for +// any arbitrary type T. CreateMessage is better when the type T supports it, +// because this interface (i) passes the arena pointer to the created object so +// that its sub-objects and internal allocations can use the arena too, and (ii) +// elides the object's destructor call when possible. Create does not place +// any special requirements on the type T, and will invoke the object's +// destructor when the arena is destroyed. +// +// The arena message allocation protocol, required by CreateMessage, is as +// follows: +// +// - The type T must have (at least) two constructors: a constructor with no +// arguments, called when a T is allocated on the heap; and a constructor with +// a google::protobuf::Arena* argument, called when a T is allocated on an arena. If the +// second constructor is called with a NULL arena pointer, it must be +// equivalent to invoking the first (no-argument) constructor. +// +// - The type T must have a particular type trait: a nested type +// |InternalArenaConstructable_|. This is usually a typedef to |void|. If no +// such type trait exists, then the instantiation CreateMessage will fail +// to compile. +// +// - The type T *may* have the type trait |DestructorSkippable_|. If this type +// trait is present in the type, then its destructor will not be called if and +// only if it was passed a non-NULL arena pointer. If this type trait is not +// present on the type, then its destructor is always called when the +// containing arena is destroyed. +// +// - One- and two-user-argument forms of CreateMessage() also exist that +// forward these constructor arguments to T's constructor: for example, +// CreateMessage(Arena*, arg1, arg2) forwards to a constructor T(Arena*, +// arg1, arg2). +// +// This protocol is implemented by all arena-enabled proto2 message classes as +// well as RepeatedPtrField. class LIBPROTOBUF_EXPORT Arena { public: // Arena constructor taking custom options. See ArenaOptions below for @@ -172,8 +221,10 @@ class LIBPROTOBUF_EXPORT Arena { // compilation error will occur. // // RepeatedField and RepeatedPtrField may also be instantiated directly on an - // arena with this method: they act as "arena-capable message types" for the - // purposes of the Arena API. + // arena with this method. + // + // This function also accepts any type T that satisfies the arena message + // allocation protocol, documented above. template GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* CreateMessage(::google::protobuf::Arena* arena) { if (arena == NULL) { @@ -183,17 +234,55 @@ class LIBPROTOBUF_EXPORT Arena { } } + // One-argument form of CreateMessage. This is useful for constructing objects + // that implement the arena message construction protocol described above but + // take additional constructor arguments. + template GOOGLE_ATTRIBUTE_ALWAYS_INLINE + static T* CreateMessage(::google::protobuf::Arena* arena, const Arg& arg) { + if (arena == NULL) { + return new T(NULL, arg); + } else { + return arena->CreateMessageInternal(static_cast(0), + arg); + } + } + + // Two-argument form of CreateMessage. This is useful for constructing objects + // that implement the arena message construction protocol described above but + // take additional constructor arguments. + template GOOGLE_ATTRIBUTE_ALWAYS_INLINE + static T* CreateMessage(::google::protobuf::Arena* arena, + const Arg1& arg1, + const Arg2& arg2) { + if (arena == NULL) { + return new T(NULL, arg1, arg2); + } else { + return arena->CreateMessageInternal(static_cast(0), + arg1, arg2); + } + } + // API to create any objects on the arena. Note that only the object will // be created on the arena; the underlying ptrs (in case of a proto2 message) // will be still heap allocated. Proto messages should usually be allocated // with CreateMessage() instead. + // + // Note that even if T satisfies the arena message construction protocol + // (InternalArenaConstructable_ trait and optional DestructorSkippable_ + // trait), as described above, this function does not follow the protocol; + // instead, it treats T as a black-box type, just as if it did not have these + // traits. Specifically, T's constructor arguments will always be only those + // passed to Create() -- no additional arena pointer is implicitly added. + // Furthermore, the destructor will always be called at arena destruction time + // (unless the destructor is trivial). Hence, from T's point of view, it is as + // if the object were allocated on the heap (except that the underlying memory + // is obtained from the arena). template GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* Create(::google::protobuf::Arena* arena) { if (arena == NULL) { return new T(); } else { - return arena->CreateInternal( - SkipDeleteList(static_cast(0))); + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value); } } @@ -203,7 +292,7 @@ class LIBPROTOBUF_EXPORT Arena { if (arena == NULL) { return new T(arg); } else { - return arena->CreateInternal(SkipDeleteList(static_cast(0)), + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, arg); } } @@ -214,9 +303,8 @@ class LIBPROTOBUF_EXPORT Arena { if (arena == NULL) { return new T(arg1, arg2); } else { - return arena->CreateInternal(SkipDeleteList(static_cast(0)), - arg1, - arg2); + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2); } } @@ -229,10 +317,8 @@ class LIBPROTOBUF_EXPORT Arena { if (arena == NULL) { return new T(arg1, arg2, arg3); } else { - return arena->CreateInternal(SkipDeleteList(static_cast(0)), - arg1, - arg2, - arg3); + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2, arg3); } } @@ -246,20 +332,95 @@ class LIBPROTOBUF_EXPORT Arena { if (arena == NULL) { return new T(arg1, arg2, arg3, arg4); } else { - return arena->CreateInternal(SkipDeleteList(static_cast(0)), - arg1, - arg2, - arg3, - arg4); + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2, arg3, arg4); } } - // Create an array of object type T on the arena. Type T must have a trivial - // constructor, as it will not be invoked when created on the arena. + // Version of the above with five constructor arguments for the created + // object. + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* Create(::google::protobuf::Arena* arena, + const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, + const Arg5& arg5) { + if (arena == NULL) { + return new T(arg1, arg2, arg3, arg4, arg5); + } else { + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2, arg3, arg4, arg5); + } + } + + // Version of the above with six constructor arguments for the created + // object. + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* Create(::google::protobuf::Arena* arena, + const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, + const Arg5& arg5, const Arg6& arg6) { + if (arena == NULL) { + return new T(arg1, arg2, arg3, arg4, arg5, arg6); + } else { + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2, arg3, arg4, arg5, arg6); + } + } + + // Version of the above with seven constructor arguments for the created + // object. + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* Create(::google::protobuf::Arena* arena, + const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, + const Arg5& arg5, const Arg6& arg6, + const Arg7& arg7) { + if (arena == NULL) { + return new T(arg1, arg2, arg3, arg4, arg5, arg6, arg7); + } else { + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2, arg3, arg4, arg5, arg6, arg7); + } + } + + // Version of the above with eight constructor arguments for the created + // object. + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* Create(::google::protobuf::Arena* arena, + const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, + const Arg5& arg5, const Arg6& arg6, + const Arg7& arg7, const Arg8& arg8) { + if (arena == NULL) { + return new T(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + } else { + return arena->CreateInternal( + google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + } + } + + // Create an array of object type T on the arena *without* invoking the + // constructor of T. If `arena` is null, then the return value should be freed + // with `delete[] x;` (or `::operator delete[](x);`). + // To ensure safe uses, this function checks at compile time + // (when compiled as C++11) that T is trivially default-constructible and + // trivially destructible. template GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* CreateArray(::google::protobuf::Arena* arena, size_t num_elements) { +#if __cplusplus >= 201103L + static_assert(std::is_trivially_default_constructible::value, + "CreateArray requires a trivially constructible type"); + static_assert(std::is_trivially_destructible::value, + "CreateArray requires a trivially destructible type"); +#endif if (arena == NULL) { - return new T[num_elements]; + return static_cast(::operator new[](num_elements * sizeof(T))); } else { return arena->CreateInternalRawArray(num_elements); } @@ -374,27 +535,26 @@ class LIBPROTOBUF_EXPORT Arena { // wrap them in static functions. static ThreadCache& thread_cache(); #elif defined(GOOGLE_PROTOBUF_OS_ANDROID) || defined(GOOGLE_PROTOBUF_OS_IPHONE) - // Android ndk does not support __thread keyword so we use a custom thread + // Android ndk does not support GOOGLE_THREAD_LOCAL keyword so we use a custom thread // local storage class we implemented. - // iOS also does not support the __thread keyword. + // iOS also does not support the GOOGLE_THREAD_LOCAL keyword. static ThreadCache& thread_cache(); #else static GOOGLE_THREAD_LOCAL ThreadCache thread_cache_; static ThreadCache& thread_cache() { return thread_cache_; } #endif - // SFINAE for skipping addition to delete list for a Type. This is mainly to - // skip proto2/proto1 message objects with cc_enable_arenas=true from being - // part of the delete list. Also, note, compiler will optimize out the branch - // in CreateInternal. - // + // SFINAE for skipping addition to delete list for a message type when created + // with CreateMessage. This is mainly to skip proto2/proto1 message objects + // with cc_enable_arenas=true from being part of the delete list. Also, note, + // compiler will optimize out the branch in CreateInternal. template static inline bool SkipDeleteList(typename T::DestructorSkippable_*) { return true; } - // For non message objects, we skip addition to delete list if the object has - // a trivial destructor. + // For message objects that don't have the DestructorSkippable_ trait, we + // always add to the delete list. template static inline bool SkipDeleteList(...) { return google::protobuf::internal::has_trivial_destructor::value; @@ -419,14 +579,15 @@ class LIBPROTOBUF_EXPORT Arena { // Just allocate the required size for the given type assuming the // type has a trivial constructor. template GOOGLE_ATTRIBUTE_ALWAYS_INLINE - inline T* CreateInternalRawArray(uint32 num_elements) { - return static_cast(AllocateAligned(sizeof(T) * num_elements)); + inline T* CreateInternalRawArray(size_t num_elements) { + return static_cast( + AllocateAligned(RTTI_TYPE_ID(T), sizeof(T) * num_elements)); } template GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal( bool skip_explicit_ownership) { - T* t = new (AllocateAligned(sizeof(T))) T(); + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) T(); if (!skip_explicit_ownership) { AddListNode(t, &internal::arena_destruct_object); } @@ -436,7 +597,7 @@ class LIBPROTOBUF_EXPORT Arena { template GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal( bool skip_explicit_ownership, const Arg& arg) { - T* t = new (AllocateAligned(sizeof(T))) T(arg); + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) T(arg); if (!skip_explicit_ownership) { AddListNode(t, &internal::arena_destruct_object); } @@ -446,7 +607,7 @@ class LIBPROTOBUF_EXPORT Arena { template GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal( bool skip_explicit_ownership, const Arg1& arg1, const Arg2& arg2) { - T* t = new (AllocateAligned(sizeof(T))) T(arg1, arg2); + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) T(arg1, arg2); if (!skip_explicit_ownership) { AddListNode(t, &internal::arena_destruct_object); } @@ -458,7 +619,8 @@ class LIBPROTOBUF_EXPORT Arena { const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) { - T* t = new (AllocateAligned(sizeof(T))) T(arg1, arg2, arg3); + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) + T(arg1, arg2, arg3); if (!skip_explicit_ownership) { AddListNode(t, &internal::arena_destruct_object); } @@ -472,7 +634,79 @@ class LIBPROTOBUF_EXPORT Arena { const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) { - T* t = new (AllocateAligned(sizeof(T))) T(arg1, arg2, arg3, arg4); + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) + T(arg1, arg2, arg3, arg4); + if (!skip_explicit_ownership) { + AddListNode(t, &internal::arena_destruct_object); + } + return t; + } + + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership, + const Arg1& arg1, + const Arg2& arg2, + const Arg3& arg3, + const Arg4& arg4, + const Arg5& arg5) { + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) + T(arg1, arg2, arg3, arg4, arg5); + if (!skip_explicit_ownership) { + AddListNode(t, &internal::arena_destruct_object); + } + return t; + } + + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership, + const Arg1& arg1, + const Arg2& arg2, + const Arg3& arg3, + const Arg4& arg4, + const Arg5& arg5, + const Arg6& arg6) { + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) + T(arg1, arg2, arg3, arg4, arg5, arg6); + if (!skip_explicit_ownership) { + AddListNode(t, &internal::arena_destruct_object); + } + return t; + } + + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership, + const Arg1& arg1, + const Arg2& arg2, + const Arg3& arg3, + const Arg4& arg4, + const Arg5& arg5, + const Arg6& arg6, + const Arg7& arg7) { + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) + T(arg1, arg2, arg3, arg4, arg5, arg6, arg7); + if (!skip_explicit_ownership) { + AddListNode(t, &internal::arena_destruct_object); + } + return t; + } + + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership, + const Arg1& arg1, + const Arg2& arg2, + const Arg3& arg3, + const Arg4& arg4, + const Arg5& arg5, + const Arg6& arg6, + const Arg7& arg7, + const Arg8& arg8) { + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) + T(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); if (!skip_explicit_ownership) { AddListNode(t, &internal::arena_destruct_object); } @@ -485,6 +719,20 @@ class LIBPROTOBUF_EXPORT Arena { this); } + template GOOGLE_ATTRIBUTE_ALWAYS_INLINE + inline T* CreateMessageInternal(typename T::InternalArenaConstructable_*, + const Arg& arg) { + return CreateInternal(SkipDeleteList(static_cast(0)), + this, arg); + } + + template GOOGLE_ATTRIBUTE_ALWAYS_INLINE + inline T* CreateMessageInternal(typename T::InternalArenaConstructable_*, + const Arg1& arg1, const Arg2& arg2) { + return CreateInternal(SkipDeleteList(static_cast(0)), + this, arg1, arg2); + } + // CreateInArenaStorage is used to implement map field. Without it, // google::protobuf::Map need to call generated message's protected arena constructor, // which needs to declare google::protobuf::Map as friend of generated message. @@ -536,8 +784,15 @@ class LIBPROTOBUF_EXPORT Arena { return NULL; } + // Allocate and also optionally call on_arena_allocation callback with the + // allocated type info when the hooks are in place in ArenaOptions and + // the cookie is not null. + void* AllocateAligned(const std::type_info* allocated, size_t n); - void* AllocateAligned(size_t size); + // Allocate an internal allocation, avoiding optional typed monitoring. + GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline void* AllocateAligned(size_t n) { + return AllocateAligned(NULL, n); + } void Init(); @@ -596,6 +851,9 @@ class LIBPROTOBUF_EXPORT Arena { GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Arena); }; +// Defined above for supporting environments without RTTI. +#undef RTTI_TYPE_ID + template const typename Arena::is_arena_constructable::type Arena::is_arena_constructable::value = diff --git a/src/google/protobuf/arena_nc_test.py b/src/google/protobuf/arena_nc_test.py index f390df36..87a69b2a 100644 --- a/src/google/protobuf/arena_nc_test.py +++ b/src/google/protobuf/arena_nc_test.py @@ -35,6 +35,7 @@ import unittest from google3.testing.pybase import fake_target_util +import unittest class ArenaNcTest(unittest.TestCase): diff --git a/src/google/protobuf/arena_test_util.h b/src/google/protobuf/arena_test_util.h index 7db7a90e..690cc706 100644 --- a/src/google/protobuf/arena_test_util.h +++ b/src/google/protobuf/arena_test_util.h @@ -31,6 +31,7 @@ #ifndef GOOGLE_PROTOBUF_ARENA_TEST_UTIL_H__ #define GOOGLE_PROTOBUF_ARENA_TEST_UTIL_H__ + namespace google { namespace protobuf { namespace internal { diff --git a/src/google/protobuf/arena_unittest.cc b/src/google/protobuf/arena_unittest.cc index d9b198e0..873e85ae 100644 --- a/src/google/protobuf/arena_unittest.cc +++ b/src/google/protobuf/arena_unittest.cc @@ -39,6 +39,7 @@ #include #endif #include +#include #include #include @@ -47,11 +48,14 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include #include @@ -125,6 +129,29 @@ class MustBeConstructedWithOneThroughFour { GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MustBeConstructedWithOneThroughFour); }; +// A class that takes eight different types as constructor arguments. +class MustBeConstructedWithOneThroughEight { + public: + MustBeConstructedWithOneThroughEight( + int one, const char* two, const string& three, + const PleaseDontCopyMe* four, int five, const char* six, + const string& seven, const string& eight) + : one_(one), two_(two), three_(three), four_(four), five_(five), + six_(six), seven_(seven), eight_(eight) {} + + int one_; + const char* const two_; + string three_; + const PleaseDontCopyMe* four_; + int five_; + const char* const six_; + string seven_; + string eight_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MustBeConstructedWithOneThroughEight); +}; + } // namespace TEST(ArenaTest, ArenaConstructable) { @@ -156,7 +183,7 @@ TEST(ArenaTest, BasicCreate) { EXPECT_EQ(2, notifier.GetCount()); } -TEST(ArenaTest, CreateWithManyConstructorArguments) { +TEST(ArenaTest, CreateWithFourConstructorArguments) { Arena arena; const string three("3"); const PleaseDontCopyMe four(4); @@ -170,6 +197,26 @@ TEST(ArenaTest, CreateWithManyConstructorArguments) { ASSERT_EQ(4, new_object->four_->value()); } +TEST(ArenaTest, CreateWithEightConstructorArguments) { + Arena arena; + const string three("3"); + const PleaseDontCopyMe four(4); + const string seven("7"); + const string eight("8"); + const MustBeConstructedWithOneThroughEight* new_object = + Arena::Create( + &arena, 1, "2", three, &four, 5, "6", seven, eight); + EXPECT_TRUE(new_object != NULL); + ASSERT_EQ(1, new_object->one_); + ASSERT_STREQ("2", new_object->two_); + ASSERT_EQ("3", new_object->three_); + ASSERT_EQ(4, new_object->four_->value()); + ASSERT_EQ(5, new_object->five_); + ASSERT_STREQ("6", new_object->six_); + ASSERT_EQ("7", new_object->seven_); + ASSERT_EQ("8", new_object->eight_); +} + TEST(ArenaTest, InitialBlockTooSmall) { // Construct a small (64 byte) initial block of memory to be used by the // arena allocator; then, allocate an object which will not fit in the @@ -1113,6 +1160,19 @@ TEST(ArenaTest, GetArenaShouldReturnNullForNonArenaAllocatedMessages) { EXPECT_EQ(NULL, Arena::GetArena(const_pointer_to_message)); } +TEST(ArenaTest, UnsafeSetAllocatedOnArena) { + ::google::protobuf::Arena arena; + TestAllTypes* message = Arena::CreateMessage(&arena); + EXPECT_FALSE(message->has_optional_string()); + + string owned_string = "test with long enough content to heap-allocate"; + message->unsafe_arena_set_allocated_optional_string(&owned_string); + EXPECT_TRUE(message->has_optional_string()); + + message->unsafe_arena_set_allocated_optional_string(NULL); + EXPECT_FALSE(message->has_optional_string()); +} + // A helper utility class to only contain static hook functions, some // counters to be used to verify the counters have been called and a cookie // value to be verified. @@ -1124,6 +1184,13 @@ class ArenaHooksTestUtil { return static_cast(cookie); } + static void on_allocation(const std::type_info* /*unused*/, uint64 alloc_size, + void* cookie) { + ++num_allocations; + int cookie_value = *static_cast(cookie); + EXPECT_EQ(kCookieValue, cookie_value); + } + static void on_reset(::google::protobuf::Arena* arena, void* cookie, uint64 space_used) { ++num_reset; @@ -1141,10 +1208,12 @@ class ArenaHooksTestUtil { static const int kCookieValue = 999; static uint32 num_init; + static uint32 num_allocations; static uint32 num_reset; static uint32 num_destruct; }; uint32 ArenaHooksTestUtil::num_init = 0; +uint32 ArenaHooksTestUtil::num_allocations = 0; uint32 ArenaHooksTestUtil::num_reset = 0; uint32 ArenaHooksTestUtil::num_destruct = 0; const int ArenaHooksTestUtil::kCookieValue; @@ -1153,6 +1222,7 @@ const int ArenaHooksTestUtil::kCookieValue; TEST(ArenaTest, ArenaHooksSanity) { ::google::protobuf::ArenaOptions options; options.on_arena_init = ArenaHooksTestUtil::on_init; + options.on_arena_allocation = ArenaHooksTestUtil::on_allocation; options.on_arena_reset = ArenaHooksTestUtil::on_reset; options.on_arena_destruction = ArenaHooksTestUtil::on_destruction; @@ -1160,6 +1230,9 @@ TEST(ArenaTest, ArenaHooksSanity) { { ::google::protobuf::Arena arena(options); EXPECT_EQ(1, ArenaHooksTestUtil::num_init); + EXPECT_EQ(0, ArenaHooksTestUtil::num_allocations); + ::google::protobuf::Arena::Create(&arena); + EXPECT_EQ(1, ArenaHooksTestUtil::num_allocations); arena.Reset(); arena.Reset(); diff --git a/src/google/protobuf/compiler/code_generator.h b/src/google/protobuf/compiler/code_generator.h index 321a8ccd..b989f151 100644 --- a/src/google/protobuf/compiler/code_generator.h +++ b/src/google/protobuf/compiler/code_generator.h @@ -79,6 +79,37 @@ class LIBPROTOC_EXPORT CodeGenerator { GeneratorContext* generator_context, string* error) const = 0; + // Generates code for all given proto files, generating one or more files in + // the given output directory. + // + // This method should be called instead of |Generate()| when + // |HasGenerateAll()| returns |true|. It is used to emulate legacy semantics + // when more than one `.proto` file is specified on one compiler invocation. + // + // WARNING: Please do not use unless legacy semantics force the code generator + // to produce a single output file for all input files, or otherwise require + // an examination of all input files first. The canonical code generator + // design produces one output file per input .proto file, and we do not wish + // to encourage alternate designs. + // + // A parameter is given as passed on the command line, as in |Generate()| + // above. + // + // Returns true if successful. Otherwise, sets *error to a description of + // the problem (e.g. "invalid parameter") and returns false. + virtual bool GenerateAll(const vector& files, + const string& parameter, + GeneratorContext* generator_context, + string* error) const { + *error = "Unimplemented GenerateAll() method."; + return false; + } + + // Returns true if the code generator expects to receive all FileDescriptors + // at once (via |GenerateAll()|), rather than one at a time (via + // |Generate()|). This is required to implement legacy semantics. + virtual bool HasGenerateAll() const { return false; } + private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CodeGenerator); }; diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index 567238ae..291fb054 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -898,12 +898,14 @@ CommandLineInterface::ParseArguments(int argc, const char* const argv[]) { return PARSE_ARGUMENT_FAIL; } if (mode_ != MODE_COMPILE && !dependency_out_name_.empty()) { - cerr << "Can only use --dependency_out=FILE when generating code." << endl; + std::cerr << "Can only use --dependency_out=FILE when generating code." + << std::endl; return PARSE_ARGUMENT_FAIL; } if (!dependency_out_name_.empty() && input_files_.size() > 1) { - cerr << "Can only process one input file when using --dependency_out=FILE." - << endl; + std::cerr + << "Can only process one input file when using --dependency_out=FILE." + << std::endl; return PARSE_ARGUMENT_FAIL; } if (imports_in_descriptor_set_ && descriptor_set_name_.empty()) { @@ -1054,11 +1056,11 @@ CommandLineInterface::InterpretArgument(const string& name, } else if (name == "--dependency_out") { if (!dependency_out_name_.empty()) { - cerr << name << " may only be passed once." << endl; + std::cerr << name << " may only be passed once." << std::endl; return PARSE_ARGUMENT_FAIL; } if (value.empty()) { - cerr << name << " requires a non-empty value." << endl; + std::cerr << name << " requires a non-empty value." << std::endl; return PARSE_ARGUMENT_FAIL; } dependency_out_name_ = value; @@ -1272,7 +1274,8 @@ void CommandLineInterface::PrintHelpText() { " defined in the given proto files. Groups share\n" " the same field number space with the parent \n" " message. Extension ranges are counted as \n" -" occupied fields numbers." << std::endl; +" occupied fields numbers." + << std::endl; if (!plugin_prefix_.empty()) { std::cerr << " --plugin=EXECUTABLE Specifies a plugin executable to use.\n" @@ -1327,13 +1330,23 @@ bool CommandLineInterface::GenerateOutput( } parameters.append(generator_parameters_[output_directive.name]); } - for (int i = 0; i < parsed_files.size(); i++) { - if (!output_directive.generator->Generate(parsed_files[i], parameters, - generator_context, &error)) { - // Generator returned an error. - std::cerr << output_directive.name << ": " << parsed_files[i]->name() - << ": " << error << std::endl; - return false; + if (output_directive.generator->HasGenerateAll()) { + if (!output_directive.generator->GenerateAll( + parsed_files, parameters, generator_context, &error)) { + // Generator returned an error. + std::cerr << output_directive.name << ": " + << ": " << error << std::endl; + return false; + } + } else { + for (int i = 0; i < parsed_files.size(); i++) { + if (!output_directive.generator->Generate(parsed_files[i], parameters, + generator_context, &error)) { + // Generator returned an error. + std::cerr << output_directive.name << ": " << parsed_files[i]->name() + << ": " << error << std::endl; + return false; + } } } } @@ -1403,7 +1416,8 @@ bool CommandLineInterface::GenerateDependencyManifestFile( printer.Print(" $disk_file$", "disk_file", disk_file); if (i < file_set.file_size() - 1) printer.Print("\\\n"); } else { - cerr << "Unable to identify path for file " << virtual_file << endl; + std::cerr << "Unable to identify path for file " << virtual_file + << std::endl; return false; } } @@ -1673,6 +1687,10 @@ void GatherOccupiedFieldRanges(const Descriptor* descriptor, ranges->insert(FieldRange(descriptor->extension_range(i)->start, descriptor->extension_range(i)->end)); } + for (int i = 0; i < descriptor->reserved_range_count(); ++i) { + ranges->insert(FieldRange(descriptor->reserved_range(i)->start, + descriptor->reserved_range(i)->end)); + } // Handle the nested messages/groups in declaration order to make it // post-order strict. for (int i = 0; i < descriptor->nested_type_count(); ++i) { diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index e284c791..23d67e2b 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -115,10 +115,15 @@ class CommandLineInterfaceTest : public testing::Test { // Create a subdirectory within temp_directory_. void CreateTempDir(const string& name); +#ifdef PROTOBUF_OPENSOURCE // Change working directory to temp directory. void SwitchToTempDirectory() { File::ChangeWorkingDirectory(temp_directory_); } +#else // !PROTOBUF_OPENSOURCE + // TODO(teboring): Figure out how to change and get working directory in + // google3. +#endif // !PROTOBUF_OPENSOURCE void SetInputsAreProtoPathRelative(bool enable) { cli_.SetInputsAreProtoPathRelative(enable); @@ -986,6 +991,7 @@ TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileGivenTwoInputs) { "Can only process one input file when using --dependency_out=FILE.\n"); } +#ifdef PROTOBUF_OPENSOURCE TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) { CreateTempFile("foo.proto", "syntax = \"proto2\";\n" @@ -1011,6 +1017,10 @@ TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) { File::ChangeWorkingDirectory(current_working_directory); } +#else // !PROTOBUF_OPENSOURCE +// TODO(teboring): Figure out how to change and get working directory in +// google3. +#endif // !PROTOBUF_OPENSOURCE TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileForAbsolutePath) { CreateTempFile("foo.proto", diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc index 3eb20ab1..70d3a600 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc @@ -70,12 +70,23 @@ EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, EnumGenerator::~EnumGenerator() {} +void EnumGenerator::GenerateForwardDeclaration(io::Printer* printer) { + if (!options_.proto_h) { + return; + } + map vars; + vars["classname"] = classname_; + printer->Print(vars, "enum $classname$ : int;\n"); + printer->Print(vars, "bool $classname$_IsValid(int value);\n"); +} + void EnumGenerator::GenerateDefinition(io::Printer* printer) { map vars; vars["classname"] = classname_; vars["short_name"] = descriptor_->name(); + vars["enumbase"] = classname_ + (options_.proto_h ? " : int" : ""); - printer->Print(vars, "enum $classname$ {\n"); + printer->Print(vars, "enum $enumbase$ {\n"); printer->Indent(); const EnumValueDescriptor* min_value = descriptor_->value(0); @@ -90,7 +101,6 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) { vars["prefix"] = (descriptor_->containing_type() == NULL) ? "" : classname_ + "_"; - if (i > 0) printer->Print(",\n"); printer->Print(vars, "$prefix$$name$ = $number$"); diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.h b/src/google/protobuf/compiler/cpp/cpp_enum.h index 1ebd7cf7..3e930856 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.h +++ b/src/google/protobuf/compiler/cpp/cpp_enum.h @@ -60,6 +60,12 @@ class EnumGenerator { // Header stuff. + // Generate header code to forward-declare the enum. This is for use when + // generating other .proto.h files. This code should be placed within the + // enum's package namespace, but NOT within any class, even for nested + // enums. + void GenerateForwardDeclaration(io::Printer* printer); + // Generate header code defining the enum. This code should be placed // within the enum's package namespace, but NOT within any class, even for // nested enums. diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc index 74989703..965327b1 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -242,7 +242,7 @@ void RepeatedEnumFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { printer->Print(variables_, "::google::protobuf::RepeatedField $name$_;\n"); - if (descriptor_->options().packed() + if (descriptor_->is_packed() && HasGeneratedMethods(descriptor_->file())) { printer->Print(variables_, "mutable int _$name$_cached_byte_size_;\n"); @@ -352,7 +352,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { void RepeatedEnumFieldGenerator:: GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { - if (!descriptor_->options().packed()) { + if (!descriptor_->is_packed()) { // This path is rarely executed, so we use a non-inlined implementation. if (HasPreservingUnknownEnumSemantics(descriptor_->file())) { printer->Print(variables_, @@ -419,7 +419,7 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { void RepeatedEnumFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { // Write the tag and the size. printer->Print(variables_, "if (this->$name$_size() > 0) {\n" @@ -432,7 +432,7 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { } printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, " ::google::protobuf::internal::WireFormatLite::WriteEnumNoTag(\n" " this->$name$(i), output);\n"); @@ -446,7 +446,7 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { void RepeatedEnumFieldGenerator:: GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { // Write the tag and the size. printer->Print(variables_, "if (this->$name$_size() > 0) {\n" @@ -460,7 +460,7 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { } printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, " target = ::google::protobuf::internal::WireFormatLite::WriteEnumNoTagToArray(\n" " this->$name$(i), target);\n"); @@ -484,7 +484,7 @@ GenerateByteSize(io::Printer* printer) const { " this->$name$(i));\n" "}\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "if (data_size > 0) {\n" " total_size += $tag_size$ +\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_field.h b/src/google/protobuf/compiler/cpp/cpp_field.h index cd2b6b75..1d7f8233 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_field.h @@ -82,12 +82,38 @@ class FieldGenerator { // implementation is empty. virtual void GenerateStaticMembers(io::Printer* /*printer*/) const {} + // Generate prototypes for accessors that will manipulate imported + // messages inline. These are for .proto.h headers. + // + // In .proto.h mode, the headers of imports are not #included. However, + // functions that manipulate the imported message types need access to + // the class definition of the imported message, meaning that the headers + // must be #included. To get around this, functions that manipulate + // imported message objects are defined as dependent functions in a base + // template class. By making them dependent template functions, the + // function templates will not be instantiated until they are called, so + // we can defer to those translation units to #include the necessary + // generated headers. + // + // See: + // http://en.cppreference.com/w/cpp/language/class_template#Implicit_instantiation + // + // Most field types don't need this, so the default implementation is empty. + virtual void GenerateDependentAccessorDeclarations( + io::Printer* printer) const {} + // Generate prototypes for all of the accessor functions related to this // field. These are placed inside the class definition. virtual void GenerateAccessorDeclarations(io::Printer* printer) const = 0; + // Generate inline definitions of depenent accessor functions for this field. + // These are placed inside the header after all class definitions. + virtual void GenerateDependentInlineAccessorDefinitions( + io::Printer* printer) const {} + // Generate inline definitions of accessor functions for this field. // These are placed inside the header after all class definitions. + // In non-.proto.h mode, this generates dependent accessor functions as well. virtual void GenerateInlineAccessorDefinitions( io::Printer* printer, bool is_inline) const = 0; diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index a98c7d92..b997a51a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -94,113 +94,10 @@ FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options) FileGenerator::~FileGenerator() {} void FileGenerator::GenerateHeader(io::Printer* printer) { - string filename_identifier = FilenameIdentifier(file_->name()); - - // Generate top of header. - printer->Print( - "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" - "// source: $filename$\n" - "\n" - "#ifndef PROTOBUF_$filename_identifier$__INCLUDED\n" - "#define PROTOBUF_$filename_identifier$__INCLUDED\n" - "\n" - "#include \n" - "\n", - "filename", file_->name(), - "filename_identifier", filename_identifier); - - - printer->Print( - "#include \n" - "\n"); - - // Verify the protobuf library header version is compatible with the protoc - // version before going any further. - printer->Print( - "#if GOOGLE_PROTOBUF_VERSION < $min_header_version$\n" - "#error This file was generated by a newer version of protoc which is\n" - "#error incompatible with your Protocol Buffer headers. Please update\n" - "#error your headers.\n" - "#endif\n" - "#if $protoc_version$ < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION\n" - "#error This file was generated by an older version of protoc which is\n" - "#error incompatible with your Protocol Buffer headers. Please\n" - "#error regenerate this file with a newer version of protoc.\n" - "#endif\n" - "\n", - "min_header_version", - SimpleItoa(protobuf::internal::kMinHeaderVersionForProtoc), - "protoc_version", SimpleItoa(GOOGLE_PROTOBUF_VERSION)); - - // OK, it's now safe to #include other files. - printer->Print( - "#include \n" - "#include \n" - "#include \n"); - if (UseUnknownFieldSet(file_)) { - printer->Print( - "#include \n"); - } - if (file_->message_type_count() > 0) { - if (HasDescriptorMethods(file_)) { - printer->Print( - "#include \n"); - } else { - printer->Print( - "#include \n"); - } - } - printer->Print( - "#include \n" - "#include \n"); - if (HasMapFields(file_)) { - printer->Print( - "#include \n"); - if (HasDescriptorMethods(file_)) { - printer->Print( - "#include \n"); - } else { - printer->Print( - "#include \n"); - } - } - - if (HasEnumDefinitions(file_)) { - if (HasDescriptorMethods(file_)) { - printer->Print( - "#include \n"); - } else { - printer->Print( - "#include \n"); - } - } - - if (HasGenericServices(file_)) { - printer->Print( - "#include \n"); - } - - if (UseUnknownFieldSet(file_) && file_->message_type_count() > 0) { - printer->Print( - "#include \n"); - } - - - set public_import_names; - for (int i = 0; i < file_->public_dependency_count(); i++) { - public_import_names.insert(file_->public_dependency(i)->name()); - } - - for (int i = 0; i < file_->dependency_count(); i++) { - const string& name = file_->dependency(i)->name(); - bool public_import = (public_import_names.count(name) != 0); + GenerateTopHeaderGuard(printer); - - printer->Print( - "#include \"$dependency$.pb.h\"$iwyu$\n", - "dependency", StripProto(name), - "iwyu", (public_import) ? " // IWYU pragma: export" : ""); - } + GenerateLibraryIncludes(printer); + GenerateDependencyIncludes(printer); printer->Print( "// @@protoc_insertion_point(includes)\n"); @@ -210,132 +107,44 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { // Open namespace. GenerateNamespaceOpeners(printer); - // Forward-declare the AddDescriptors, AssignDescriptors, and ShutdownFile - // functions, so that we can declare them to be friends of each class. - printer->Print( - "\n" - "// Internal implementation detail -- do not call these.\n" - "void $dllexport_decl$$adddescriptorsname$();\n", - "adddescriptorsname", GlobalAddDescriptorsName(file_->name()), - "dllexport_decl", - options_.dllexport_decl.empty() ? "" : options_.dllexport_decl + " "); - - printer->Print( - // Note that we don't put dllexport_decl on these because they are only - // called by the .pb.cc file in which they are defined. - "void $assigndescriptorsname$();\n" - "void $shutdownfilename$();\n" - "\n", - "assigndescriptorsname", GlobalAssignDescriptorsName(file_->name()), - "shutdownfilename", GlobalShutdownFileName(file_->name())); - - // Generate forward declarations of classes. - for (int i = 0; i < file_->message_type_count(); i++) { - message_generators_[i]->GenerateForwardDeclaration(printer); - } + GenerateGlobalStateFunctionDeclarations(printer); + GenerateMessageForwardDeclarations(printer); printer->Print("\n"); - // Generate enum definitions. - for (int i = 0; i < file_->message_type_count(); i++) { - message_generators_[i]->GenerateEnumDefinitions(printer); - } - for (int i = 0; i < file_->enum_type_count(); i++) { - enum_generators_[i]->GenerateDefinition(printer); - } + GenerateEnumDefinitions(printer); printer->Print(kThickSeparator); printer->Print("\n"); - // Generate class definitions. - for (int i = 0; i < file_->message_type_count(); i++) { - if (i > 0) { - printer->Print("\n"); - printer->Print(kThinSeparator); - printer->Print("\n"); - } - message_generators_[i]->GenerateClassDefinition(printer); - } + GenerateMessageDefinitions(printer); printer->Print("\n"); printer->Print(kThickSeparator); printer->Print("\n"); - if (HasGenericServices(file_)) { - // Generate service definitions. - for (int i = 0; i < file_->service_count(); i++) { - if (i > 0) { - printer->Print("\n"); - printer->Print(kThinSeparator); - printer->Print("\n"); - } - service_generators_[i]->GenerateDeclarations(printer); - } - - printer->Print("\n"); - printer->Print(kThickSeparator); - printer->Print("\n"); - } + GenerateServiceDefinitions(printer); - // Declare extension identifiers. - for (int i = 0; i < file_->extension_count(); i++) { - extension_generators_[i]->GenerateDeclaration(printer); - } + GenerateExtensionIdentifiers(printer); printer->Print("\n"); printer->Print(kThickSeparator); printer->Print("\n"); - printer->Print("#if !PROTOBUF_INLINE_NOT_IN_HEADERS\n"); - // Generate class inline methods. - for (int i = 0; i < file_->message_type_count(); i++) { - if (i > 0) { - printer->Print(kThinSeparator); - printer->Print("\n"); - } - message_generators_[i]->GenerateInlineMethods(printer, - /* is_inline = */ true); - } - printer->Print("#endif // !PROTOBUF_INLINE_NOT_IN_HEADERS\n"); - - printer->Print( - "\n" - "// @@protoc_insertion_point(namespace_scope)\n"); + GenerateInlineFunctionDefinitions(printer); // Close up namespace. GenerateNamespaceClosers(printer); - // Emit GetEnumDescriptor specializations into google::protobuf namespace: - if (HasEnumDefinitions(file_)) { - // The SWIG conditional is to avoid a null-pointer dereference - // (bug 1984964) in swig-1.3.21 resulting from the following syntax: - // namespace X { void Y(); } - // which appears in GetEnumDescriptor() specializations. - printer->Print( - "\n" - "#ifndef SWIG\n" - "namespace google {\nnamespace protobuf {\n" - "\n"); - for (int i = 0; i < file_->message_type_count(); i++) { - message_generators_[i]->GenerateGetEnumDescriptorSpecializations(printer); - } - for (int i = 0; i < file_->enum_type_count(); i++) { - enum_generators_[i]->GenerateGetEnumDescriptorSpecializations(printer); - } - printer->Print( - "\n" - "} // namespace protobuf\n} // namespace google\n" - "#endif // SWIG\n"); - } + // We need to specialize some templates in the ::google::protobuf namespace: + GenerateProto2NamespaceEnumSpecializations(printer); printer->Print( "\n" "// @@protoc_insertion_point(global_scope)\n" "\n"); - printer->Print( - "#endif // PROTOBUF_$filename_identifier$__INCLUDED\n", - "filename_identifier", filename_identifier); + GenerateBottomHeaderGuard(printer); } void FileGenerator::GenerateSource(io::Printer* printer) { @@ -707,6 +516,294 @@ void FileGenerator::GenerateNamespaceClosers(io::Printer* printer) { } } +void FileGenerator::GenerateTopHeaderGuard(io::Printer* printer) { + string filename_identifier = FilenameIdentifier(file_->name()); + // Generate top of header. + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "// source: $filename$\n" + "\n" + "#ifndef PROTOBUF_$filename_identifier$__INCLUDED\n" + "#define PROTOBUF_$filename_identifier$__INCLUDED\n" + "\n" + "#include \n" + "\n", + "filename", file_->name(), + "filename_identifier", filename_identifier); +} + +void FileGenerator::GenerateBottomHeaderGuard(io::Printer* printer) { + string filename_identifier = FilenameIdentifier(file_->name()); + printer->Print( + "#endif // PROTOBUF_$filename_identifier$__INCLUDED\n", + "filename_identifier", filename_identifier); +} + +void FileGenerator::GenerateLibraryIncludes(io::Printer* printer) { + + printer->Print( + "#include \n" + "\n"); + + // Verify the protobuf library header version is compatible with the protoc + // version before going any further. + printer->Print( + "#if GOOGLE_PROTOBUF_VERSION < $min_header_version$\n" + "#error This file was generated by a newer version of protoc which is\n" + "#error incompatible with your Protocol Buffer headers. Please update\n" + "#error your headers.\n" + "#endif\n" + "#if $protoc_version$ < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION\n" + "#error This file was generated by an older version of protoc which is\n" + "#error incompatible with your Protocol Buffer headers. Please\n" + "#error regenerate this file with a newer version of protoc.\n" + "#endif\n" + "\n", + "min_header_version", + SimpleItoa(protobuf::internal::kMinHeaderVersionForProtoc), + "protoc_version", SimpleItoa(GOOGLE_PROTOBUF_VERSION)); + + // OK, it's now safe to #include other files. + printer->Print( + "#include \n" + "#include \n" + "#include \n"); + if (UseUnknownFieldSet(file_)) { + printer->Print( + "#include \n"); + } + if (file_->message_type_count() > 0) { + if (HasDescriptorMethods(file_)) { + printer->Print( + "#include \n"); + } else { + printer->Print( + "#include \n"); + } + } + printer->Print( + "#include \n" + "#include \n"); + if (HasMapFields(file_)) { + printer->Print( + "#include \n"); + if (HasDescriptorMethods(file_)) { + printer->Print( + "#include \n"); + } else { + printer->Print( + "#include \n"); + } + } + + if (HasEnumDefinitions(file_)) { + if (HasDescriptorMethods(file_)) { + printer->Print( + "#include \n"); + } else { + printer->Print( + "#include \n"); + } + } + + if (HasGenericServices(file_)) { + printer->Print( + "#include \n"); + } + + if (UseUnknownFieldSet(file_) && file_->message_type_count() > 0) { + printer->Print( + "#include \n"); + } + + + if (IsAnyMessage(file_)) { + printer->Print( + "#include \"google/protobuf/any.h\"\n"); + } +} + +void FileGenerator::GenerateDependencyIncludes(io::Printer* printer) { + set public_import_names; + for (int i = 0; i < file_->public_dependency_count(); i++) { + public_import_names.insert(file_->public_dependency(i)->name()); + } + + for (int i = 0; i < file_->dependency_count(); i++) { + const string& name = file_->dependency(i)->name(); + bool public_import = (public_import_names.count(name) != 0); + + + printer->Print( + "#include \"$dependency$.pb.h\"$iwyu$\n", + "dependency", StripProto(name), + "iwyu", (public_import) ? " // IWYU pragma: export" : ""); + } +} + +void FileGenerator::GenerateGlobalStateFunctionDeclarations( + io::Printer* printer) { + // Forward-declare the AddDescriptors, AssignDescriptors, and ShutdownFile + // functions, so that we can declare them to be friends of each class. + printer->Print( + "\n" + "// Internal implementation detail -- do not call these.\n" + "void $dllexport_decl$$adddescriptorsname$();\n", + "adddescriptorsname", GlobalAddDescriptorsName(file_->name()), + "dllexport_decl", + options_.dllexport_decl.empty() ? "" : options_.dllexport_decl + " "); + + printer->Print( + // Note that we don't put dllexport_decl on these because they are only + // called by the .pb.cc file in which they are defined. + "void $assigndescriptorsname$();\n" + "void $shutdownfilename$();\n" + "\n", + "assigndescriptorsname", GlobalAssignDescriptorsName(file_->name()), + "shutdownfilename", GlobalShutdownFileName(file_->name())); +} + +void FileGenerator::GenerateMessageForwardDeclarations(io::Printer* printer) { + // Generate forward declarations of classes. + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateMessageForwardDeclaration(printer); + } +} + +void FileGenerator::GenerateMessageDefinitions(io::Printer* printer) { + // Generate class definitions. + for (int i = 0; i < file_->message_type_count(); i++) { + if (i > 0) { + printer->Print("\n"); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + message_generators_[i]->GenerateClassDefinition(printer); + } +} + +void FileGenerator::GenerateEnumDefinitions(io::Printer* printer) { + // Generate enum definitions. + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateEnumDefinitions(printer); + } + for (int i = 0; i < file_->enum_type_count(); i++) { + enum_generators_[i]->GenerateDefinition(printer); + } +} + +void FileGenerator::GenerateServiceDefinitions(io::Printer* printer) { + if (HasGenericServices(file_)) { + // Generate service definitions. + for (int i = 0; i < file_->service_count(); i++) { + if (i > 0) { + printer->Print("\n"); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + service_generators_[i]->GenerateDeclarations(printer); + } + + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + } +} + +void FileGenerator::GenerateExtensionIdentifiers(io::Printer* printer) { + // Declare extension identifiers. + for (int i = 0; i < file_->extension_count(); i++) { + extension_generators_[i]->GenerateDeclaration(printer); + } +} + +void FileGenerator::GenerateInlineFunctionDefinitions(io::Printer* printer) { + // An aside about inline functions in .proto.h mode: + // + // The PROTOBUF_INLINE_NOT_IN_HEADERS symbol controls conditionally + // moving much of the inline functions to the .pb.cc file, which can be a + // significant performance benefit for compilation time, at the expense + // of non-inline function calls. + // + // However, in .proto.h mode, the definition of the internal dependent + // base class must remain in the header, and can never be out-lined. The + // dependent base class also needs access to has-bit manipuation + // functions, so the has-bit functions must be unconditionally inlined in + // proto_h mode. + // + // This gives us three flavors of functions: + // + // 1. Functions on the message not used by the internal dependent base + // class: in .proto.h mode, only some functions are defined on the + // message class; others are defined on the dependent base class. + // These are guarded and can be out-lined. These are generated by + // GenerateInlineMethods, and include has_* bit functions in + // non-proto_h mode. + // + // 2. Functions on the internal dependent base class: these functions + // are dependent on a template parameter, so they always need to + // remain in the header. + // + // 3. Functions on the message that are used by the dependent base: the + // dependent base class down casts itself to the message + // implementation class to access these functions (the has_* bit + // manipulation functions). Unlike #1, these functions must + // unconditionally remain in the header. These are emitted by + // GenerateDependentInlineMethods, even though they are not actually + // dependent. + + printer->Print("#if !PROTOBUF_INLINE_NOT_IN_HEADERS\n"); + // Generate class inline methods. + for (int i = 0; i < file_->message_type_count(); i++) { + if (i > 0) { + printer->Print(kThinSeparator); + printer->Print("\n"); + } + message_generators_[i]->GenerateInlineMethods(printer, + /* is_inline = */ true); + } + printer->Print("#endif // !PROTOBUF_INLINE_NOT_IN_HEADERS\n"); + + for (int i = 0; i < file_->message_type_count(); i++) { + if (i > 0) { + printer->Print(kThinSeparator); + printer->Print("\n"); + } + // Methods of the dependent base class must always be inline in the header. + message_generators_[i]->GenerateDependentInlineMethods(printer); + } + + printer->Print( + "\n" + "// @@protoc_insertion_point(namespace_scope)\n"); +} + +void FileGenerator::GenerateProto2NamespaceEnumSpecializations( + io::Printer* printer) { + // Emit GetEnumDescriptor specializations into google::protobuf namespace: + if (HasEnumDefinitions(file_)) { + // The SWIG conditional is to avoid a null-pointer dereference + // (bug 1984964) in swig-1.3.21 resulting from the following syntax: + // namespace X { void Y(); } + // which appears in GetEnumDescriptor() specializations. + printer->Print( + "\n" + "#ifndef SWIG\n" + "namespace google {\nnamespace protobuf {\n" + "\n"); + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateGetEnumDescriptorSpecializations(printer); + } + for (int i = 0; i < file_->enum_type_count(); i++) { + enum_generators_[i]->GenerateGetEnumDescriptorSpecializations(printer); + } + printer->Print( + "\n" + "} // namespace protobuf\n} // namespace google\n" + "#endif // SWIG\n"); + } +} + } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_file.h b/src/google/protobuf/compiler/cpp/cpp_file.h index 0e06547d..e68f67bb 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.h +++ b/src/google/protobuf/compiler/cpp/cpp_file.h @@ -80,6 +80,35 @@ class FileGenerator { void GenerateNamespaceOpeners(io::Printer* printer); void GenerateNamespaceClosers(io::Printer* printer); + // Generates top or bottom of a header file. + void GenerateTopHeaderGuard(io::Printer* printer); + void GenerateBottomHeaderGuard(io::Printer* printer); + + // Generates #include directives. + void GenerateLibraryIncludes(io::Printer* printer); + void GenerateDependencyIncludes(io::Printer* printer); + + // Generates a couple of different pieces before definitions: + void GenerateGlobalStateFunctionDeclarations(io::Printer* printer); + + // Generates types for classes. + void GenerateMessageForwardDeclarations(io::Printer* printer); + void GenerateMessageDefinitions(io::Printer* printer); + + // Generates enum definitions. + void GenerateEnumDefinitions(io::Printer* printer); + + // Generates generic service definitions. + void GenerateServiceDefinitions(io::Printer* printer); + + // Generates extension identifiers. + void GenerateExtensionIdentifiers(io::Printer* printer); + + // Generates inline function defintions. + void GenerateInlineFunctionDefinitions(io::Printer* printer); + + void GenerateProto2NamespaceEnumSpecializations(io::Printer* printer); + const FileDescriptor* file_; google::protobuf::scoped_array > message_generators_; diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.cc b/src/google/protobuf/compiler/cpp/cpp_generator.cc index c999b93f..99416372 100644 --- a/src/google/protobuf/compiler/cpp/cpp_generator.cc +++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc @@ -82,6 +82,7 @@ bool CppGenerator::Generate(const FileDescriptor* file, // } // FOO_EXPORT is a macro which should expand to __declspec(dllexport) or // __declspec(dllimport) depending on what is being compiled. + // Options file_options; for (int i = 0; i < options.size(); i++) { diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc index 4e7155c3..0f3688d0 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc @@ -51,6 +51,9 @@ namespace cpp { namespace { +static const char kAnyMessageName[] = "Any"; +static const char kAnyProtoFile[] = "google/protobuf/any.proto"; + string DotsToUnderscores(const string& name) { return StringReplace(name, ".", "_", true); } @@ -162,6 +165,10 @@ string ClassName(const EnumDescriptor* enum_descriptor, bool qualified) { } +string DependentBaseClassTemplateName(const Descriptor* descriptor) { + return ClassName(descriptor, false) + "_InternalBase"; +} + string SuperClassName(const Descriptor* descriptor) { return HasDescriptorMethods(descriptor->file()) ? "::google::protobuf::Message" : "::google::protobuf::MessageLite"; @@ -200,6 +207,47 @@ string FieldConstantName(const FieldDescriptor *field) { return result; } +bool IsFieldDependent(const FieldDescriptor* field) { + if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { + return false; + } + if (field->containing_oneof() != NULL) { + // Oneof fields will always be dependent. + // + // This is a unique case for field codegen. Field generators are + // responsible for generating all the field-specific accessor + // functions, except for the clear_*() function; instead, field + // generators produce inline clearing code. + // + // For non-oneof fields, the Message class uses the inline clearing + // code to define the field's clear_*() function, as well as in the + // destructor. For oneof fields, the Message class generates a much + // more complicated clear_*() function, which clears only the oneof + // member that is set, in addition to clearing methods for each of the + // oneof members individually. + // + // Since oneofs do not have their own generator class, the Message code + // generation logic would be significantly complicated in order to + // split dependent and non-dependent manipulation logic based on + // whether the oneof truly needs to be dependent; so, for oneof fields, + // we just assume it (and its constituents) should be manipulated by a + // dependent base class function. + // + // This is less precise than how dependent message-typed fields are + // handled, but the cost is limited to only the generated code for the + // oneof field, which seems like an acceptable tradeoff. + return true; + } + if (field->file() == field->message_type()->file()) { + return false; + } + return true; +} + +string DependentTypeName(const FieldDescriptor* field) { + return "InternalBase_" + field->name() + "_T"; +} + string FieldMessageTypeName(const FieldDescriptor* field) { // Note: The Google-internal version of Protocol Buffers uses this function // as a hook point for hacks to support legacy code. @@ -360,7 +408,7 @@ string FilenameIdentifier(const string& filename) { } else { // Not alphanumeric. To avoid any possibility of name conflicts we // use the hex code for the character. - StrAppend(&result, "_", ToHex(static_cast(filename[i]))); + StrAppend(&result, "_", strings::Hex(static_cast(filename[i]))); } } return result; @@ -521,6 +569,15 @@ FieldOptions::CType EffectiveStringCType(const FieldDescriptor* field) { } +bool IsAnyMessage(const FileDescriptor* descriptor) { + return descriptor->name() == kAnyProtoFile; +} + +bool IsAnyMessage(const Descriptor* descriptor) { + return descriptor->name() == kAnyMessageName && + descriptor->file()->name() == kAnyProtoFile; +} + } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h index 284fa2c1..4bbf8303 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.h +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h @@ -66,6 +66,10 @@ extern const char kThinSeparator[]; string ClassName(const Descriptor* descriptor, bool qualified); string ClassName(const EnumDescriptor* enum_descriptor, bool qualified); +// Name of the CRTP class template (for use with proto_h). +// This is a class name, like "ProtoName_InternalBase". +string DependentBaseClassTemplateName(const Descriptor* descriptor); + string SuperClassName(const Descriptor* descriptor); // Get the (unqualified) name that should be used for this field in C++ code. @@ -88,6 +92,20 @@ inline const Descriptor* FieldScope(const FieldDescriptor* field) { field->extension_scope() : field->containing_type(); } +// Returns true if the given 'field_descriptor' has a message type that is +// a dependency of the file where the field is defined (i.e., the field +// type is defined in a different file than the message holding the field). +// +// This only applies to Message-typed fields. Enum-typed fields may refer +// to an enum in a dependency; however, enums are specified and +// forward-declared with an enum-base, so the definition is not required to +// manipulate the field value. +bool IsFieldDependent(const FieldDescriptor* field_descriptor); + +// Returns the name that should be used for forcing dependent lookup from a +// dependent base class. +string DependentTypeName(const FieldDescriptor* field); + // Returns the fully-qualified type name field->message_type(). Usually this // is just ClassName(field->message_type(), true); string FieldMessageTypeName(const FieldDescriptor* field); @@ -242,6 +260,9 @@ inline bool SupportsArenas(const FieldDescriptor* field) { return SupportsArenas(field->file()); } +bool IsAnyMessage(const FileDescriptor* descriptor); +bool IsAnyMessage(const Descriptor* descriptor); + } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_map_field.cc b/src/google/protobuf/compiler/cpp/cpp_map_field.cc index 8c38db2b..0ff0d27c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_map_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_map_field.cc @@ -244,7 +244,8 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { "{\n" " ::google::protobuf::scoped_ptr<$map_classname$> entry;\n" " for (::google::protobuf::Map< $key_cpp$, $val_cpp$ >::const_iterator\n" - " it = $name$().begin(); it != $name$().end(); ++it) {\n"); + " it = this->$name$().begin();\n" + " it != this->$name$().end(); ++it) {\n"); // If entry is allocated by arena, its desctructor should be avoided. if (SupportsArenas(descriptor_)) { @@ -277,7 +278,8 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { "{\n" " ::google::protobuf::scoped_ptr<$map_classname$> entry;\n" " for (::google::protobuf::Map< $key_cpp$, $val_cpp$ >::const_iterator\n" - " it = $name$().begin(); it != $name$().end(); ++it) {\n"); + " it = this->$name$().begin();\n" + " it != this->$name$().end(); ++it) {\n"); // If entry is allocated by arena, its desctructor should be avoided. if (SupportsArenas(descriptor_)) { @@ -312,7 +314,8 @@ GenerateByteSize(io::Printer* printer) const { "{\n" " ::google::protobuf::scoped_ptr<$map_classname$> entry;\n" " for (::google::protobuf::Map< $key_cpp$, $val_cpp$ >::const_iterator\n" - " it = $name$().begin(); it != $name$().end(); ++it) {\n"); + " it = this->$name$().begin();\n" + " it != this->$name$().end(); ++it) {\n"); // If entry is allocated by arena, its desctructor should be avoided. if (SupportsArenas(descriptor_)) { diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index 98929b1e..212fe2e1 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -64,10 +64,15 @@ using internal::WireFormatLite; namespace { -void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) { - // Print the field's proto-syntax definition as a comment. We don't want to - // print group bodies so we cut off after the first line. - string def = field->DebugString(); +template +void PrintFieldComment(io::Printer* printer, const T* field) { + // Print the field's (or oneof's) proto-syntax definition as a comment. + // We don't want to print group bodies so we cut off after the first + // line. + DebugStringOptions options; + options.elide_group_body = true; + options.elide_oneof_body = true; + string def = field->DebugStringWithOptions(options); printer->Print("// $def$\n", "def", def.substr(0, def.find_first_of('\n'))); } @@ -280,6 +285,10 @@ void OptimizePadding(vector* fields) { } } +string MessageTypeProtoName(const FieldDescriptor* field) { + return field->message_type()->full_name(); +} + // Emits an if-statement with a condition that evaluates to true if |field| is // considered non-default (will be sent over the wire), for message types // without true field presence. Should only be called if @@ -379,7 +388,8 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, enum_generators_( new google::protobuf::scoped_ptr[descriptor->enum_type_count()]), extension_generators_(new google::protobuf::scoped_ptr< - ExtensionGenerator>[descriptor->extension_count()]) { + ExtensionGenerator>[descriptor->extension_count()]), + use_dependent_base_(false) { for (int i = 0; i < descriptor->nested_type_count(); i++) { nested_generators_[i].reset( @@ -401,13 +411,16 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, if (descriptor->field(i)->is_required()) { ++num_required_fields_; } + if (options.proto_h && IsFieldDependent(descriptor->field(i))) { + use_dependent_base_ = true; + } } } MessageGenerator::~MessageGenerator() {} void MessageGenerator:: -GenerateForwardDeclaration(io::Printer* printer) { +GenerateMessageForwardDeclaration(io::Printer* printer) { printer->Print("class $classname$;\n", "classname", classname_); @@ -416,7 +429,17 @@ GenerateForwardDeclaration(io::Printer* printer) { // message cannot be a top level class, we just need to avoid calling // GenerateForwardDeclaration here. if (IsMapEntryMessage(descriptor_->nested_type(i))) continue; - nested_generators_[i]->GenerateForwardDeclaration(printer); + nested_generators_[i]->GenerateMessageForwardDeclaration(printer); + } +} + +void MessageGenerator:: +GenerateEnumForwardDeclaration(io::Printer* printer) { + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateEnumForwardDeclaration(printer); + } + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + enum_generators_[i]->GenerateForwardDeclaration(printer); } } @@ -441,6 +464,35 @@ GenerateGetEnumDescriptorSpecializations(io::Printer* printer) { } } +void MessageGenerator:: +GenerateDependentFieldAccessorDeclarations(io::Printer* printer) { + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + PrintFieldComment(printer, field); + + map vars; + SetCommonFieldVariables(field, &vars, options_); + + if (use_dependent_base_ && IsFieldDependent(field)) { + // If the message is dependent, the inline clear_*() method will need + // to delete the message type, so it must be in the dependent base + // class. (See also GenerateFieldAccessorDeclarations.) + printer->Print(vars, "void clear_$name$()$deprecation$;\n"); + } + // Generate type-specific accessor declarations. + field_generators_.get(field).GenerateDependentAccessorDeclarations(printer); + printer->Print("\n"); + } + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + const OneofDescriptor* oneof = descriptor_->oneof_decl(i); + PrintFieldComment(printer, oneof); + printer->Print( + "void clear_$oneof_name$();\n", + "oneof_name", oneof->name()); + } +} + void MessageGenerator:: GenerateFieldAccessorDeclarations(io::Printer* printer) { for (int i = 0; i < descriptor_->field_count(); i++) { @@ -452,6 +504,19 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { SetCommonFieldVariables(field, &vars, options_); vars["constant_name"] = FieldConstantName(field); + bool dependent_field = use_dependent_base_ && IsFieldDependent(field); + if (dependent_field) { + // If this field is dependent, the dependent base class determines + // the message type from the derived class (which is a template + // parameter). This typedef is for that: + printer->Print( + "private:\n" + "typedef $field_type$ $dependent_type$;\n" + "public:\n", + "field_type", FieldMessageTypeName(field), + "dependent_type", DependentTypeName(field)); + } + if (field->is_repeated()) { printer->Print(vars, "int $name$_size() const$deprecation$;\n"); } else if (HasHasMethod(field)) { @@ -463,7 +528,11 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { "public:\n"); } - printer->Print(vars, "void clear_$name$()$deprecation$;\n"); + if (!dependent_field) { + // If this field is dependent, then its clear_() method is in the + // depenent base class. (See also GenerateDependentAccessorDeclarations.) + printer->Print(vars, "void clear_$name$()$deprecation$;\n"); + } printer->Print(vars, "static const int $constant_name$ = $number$;\n"); // Generate type-specific accessor declarations. @@ -489,6 +558,188 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { } } +void MessageGenerator:: +GenerateDependentFieldAccessorDefinitions(io::Printer* printer) { + if (!use_dependent_base_) return; + + printer->Print("// $classname$\n\n", "classname", + DependentBaseClassTemplateName(descriptor_)); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + PrintFieldComment(printer, field); + + // These functions are not really dependent: they are part of the + // (non-dependent) derived class. However, they need to live outside + // any #ifdef guards, so we treat them as if they were dependent. + // + // See the comment in FileGenerator::GenerateInlineFunctionDefinitions + // for a more complete explanation. + if (use_dependent_base_ && IsFieldDependent(field)) { + map vars; + SetCommonFieldVariables(field, &vars, options_); + vars["inline"] = "inline "; + if (field->containing_oneof()) { + vars["field_name"] = UnderscoresToCamelCase(field->name(), true); + vars["oneof_name"] = field->containing_oneof()->name(); + vars["oneof_index"] = SimpleItoa(field->containing_oneof()->index()); + GenerateOneofMemberHasBits(field, vars, printer); + } else if (!field->is_repeated()) { + // There will be no header guard, so this always has to be inline. + GenerateSingularFieldHasBits(field, vars, printer); + } + // vars needed for clear_(), which is in the dependent base: + // (See also GenerateDependentFieldAccessorDeclarations.) + vars["tmpl"] = "template\n"; + vars["dependent_classname"] = + DependentBaseClassTemplateName(descriptor_) + ""; + vars["this_message"] = "reinterpret_cast(this)->"; + vars["this_const_message"] = "reinterpret_cast(this)->"; + GenerateFieldClear(field, vars, printer); + } + + // Generate type-specific accessors. + field_generators_.get(field) + .GenerateDependentInlineAccessorDefinitions(printer); + + printer->Print("\n"); + } + + // Generate has_$name$() and clear_has_$name$() functions for oneofs + // Similar to other has-bits, these must always be in the header if we + // are using a dependent base class. + GenerateOneofHasBits(printer, true /* is_inline */); +} + +void MessageGenerator:: +GenerateSingularFieldHasBits(const FieldDescriptor* field, + map vars, + io::Printer* printer) { + if (HasFieldPresence(descriptor_->file())) { + // N.B.: without field presence, we do not use has-bits or generate + // has_$name$() methods. + vars["has_array_index"] = SimpleItoa(field->index() / 32); + vars["has_mask"] = StrCat(strings::Hex(1u << (field->index() % 32), + strings::Hex::ZERO_PAD_8)); + printer->Print(vars, + "$inline$" + "bool $classname$::has_$name$() const {\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"); + } else { + // Message fields have a has_$name$() method. + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + bool is_lazy = false; + if (is_lazy) { + printer->Print(vars, + "$inline$" + "bool $classname$::has_$name$() const {\n" + " return !$name$_.IsCleared();\n" + "}\n"); + } else { + printer->Print(vars, + "$inline$" + "bool $classname$::has_$name$() const {\n" + " return !_is_default_instance_ && $name$_ != NULL;\n" + "}\n"); + } + } + } +} + +void MessageGenerator:: +GenerateOneofHasBits(io::Printer* printer, bool is_inline) { + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + map vars; + vars["oneof_name"] = descriptor_->oneof_decl(i)->name(); + vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); + vars["cap_oneof_name"] = + ToUpper(descriptor_->oneof_decl(i)->name()); + vars["classname"] = classname_; + vars["inline"] = (is_inline ? "inline " : ""); + printer->Print( + vars, + "$inline$" + "bool $classname$::has_$oneof_name$() const {\n" + " return $oneof_name$_case() != $cap_oneof_name$_NOT_SET;\n" + "}\n" + "$inline$" + "void $classname$::clear_has_$oneof_name$() {\n" + " _oneof_case_[$oneof_index$] = $cap_oneof_name$_NOT_SET;\n" + "}\n"); + } +} + +void MessageGenerator:: +GenerateOneofMemberHasBits(const FieldDescriptor* field, + const map& vars, + io::Printer* printer) { + // Singular field in a oneof + // N.B.: Without field presence, we do not use has-bits or generate + // has_$name$() methods, but oneofs still have set_has_$name$(). + // Oneofs also have has_$name$() but only as a private helper + // method, so that generated code is slightly cleaner (vs. comparing + // _oneof_case_[index] against a constant everywhere). + printer->Print(vars, + "$inline$" + "bool $classname$::has_$name$() const {\n" + " return $oneof_name$_case() == k$field_name$;\n" + "}\n"); + printer->Print(vars, + "$inline$" + "void $classname$::set_has_$name$() {\n" + " _oneof_case_[$oneof_index$] = k$field_name$;\n" + "}\n"); +} + +void MessageGenerator:: +GenerateFieldClear(const FieldDescriptor* field, + const map& vars, + io::Printer* printer) { + // Generate clear_$name$() (See GenerateFieldAccessorDeclarations and + // GenerateDependentFieldAccessorDeclarations, $dependent_classname$ is + // set by the Generate*Definitions functions.) + printer->Print(vars, + "$tmpl$" + "$inline$" + "void $dependent_classname$::clear_$name$() {\n"); + + printer->Indent(); + + if (field->containing_oneof()) { + // Clear this field only if it is the active field in this oneof, + // otherwise ignore + printer->Print(vars, + "if ($this_message$has_$name$()) {\n"); + printer->Indent(); + field_generators_.get(field).GenerateClearingCode(printer); + printer->Print(vars, + "$this_message$clear_has_$oneof_name$();\n"); + printer->Outdent(); + printer->Print("}\n"); + } else { + field_generators_.get(field).GenerateClearingCode(printer); + if (HasFieldPresence(descriptor_->file())) { + if (!field->is_repeated()) { + printer->Print(vars, + "$this_message$clear_has_$name$();\n"); + } + } + } + + printer->Outdent(); + printer->Print("}\n"); +} + void MessageGenerator:: GenerateFieldAccessorDefinitions(io::Printer* printer, bool is_inline) { printer->Print("// $classname$\n\n", "classname", classname_); @@ -500,101 +751,37 @@ GenerateFieldAccessorDefinitions(io::Printer* printer, bool is_inline) { map vars; SetCommonFieldVariables(field, &vars, options_); - vars["inline"] = is_inline ? "inline" : ""; + vars["inline"] = is_inline ? "inline " : ""; // Generate has_$name$() or $name$_size(). if (field->is_repeated()) { printer->Print(vars, - "$inline$ int $classname$::$name$_size() const {\n" + "$inline$" + "int $classname$::$name$_size() const {\n" " return $name$_.size();\n" "}\n"); } else if (field->containing_oneof()) { - // Singular field in a oneof - // N.B.: Without field presence, we do not use has-bits or generate - // has_$name$() methods, but oneofs still have set_has_$name$(). - // Oneofs also have has_$name$() but only as a private helper - // method, so that generated code is slightly cleaner (vs. comparing - // _oneof_case_[index] against a constant everywhere). vars["field_name"] = UnderscoresToCamelCase(field->name(), true); vars["oneof_name"] = field->containing_oneof()->name(); vars["oneof_index"] = SimpleItoa(field->containing_oneof()->index()); - printer->Print(vars, - "$inline$ bool $classname$::has_$name$() const {\n" - " return $oneof_name$_case() == k$field_name$;\n" - "}\n"); - printer->Print(vars, - "$inline$ void $classname$::set_has_$name$() {\n" - " _oneof_case_[$oneof_index$] = k$field_name$;\n" - "}\n"); + if (!use_dependent_base_ || !IsFieldDependent(field)) { + GenerateOneofMemberHasBits(field, vars, printer); + } } else { // Singular field. - if (HasFieldPresence(descriptor_->file())) { - // N.B.: without field presence, we do not use has-bits or generate - // has_$name$() methods. - 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_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" - ); - } else { - // Message fields have a has_$name$() method. - if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - bool is_lazy = false; - if (is_lazy) { - printer->Print(vars, - "$inline$ bool $classname$::has_$name$() const {\n" - " return !$name$_.IsCleared();\n" - "}\n"); - } else { - printer->Print(vars, - "$inline$ bool $classname$::has_$name$() const {\n" - " return !_is_default_instance_ && $name$_ != NULL;\n" - "}\n"); - } - } + if (!use_dependent_base_ || !IsFieldDependent(field)) { + GenerateSingularFieldHasBits(field, vars, printer); } } - // Generate clear_$name$() - printer->Print(vars, - "$inline$ void $classname$::clear_$name$() {\n"); - - printer->Indent(); - - if (field->containing_oneof()) { - // Clear this field only if it is the active field in this oneof, - // otherwise ignore - printer->Print(vars, - "if (has_$name$()) {\n"); - printer->Indent(); - field_generators_.get(field).GenerateClearingCode(printer); - printer->Print(vars, - "clear_has_$oneof_name$();\n"); - printer->Outdent(); - printer->Print("}\n"); - } else { - field_generators_.get(field).GenerateClearingCode(printer); - if (HasFieldPresence(descriptor_->file())) { - if (!field->is_repeated()) { - printer->Print(vars, - "clear_has_$name$();\n"); - } - } + if (!use_dependent_base_ || !IsFieldDependent(field)) { + vars["tmpl"] = ""; + vars["dependent_classname"] = vars["classname"]; + vars["this_message"] = ""; + vars["this_const_message"] = ""; + GenerateFieldClear(field, vars, printer); } - printer->Outdent(); - printer->Print("}\n"); - // Generate type-specific accessors. field_generators_.get(field).GenerateInlineAccessorDefinitions(printer, is_inline); @@ -602,23 +789,11 @@ GenerateFieldAccessorDefinitions(io::Printer* printer, bool is_inline) { printer->Print("\n"); } - // Generate has_$name$() and clear_has_$name$() functions for oneofs - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - map vars; - vars["oneof_name"] = descriptor_->oneof_decl(i)->name(); - vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); - vars["cap_oneof_name"] = - ToUpper(descriptor_->oneof_decl(i)->name()); - vars["classname"] = classname_; - vars["inline"] = is_inline ? "inline" : ""; - printer->Print( - vars, - "$inline$ bool $classname$::has_$oneof_name$() const {\n" - " return $oneof_name$_case() != $cap_oneof_name$_NOT_SET;\n" - "}\n" - "$inline$ void $classname$::clear_has_$oneof_name$() {\n" - " _oneof_case_[$oneof_index$] = $cap_oneof_name$_NOT_SET;\n" - "}\n"); + if (!use_dependent_base_) { + // Generate has_$name$() and clear_has_$name$() functions for oneofs + // If we aren't using a dependent base, they can be with the other functions + // that are #ifdef-guarded. + GenerateOneofHasBits(printer, is_inline); } } @@ -647,6 +822,34 @@ static bool CanClearByZeroing(const FieldDescriptor* field) { } } +void MessageGenerator:: +GenerateDependentBaseClassDefinition(io::Printer* printer) { + if (!use_dependent_base_) { + return; + } + + map vars; + vars["classname"] = DependentBaseClassTemplateName(descriptor_); + vars["superclass"] = SuperClassName(descriptor_); + + printer->Print(vars, + "template \n" + "class $classname$ : public $superclass$ {\n" + " public:\n"); + printer->Indent(); + + printer->Print(vars, + "$classname$() {}\n" + "virtual ~$classname$() {}\n" + "\n"); + + // Generate dependent accessor methods for all fields. + GenerateDependentFieldAccessorDeclarations(printer); + + printer->Outdent(); + printer->Print("};\n"); +} + void MessageGenerator:: GenerateClassDefinition(io::Printer* printer) { for (int i = 0; i < descriptor_->nested_type_count(); i++) { @@ -660,6 +863,11 @@ GenerateClassDefinition(io::Printer* printer) { printer->Print("\n"); } + if (use_dependent_base_) { + GenerateDependentBaseClassDefinition(printer); + printer->Print("\n"); + } + map vars; vars["classname"] = classname_; vars["field_count"] = SimpleItoa(descriptor_->field_count()); @@ -669,11 +877,18 @@ GenerateClassDefinition(io::Printer* printer) { } else { vars["dllexport"] = options_.dllexport_decl + " "; } - vars["superclass"] = SuperClassName(descriptor_); - + if (use_dependent_base_) { + vars["superclass"] = + DependentBaseClassTemplateName(descriptor_) + "<" + classname_ + ">"; + } else { + vars["superclass"] = SuperClassName(descriptor_); + } printer->Print(vars, - "class $dllexport$$classname$ : public $superclass$ {\n" - " public:\n"); + "class $dllexport$$classname$ : public $superclass$ {\n"); + if (use_dependent_base_) { + printer->Print(vars, " friend class $superclass$;\n"); + } + printer->Print(" public:\n"); printer->Indent(); printer->Print(vars, @@ -782,6 +997,19 @@ GenerateClassDefinition(io::Printer* printer) { printer->Print(vars, "void UnsafeArenaSwap($classname$* other);\n"); } + + if (IsAnyMessage(descriptor_)) { + printer->Print(vars, + "// implements Any -----------------------------------------------\n" + "\n" + "void PackFrom(const ::google::protobuf::Message& message);\n" + "bool UnpackTo(::google::protobuf::Message* message) const;\n" + "template bool Is() const {\n" + " return _any_metadata_.Is();\n" + "}\n" + "\n"); + } + printer->Print(vars, "void Swap($classname$* other);\n" "\n" @@ -973,11 +1201,18 @@ GenerateClassDefinition(io::Printer* printer) { // Generate oneof function declarations for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - printer->Print( - "inline bool has_$oneof_name$() const;\n" - "void clear_$oneof_name$();\n" - "inline void clear_has_$oneof_name$();\n\n", - "oneof_name", descriptor_->oneof_decl(i)->name()); + if (use_dependent_base_) { + printer->Print( + "inline bool has_$oneof_name$() const;\n" + "inline void clear_has_$oneof_name$();\n\n", + "oneof_name", descriptor_->oneof_decl(i)->name()); + } else { + printer->Print( + "inline bool has_$oneof_name$() const;\n" + "void clear_$oneof_name$();\n" + "inline void clear_has_$oneof_name$();\n\n", + "oneof_name", descriptor_->oneof_decl(i)->name()); + } } if (HasGeneratedMethods(descriptor_->file()) && @@ -1139,6 +1374,12 @@ GenerateClassDefinition(io::Printer* printer) { "\n"); } + // Generate _any_metadata_ for the Any type. + if (IsAnyMessage(descriptor_)) { + printer->Print(vars, + "::google::protobuf::internal::AnyMetadata _any_metadata_;\n"); + } + // Declare AddDescriptors(), BuildDescriptors(), and ShutdownFile() as // friends so that they can access private static variables like // default_instance_ and reflection_. @@ -1171,6 +1412,21 @@ GenerateClassDefinition(io::Printer* printer) { GOOGLE_DCHECK(!need_to_emit_cached_size); } +void MessageGenerator:: +GenerateDependentInlineMethods(io::Printer* printer) { + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + // map entry message doesn't need inline methods. Since map entry message + // cannot be a top level class, we just need to avoid calling + // GenerateInlineMethods here. + if (IsMapEntryMessage(descriptor_->nested_type(i))) continue; + nested_generators_[i]->GenerateDependentInlineMethods(printer); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + + GenerateDependentFieldAccessorDefinitions(printer); +} + void MessageGenerator:: GenerateInlineMethods(io::Printer* printer, bool is_inline) { for (int i = 0; i < descriptor_->nested_type_count(); i++) { @@ -1196,7 +1452,8 @@ GenerateInlineMethods(io::Printer* printer, bool is_inline) { vars["inline"] = is_inline ? "inline " : ""; printer->Print( vars, - "$inline$$class_name$::$camel_oneof_name$Case $class_name$::" + "$inline$" + "$class_name$::$camel_oneof_name$Case $class_name$::" "$oneof_name$_case() const {\n" " return $class_name$::$camel_oneof_name$Case(" "_oneof_case_[$oneof_index$]);\n" @@ -1494,6 +1751,19 @@ GenerateShutdownCode(io::Printer* printer) { void MessageGenerator:: GenerateClassMethods(io::Printer* printer) { + if (IsAnyMessage(descriptor_)) { + printer->Print( + "void $classname$::PackFrom(const ::google::protobuf::Message& message) {\n" + " _any_metadata_.PackFrom(message);\n" + "}\n" + "\n" + "bool $classname$::UnpackTo(::google::protobuf::Message* message) const {\n" + " return _any_metadata_.UnpackTo(message);\n" + "}\n" + "\n", + "classname", classname_); + } + for (int i = 0; i < descriptor_->enum_type_count(); i++) { enum_generators_[i]->GenerateMethods(printer); } @@ -1767,7 +2037,7 @@ GenerateArenaDestructorCode(io::Printer* printer) { if (need_registration) { printer->Print( "inline void $classname$::RegisterArenaDtor(::google::protobuf::Arena* arena) {\n" - " if (arena != NULL) {" + " if (arena != NULL) {\n" " arena->OwnCustomDestructor(this, &$classname$::ArenaDtor);\n" " }\n" "}\n", @@ -1782,18 +2052,23 @@ GenerateArenaDestructorCode(io::Printer* printer) { void MessageGenerator:: GenerateStructors(io::Printer* printer) { - string superclass = SuperClassName(descriptor_); - string initializer_with_arena; - if (UseUnknownFieldSet(descriptor_->file())) { - initializer_with_arena = "_internal_metadata_(arena)"; + string superclass; + if (use_dependent_base_) { + superclass = + DependentBaseClassTemplateName(descriptor_) + "<" + classname_ + ">"; } else { - initializer_with_arena = "_arena_ptr_(arena)"; + superclass = SuperClassName(descriptor_); } + string initializer_with_arena = superclass + "()"; + if (descriptor_->extension_range_count() > 0) { - initializer_with_arena = string("\n _extensions_(arena)") + - (!initializer_with_arena.empty() ? ", " : "") + initializer_with_arena; + initializer_with_arena += ",\n _extensions_(arena)"; + } + + if (UseUnknownFieldSet(descriptor_->file())) { + initializer_with_arena += ",\n _internal_metadata_(arena)"; } else { - initializer_with_arena = "\n " + initializer_with_arena; + initializer_with_arena += ",\n _arena_ptr_(arena)"; } // Initialize member variables with arena constructor. @@ -1804,16 +2079,21 @@ GenerateStructors(io::Printer* printer) { FieldName(descriptor_->field(i)) + string("_(arena)"); } } - initializer_with_arena = superclass + "()" + - (!initializer_with_arena.empty() ? "," : " ") + initializer_with_arena; + + if (IsAnyMessage(descriptor_)) { + initializer_with_arena += ",\n _any_metadata_(&type_url, &value_)"; + } string initializer_null; initializer_null = (UseUnknownFieldSet(descriptor_->file()) ? - ", _internal_metadata_(NULL) " : ", _arena_ptr_(NULL)"); + ", _internal_metadata_(NULL)" : ", _arena_ptr_(NULL)"); + if (IsAnyMessage(descriptor_)) { + initializer_null += ", _any_metadata_(&type_url_, &value_)"; + } printer->Print( "$classname$::$classname$()\n" - " : $superclass$() $initializer$ {\n" + " : $superclass$()$initializer$ {\n" " SharedCtor();\n" " // @@protoc_insertion_point(constructor:$full_name$)\n" "}\n", @@ -1894,10 +2174,14 @@ GenerateStructors(io::Printer* printer) { "full_name", descriptor_->full_name()); if (UseUnknownFieldSet(descriptor_->file())) { printer->Print( - ",\n _internal_metadata_(NULL) {\n"); + ",\n _internal_metadata_(NULL)"); } else if (!UseUnknownFieldSet(descriptor_->file())) { - printer->Print(",\n _arena_ptr_(NULL) {\n"); + printer->Print(",\n _arena_ptr_(NULL)"); } + if (IsAnyMessage(descriptor_)) { + printer->Print(",\n _any_metadata_(&type_url_, &value_)"); + } + printer->Print(" {\n"); printer->Print( " SharedCtor();\n" " MergeFrom(from);\n" @@ -2124,7 +2408,11 @@ GenerateClear(io::Printer* printer) { have_enclosing_if = true; } - field_generators_.get(field).GenerateClearingCode(printer); + if (use_dependent_base_ && IsFieldDependent(field)) { + printer->Print("clear_$name$();\n", "name", fieldname); + } else { + field_generators_.get(field).GenerateClearingCode(printer); + } if (have_enclosing_if) { printer->Outdent(); @@ -2147,7 +2435,11 @@ GenerateClear(io::Printer* printer) { const FieldDescriptor* field = descriptor_->field(i); if (field->is_repeated()) { - field_generators_.get(field).GenerateClearingCode(printer); + if (use_dependent_base_ && IsFieldDependent(field)) { + printer->Print("clear_$name$();\n", "name", FieldName(field)); + } else { + field_generators_.get(field).GenerateClearingCode(printer); + } } } @@ -2184,19 +2476,38 @@ void MessageGenerator:: GenerateOneofClear(io::Printer* printer) { // Generated function clears the active field and union case (e.g. foo_case_). for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - printer->Print( - "void $classname$::clear_$oneofname$() {\n", - "classname", classname_, - "oneofname", descriptor_->oneof_decl(i)->name()); + map oneof_vars; + oneof_vars["classname"] = classname_; + oneof_vars["oneofname"] = descriptor_->oneof_decl(i)->name(); + string message_class; + + if (use_dependent_base_) { + oneof_vars["tmpl"] = "template\n"; + oneof_vars["inline"] = "inline "; + oneof_vars["dependent_classname"] = + DependentBaseClassTemplateName(descriptor_) + ""; + oneof_vars["this_message"] = "reinterpret_cast(this)->"; + message_class = "T::"; + } else { + oneof_vars["tmpl"] = ""; + oneof_vars["inline"] = ""; + oneof_vars["dependent_classname"] = classname_; + oneof_vars["this_message"] = ""; + } + + printer->Print(oneof_vars, + "$tmpl$" + "$inline$" + "void $dependent_classname$::clear_$oneofname$() {\n"); printer->Indent(); - printer->Print( - "switch($oneofname$_case()) {\n", - "oneofname", descriptor_->oneof_decl(i)->name()); + printer->Print(oneof_vars, + "switch($this_message$$oneofname$_case()) {\n"); printer->Indent(); for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); printer->Print( - "case k$field_name$: {\n", + "case $message_class$k$field_name$: {\n", + "message_class", message_class, "field_name", UnderscoresToCamelCase(field->name(), true)); printer->Indent(); // We clear only allocated objects in oneofs @@ -2213,16 +2524,20 @@ GenerateOneofClear(io::Printer* printer) { "}\n"); } printer->Print( - "case $cap_oneof_name$_NOT_SET: {\n" + "case $message_class$$cap_oneof_name$_NOT_SET: {\n" " break;\n" "}\n", + "message_class", message_class, "cap_oneof_name", ToUpper(descriptor_->oneof_decl(i)->name())); printer->Outdent(); printer->Print( "}\n" - "_oneof_case_[$oneof_index$] = $cap_oneof_name$_NOT_SET;\n", + "$this_message$_oneof_case_[$oneof_index$] = " + "$message_class$$cap_oneof_name$_NOT_SET;\n", + "this_message", oneof_vars["this_message"], "oneof_index", SimpleItoa(i), + "message_class", message_class, "cap_oneof_name", ToUpper(descriptor_->oneof_decl(i)->name())); printer->Outdent(); @@ -2333,9 +2648,9 @@ GenerateMergeFrom(io::Printer* printer) { // system, as the GOOGLE_CHECK above ensured that we have the same descriptor // for each message. printer->Print( - "const $classname$* source =\n" - " ::google::protobuf::internal::dynamic_cast_if_available(\n" - " &from);\n" + "const $classname$* source = \n" + " ::google::protobuf::internal::DynamicCastToGenerated(\n" + " &from);\n" "if (source == NULL) {\n" " ::google::protobuf::internal::ReflectionOps::Merge(from, this);\n" "} else {\n" @@ -2585,8 +2900,24 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Indent(); + // Find repeated messages and groups now, to simplify what follows. + hash_set fields_with_parse_loop; + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = ordered_fields[i]; + if (field->is_repeated() && + (field->type() == FieldDescriptor::TYPE_MESSAGE || + field->type() == FieldDescriptor::TYPE_GROUP)) { + fields_with_parse_loop.insert(i); + } + } + + // need_label is true if we generated "goto parse_$name$" while handling the + // previous field. + bool need_label = false; for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = ordered_fields[i]; + const bool loops = fields_with_parse_loop.count(i) > 0; + const bool next_field_loops = fields_with_parse_loop.count(i + 1) > 0; PrintFieldComment(printer, field); @@ -2600,9 +2931,16 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Print("if (tag == $commontag$) {\n", "commontag", SimpleItoa(WireFormat::MakeTag(field))); - if (i > 0 || (field->is_repeated() && !field->options().packed())) { + if (need_label || + (field->is_repeated() && !field->options().packed() && !loops)) { + printer->Print( + " parse_$name$:\n", + "name", field->name()); + } + if (loops) { printer->Print( - " parse_$name$:\n", + " DO_(input->IncrementRecursionDepth());\n" + " parse_loop_$name$:\n", "name", field->name()); } @@ -2644,26 +2982,53 @@ GenerateMergeFromCodedStream(io::Printer* printer) { // switch() is slow since it can't be predicted well. Insert some if()s // here that attempt to predict the next tag. - if (field->is_repeated() && !field->options().packed()) { - // Expect repeats of this field. + // For non-packed repeated fields, expect the same tag again. + if (loops) { + printer->Print( + "if (input->ExpectTag($tag$)) goto parse_loop_$name$;\n", + "tag", SimpleItoa(WireFormat::MakeTag(field)), + "name", field->name()); + } else if (field->is_repeated() && !field->options().packed()) { printer->Print( "if (input->ExpectTag($tag$)) goto parse_$name$;\n", "tag", SimpleItoa(WireFormat::MakeTag(field)), "name", field->name()); } - if (i + 1 < descriptor_->field_count()) { - // Expect the next field in order. - const FieldDescriptor* next_field = ordered_fields[i + 1]; - printer->Print( - "if (input->ExpectTag($next_tag$)) goto parse_$next_name$;\n", - "next_tag", SimpleItoa(WireFormat::MakeTag(next_field)), - "next_name", next_field->name()); - } else { - // Expect EOF. - // TODO(kenton): Expect group end-tag? + // Have we emitted "if (input->ExpectTag($next_tag$)) ..." yet? + bool emitted_goto_next_tag = false; + + // For repeated messages/groups, we need to decrement recursion depth, + // unless the next tag is also for a repeated message/group. + if (loops) { + if (next_field_loops) { + const FieldDescriptor* next_field = ordered_fields[i + 1]; + printer->Print( + "if (input->ExpectTag($next_tag$)) goto parse_loop_$next_name$;\n", + "next_tag", SimpleItoa(WireFormat::MakeTag(next_field)), + "next_name", next_field->name()); + emitted_goto_next_tag = true; + } printer->Print( - "if (input->ExpectAtEnd()) goto success;\n"); + "input->UnsafeDecrementRecursionDepth();\n"); + } + + // If there are more fields, expect the next one. + need_label = false; + if (!emitted_goto_next_tag) { + if (i + 1 == descriptor_->field_count()) { + // Expect EOF. + // TODO(kenton): Expect group end-tag? + printer->Print( + "if (input->ExpectAtEnd()) goto success;\n"); + } else { + const FieldDescriptor* next_field = ordered_fields[i + 1]; + printer->Print( + "if (input->ExpectTag($next_tag$)) goto parse_$next_name$;\n", + "next_tag", SimpleItoa(WireFormat::MakeTag(next_field)), + "next_name", next_field->name()); + need_label = true; + } } printer->Print( @@ -2999,9 +3364,7 @@ static string ConditionalToCheckBitmasks(const vector& masks) { vector parts; for (int i = 0; i < masks.size(); i++) { if (masks[i] == 0) continue; - char buffer[kFastToBufferSize]; - FastHex32ToBuffer(masks[i], buffer); - string m = StrCat("0x", buffer); + string m = StrCat("0x", strings::Hex(masks[i], strings::Hex::ZERO_PAD_8)); // Each xor evaluates to 0 if the expected bits are present. parts.push_back(StrCat("((_has_bits_[", i, "] & ", m, ") ^ ", m, ")")); } @@ -3293,11 +3656,10 @@ GenerateIsInitialized(io::Printer* printer) { } if (mask != 0) { - char buffer[kFastToBufferSize]; printer->Print( "if ((_has_bits_[$i$] & 0x$mask$) != 0x$mask$) return false;\n", "i", SimpleItoa(i), - "mask", FastHex32ToBuffer(mask, buffer)); + "mask", StrCat(strings::Hex(mask, strings::Hex::ZERO_PAD_8))); } } } diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h index ea96581d..23dad10c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.h +++ b/src/google/protobuf/compiler/cpp/cpp_message.h @@ -67,7 +67,8 @@ class MessageGenerator { // Header stuff. // Generate foward declarations for this class and all its nested types. - void GenerateForwardDeclaration(io::Printer* printer); + void GenerateMessageForwardDeclaration(io::Printer* printer); + void GenerateEnumForwardDeclaration(io::Printer* printer); // Generate definitions of all nested enums (must come before class // definitions because those classes use the enums definitions). @@ -84,6 +85,9 @@ class MessageGenerator { // file). void GenerateInlineMethods(io::Printer* printer, bool is_inline); + // Dependent methods are always inline. + void GenerateDependentInlineMethods(io::Printer* printer); + // Source file stuff. // Generate code which declares all the global descriptor pointers which @@ -115,7 +119,10 @@ class MessageGenerator { private: // Generate declarations and definitions of accessors for fields. + void GenerateDependentBaseClassDefinition(io::Printer* printer); + void GenerateDependentFieldAccessorDeclarations(io::Printer* printer); void GenerateFieldAccessorDeclarations(io::Printer* printer); + void GenerateDependentFieldAccessorDefinitions(io::Printer* printer); void GenerateFieldAccessorDefinitions(io::Printer* printer, bool is_inline); // Generate the field offsets array. @@ -158,6 +165,21 @@ class MessageGenerator { bool unbounded); + // Generates has_foo() functions and variables for singular field has-bits. + void GenerateSingularFieldHasBits(const FieldDescriptor* field, + map vars, + io::Printer* printer); + // Generates has_foo() functions and variables for oneof field has-bits. + void GenerateOneofHasBits(io::Printer* printer, bool is_inline); + // Generates has_foo_bar() functions for oneof members. + void GenerateOneofMemberHasBits(const FieldDescriptor* field, + const map& vars, + io::Printer* printer); + // Generates the clear_foo() method for a field. + void GenerateFieldClear(const FieldDescriptor* field, + const map& vars, + io::Printer* printer); + const Descriptor* descriptor_; string classname_; Options options_; @@ -168,6 +190,7 @@ class MessageGenerator { google::protobuf::scoped_array > extension_generators_; int num_required_fields_; bool uses_string_; + bool use_dependent_base_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); }; diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc index 467b6bf6..ba318d10 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -72,7 +72,8 @@ void SetMessageVariables(const FieldDescriptor* descriptor, MessageFieldGenerator:: MessageFieldGenerator(const FieldDescriptor* descriptor, const Options& options) - : descriptor_(descriptor) { + : descriptor_(descriptor), + dependent_field_(options.proto_h && IsFieldDependent(descriptor)) { SetMessageVariables(descriptor, &variables_, options); } @@ -83,6 +84,10 @@ GeneratePrivateMembers(io::Printer* printer) const { printer->Print(variables_, "$type$* $name$_;\n"); } +void MessageFieldGenerator:: +GenerateDependentAccessorDeclarations(io::Printer* printer) const { +} + void MessageFieldGenerator:: GenerateAccessorDeclarations(io::Printer* printer) const { if (SupportsArenas(descriptor_)) { @@ -144,6 +149,8 @@ void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions( " return temp;\n" "}\n"); if (SupportsArenas(descriptor_->message_type())) { + // NOTE: the same logic is mirrored in weak_message_field.cc. Any + // arena-related semantics changes should be made in both places. printer->Print(variables_, "void $classname$::_slow_set_allocated_$name$(\n" " ::google::protobuf::Arena* message_arena, $type$** $name$) {\n" @@ -180,6 +187,10 @@ void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions( } } +void MessageFieldGenerator:: +GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { +} + void MessageFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const { @@ -294,7 +305,7 @@ GenerateClearingCode(io::Printer* printer) const { // If we don't have has-bits, message presence is indicated only by ptr != // NULL. Thus on clear, we need to delete the object. printer->Print(variables_, - "if ($name$_ != NULL) delete $name$_;\n" + "if (GetArenaNoVirtual() == NULL && $name$_ != NULL) delete $name$_;\n" "$name$_ = NULL;\n"); } else { printer->Print(variables_, @@ -365,6 +376,10 @@ MessageOneofFieldGenerator(const FieldDescriptor* descriptor, MessageOneofFieldGenerator::~MessageOneofFieldGenerator() {} +void MessageOneofFieldGenerator:: +GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { +} + void MessageOneofFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const { @@ -559,6 +574,10 @@ GeneratePrivateMembers(io::Printer* printer) const { "::google::protobuf::RepeatedPtrField< $type$ > $name$_;\n"); } +void RepeatedMessageFieldGenerator:: +GenerateDependentAccessorDeclarations(io::Printer* printer) const { +} + void RepeatedMessageFieldGenerator:: GenerateAccessorDeclarations(io::Printer* printer) const { printer->Print(variables_, @@ -572,6 +591,10 @@ GenerateAccessorDeclarations(io::Printer* printer) const { " mutable_$name$()$deprecation$;\n"); } +void RepeatedMessageFieldGenerator:: +GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { +} + void RepeatedMessageFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const { @@ -627,11 +650,13 @@ void RepeatedMessageFieldGenerator:: GenerateMergeFromCodedStream(io::Printer* printer) const { if (descriptor_->type() == FieldDescriptor::TYPE_MESSAGE) { printer->Print(variables_, - "DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(\n" + "DO_(::google::protobuf::internal::WireFormatLite::" + "ReadMessageNoVirtualNoRecursionDepth(\n" " input, add_$name$()));\n"); } else { printer->Print(variables_, - "DO_(::google::protobuf::internal::WireFormatLite::ReadGroupNoVirtual(\n" + "DO_(::google::protobuf::internal::WireFormatLite::" + "ReadGroupNoVirtualNoRecursionDepth(\n" " $number$, input, add_$name$()));\n"); } } diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.h b/src/google/protobuf/compiler/cpp/cpp_message_field.h index c1704fc1..9ddf9643 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.h @@ -52,7 +52,9 @@ class MessageFieldGenerator : public FieldGenerator { // implements FieldGenerator --------------------------------------- void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateDependentAccessorDeclarations(io::Printer* printer) const; void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const; void GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const; void GenerateNonInlineAccessorDefinitions(io::Printer* printer) const; @@ -67,6 +69,7 @@ class MessageFieldGenerator : public FieldGenerator { protected: const FieldDescriptor* descriptor_; + const bool dependent_field_; map variables_; private: @@ -80,6 +83,7 @@ class MessageOneofFieldGenerator : public MessageFieldGenerator { ~MessageOneofFieldGenerator(); // implements FieldGenerator --------------------------------------- + void GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const; void GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const; void GenerateNonInlineAccessorDefinitions(io::Printer* printer) const {} @@ -99,7 +103,9 @@ class RepeatedMessageFieldGenerator : public FieldGenerator { // implements FieldGenerator --------------------------------------- void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateDependentAccessorDeclarations(io::Printer* printer) const; void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const; void GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const; void GenerateClearingCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/cpp/cpp_options.h b/src/google/protobuf/compiler/cpp/cpp_options.h index 0c99cff1..4463f200 100644 --- a/src/google/protobuf/compiler/cpp/cpp_options.h +++ b/src/google/protobuf/compiler/cpp/cpp_options.h @@ -41,12 +41,13 @@ namespace protobuf { namespace compiler { namespace cpp { -// Generator options: +// Generator options (see generator.cc for a description of each): struct Options { - Options() : safe_boundary_check(false) { + Options() : safe_boundary_check(false), proto_h(false) { } string dllexport_decl; bool safe_boundary_check; + bool proto_h; }; } // namespace cpp diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc index 329eae32..9f929d37 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc @@ -262,7 +262,7 @@ RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, : descriptor_(descriptor) { SetPrimitiveVariables(descriptor, &variables_, options); - if (descriptor->options().packed()) { + if (descriptor->is_packed()) { variables_["packed_reader"] = "ReadPackedPrimitive"; variables_["repeated_reader"] = "ReadRepeatedPrimitiveNoInline"; } else { @@ -277,7 +277,7 @@ void RepeatedPrimitiveFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { printer->Print(variables_, "::google::protobuf::RepeatedField< $type$ > $name$_;\n"); - if (descriptor_->options().packed() && HasGeneratedMethods(descriptor_->file())) { + if (descriptor_->is_packed() && HasGeneratedMethods(descriptor_->file())) { printer->Print(variables_, "mutable int _$name$_cached_byte_size_;\n"); } @@ -364,7 +364,7 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { void RepeatedPrimitiveFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { // Write the tag and the size. printer->Print(variables_, "if (this->$name$_size() > 0) {\n" @@ -377,7 +377,7 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { } printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, " ::google::protobuf::internal::WireFormatLite::Write$declared_type$NoTag(\n" " this->$name$(i), output);\n"); @@ -391,7 +391,7 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { void RepeatedPrimitiveFieldGenerator:: GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { // Write the tag and the size. printer->Print(variables_, "if (this->$name$_size() > 0) {\n" @@ -405,7 +405,7 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { } printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, " target = ::google::protobuf::internal::WireFormatLite::\n" " Write$declared_type$NoTagToArray(this->$name$(i), target);\n"); @@ -435,7 +435,7 @@ GenerateByteSize(io::Printer* printer) const { "data_size = $fixed_size$ * this->$name$_size();\n"); } - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "if (data_size > 0) {\n" " total_size += $tag_size$ +\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc index 1a1bcd3d..1a3896a1 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -226,7 +226,6 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " } else {\n" " $clear_hasbit$\n" " }\n" - " $set_hasbit$\n" " $name$_.UnsafeArenaSetAllocated($default_variable$,\n" " $name$, GetArenaNoVirtual());\n" " // @@protoc_insertion_point(field_set_allocated:$full_name$)\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 9f63155b..4e25b2ea 100644 --- a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto +++ b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto @@ -131,23 +131,23 @@ message TestConflictingSymbolNamesExtension { // NO_PROTO3 } // NO_PROTO3 } // NO_PROTO3 -message TestConflictingEnumNames { - enum NestedConflictingEnum { - and = 1; - class = 2; - int = 3; - typedef = 4; - XOR = 5; - } - - optional NestedConflictingEnum conflicting_enum = 1; -} - -enum ConflictingEnum { - NOT_EQ = 1; - volatile = 2; - return = 3; -} +message TestConflictingEnumNames { // NO_PROTO3 + enum NestedConflictingEnum { // NO_PROTO3 + and = 1; // NO_PROTO3 + class = 2; // NO_PROTO3 + int = 3; // NO_PROTO3 + typedef = 4; // NO_PROTO3 + XOR = 5; // NO_PROTO3 + } // NO_PROTO3 + + optional NestedConflictingEnum conflicting_enum = 1; // NO_PROTO3 +} // NO_PROTO3 + +enum ConflictingEnum { // NO_PROTO3 + NOT_EQ = 1; // NO_PROTO3 + volatile = 2; // NO_PROTO3 + return = 3; // NO_PROTO3 +} // NO_PROTO3 message DummyMessage {} diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc index 9d14a924..b11fb21a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -941,6 +941,22 @@ TEST(GeneratedMessageTest, ExtensionConstantValues) { EXPECT_EQ(unittest::kRepeatedNestedEnumExtensionFieldNumber, 51); } +TEST(GeneratedMessageTest, ParseFromTruncated) { + const string long_string = string(128, 'q'); + FileDescriptorProto p; + p.add_extension()->set_name(long_string); + const string msg = p.SerializeAsString(); + int successful_count = 0; + for (int i = 0; i <= msg.size(); i++) { + if (p.ParseFromArray(msg.c_str(), i)) { + ++successful_count; + } + } + // We don't really care about how often we succeeded. + // As long as we didn't crash, we're happy. + EXPECT_GE(successful_count, 1); +} + // =================================================================== TEST(GeneratedEnumTest, EnumValuesAsSwitchCases) { diff --git a/src/google/protobuf/compiler/cpp/test_large_enum_value.proto b/src/google/protobuf/compiler/cpp/test_large_enum_value.proto new file mode 100644 index 00000000..cb6ca1b1 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/test_large_enum_value.proto @@ -0,0 +1,43 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test that proto2 compiler can generate valid code when the enum value +// is INT_MAX. Note that this is a compile-only test and this proto is not +// referenced in any C++ code. +syntax = "proto2"; + +package protobuf_unittest; + +message TestLargeEnumValue { + enum EnumWithLargeValue { + VALUE_1 = 1; + VALUE_MAX = 0x7fffffff; + } +} diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc index 71a2ba4b..39318a19 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.cc +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -67,7 +67,13 @@ void SetEnumVariables(const FieldDescriptor* descriptor, (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); (*variables)["default_number"] = SimpleItoa( descriptor->default_value_enum()->number()); - (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); + if (descriptor->is_packed()) { + (*variables)["tag"] = SimpleItoa(internal::WireFormatLite::MakeTag( + descriptor->number(), + internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED)); + } else { + (*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 @@ -641,7 +647,7 @@ GenerateMembers(io::Printer* printer) const { "}\n"); } - if (descriptor_->options().packed() && + if (descriptor_->is_packed() && HasGeneratedMethods(descriptor_->containing_type())) { printer->Print(variables_, "private int $name$MemoizedSerializedSize;\n"); @@ -884,7 +890,7 @@ GenerateParsingDoneCode(io::Printer* printer) const { void RepeatedImmutableEnumFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "if (get$capitalized_name$List().size() > 0) {\n" " output.writeRawVarint32($tag$);\n" @@ -915,7 +921,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); printer->Print( "size += dataSize;\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "if (!get$capitalized_name$List().isEmpty()) {" " size += $tag_size$;\n" @@ -928,7 +934,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { } // cache the data size for packed fields. - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "$name$MemoizedSerializedSize = dataSize;\n"); } diff --git a/src/google/protobuf/compiler/java/java_enum_field_lite.cc b/src/google/protobuf/compiler/java/java_enum_field_lite.cc new file mode 100644 index 00000000..697a07a7 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.cc @@ -0,0 +1,967 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +void SetEnumVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, + map* variables) { + SetCommonFieldVariables(descriptor, info, variables); + + (*variables)["type"] = + name_resolver->GetImmutableClassName(descriptor->enum_type()); + (*variables)["mutable_type"] = + name_resolver->GetMutableClassName(descriptor->enum_type()); + (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); + (*variables)["default_number"] = SimpleItoa( + descriptor->default_value_enum()->number()); + (*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();" : ""; + + if (SupportFieldPresence(descriptor->file())) { + // For singular messages and builders, one bit is used for the hasField bit. + (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); + + // Note that these have a trailing ";". + (*variables)["set_has_field_bit_message"] = + GenerateSetBit(messageBitIndex) + ";"; + (*variables)["clear_has_field_bit_message"] = + GenerateClearBit(messageBitIndex) + ";"; + + (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex); + } else { + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["clear_has_field_bit_message"] = ""; + + (*variables)["is_field_present_message"] = + (*variables)["name"] + "_ != " + + (*variables)["default"] + ".getNumber()"; + } + + // For repeated builders, the underlying list tracks mutability state. + (*variables)["is_mutable"] = (*variables)["name"] + "_.isModifiable()"; + + (*variables)["get_has_field_bit_from_local"] = + GenerateGetBitFromLocal(builderBitIndex); + (*variables)["set_has_field_bit_to_local"] = + GenerateSetBitToLocal(messageBitIndex); + + if (SupportUnknownEnumValue(descriptor->file())) { + (*variables)["unknown"] = (*variables)["type"] + ".UNRECOGNIZED"; + } else { + (*variables)["unknown"] = (*variables)["default"]; + } +} + +} // namespace + +// =================================================================== + +ImmutableEnumFieldLiteGenerator:: +ImmutableEnumFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), + name_resolver_(context->GetNameResolver()) { + SetEnumVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +ImmutableEnumFieldLiteGenerator::~ImmutableEnumFieldLiteGenerator() {} + +int ImmutableEnumFieldLiteGenerator::GetNumBitsForMessage() const { + return 1; +} + +int ImmutableEnumFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n"); + } + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Value();\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$();\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private int $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n"); + } + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Value() {\n" + " return $name$_;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " $type$ result = $type$.valueOf($name$_);\n" + " return result == null ? $unknown$ : result;\n" + "}\n"); + + // Generate private setters for the builder to proxy into. + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$Value(int value) {\n" + " $set_has_field_bit_message$" + " $name$_ = value;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " $set_has_field_bit_message$\n" + " $name$_ = value.getNumber();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $clear_has_field_bit_message$\n" + " $name$_ = $default_number$;\n" + "}\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Value() {\n" + " return instance.get$capitalized_name$Value();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$Value(int value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$Value(int value);\n" + " return this;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for enums +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default_number$;\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " set$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); + } else if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "if (other.$name$_ != $default_number$) {\n" + " set$capitalized_name$Value(other.get$capitalized_name$Value());\n" + "}\n"); + } else { + GOOGLE_LOG(FATAL) << "Can't reach here."; + } +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + // noop for scalars +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$set_has_field_bit_message$\n" + "$name$_ = rawValue;\n"); + } else { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n"); + if (PreserveUnknownFields(descriptor_->containing_type())) { + printer->Print(variables_, + " unknownFields.mergeVarintField($number$, rawValue);\n"); + } + printer->Print(variables_, + "} else {\n" + " $set_has_field_bit_message$\n" + " $name$_ = rawValue;\n" + "}\n"); + } +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // noop for enums +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " output.writeEnum($number$, $name$_);\n" + "}\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeEnumSize($number$, $name$_);\n" + "}\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && $name$_ == other.$name$_;\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n" + "hash = (53 * hash) + $name$_;\n"); +} + +string ImmutableEnumFieldLiteGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->enum_type()); +} + +// =================================================================== + +ImmutableEnumOneofFieldLiteGenerator:: +ImmutableEnumOneofFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableEnumFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); +} + +ImmutableEnumOneofFieldLiteGenerator:: +~ImmutableEnumOneofFieldLiteGenerator() {} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + PrintExtraFieldInfo(variables_, printer); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $has_oneof_case_message$;\n" + "}\n"); + } + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Value() {\n" + " if ($has_oneof_case_message$) {\n" + " return (java.lang.Integer) $oneof_name$_;\n" + " }\n" + " return $default_number$;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $type$ result = $type$.valueOf((java.lang.Integer) $oneof_name$_);\n" + " return result == null ? $unknown$ : result;\n" + " }\n" + " return $default$;\n" + "}\n"); + + // Generate private setters for the builder to proxy into. + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$Value(int value) {\n" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value.getNumber();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " }\n" + "}\n"); +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Value() {\n" + " return instance.get$capitalized_name$Value();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$Value(int value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$Value(value);\n" + " return this;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "set$capitalized_name$Value(other.get$capitalized_name$Value());\n"); + } else { + printer->Print(variables_, + "set$capitalized_name$(other.get$capitalized_name$());\n"); + } +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$set_oneof_case_message$;\n" + "$oneof_name$_ = rawValue;\n"); + } else { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n"); + if (PreserveUnknownFields(descriptor_->containing_type())) { + printer->Print(variables_, + " unknownFields.mergeVarintField($number$, rawValue);\n"); + } + printer->Print(variables_, + "} else {\n" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = rawValue;\n" + "}\n"); + } +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " output.writeEnum($number$, ((java.lang.Integer) $oneof_name$_));\n" + "}\n"); +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeEnumSize($number$, ((java.lang.Integer) $oneof_name$_));\n" + "}\n"); +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "result = result && get$capitalized_name$Value()\n" + " == other.get$capitalized_name$Value();\n"); + } else { + printer->Print(variables_, + "result = result && get$capitalized_name$()\n" + " .equals(other.get$capitalized_name$());\n"); + } +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n" + "hash = (53 * hash) + get$capitalized_name$Value();\n"); + } else { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n" + "hash = (53 * hash) + get$capitalized_name$().getNumber();\n"); + } +} + +// =================================================================== + +RepeatedImmutableEnumFieldLiteGenerator:: +RepeatedImmutableEnumFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetEnumVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +RepeatedImmutableEnumFieldLiteGenerator:: +~RepeatedImmutableEnumFieldLiteGenerator() {} + +int RepeatedImmutableEnumFieldLiteGenerator::GetNumBitsForMessage() const { + return 0; +} + +int RepeatedImmutableEnumFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.util.List<$type$> get$capitalized_name$List();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Count();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$(int index);\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.util.List\n" + "get$capitalized_name$ValueList();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Value(int index);\n"); + } +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + // TODO(dweis): Switch to IntList? + "private com.google.protobuf.Internal.ProtobufList<\n" + " java.lang.Integer> $name$_;\n" + "private static final com.google.protobuf.Internal.ListAdapter.Converter<\n" + " java.lang.Integer, $type$> $name$_converter_ =\n" + " new com.google.protobuf.Internal.ListAdapter.Converter<\n" + " java.lang.Integer, $type$>() {\n" + " public $type$ convert(java.lang.Integer from) {\n" + " $type$ result = $type$.valueOf(from);\n" + " return result == null ? $unknown$ : result;\n" + " }\n" + " };\n"); + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" + " return new com.google.protobuf.Internal.ListAdapter<\n" + " java.lang.Integer, $type$>($name$_, $name$_converter_);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return $name$_converter_.convert($name$_.get(index));\n" + "}\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List\n" + "get$capitalized_name$ValueList() {\n" + " return $name$_;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Value(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); + } + + if (descriptor_->options().packed() && + HasGeneratedMethods(descriptor_->containing_type())) { + printer->Print(variables_, + "private int $name$MemoizedSerializedSize;\n"); + } + + // Generate private setters for the builder to proxy into. + printer->Print(variables_, + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$is_mutable$) {\n" + " $name$_ = newProtobufList($name$_);\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, value.getNumber());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value.getNumber());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " for ($type$ value : values) {\n" + " $name$_.add(value.getNumber());\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $name$_ = emptyProtobufList();\n" + "}\n"); + + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$Value(\n" + " int index, int value) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, value);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$Value(int value) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void addAll$capitalized_name$Value(\n" + " java.lang.Iterable values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " for (int value : values) {\n" + " $name$_.add(value);\n" + " }\n" + "}\n"); + } +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" + " return instance.get$capitalized_name$List();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return instance.get$capitalized_name$Count();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return instance.get$capitalized_name$(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " copyOnWrite();\n" + " instance.addAll$capitalized_name$(values);" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); + + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List\n" + "get$capitalized_name$ValueList() {\n" + " return java.util.Collections.unmodifiableList(\n" + " instance.get$capitalized_name$ValueList());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Value(int index) {\n" + " return instance.get$capitalized_name$Value(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$Value(\n" + " int index, int value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$Value(index, value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$Value(int value) {\n" + " instance.add$capitalized_name$Value(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder addAll$capitalized_name$Value(\n" + " java.lang.Iterable values) {\n" + " copyOnWrite();\n" + " instance.addAll$capitalized_name$Value(values);\n" + " return this;\n" + "}\n"); + } +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for enums +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = emptyProtobufList();\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +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" + " } else {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.addAll(other.$name$_);\n" + " }\n" + " $on_changed$\n" + "}\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.makeImmutable();\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + // Read and store the enum + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "if (!$is_mutable$) {\n" + " $name$_ = newProtobufList();\n" + "}\n" + "$name$_.add(rawValue);\n"); + } else { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n"); + if (PreserveUnknownFields(descriptor_->containing_type())) { + printer->Print(variables_, + " unknownFields.mergeVarintField($number$, rawValue);\n"); + } + printer->Print(variables_, + "} else {\n" + " if (!$is_mutable$) {\n" + " $name$_ = newProtobufList();\n" + " }\n" + " $name$_.add(rawValue);\n" + "}\n"); + } +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateParsingCodeFromPacked(io::Printer* printer) const { + // Wrap GenerateParsingCode's contents with a while loop. + + printer->Print(variables_, + "int length = input.readRawVarint32();\n" + "int oldLimit = input.pushLimit(length);\n" + "while(input.getBytesUntilLimit() > 0) {\n"); + printer->Indent(); + + GenerateParsingCode(printer); + + printer->Outdent(); + printer->Print(variables_, + "}\n" + "input.popLimit(oldLimit);\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_mutable$) {\n" + " $name$_.makeImmutable();\n" + "}\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +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.writeEnumNoTag($name$_.get(i));\n" + "}\n"); + } else { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.writeEnum($number$, $name$_.get(i));\n" + "}\n"); + } +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +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" + " .computeEnumSizeNoTag($name$_.get(i));\n" + "}\n"); + printer->Print( + "size += dataSize;\n"); + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (!get$capitalized_name$List().isEmpty()) {" + " size += $tag_size$;\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeRawVarint32Size(dataSize);\n" + "}"); + } else { + printer->Print(variables_, + "size += $tag_size$ * $name$_.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 RepeatedImmutableEnumFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && $name$_.equals(other.$name$_);\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "if (get$capitalized_name$Count() > 0) {\n" + " hash = (37 * hash) + $constant_name$;\n" + " hash = (53 * hash) + $name$_.hashCode();\n" + "}\n"); +} + +string RepeatedImmutableEnumFieldLiteGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->enum_type()); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_enum_field_lite.h b/src/google/protobuf/compiler/java/java_enum_field_lite.h new file mode 100644 index 00000000..2c41c3e4 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.h @@ -0,0 +1,159 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_FIELD_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_FIELD_LITE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableEnumFieldLiteGenerator : public ImmutableFieldLiteGenerator { + public: + explicit ImmutableEnumFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableEnumFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + 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 GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(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; + + protected: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableEnumFieldLiteGenerator); +}; + +class ImmutableEnumOneofFieldLiteGenerator + : public ImmutableEnumFieldLiteGenerator { + public: + ImmutableEnumOneofFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableEnumOneofFieldLiteGenerator(); + + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableEnumOneofFieldLiteGenerator); +}; + +class RepeatedImmutableEnumFieldLiteGenerator + : public ImmutableFieldLiteGenerator { + public: + explicit RepeatedImmutableEnumFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~RepeatedImmutableEnumFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + 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 GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingCodeFromPacked(io::Printer* printer) const; + void GenerateParsingDoneCode(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 variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableEnumFieldLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_FIELD_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc index af9978e2..3f0fa11f 100644 --- a/src/google/protobuf/compiler/java/java_field.cc +++ b/src/google/protobuf/compiler/java/java_field.cc @@ -42,12 +42,18 @@ #include #include #include +#include #include #include +#include #include +#include #include +#include #include +#include #include +#include #include #include #include @@ -133,6 +139,79 @@ ImmutableFieldGenerator* MakeImmutableGenerator( } } +ImmutableFieldLiteGenerator* MakeImmutableLiteGenerator( + const FieldDescriptor* field, int messageBitIndex, int builderBitIndex, + Context* context) { + if (field->is_repeated()) { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + if (IsMapEntry(field->message_type())) { + return new ImmutableMapFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } else { + if (IsLazy(field)) { + return new RepeatedImmutableLazyMessageFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } else { + return new RepeatedImmutableMessageFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } + } + case JAVATYPE_ENUM: + return new RepeatedImmutableEnumFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + case JAVATYPE_STRING: + return new RepeatedImmutableStringFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + default: + return new RepeatedImmutablePrimitiveFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } + } else { + if (field->containing_oneof()) { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + if (IsLazy(field)) { + return new ImmutableLazyMessageOneofFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } else { + return new ImmutableMessageOneofFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } + case JAVATYPE_ENUM: + return new ImmutableEnumOneofFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + case JAVATYPE_STRING: + return new ImmutableStringOneofFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + default: + return new ImmutablePrimitiveOneofFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } + } else { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + if (IsLazy(field)) { + return new ImmutableLazyMessageFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } else { + return new ImmutableMessageFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } + case JAVATYPE_ENUM: + return new ImmutableEnumFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + case JAVATYPE_STRING: + return new ImmutableStringFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + default: + return new ImmutablePrimitiveFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } + } + } +} + static inline void ReportUnexpectedPackedFieldsCall(io::Printer* printer) { // Reaching here indicates a bug. Cases are: @@ -153,6 +232,13 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const { ReportUnexpectedPackedFieldsCall(printer); } +ImmutableFieldLiteGenerator::~ImmutableFieldLiteGenerator() {} + +void ImmutableFieldLiteGenerator:: +GenerateParsingCodeFromPacked(io::Printer* printer) const { + ReportUnexpectedPackedFieldsCall(printer); +} + // =================================================================== template <> @@ -178,6 +264,28 @@ FieldGeneratorMap::FieldGeneratorMap( template<> FieldGeneratorMap::~FieldGeneratorMap() {} +template <> +FieldGeneratorMap::FieldGeneratorMap( + const Descriptor* descriptor, Context* context) + : descriptor_(descriptor), + field_generators_(new google::protobuf::scoped_ptr< + ImmutableFieldLiteGenerator>[descriptor->field_count()]) { + // 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++) { + ImmutableFieldLiteGenerator* generator = MakeImmutableLiteGenerator( + descriptor->field(i), messageBitIndex, builderBitIndex, context); + field_generators_[i].reset(generator); + messageBitIndex += generator->GetNumBitsForMessage(); + builderBitIndex += generator->GetNumBitsForBuilder(); + } +} + +template<> +FieldGeneratorMap::~FieldGeneratorMap() {} + void SetCommonFieldVariables(const FieldDescriptor* descriptor, const FieldGeneratorInfo* info, diff --git a/src/google/protobuf/compiler/java/java_field.h b/src/google/protobuf/compiler/java/java_field.h index 1cf360c1..00f3c601 100644 --- a/src/google/protobuf/compiler/java/java_field.h +++ b/src/google/protobuf/compiler/java/java_field.h @@ -83,7 +83,6 @@ class ImmutableFieldGenerator { virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; virtual void GenerateFieldBuilderInitializationCode(io::Printer* printer) const = 0; - virtual void GenerateStaticInitializationCode(io::Printer* printer) const {} virtual void GenerateEqualsCode(io::Printer* printer) const = 0; virtual void GenerateHashCode(io::Printer* printer) const = 0; @@ -94,6 +93,37 @@ class ImmutableFieldGenerator { GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableFieldGenerator); }; +class ImmutableFieldLiteGenerator { + public: + ImmutableFieldLiteGenerator() {} + virtual ~ImmutableFieldLiteGenerator(); + + 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 GenerateMergingCode(io::Printer* printer) const = 0; + virtual void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) + const = 0; + virtual void GenerateParsingCode(io::Printer* printer) const = 0; + virtual void GenerateParsingCodeFromPacked(io::Printer* printer) const; + virtual void GenerateParsingDoneCode(io::Printer* printer) const = 0; + 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; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableFieldLiteGenerator); +}; + // Convenience class which constructs FieldGenerators for a Descriptor. template diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc index 4a1f4529..68b47ee1 100644 --- a/src/google/protobuf/compiler/java/java_file.cc +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -35,10 +35,10 @@ #include #include -#include #ifndef _SHARED_PTR_H #include #endif +#include #include #include @@ -153,6 +153,12 @@ void CollectExtensions(const FileDescriptorProto& file_proto, } } +// Compare two field descriptors, returning true if the first should come +// before the second. +bool CompareFieldsByName(const FieldDescriptor *a, const FieldDescriptor *b) { + return a->full_name() < b->full_name(); +} + // Our static initialization methods can become very, very large. // So large that if we aren't careful we end up blowing the JVM's // 64K bytes of bytecode/method. Fortunately, since these static @@ -166,7 +172,6 @@ void MaybeRestartJavaMethod(io::Printer* printer, int *method_num, const char *chain_statement, const char *method_decl) { - // The goal here is to stay under 64K bytes of jvm bytecode/method, // since otherwise we hit a hardcoded limit in the jvm and javac will // then fail with the error "code too large". This limit lets our @@ -184,6 +189,7 @@ void MaybeRestartJavaMethod(io::Printer* printer, } } + } // namespace FileGenerator::FileGenerator(const FileDescriptor* file, bool immutable_api) diff --git a/src/google/protobuf/compiler/java/java_generator_factory.cc b/src/google/protobuf/compiler/java/java_generator_factory.cc index 2d1437f0..92ef851b 100644 --- a/src/google/protobuf/compiler/java/java_generator_factory.cc +++ b/src/google/protobuf/compiler/java/java_generator_factory.cc @@ -38,6 +38,7 @@ #include #include #include +#include #include namespace google { @@ -57,7 +58,12 @@ ImmutableGeneratorFactory::~ImmutableGeneratorFactory() {} MessageGenerator* ImmutableGeneratorFactory::NewMessageGenerator( const Descriptor* descriptor) const { - return new ImmutableMessageGenerator(descriptor, context_); + if (descriptor->file()->options().optimize_for() == + FileOptions::LITE_RUNTIME) { + return new ImmutableMessageLiteGenerator(descriptor, context_); + } else { + return new ImmutableMessageGenerator(descriptor, context_); + } } ExtensionGenerator* ImmutableGeneratorFactory::NewExtensionGenerator( diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h index 62efbefa..96d2545f 100644 --- a/src/google/protobuf/compiler/java/java_helpers.h +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -196,12 +196,6 @@ 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 && @@ -330,6 +324,10 @@ inline bool IsMapEntry(const Descriptor* descriptor) { return descriptor->options().map_entry(); } +inline bool IsMapField(const FieldDescriptor* descriptor) { + return descriptor->is_map(); +} + inline bool PreserveUnknownFields(const Descriptor* descriptor) { return descriptor->file()->syntax() != FileDescriptor::SYNTAX_PROTO3; } diff --git a/src/google/protobuf/compiler/java/java_lazy_message_field.cc b/src/google/protobuf/compiler/java/java_lazy_message_field.cc index 6e29a40b..0de8cbe5 100644 --- a/src/google/protobuf/compiler/java/java_lazy_message_field.cc +++ b/src/google/protobuf/compiler/java/java_lazy_message_field.cc @@ -72,14 +72,12 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" " return ($type$) $name$_.getValue($type$.getDefaultInstance());\n" - "}\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" - " return $name$_;\n" - "}\n"); - } + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + " return $name$_;\n" + "}\n"); } void ImmutableLazyMessageFieldGenerator:: @@ -92,14 +90,12 @@ GenerateBuilderMembers(io::Printer* printer) const { "private com.google.protobuf.LazyFieldLite $name$_ =\n" " new com.google.protobuf.LazyFieldLite();\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"); - } + 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". @@ -179,39 +175,37 @@ GenerateBuilderMembers(io::Printer* printer) const { "$clear_has_field_bit_builder$;\n" "return this;\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - 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"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$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"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "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"); - } + WriteFieldDocComment(printer, descriptor_); + 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"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$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"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "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"); } @@ -538,14 +532,12 @@ GenerateBuilderMembers(io::Printer* printer) const { "}\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"); - } + 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". @@ -723,70 +715,68 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder get$capitalized_name$Builder(\n" - " int index) {\n" - " return get$capitalized_name$FieldBuilder().getBuilder(index);\n" - "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder get$capitalized_name$Builder(\n" + " int index) {\n" + " return get$capitalized_name$FieldBuilder().getBuilder(index);\n" + "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$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"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$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"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public java.util.List \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"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List \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"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder add$capitalized_name$Builder() {\n" - " return get$capitalized_name$FieldBuilder().addBuilder(\n" - " $type$.getDefaultInstance());\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder add$capitalized_name$Builder(\n" - " int index) {\n" - " return get$capitalized_name$FieldBuilder().addBuilder(\n" - " index, $type$.getDefaultInstance());\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$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"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder add$capitalized_name$Builder() {\n" + " return get$capitalized_name$FieldBuilder().addBuilder(\n" + " $type$.getDefaultInstance());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder add$capitalized_name$Builder(\n" + " int index) {\n" + " return get$capitalized_name$FieldBuilder().addBuilder(\n" + " index, $type$.getDefaultInstance());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$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 RepeatedImmutableLazyMessageFieldGenerator:: diff --git a/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc new file mode 100644 index 00000000..283ba1d3 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc @@ -0,0 +1,708 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: niwasaki@google.com (Naoki Iwasaki) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +ImmutableLazyMessageFieldLiteGenerator:: +ImmutableLazyMessageFieldLiteGenerator( + const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableMessageFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { +} + +ImmutableLazyMessageFieldLiteGenerator:: +~ImmutableLazyMessageFieldLiteGenerator() {} + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private com.google.protobuf.LazyFieldLite $name$_ =\n" + " new com.google.protobuf.LazyFieldLite();\n"); + + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return ($type$) $name$_.getValue($type$.getDefaultInstance());\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " $name$_.setValue(value);\n" + " $set_has_field_bit_message$;\n" + "}\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " $name$_.setValue(builderForValue.build());\n" + " $set_has_field_bit_message$;\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void merge$capitalized_name$($type$ value) {\n" + " if ($get_has_field_bit_message$ &&\n" + " !$name$_.containsDefaultInstance()) {\n" + " $name$_.setValue(\n" + " $type$.newBuilder(\n" + " get$capitalized_name$()).mergeFrom(value).buildPartial());\n" + " } else {\n" + " $name$_.setValue(value);\n" + " }\n" + " $set_has_field_bit_message$;\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $name$_.clear();\n" + " $clear_has_field_bit_message$;\n" + "}\n"); +} + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // The comments above the methods below are based on a hypothetical + // field of type "Field" called "Field". + + // boolean hasField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(builderForValue);\n" + " return this;\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder merge$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.merge$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.clear();\n"); +} + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " $name$_.merge(other.$name$_);\n" + " $set_has_field_bit_message$;\n" + "}\n"); +} + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.setByteString(input.readBytes(), extensionRegistry);\n"); + printer->Print(variables_, + "$set_has_field_bit_message$;\n"); +} + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + // Do not de-serialize lazy fields. + printer->Print(variables_, + "if ($get_has_field_bit_message$) {\n" + " output.writeBytes($number$, $name$_.toByteString());\n" + "}\n"); +} + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_has_field_bit_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeLazyFieldSize($number$, $name$_);\n" + "}\n"); +} + +// =================================================================== + +ImmutableLazyMessageOneofFieldLiteGenerator:: +ImmutableLazyMessageOneofFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableLazyMessageFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); + variables_["lazy_type"] = "com.google.protobuf.LazyFieldLite"; +} + +ImmutableLazyMessageOneofFieldLiteGenerator:: +~ImmutableLazyMessageOneofFieldLiteGenerator() {} + +void ImmutableLazyMessageOneofFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $has_oneof_case_message$;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " return ($type$) (($lazy_type$) $oneof_name$_).getValue(\n" + " $type$.getDefaultInstance());\n" + " }\n" + " return $type$.getDefaultInstance();\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " if (!($has_oneof_case_message$)) {\n" + " $oneof_name$_ = new $lazy_type$();\n" + " $set_oneof_case_message$;\n" + " }\n" + " (($lazy_type$) $oneof_name$_).setValue(value);\n" + "}\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " if (!($has_oneof_case_message$)) {\n" + " $oneof_name$_ = new $lazy_type$();\n" + " $set_oneof_case_message$;\n" + " }\n" + " (($lazy_type$) $oneof_name$_).setValue(builderForValue.build());\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void merge$capitalized_name$($type$ value) {\n" + " if ($has_oneof_case_message$ &&\n" + " !(($lazy_type$) $oneof_name$_).containsDefaultInstance()) {\n" + " (($lazy_type$) $oneof_name$_).setValue(\n" + " $type$.newBuilder(\n" + " get$capitalized_name$()).mergeFrom(value).buildPartial());\n" + " } else {\n" + " if (!($has_oneof_case_message$)) {\n" + " $oneof_name$_ = new $lazy_type$();\n" + " $set_oneof_case_message$;\n" + " }\n" + " (($lazy_type$) $oneof_name$_).setValue(value);\n" + " }\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " }\n" + "}\n"); +} + +void ImmutableLazyMessageOneofFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // boolean hasField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(builderForValue);\n" + " return this;\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder merge$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.merge$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutableLazyMessageOneofFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!($has_oneof_case_message$)) {\n" + " $oneof_name$_ = new $lazy_type$();\n" + "}\n" + "(($lazy_type$) $oneof_name$_).merge(\n" + " ($lazy_type$) other.$oneof_name$_);\n" + "$set_oneof_case_message$;\n"); +} + +void ImmutableLazyMessageOneofFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!($has_oneof_case_message$)) {\n" + " $oneof_name$_ = new $lazy_type$();\n" + "}\n" + "(($lazy_type$) $oneof_name$_).setByteString(\n" + " input.readBytes(), extensionRegistry);\n" + "$set_oneof_case_message$;\n"); +} + +void ImmutableLazyMessageOneofFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + // Do not de-serialize lazy fields. + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " output.writeBytes(\n" + " $number$, (($lazy_type$) $oneof_name$_).toByteString());\n" + "}\n"); +} + +void ImmutableLazyMessageOneofFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeLazyFieldSize($number$, ($lazy_type$) $oneof_name$_);\n" + "}\n"); +} + +// =================================================================== + +RepeatedImmutableLazyMessageFieldLiteGenerator:: +RepeatedImmutableLazyMessageFieldLiteGenerator( + const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : RepeatedImmutableMessageFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { +} + + +RepeatedImmutableLazyMessageFieldLiteGenerator:: +~RepeatedImmutableLazyMessageFieldLiteGenerator() {} + +void RepeatedImmutableLazyMessageFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private com.google.protobuf.Internal.ProtobufList<\n" + " com.google.protobuf.LazyFieldLite> $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$>\n" + " get$capitalized_name$List() {\n" + " java.util.List<$type$> list =\n" + " new java.util.ArrayList<$type$>($name$_.size());\n" + " for (com.google.protobuf.LazyFieldLite lf : $name$_) {\n" + " list.add(($type$) lf.getValue($type$.getDefaultInstance()));\n" + " }\n" + // TODO(dweis): Make this list immutable? + " return list;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List\n" + " get$capitalized_name$OrBuilderList() {\n" + " return get$capitalized_name$List();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return ($type$)\n" + " $name$_.get(index).getValue($type$.getDefaultInstance());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n" + " int index) {\n" + " return ($type$OrBuilder)\n" + " $name$_.get(index).getValue($type$.getDefaultInstance());\n" + "}\n"); + + printer->Print(variables_, + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$is_mutable$) {\n" + " $name$_ = newProtobufList($name$_);\n" + " }\n" + "}\n" + "\n"); + + // Builder setRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(\n" + " index, com.google.protobuf.LazyFieldLite.fromValue(value));\n" + "}\n"); + + // Builder setRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, com.google.protobuf.LazyFieldLite.fromValue(\n" + " builderForValue.build()));\n" + "}\n"); + + // Builder addRepeatedField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(com.google.protobuf.LazyFieldLite.fromValue(value));\n" + "}\n"); + + // Builder addRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " int index, $type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(\n" + " index, com.google.protobuf.LazyFieldLite.fromValue(value));\n" + "}\n"); + + // Builder addRepeatedField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(com.google.protobuf.LazyFieldLite.fromValue(\n" + " builderForValue.build()));\n" + "}\n"); + + // Builder addRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(index, com.google.protobuf.LazyFieldLite.fromValue(\n" + " builderForValue.build()));\n" + "}\n"); + + // Builder addAllRepeatedField(Iterable values) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " for (com.google.protobuf.MessageLite v : values) {\n" + " $name$_.add(com.google.protobuf.LazyFieldLite.fromValue(v));\n" + " }\n" + "}\n"); + + // Builder clearAllRepeatedField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $name$_ = emptyProtobufList();\n" + "}\n"); + + // Builder removeRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void remove$capitalized_name$(int index) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.remove(index);\n" + "}\n"); +} + +void RepeatedImmutableLazyMessageFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // List getRepeatedFieldList() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList(\n" + " instance.get$capitalized_name$List());\n" + "}\n"); + + // int getRepeatedFieldCount() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return instance.get$capitalized_name$Count();\n" + "}\n"); + + // Field getRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return instance.get$capitalized_name$(index);\n" + "}\n"); + + // Builder setRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + + // Builder setRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, builderForValue);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " int index, $type$ value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(builderForValue);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(index, builderForValue);\n" + " return this;\n" + "}\n"); + + // Builder addAllRepeatedField(Iterable values) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " copyOnWrite();\n" + " instance.addAll$capitalized_name$(values);\n" + " return this;\n" + "}\n"); + + // Builder clearAllRepeatedField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); + + // Builder removeRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder remove$capitalized_name$(int index) {\n" + " copyOnWrite();\n" + " instance.remove$capitalized_name$(index);\n" + " return this;\n" + "}\n"); +} + +void RepeatedImmutableLazyMessageFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!$is_mutable$) {\n" + " $name$_ = newProtobufList();\n" + "}\n" + "$name$_.add(new com.google.protobuf.LazyFieldLite(\n" + " extensionRegistry, input.readBytes()));\n"); +} + +void RepeatedImmutableLazyMessageFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.writeBytes($number$, $name$_.get(i).toByteString());\n" + "}\n"); +} + +void RepeatedImmutableLazyMessageFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeLazyFieldSize($number$, $name$_.get(i));\n" + "}\n"); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_lazy_message_field_lite.h b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.h new file mode 100644 index 00000000..e85ec0f3 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.h @@ -0,0 +1,118 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: niwasaki@google.com (Naoki Iwasaki) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_LAZY_MESSAGE_FIELD_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_LAZY_MESSAGE_FIELD_LITE_H__ + +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + } + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableLazyMessageFieldLiteGenerator + : public ImmutableMessageFieldLiteGenerator { + public: + explicit ImmutableLazyMessageFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableLazyMessageFieldLiteGenerator(); + + // overroads ImmutableMessageFieldLiteGenerator ------------------------------ + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableLazyMessageFieldLiteGenerator); +}; + +class ImmutableLazyMessageOneofFieldLiteGenerator + : public ImmutableLazyMessageFieldLiteGenerator { + public: + ImmutableLazyMessageOneofFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableLazyMessageOneofFieldLiteGenerator(); + + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableLazyMessageOneofFieldLiteGenerator); +}; + +class RepeatedImmutableLazyMessageFieldLiteGenerator + : public RepeatedImmutableMessageFieldLiteGenerator { + public: + explicit RepeatedImmutableLazyMessageFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~RepeatedImmutableLazyMessageFieldLiteGenerator(); + + // overroads RepeatedImmutableMessageFieldLiteGenerator ---------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableLazyMessageFieldLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_LAZY_MESSAGE_FIELD_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_map_field.cc b/src/google/protobuf/compiler/java/java_map_field.cc index cf1ef589..f25970e5 100644 --- a/src/google/protobuf/compiler/java/java_map_field.cc +++ b/src/google/protobuf/compiler/java/java_map_field.cc @@ -133,9 +133,11 @@ void SetMessageVariables(const FieldDescriptor* descriptor, (*variables)["set_mutable_bit_parser"] = GenerateSetBitMutableLocal(builderBitIndex); + (*variables)["default_entry"] = (*variables)["capitalized_name"] + + "DefaultEntryHolder.defaultEntry"; if (HasDescriptorMethods(descriptor->file())) { (*variables)["lite"] = ""; - (*variables)["map_field_parameter"] = (*variables)["name"] + "DefaultEntry"; + (*variables)["map_field_parameter"] = (*variables)["default_entry"]; (*variables)["descriptor"] = name_resolver->GetImmutableClassName(descriptor->file()) + ".internal_" + UniqueFileScopeIdentifier(descriptor->message_type()) + @@ -197,27 +199,21 @@ GenerateInterfaceMembers(io::Printer* printer) const { } } -void ImmutableMapFieldGenerator:: -GenerateStaticInitializationCode(io::Printer* printer) const { - printer->Print( - variables_, - "$name$DefaultEntry =\n" - " com.google.protobuf.MapEntry$lite$\n" - " .<$type_parameters$>newDefaultInstance(\n" - " $descriptor$\n" - " $key_wire_type$,\n" - " $key_default_value$,\n" - " $value_wire_type$,\n" - " $value_default_value$);\n" - "\n"); -} - void ImmutableMapFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print( variables_, - "private static final com.google.protobuf.MapEntry$lite$<\n" - " $type_parameters$> $name$DefaultEntry;\n"); + "private static final class $capitalized_name$DefaultEntryHolder {\n" + " static final com.google.protobuf.MapEntry$lite$<\n" + " $type_parameters$> defaultEntry =\n" + " com.google.protobuf.MapEntry$lite$\n" + " .<$type_parameters$>newDefaultInstance(\n" + " $descriptor$\n" + " $key_wire_type$,\n" + " $key_default_value$,\n" + " $value_wire_type$,\n" + " $value_default_value$);\n" + "}\n"); printer->Print( variables_, "private com.google.protobuf.MapField$lite$<\n" @@ -291,7 +287,10 @@ GenerateBuilderMembers(io::Printer* printer) const { " if ($name$_ == null) {\n" " $name$_ = com.google.protobuf.MapField$lite$.newMapField(\n" " $map_field_parameter$);\n" - " }\n" + " }\n" + " if (!$name$_.isMutable()) {\n" + " $name$_ = $name$_.copy();\n" + " }\n" " return $name$_;\n" "}\n"); if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { @@ -381,10 +380,8 @@ void ImmutableMapFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { printer->Print( variables_, - // We do a copy of the map field to ensure that the built result is - // immutable. Implementation of this copy() method can do copy-on-write - // to defer this copy until further modifications are made on the field. - "result.$name$_ = internalGet$capitalized_name$().copy();\n"); + "result.$name$_ = internalGet$capitalized_name$();\n" + "result.$name$_.makeImmutable();\n"); } void ImmutableMapFieldGenerator:: @@ -402,7 +399,7 @@ GenerateParsingCode(io::Printer* printer) const { variables_, "com.google.protobuf.ByteString bytes = input.readBytes();\n" "com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" - "$name$ = $name$DefaultEntry.getParserForType().parseFrom(bytes);\n"); + "$name$ = $default_entry$.getParserForType().parseFrom(bytes);\n"); printer->Print( variables_, "if ($value_enum_type$.valueOf($name$.getValue()) == null) {\n" @@ -415,7 +412,7 @@ GenerateParsingCode(io::Printer* printer) const { variables_, "com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" "$name$ = input.readMessage(\n" - " $name$DefaultEntry.getParserForType(), extensionRegistry);\n" + " $default_entry$.getParserForType(), extensionRegistry);\n" "$name$_.getMutableMap().put($name$.getKey(), $name$.getValue());\n"); } } @@ -432,7 +429,7 @@ GenerateSerializationCode(io::Printer* printer) const { "for (java.util.Map.Entry<$type_parameters$> entry\n" " : internalGet$capitalized_name$().getMap().entrySet()) {\n" " com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" - " $name$ = $name$DefaultEntry.newBuilderForType()\n" + " $name$ = $default_entry$.newBuilderForType()\n" " .setKey(entry.getKey())\n" " .setValue(entry.getValue())\n" " .build();\n" @@ -447,7 +444,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "for (java.util.Map.Entry<$type_parameters$> entry\n" " : internalGet$capitalized_name$().getMap().entrySet()) {\n" " com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" - " $name$ = $name$DefaultEntry.newBuilderForType()\n" + " $name$ = $default_entry$.newBuilderForType()\n" " .setKey(entry.getKey())\n" " .setValue(entry.getValue())\n" " .build();\n" diff --git a/src/google/protobuf/compiler/java/java_map_field.h b/src/google/protobuf/compiler/java/java_map_field.h index 3e6dd973..80a94f45 100644 --- a/src/google/protobuf/compiler/java/java_map_field.h +++ b/src/google/protobuf/compiler/java/java_map_field.h @@ -60,7 +60,6 @@ class ImmutableMapFieldGenerator : public ImmutableFieldGenerator { void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; - void GenerateStaticInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/java/java_map_field_lite.cc b/src/google/protobuf/compiler/java/java_map_field_lite.cc new file mode 100644 index 00000000..ccc1b32e --- /dev/null +++ b/src/google/protobuf/compiler/java/java_map_field_lite.cc @@ -0,0 +1,461 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +const FieldDescriptor* KeyField(const FieldDescriptor* descriptor) { + GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type()); + const Descriptor* message = descriptor->message_type(); + GOOGLE_CHECK(message->options().map_entry()); + return message->FindFieldByName("key"); +} + +const FieldDescriptor* ValueField(const FieldDescriptor* descriptor) { + GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type()); + const Descriptor* message = descriptor->message_type(); + GOOGLE_CHECK(message->options().map_entry()); + return message->FindFieldByName("value"); +} + +string TypeName(const FieldDescriptor* field, + ClassNameResolver* name_resolver, + bool boxed) { + if (GetJavaType(field) == JAVATYPE_MESSAGE) { + return name_resolver->GetImmutableClassName(field->message_type()); + } else if (GetJavaType(field) == JAVATYPE_ENUM) { + return name_resolver->GetImmutableClassName(field->enum_type()); + } else { + return boxed ? BoxedPrimitiveTypeName(GetJavaType(field)) + : PrimitiveTypeName(GetJavaType(field)); + } +} + +string WireType(const FieldDescriptor* field) { + return "com.google.protobuf.WireFormat.FieldType." + + string(FieldTypeName(field->type())); +} + +void SetMessageVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, + map* variables) { + SetCommonFieldVariables(descriptor, info, variables); + + (*variables)["type"] = + name_resolver->GetImmutableClassName(descriptor->message_type()); + const FieldDescriptor* key = KeyField(descriptor); + const FieldDescriptor* value = ValueField(descriptor); + (*variables)["key_type"] = TypeName(key, name_resolver, false); + (*variables)["boxed_key_type"] = TypeName(key, name_resolver, true); + (*variables)["key_wire_type"] = WireType(key); + (*variables)["key_default_value"] = DefaultValue(key, true, name_resolver); + if (GetJavaType(value) == JAVATYPE_ENUM) { + // We store enums as Integers internally. + (*variables)["value_type"] = "int"; + (*variables)["boxed_value_type"] = "java.lang.Integer"; + (*variables)["value_wire_type"] = WireType(value); + (*variables)["value_default_value"] = + DefaultValue(value, true, name_resolver) + ".getNumber()"; + + (*variables)["value_enum_type"] = TypeName(value, name_resolver, false); + + if (SupportUnknownEnumValue(descriptor->file())) { + // Map unknown values to a special UNRECOGNIZED value if supported. + (*variables)["unrecognized_value"] = + (*variables)["value_enum_type"] + ".UNRECOGNIZED"; + } else { + // Map unknown values to the default value if we don't have UNRECOGNIZED. + (*variables)["unrecognized_value"] = + DefaultValue(value, true, name_resolver); + } + } else { + (*variables)["value_type"] = TypeName(value, name_resolver, false); + (*variables)["boxed_value_type"] = TypeName(value, name_resolver, true); + (*variables)["value_wire_type"] = WireType(value); + (*variables)["value_default_value"] = + DefaultValue(value, true, name_resolver); + } + (*variables)["type_parameters"] = + (*variables)["boxed_key_type"] + ", " + (*variables)["boxed_value_type"]; + // 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();" : ""; + + (*variables)["default_entry"] = (*variables)["capitalized_name"] + + "DefaultEntryHolder.defaultEntry"; + (*variables)["lite"] = "Lite"; + (*variables)["descriptor"] = ""; +} + +} // namespace + +ImmutableMapFieldLiteGenerator:: +ImmutableMapFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +ImmutableMapFieldLiteGenerator:: +~ImmutableMapFieldLiteGenerator() {} + +int ImmutableMapFieldLiteGenerator::GetNumBitsForMessage() const { + return 0; +} + +int ImmutableMapFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void ImmutableMapFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$java.util.Map<$boxed_key_type$, $value_enum_type$>\n" + "get$capitalized_name$();\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$java.util.Map<$type_parameters$>\n" + "get$capitalized_name$Value();\n"); + } + } else { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$java.util.Map<$type_parameters$>\n" + "get$capitalized_name$();\n"); + } +} + +void ImmutableMapFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print( + variables_, + "private static final class $capitalized_name$DefaultEntryHolder {\n" + " static final com.google.protobuf.MapEntry$lite$<\n" + " $type_parameters$> defaultEntry =\n" + " com.google.protobuf.MapEntry$lite$\n" + " .<$type_parameters$>newDefaultInstance(\n" + " $descriptor$\n" + " $key_wire_type$,\n" + " $key_default_value$,\n" + " $value_wire_type$,\n" + " $value_default_value$);\n" + "}\n"); + printer->Print( + variables_, + "private com.google.protobuf.MapField$lite$<\n" + " $type_parameters$> $name$_ =\n" + " com.google.protobuf.MapField$lite$.emptyMapField();\n" + "private com.google.protobuf.MapField$lite$<$type_parameters$>\n" + "internalGet$capitalized_name$() {\n" + " return $name$_;\n" + "}\n" + "private com.google.protobuf.MapField$lite$<$type_parameters$>\n" + "internalGetMutable$capitalized_name$() {\n" + " if (!$name$_.isMutable()) {\n" + " $name$_ = $name$_.copy();\n" + " }\n" + " return $name$_;\n" + "}\n"); + if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { + printer->Print( + variables_, + "private static final\n" + "com.google.protobuf.Internal.MapAdapter.Converter<\n" + " java.lang.Integer, $value_enum_type$> $name$ValueConverter =\n" + " com.google.protobuf.Internal.MapAdapter.newEnumConverter(\n" + " $value_enum_type$.internalGetValueMap(),\n" + " $unrecognized_value$);\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$boxed_key_type$, $boxed_value_type$>\n" + "get$capitalized_name$Value() {\n" + " return internalGet$capitalized_name$().getMap();\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$boxed_key_type$, $value_enum_type$>\n" + "get$capitalized_name$() {\n" + " return new com.google.protobuf.Internal.MapAdapter<\n" + " $boxed_key_type$, $value_enum_type$, java.lang.Integer>(\n" + " internalGet$capitalized_name$().getMap(),\n" + " $name$ValueConverter);\n" + "}\n"); + } else { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$type_parameters$> get$capitalized_name$() {\n" + " return internalGet$capitalized_name$().getMap();\n" + "}\n"); + } + + // Generate private setters for the builder to proxy into. + if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "private java.util.Map<$boxed_key_type$, $value_enum_type$>\n" + "getMutable$capitalized_name$() {\n" + " return new com.google.protobuf.Internal.MapAdapter<\n" + " $boxed_key_type$, $value_enum_type$, java.lang.Integer>(\n" + " internalGetMutable$capitalized_name$().getMutableMap(),\n" + " $name$ValueConverter);\n" + "}\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "private java.util.Map<$boxed_key_type$, $boxed_value_type$>\n" + "getMutable$capitalized_name$Value() {\n" + " return internalGetMutable$capitalized_name$().getMutableMap();\n" + "}\n"); + } + } else { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "private java.util.Map<$type_parameters$>\n" + "getMutable$capitalized_name$() {\n" + " return internalGetMutable$capitalized_name$().getMutableMap();\n" + "}\n"); + } +} + +void ImmutableMapFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$boxed_key_type$, $value_enum_type$>\n" + "get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$boxed_key_type$, $value_enum_type$>\n" + "getMutable$capitalized_name$() {\n" + " copyOnWrite();\n" + " return instance.getMutable$capitalized_name$();\n" + "}\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$boxed_key_type$, $boxed_value_type$>\n" + "get$capitalized_name$Value() {\n" + " return instance.get$capitalized_name$Value();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$boxed_key_type$, $boxed_value_type$>\n" + "getMutable$capitalized_name$Value() {\n" + " copyOnWrite();\n" + " return instance.getMutable$capitalized_name$Value();\n" + "}\n"); + } + } else { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "public java.util.Map<$type_parameters$> get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "public java.util.Map<$type_parameters$>\n" + "getMutable$capitalized_name$() {\n" + " copyOnWrite();\n" + " return instance.getMutable$capitalized_name$();\n" + "}\n"); + } +} + +void ImmutableMapFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // Nothing to initialize. +} + +void ImmutableMapFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + // Nothing to initialize. +} + +void ImmutableMapFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print( + variables_, + "internalGetMutable$capitalized_name$().mergeFrom(\n" + " other.internalGet$capitalized_name$());\n"); +} + +void ImmutableMapFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.makeImmutable();\n"); +} + +void ImmutableMapFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print( + variables_, + "if (!$name$_.isMutable()) {\n" + " $name$_ = $name$_.copy();\n" + "}\n"); + if (!SupportUnknownEnumValue(descriptor_->file()) && + GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { + printer->Print( + variables_, + "com.google.protobuf.ByteString bytes = input.readBytes();\n" + "com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" + "$name$ = $default_entry$.getParserForType().parseFrom(bytes);\n"); + printer->Print( + variables_, + "if ($value_enum_type$.valueOf($name$.getValue()) == null) {\n" + " unknownFields.mergeLengthDelimitedField($number$, bytes);\n" + "} else {\n" + " $name$_.getMutableMap().put($name$.getKey(), $name$.getValue());\n" + "}\n"); + } else { + printer->Print( + variables_, + "com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" + "$name$ = input.readMessage(\n" + " $default_entry$.getParserForType(), extensionRegistry);\n" + "$name$_.getMutableMap().put($name$.getKey(), $name$.getValue());\n"); + } +} + +void ImmutableMapFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // Nothing to do here. +} + +void ImmutableMapFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print( + variables_, + "for (java.util.Map.Entry<$type_parameters$> entry\n" + " : internalGet$capitalized_name$().getMap().entrySet()) {\n" + " com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" + " $name$ = $default_entry$.newBuilderForType()\n" + " .setKey(entry.getKey())\n" + " .setValue(entry.getValue())\n" + " .build();\n" + " output.writeMessage($number$, $name$);\n" + "}\n"); +} + +void ImmutableMapFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print( + variables_, + "for (java.util.Map.Entry<$type_parameters$> entry\n" + " : internalGet$capitalized_name$().getMap().entrySet()) {\n" + " com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" + " $name$ = $default_entry$.newBuilderForType()\n" + " .setKey(entry.getKey())\n" + " .setValue(entry.getValue())\n" + " .build();\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeMessageSize($number$, $name$);\n" + "}\n"); +} + +void ImmutableMapFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print( + variables_, + "result = result && internalGet$capitalized_name$().equals(\n" + " other.internalGet$capitalized_name$());\n"); +} + +void ImmutableMapFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print( + variables_, + "if (!internalGet$capitalized_name$().getMap().isEmpty()) {\n" + " hash = (37 * hash) + $constant_name$;\n" + " hash = (53 * hash) + internalGet$capitalized_name$().hashCode();\n" + "}\n"); +} + +string ImmutableMapFieldLiteGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->message_type()); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_map_field_lite.h b/src/google/protobuf/compiler/java/java_map_field_lite.h new file mode 100644 index 00000000..82472602 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_map_field_lite.h @@ -0,0 +1,81 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MAP_FIELD_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MAP_FIELD_LITE_H__ + +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableMapFieldLiteGenerator : public ImmutableFieldLiteGenerator { + public: + explicit ImmutableMapFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableMapFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + 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 GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(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 variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MAP_FIELD_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc index 63df10b4..09b0fd94 100644 --- a/src/google/protobuf/compiler/java/java_message.cc +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include #include #include @@ -93,43 +95,46 @@ ImmutableMessageGenerator::ImmutableMessageGenerator( : MessageGenerator(descriptor), context_(context), name_resolver_(context->GetNameResolver()), field_generators_(descriptor, context_) { + GOOGLE_CHECK_NE( + FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); } ImmutableMessageGenerator::~ImmutableMessageGenerator() {} void ImmutableMessageGenerator::GenerateStaticVariables(io::Printer* printer) { - if (HasDescriptorMethods(descriptor_)) { - // Because descriptor.proto (com.google.protobuf.DescriptorProtos) is - // used in the construction of descriptors, we have a tricky bootstrapping - // problem. To help control static initialization order, we make sure all - // descriptors and other static data that depends on them are members of - // the outermost class in the file. This way, they will be initialized in - // a deterministic order. - - map vars; - vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); - vars["index"] = SimpleItoa(descriptor_->index()); - vars["classname"] = name_resolver_->GetImmutableClassName(descriptor_); - if (descriptor_->containing_type() != NULL) { - vars["parent"] = UniqueFileScopeIdentifier( - descriptor_->containing_type()); - } - if (MultipleJavaFiles(descriptor_->file(), /* immutable = */ true)) { - // We can only make these package-private since the classes that use them - // are in separate files. - vars["private"] = ""; - } else { - vars["private"] = "private "; - } - - // The descriptor for this type. - printer->Print(vars, - "$private$static com.google.protobuf.Descriptors.Descriptor\n" - " internal_$identifier$_descriptor;\n"); + // Because descriptor.proto (com.google.protobuf.DescriptorProtos) is + // used in the construction of descriptors, we have a tricky bootstrapping + // problem. To help control static initialization order, we make sure all + // descriptors and other static data that depends on them are members of + // the outermost class in the file. This way, they will be initialized in + // a deterministic order. - // And the FieldAccessorTable. - GenerateFieldAccessorTable(printer); + map vars; + vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); + vars["index"] = SimpleItoa(descriptor_->index()); + vars["classname"] = name_resolver_->GetImmutableClassName(descriptor_); + if (descriptor_->containing_type() != NULL) { + vars["parent"] = UniqueFileScopeIdentifier( + descriptor_->containing_type()); } + if (MultipleJavaFiles(descriptor_->file(), /* immutable = */ true)) { + // We can only make these package-private since the classes that use them + // are in separate files. + vars["private"] = ""; + } else { + vars["private"] = "private "; + } + + // The descriptor for this type. + printer->Print(vars, + // TODO(teboring): final needs to be added back. The way to fix it is to + // generate methods that can construct the types, and then still declare the + // types, and then init them in clinit with the new method calls. + "$private$static com.google.protobuf.Descriptors.Descriptor\n" + " internal_$identifier$_descriptor;\n"); + + // And the FieldAccessorTable. + GenerateFieldAccessorTable(printer); // Generate static members for all nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { @@ -142,38 +147,37 @@ void ImmutableMessageGenerator::GenerateStaticVariables(io::Printer* printer) { int ImmutableMessageGenerator::GenerateStaticVariableInitializers( io::Printer* printer) { int bytecode_estimate = 0; - if (HasDescriptorMethods(descriptor_)) { - map vars; - vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); - vars["index"] = SimpleItoa(descriptor_->index()); - vars["classname"] = name_resolver_->GetImmutableClassName(descriptor_); - if (descriptor_->containing_type() != NULL) { - vars["parent"] = UniqueFileScopeIdentifier( - descriptor_->containing_type()); - } - - // The descriptor for this type. - if (descriptor_->containing_type() == NULL) { - printer->Print(vars, - "internal_$identifier$_descriptor =\n" - " getDescriptor().getMessageTypes().get($index$);\n"); - bytecode_estimate += 30; - } else { - printer->Print(vars, - "internal_$identifier$_descriptor =\n" - " internal_$parent$_descriptor.getNestedTypes().get($index$);\n"); - bytecode_estimate += 30; - } + map vars; + vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); + vars["index"] = SimpleItoa(descriptor_->index()); + vars["classname"] = name_resolver_->GetImmutableClassName(descriptor_); + if (descriptor_->containing_type() != NULL) { + vars["parent"] = UniqueFileScopeIdentifier( + descriptor_->containing_type()); + } - // And the FieldAccessorTable. - bytecode_estimate += GenerateFieldAccessorTableInitializer(printer); + // The descriptor for this type. + if (descriptor_->containing_type() == NULL) { + printer->Print(vars, + "internal_$identifier$_descriptor =\n" + " getDescriptor().getMessageTypes().get($index$);\n"); + bytecode_estimate += 30; + } else { + printer->Print(vars, + "internal_$identifier$_descriptor =\n" + " internal_$parent$_descriptor.getNestedTypes().get($index$);\n"); + bytecode_estimate += 30; } + // And the FieldAccessorTable. + bytecode_estimate += GenerateFieldAccessorTableInitializer(printer); + // Generate static member initializers for all nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { // TODO(kenton): Reuse MessageGenerator objects? - bytecode_estimate += ImmutableMessageGenerator(descriptor_->nested_type(i), context_) - .GenerateStaticVariableInitializers(printer); + bytecode_estimate += + ImmutableMessageGenerator(descriptor_->nested_type(i), context_) + .GenerateStaticVariableInitializers(printer); } return bytecode_estimate; } @@ -229,40 +233,20 @@ GenerateFieldAccessorTableInitializer(io::Printer* printer) { void ImmutableMessageGenerator::GenerateInterface(io::Printer* printer) { if (descriptor_->extension_range_count() > 0) { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public interface $classname$OrBuilder extends\n" - " $extra_interfaces$\n" - " com.google.protobuf.GeneratedMessage.\n" - " ExtendableMessageOrBuilder<$classname$> {\n", - "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), - "classname", descriptor_->name()); - } else { - printer->Print( - "public interface $classname$OrBuilder extends \n" - " $extra_interfaces$\n" - " com.google.protobuf.GeneratedMessageLite.\n" - " ExtendableMessageOrBuilder<\n" - " $classname$, $classname$.Builder> {\n", - "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), - "classname", descriptor_->name()); - } + printer->Print( + "public interface $classname$OrBuilder extends\n" + " $extra_interfaces$\n" + " com.google.protobuf.GeneratedMessage.\n" + " ExtendableMessageOrBuilder<$classname$> {\n", + "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), + "classname", descriptor_->name()); } else { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public interface $classname$OrBuilder extends\n" - " $extra_interfaces$\n" - " com.google.protobuf.MessageOrBuilder {\n", - "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), - "classname", descriptor_->name()); - } else { - printer->Print( - "public interface $classname$OrBuilder extends\n" - " $extra_interfaces$\n" - " com.google.protobuf.MessageLiteOrBuilder {\n", - "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), - "classname", descriptor_->name()); - } + printer->Print( + "public interface $classname$OrBuilder extends\n" + " $extra_interfaces$\n" + " com.google.protobuf.MessageOrBuilder {\n", + "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), + "classname", descriptor_->name()); } printer->Indent(); @@ -287,98 +271,71 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { variables["static"] = is_own_file ? " " : " static "; variables["classname"] = descriptor_->name(); variables["extra_interfaces"] = ExtraMessageInterfaces(descriptor_); - variables["lite"] = HasDescriptorMethods(descriptor_) ? "" : "Lite"; WriteMessageDocComment(printer, descriptor_); // The builder_type stores the super type name of the nested Builder class. string builder_type; if (descriptor_->extension_range_count() > 0) { - if (HasDescriptorMethods(descriptor_)) { - printer->Print(variables, - "public $static$final class $classname$ extends\n" - " com.google.protobuf.GeneratedMessage.ExtendableMessage<\n" - " $classname$> implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n"); - } else { - printer->Print(variables, - "public $static$final class $classname$ extends\n" - " com.google.protobuf.GeneratedMessageLite.ExtendableMessage<\n" - " $classname$, $classname$.Builder> implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n"); - } + printer->Print(variables, + "public $static$final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessage.ExtendableMessage<\n" + " $classname$> implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n"); builder_type = strings::Substitute( - "com.google.protobuf.GeneratedMessage$1.ExtendableBuilder<$0, ?>", - name_resolver_->GetImmutableClassName(descriptor_), - variables["lite"]); + "com.google.protobuf.GeneratedMessage.ExtendableBuilder<$0, ?>", + name_resolver_->GetImmutableClassName(descriptor_)); } else { - if (HasDescriptorMethods(descriptor_)) { - printer->Print(variables, - "public $static$final class $classname$ extends\n" - " com.google.protobuf.GeneratedMessage implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n"); - } else { - printer->Print(variables, - "public $static$final class $classname$ extends\n" - " com.google.protobuf.GeneratedMessageLite<\n" - " $classname$, $classname$.Builder> implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n"); - } + printer->Print(variables, + "public $static$final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessage implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n"); - builder_type = strings::Substitute( - "com.google.protobuf.GeneratedMessage$0.Builder", - variables["lite"]); + builder_type = "com.google.protobuf.GeneratedMessage.Builder"; } printer->Indent(); - if (HasDescriptorMethods(descriptor_)) { - // Using builder_type, instead of Builder, prevents the Builder class from - // being loaded into PermGen space when the default instance is created. - // This optimizes the PermGen space usage for clients that do not modify - // messages. - printer->Print( - "// Use $classname$.newBuilder() to construct.\n" - "private $classname$($buildertype$ builder) {\n" - " super(builder);\n" - "}\n", - "classname", descriptor_->name(), - "buildertype", builder_type); - printer->Print( - "private $classname$() {\n", - "classname", descriptor_->name()); - printer->Indent(); - GenerateInitializers(printer); - printer->Outdent(); - printer->Print( - "}\n" - "\n"); - } + // Using builder_type, instead of Builder, prevents the Builder class from + // being loaded into PermGen space when the default instance is created. + // This optimizes the PermGen space usage for clients that do not modify + // messages. + printer->Print( + "// Use $classname$.newBuilder() to construct.\n" + "private $classname$($buildertype$ builder) {\n" + " super(builder);\n" + "}\n", + "classname", descriptor_->name(), + "buildertype", builder_type); + printer->Print( + "private $classname$() {\n", + "classname", descriptor_->name()); + printer->Indent(); + GenerateInitializers(printer); + printer->Outdent(); + printer->Print( + "}\n" + "\n"); - if (HasDescriptorMethods(descriptor_)) { + printer->Print( + "@java.lang.Override\n" + "public final com.google.protobuf.UnknownFieldSet\n" + "getUnknownFields() {\n"); + if (PreserveUnknownFields(descriptor_)) { printer->Print( - "@java.lang.Override\n" - "public final com.google.protobuf.UnknownFieldSet\n" - "getUnknownFields() {\n"); - if (PreserveUnknownFields(descriptor_)) { - printer->Print( - " return this.unknownFields;\n"); - } else { - printer->Print( - " return com.google.protobuf.UnknownFieldSet.getDefaultInstance();\n"); - } + " return this.unknownFields;\n"); + } else { printer->Print( - "}\n"); + " return com.google.protobuf.UnknownFieldSet.getDefaultInstance();\n"); } + printer->Print( + "}\n"); if (HasGeneratedMethods(descriptor_)) { GenerateParsingConstructor(printer); } - GenerateDescriptorMethods(printer, false); - GenerateParser(printer); + GenerateDescriptorMethods(printer); // Nested types for (int i = 0; i < descriptor_->enum_type_count(); i++) { @@ -488,7 +445,7 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { } if (HasGeneratedMethods(descriptor_)) { - GenerateIsInitialized(printer, MEMOIZE); + GenerateIsInitialized(printer); GenerateMessageSerializationMethods(printer); } @@ -509,78 +466,39 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { // Carefully initialize the default instance in such a way that it doesn't // conflict with other initialization. printer->Print( - "private static final $classname$ defaultInstance;\n", + "private static final $classname$ DEFAULT_INSTANCE;\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "static {\n" - " defaultInstance = new $classname$();\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } else { - // LITE_RUNTIME only has one constructor. - printer->Print( - "static {\n" - " defaultInstance = new $classname$(\n" - " com.google.protobuf.Internal\n" - " .EMPTY_CODED_INPUT_STREAM,\n" - " com.google.protobuf.ExtensionRegistryLite\n" - " .getEmptyRegistry());\n" - "}\n" - "\n", - "classname", descriptor_->name()); - } + printer->Print( + "static {\n" + " DEFAULT_INSTANCE = new $classname$();\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + printer->Print( "public static $classname$ getDefaultInstance() {\n" - " return defaultInstance;\n" + " return DEFAULT_INSTANCE;\n" "}\n" "\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); - if (HasDescriptorMethods(descriptor_)) { - // LITE_RUNTIME implements this at the GeneratedMessageLite level. - printer->Print( - "public $classname$ getDefaultInstanceForType() {\n" - " return defaultInstance;\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } else { - // LITE_RUNTIME uses this to implement the *ForType methods at the - // GeneratedMessageLite level. - printer->Print( - "static {" - " com.google.protobuf.GeneratedMessageLite.onLoad(\n" - " $classname$.class, new com.google.protobuf.GeneratedMessageLite\n" - " .PrototypeHolder<$classname$, Builder>(\n" - " defaultInstance, PARSER));" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } + GenerateParser(printer); - // Extensions must be declared after the defaultInstance is initialized - // because the defaultInstance is used by the extension to lazily retrieve + printer->Print( + "public $classname$ getDefaultInstanceForType() {\n" + " return DEFAULT_INSTANCE;\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + // Extensions must be declared after the DEFAULT_INSTANCE is initialized + // because the DEFAULT_INSTANCE is used by the extension to lazily retrieve // the outer class's FileDescriptor. for (int i = 0; i < descriptor_->extension_count(); i++) { ImmutableExtensionGenerator(descriptor_->extension(i), context_) .Generate(printer); } - // Some fields also have static members that must be initialized after we - // have the default instance available. - printer->Print( - "static {\n"); - printer->Indent(); - for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(descriptor_->field(i)) - .GenerateStaticInitializationCode(printer); - } - printer->Outdent(); - printer->Print( - "}\n"); - printer->Outdent(); printer->Print("}\n\n"); } @@ -617,35 +535,17 @@ GenerateMessageSerializationMethods(io::Printer* printer) { if (descriptor_->extension_range_count() > 0) { if (descriptor_->options().message_set_wire_format()) { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "com.google.protobuf.GeneratedMessage\n" - " .ExtendableMessage<$classname$>.ExtensionWriter\n" - " extensionWriter = newMessageSetExtensionWriter();\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } else { - printer->Print( - "com.google.protobuf.GeneratedMessageLite\n" - " .ExtendableMessage<$classname$, $classname$.Builder>\n" - " .ExtensionWriter extensionWriter =\n" - " newMessageSetExtensionWriter();\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } + printer->Print( + "com.google.protobuf.GeneratedMessage\n" + " .ExtendableMessage<$classname$>.ExtensionWriter\n" + " extensionWriter = newMessageSetExtensionWriter();\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); } else { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "com.google.protobuf.GeneratedMessage\n" - " .ExtendableMessage<$classname$>.ExtensionWriter\n" - " extensionWriter = newExtensionWriter();\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } else { - printer->Print( - "com.google.protobuf.GeneratedMessageLite\n" - " .ExtendableMessage<$classname$, $classname$.Builder>\n" - " .ExtensionWriter extensionWriter =\n" - " newExtensionWriter();\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } + printer->Print( + "com.google.protobuf.GeneratedMessage\n" + " .ExtendableMessage<$classname$>.ExtensionWriter\n" + " extensionWriter = newExtensionWriter();\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); } } @@ -665,8 +565,7 @@ GenerateMessageSerializationMethods(io::Printer* printer) { } if (PreserveUnknownFields(descriptor_)) { - if (descriptor_->options().message_set_wire_format() - && HasDescriptorMethods(descriptor_)) { + if (descriptor_->options().message_set_wire_format()) { printer->Print( "unknownFields.writeAsMessageSetTo(output);\n"); } else { @@ -702,8 +601,7 @@ GenerateMessageSerializationMethods(io::Printer* printer) { } if (PreserveUnknownFields(descriptor_)) { - if (descriptor_->options().message_set_wire_format() - && HasDescriptorMethods(descriptor_)) { + if (descriptor_->options().message_set_wire_format()) { printer->Print( "size += unknownFields.getSerializedSizeAsMessageSet();\n"); } else { @@ -800,609 +698,115 @@ void ImmutableMessageGenerator::GenerateSerializeOneExtensionRange( // =================================================================== void ImmutableMessageGenerator::GenerateBuilder(io::Printer* printer) { - if (HasDescriptorMethods(descriptor_)) { - // LITE_RUNTIME implements this at the GeneratedMessageLite level. - printer->Print( - "public Builder newBuilderForType() { return newBuilder(); }\n"); - } + // LITE_RUNTIME implements this at the GeneratedMessageLite level. + printer->Print( + "public Builder newBuilderForType() { return newBuilder(); }\n"); printer->Print( "public static Builder newBuilder() {\n" - " return defaultInstance.toBuilder();\n" + " return DEFAULT_INSTANCE.toBuilder();\n" "}\n" "public static Builder newBuilder($classname$ prototype) {\n" - " return defaultInstance.toBuilder().mergeFrom(prototype);\n" + " return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);\n" "}\n" "public Builder toBuilder() {\n" - " return this == defaultInstance\n" + " return this == DEFAULT_INSTANCE\n" " ? new Builder() : new Builder().mergeFrom(this);\n" "}\n" "\n", "classname", name_resolver_->GetImmutableClassName(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"); - } - - WriteMessageDocComment(printer, descriptor_); - - 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> implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_), - "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); - } else { - printer->Print( - "public static final class Builder extends\n" - " com.google.protobuf.GeneratedMessageLite.ExtendableBuilder<\n" - " $classname$, Builder> implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_), - "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); - } - } else { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public static final class Builder extends\n" - " com.google.protobuf.GeneratedMessage.Builder implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_), - "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); - } else { - printer->Print( - "public static final class Builder extends\n" - " com.google.protobuf.GeneratedMessageLite.Builder<\n" - " $classname$, Builder>\n" - " implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_), - "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); - } - } - printer->Indent(); - - GenerateDescriptorMethods(printer, true); - GenerateCommonBuilderMethods(printer); - - if (HasGeneratedMethods(descriptor_)) { - GenerateIsInitialized(printer, DONT_MEMOIZE); - GenerateBuilderParsingMethods(printer); - } - - // oneof - map vars; - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - vars["oneof_name"] = context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->name; - vars["oneof_capitalized_name"] = context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->capitalized_name; - vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); - // oneofCase_ and oneof_ - printer->Print(vars, - "private int $oneof_name$Case_ = 0;\n" - "private java.lang.Object $oneof_name$_;\n"); - // oneofCase() and clearOneof() - printer->Print(vars, - "public $oneof_capitalized_name$Case\n" - " get$oneof_capitalized_name$Case() {\n" - " return $oneof_capitalized_name$Case.valueOf(\n" - " $oneof_name$Case_);\n" - "}\n" - "\n" - "public Builder clear$oneof_capitalized_name$() {\n" - " $oneof_name$Case_ = 0;\n" - " $oneof_name$_ = null;\n"); - if (HasDescriptorMethods(descriptor_)) { - printer->Print(" onChanged();\n"); - } - printer->Print( - " return this;\n" - "}\n" - "\n"); - } - - if (GenerateHasBits(descriptor_)) { - // 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"); - field_generators_.get(descriptor_->field(i)) - .GenerateBuilderMembers(printer); - } - - if (!PreserveUnknownFields(descriptor_)) { - printer->Print( - "public final Builder setUnknownFields(\n" - " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" - " return this;\n" - "}\n" - "\n" - "public final Builder mergeUnknownFields(\n" - " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" - " return this;\n" - "}\n" - "\n"); - } - printer->Print( - "\n" - "// @@protoc_insertion_point(builder_scope:$full_name$)\n", - "full_name", descriptor_->full_name()); - - printer->Outdent(); - printer->Print("}\n"); + "@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"); + + MessageBuilderGenerator builderGenerator(descriptor_, context_); + builderGenerator.Generate(printer); } void ImmutableMessageGenerator:: -GenerateDescriptorMethods(io::Printer* printer, bool is_builder) { - if (HasDescriptorMethods(descriptor_)) { - if (!descriptor_->options().no_standard_descriptor_accessor()) { - printer->Print( - "public static final com.google.protobuf.Descriptors.Descriptor\n" - " getDescriptor() {\n" - " return $fileclass$.internal_$identifier$_descriptor;\n" - "}\n" - "\n", - "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), - "identifier", UniqueFileScopeIdentifier(descriptor_)); - } - vector map_fields; - for (int i = 0; i < descriptor_->field_count(); i++) { - const FieldDescriptor* field = descriptor_->field(i); - if (GetJavaType(field) == JAVATYPE_MESSAGE && - IsMapEntry(field->message_type())) { - map_fields.push_back(field); - } - } - if (!map_fields.empty()) { - printer->Print( - "@SuppressWarnings({\"rawtypes\"})\n" - "protected com.google.protobuf.MapField internalGetMapField(\n" - " int number) {\n" - " switch (number) {\n"); - printer->Indent(); - printer->Indent(); - for (int i = 0; i < map_fields.size(); ++i) { - const FieldDescriptor* field = map_fields[i]; - const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); - printer->Print( - "case $number$:\n" - " return internalGet$capitalized_name$();\n", - "number", SimpleItoa(field->number()), - "capitalized_name", info->capitalized_name); - } - printer->Print( - "default:\n" - " throw new RuntimeException(\n" - " \"Invalid map field number: \" + number);\n"); - printer->Outdent(); - printer->Outdent(); - printer->Print( - " }\n" - "}\n"); - if (is_builder) { - printer->Print( - "@SuppressWarnings({\"rawtypes\"})\n" - "protected com.google.protobuf.MapField internalGetMutableMapField(\n" - " int number) {\n" - " switch (number) {\n"); - printer->Indent(); - printer->Indent(); - for (int i = 0; i < map_fields.size(); ++i) { - const FieldDescriptor* field = map_fields[i]; - const FieldGeneratorInfo* info = - context_->GetFieldGeneratorInfo(field); - printer->Print( - "case $number$:\n" - " return internalGetMutable$capitalized_name$();\n", - "number", SimpleItoa(field->number()), - "capitalized_name", info->capitalized_name); - } - printer->Print( - "default:\n" - " throw new RuntimeException(\n" - " \"Invalid map field number: \" + number);\n"); - printer->Outdent(); - printer->Outdent(); - printer->Print( - " }\n" - "}\n"); - } - } +GenerateDescriptorMethods(io::Printer* printer) { + if (!descriptor_->options().no_standard_descriptor_accessor()) { printer->Print( - "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" - " internalGetFieldAccessorTable() {\n" - " return $fileclass$.internal_$identifier$_fieldAccessorTable\n" - " .ensureFieldAccessorsInitialized(\n" - " $classname$.class, $classname$.Builder.class);\n" + "public static final com.google.protobuf.Descriptors.Descriptor\n" + " getDescriptor() {\n" + " return $fileclass$.internal_$identifier$_descriptor;\n" "}\n" "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_), "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), "identifier", UniqueFileScopeIdentifier(descriptor_)); } -} - -// =================================================================== - -void ImmutableMessageGenerator:: -GenerateCommonBuilderMethods(io::Printer* printer) { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "// Construct using $classname$.newBuilder()\n" - "private Builder() {\n" - " maybeForceBuilderInitialization();\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - - printer->Print( - "private Builder(\n" - " com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n" - " super(parent);\n" - " maybeForceBuilderInitialization();\n" - "}\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } else { - // LITE runtime passes along the default instance to implement - // getDefaultInstanceForType() at the GeneratedMessageLite level. - printer->Print( - "// Construct using $classname$.newBuilder()\n" - "private Builder() {\n" - " super(defaultInstance);\n" - " maybeForceBuilderInitialization();\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); + vector map_fields; + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (GetJavaType(field) == JAVATYPE_MESSAGE && + IsMapEntry(field->message_type())) { + map_fields.push_back(field); + } } - - - if (HasNestedBuilders(descriptor_)) { + if (!map_fields.empty()) { printer->Print( - "private void maybeForceBuilderInitialization() {\n" - " if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {\n"); - + "@SuppressWarnings({\"rawtypes\"})\n" + "protected com.google.protobuf.MapField internalGetMapField(\n" + " int number) {\n" + " switch (number) {\n"); printer->Indent(); printer->Indent(); - for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - field_generators_.get(descriptor_->field(i)) - .GenerateFieldBuilderInitializationCode(printer); - } + for (int i = 0; i < map_fields.size(); ++i) { + const FieldDescriptor* field = map_fields[i]; + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + printer->Print( + "case $number$:\n" + " return internalGet$capitalized_name$();\n", + "number", SimpleItoa(field->number()), + "capitalized_name", info->capitalized_name); } + printer->Print( + "default:\n" + " throw new RuntimeException(\n" + " \"Invalid map field number: \" + number);\n"); printer->Outdent(); printer->Outdent(); - - printer->Print( - " }\n" - "}\n"); - } else { - printer->Print( - "private void maybeForceBuilderInitialization() {\n" - "}\n"); - } - - printer->Print( - "public Builder clear() {\n" - " super.clear();\n"); - - printer->Indent(); - - for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - field_generators_.get(descriptor_->field(i)) - .GenerateBuilderClearCode(printer); - } - } - - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - printer->Print( - "$oneof_name$Case_ = 0;\n" - "$oneof_name$_ = null;\n", - "oneof_name", context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->name); - } - - printer->Outdent(); - - printer->Print( - " return this;\n" - "}\n" - "\n"); - - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public com.google.protobuf.Descriptors.Descriptor\n" - " getDescriptorForType() {\n" - " return $fileclass$.internal_$identifier$_descriptor;\n" - "}\n" - "\n", - "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), - "identifier", UniqueFileScopeIdentifier(descriptor_)); - - // LITE runtime implements this in GeneratedMessageLite. - printer->Print( - "public $classname$ getDefaultInstanceForType() {\n" - " return $classname$.getDefaultInstance();\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } - - // ----------------------------------------------------------------- - - if (HasDescriptorMethods(descriptor_)) { - // LITE implements build in GeneratedMessageLite to save methods. printer->Print( - "public $classname$ build() {\n" - " $classname$ result = buildPartial();\n" - " if (!result.isInitialized()) {\n" - " throw newUninitializedMessageException(result);\n" - " }\n" - " return result;\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } - - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public $classname$ buildPartial() {\n" - " $classname$ result = new $classname$(this);\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } else { - // LITE_RUNTIME only provides a single message constructor. - printer->Print( - "public $classname$ buildPartial() {\n" - " $classname$ result = new $classname$(\n" - " com.google.protobuf.Internal\n" - " .EMPTY_CODED_INPUT_STREAM,\n" - " com.google.protobuf.ExtensionRegistryLite\n" - " .getEmptyRegistry());\n" - " result.unknownFields = this.unknownFields;\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - - if (descriptor_->extension_range_count() > 0) { - printer->Print( - " result.extensions = this.buildExtensions();\n"); - } - } - - printer->Indent(); - - int totalBuilderBits = 0; - int totalMessageBits = 0; - for (int i = 0; i < descriptor_->field_count(); i++) { - const ImmutableFieldGenerator& field = - field_generators_.get(descriptor_->field(i)); - totalBuilderBits += field.GetNumBitsForBuilder(); - totalMessageBits += field.GetNumBitsForMessage(); - } - int totalBuilderInts = (totalBuilderBits + 31) / 32; - int totalMessageInts = (totalMessageBits + 31) / 32; - - if (GenerateHasBits(descriptor_)) { - // 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. - 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); - } - - if (GenerateHasBits(descriptor_)) { - // 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)); - } - } - - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - printer->Print("result.$oneof_name$Case_ = $oneof_name$Case_;\n", - "oneof_name", context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->name); - } - - printer->Outdent(); - - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - " onBuilt();\n"); + " }\n" + "}\n"); } - printer->Print( - " return result;\n" + "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" + " internalGetFieldAccessorTable() {\n" + " return $fileclass$.internal_$identifier$_fieldAccessorTable\n" + " .ensureFieldAccessorsInitialized(\n" + " $classname$.class, $classname$.Builder.class);\n" "}\n" "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - - // ----------------------------------------------------------------- - - if (HasGeneratedMethods(descriptor_)) { - // MergeFrom(Message other) requires the ability to distinguish the other - // messages type by its descriptor. - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public Builder mergeFrom(com.google.protobuf.Message other) {\n" - " if (other instanceof $classname$) {\n" - " return mergeFrom(($classname$)other);\n" - " } else {\n" - " super.mergeFrom(other);\n" - " return this;\n" - " }\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } - - printer->Print( - "public Builder mergeFrom($classname$ other) {\n" - // Optimization: If other is the default instance, we know none of its - // fields are set so we can skip the merge. - " if (other == $classname$.getDefaultInstance()) return this;\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - printer->Indent(); - - for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - field_generators_.get( - descriptor_->field(i)).GenerateMergingCode(printer); - } - } - - // Merge oneof fields. - for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) { - printer->Print( - "switch (other.get$oneof_capitalized_name$Case()) {\n", - "oneof_capitalized_name", - context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->capitalized_name); - printer->Indent(); - for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { - const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); - printer->Print( - "case $field_name$: {\n", - "field_name", - ToUpper(field->name())); - printer->Indent(); - field_generators_.get(field).GenerateMergingCode(printer); - printer->Print( - "break;\n"); - printer->Outdent(); - printer->Print( - "}\n"); - } - printer->Print( - "case $cap_oneof_name$_NOT_SET: {\n" - " break;\n" - "}\n", - "cap_oneof_name", - ToUpper(context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->name)); - printer->Outdent(); - printer->Print( - "}\n"); - } - - printer->Outdent(); - - // if message type has extensions - if (descriptor_->extension_range_count() > 0) { - printer->Print( - " this.mergeExtensionFields(other);\n"); - } - - if (PreserveUnknownFields(descriptor_)) { - printer->Print( - " this.mergeUnknownFields(other.unknownFields);\n"); - } - - if (HasDescriptorMethods(descriptor_)) { - printer->Print(" onChanged();\n"); - } - - printer->Print( - " return this;\n" - "}\n" - "\n"); - } -} - -// =================================================================== - -void ImmutableMessageGenerator:: -GenerateBuilderParsingMethods(io::Printer* printer) { - if (HasDescriptorMethods(descriptor_)) { - // LITE_RUNTIME implements this at the GeneratedMessageLite level. - printer->Print( - "public Builder mergeFrom(\n" - " com.google.protobuf.CodedInputStream input,\n" - " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" - " throws java.io.IOException {\n" - " $classname$ parsedMessage = null;\n" - " try {\n" - " parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n" - " } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" - " parsedMessage = ($classname$) e.getUnfinishedMessage();\n" - " throw e;\n" - " } finally {\n" - " if (parsedMessage != null) {\n" - " mergeFrom(parsedMessage);\n" - " }\n" - " }\n" - " return this;\n" - "}\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } + "classname", name_resolver_->GetImmutableClassName(descriptor_), + "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); } // =================================================================== void ImmutableMessageGenerator::GenerateIsInitialized( - io::Printer* printer, UseMemoization useMemoization) { - // LITE_RUNTIME avoids generating isInitialized if it's not needed. - if (!HasDescriptorMethods(descriptor_) - && !HasRequiredFields(descriptor_)) { - return; - } - - 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"); - } + io::Printer* printer) { + // 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) { - // Don't directly compare to -1 to avoid an Android x86 JIT bug. - printer->Print( - "byte isInitialized = memoizedIsInitialized;\n" - "if (isInitialized == 1) return true;\n" - "if (isInitialized == 0) return false;\n" - "\n"); - } + // Don't directly compare to -1 to avoid an Android x86 JIT bug. + printer->Print( + "byte isInitialized = memoizedIsInitialized;\n" + "if (isInitialized == 1) return true;\n" + "if (isInitialized == 0) return false;\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 @@ -1414,11 +818,10 @@ void ImmutableMessageGenerator::GenerateIsInitialized( if (field->is_required()) { printer->Print( "if (!has$name$()) {\n" - " $memoize$\n" + " memoizedIsInitialized = 0;\n" " return false;\n" "}\n", - "name", info->capitalized_name, - "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); + "name", info->capitalized_name); } } @@ -1432,13 +835,12 @@ void ImmutableMessageGenerator::GenerateIsInitialized( case FieldDescriptor::LABEL_REQUIRED: printer->Print( "if (!get$name$().isInitialized()) {\n" - " $memoize$\n" + " memoizedIsInitialized = 0;\n" " return false;\n" "}\n", "type", name_resolver_->GetImmutableClassName( field->message_type()), - "name", info->capitalized_name, - "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); + "name", info->capitalized_name); break; case FieldDescriptor::LABEL_OPTIONAL: if (!SupportFieldPresence(descriptor_->file()) && @@ -1457,38 +859,35 @@ void ImmutableMessageGenerator::GenerateIsInitialized( } printer->Print( " if (!get$name$().isInitialized()) {\n" - " $memoize$\n" + " memoizedIsInitialized = 0;\n" " return false;\n" " }\n" "}\n", - "name", info->capitalized_name, - "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); + "name", info->capitalized_name); break; case FieldDescriptor::LABEL_REPEATED: if (IsMapEntry(field->message_type())) { printer->Print( "for ($type$ item : get$name$().values()) {\n" " if (!item.isInitialized()) {\n" - " $memoize$\n" + " memoizedIsInitialized = 0;\n" " return false;\n" " }\n" "}\n", "type", MapValueImmutableClassdName(field->message_type(), name_resolver_), - "name", info->capitalized_name, - "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); + "name", info->capitalized_name); } else { printer->Print( "for (int i = 0; i < get$name$Count(); i++) {\n" " if (!get$name$(i).isInitialized()) {\n" - " $memoize$\n" + " memoizedIsInitialized = 0;\n" " return false;\n" " }\n" "}\n", "type", name_resolver_->GetImmutableClassName( field->message_type()), - "name", info->capitalized_name, - "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); + "name", info->capitalized_name); } break; } @@ -1498,18 +897,15 @@ void ImmutableMessageGenerator::GenerateIsInitialized( if (descriptor_->extension_range_count() > 0) { printer->Print( "if (!extensionsAreInitialized()) {\n" - " $memoize$\n" + " memoizedIsInitialized = 0;\n" " return false;\n" - "}\n", - "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); + "}\n"); } printer->Outdent(); - if (memoization) { - printer->Print( - " memoizedIsInitialized = 1;\n"); - } + printer->Print( + " memoizedIsInitialized = 1;\n"); printer->Print( " return true;\n" @@ -1575,12 +971,10 @@ GenerateEqualsAndHashCode(io::Printer* printer) { printer->Print( "result = result && unknownFields.equals(other.unknownFields);\n"); } - if (HasDescriptorMethods(descriptor_)) { - if (descriptor_->extension_range_count() > 0) { - printer->Print( - "result = result &&\n" - " getExtensionFields().equals(other.getExtensionFields());\n"); - } + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "result = result &&\n" + " getExtensionFields().equals(other.getExtensionFields());\n"); } printer->Print( "return result;\n"); @@ -1603,14 +997,7 @@ GenerateEqualsAndHashCode(io::Printer* printer) { "}\n" "int hash = 41;\n"); - if (HasDescriptorMethods(descriptor_)) { - printer->Print("hash = (19 * hash) + getDescriptorForType().hashCode();\n"); - } else { - // Include the hash of the class so that two objects with different types - // but the same field values will probably have different hashes. - printer->Print("hash = (19 * hash) + $classname$.class.hashCode();\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } + printer->Print("hash = (19 * hash) + getDescriptorForType().hashCode();\n"); for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); @@ -1628,11 +1015,9 @@ GenerateEqualsAndHashCode(io::Printer* printer) { printer->Print("}\n"); } } - if (HasDescriptorMethods(descriptor_)) { - if (descriptor_->extension_range_count() > 0) { - printer->Print( - "hash = hashFields(hash, getExtensionFields());\n"); - } + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "hash = hashFields(hash, getExtensionFields());\n"); } printer->Print( @@ -1675,13 +1060,8 @@ GenerateParsingConstructor(io::Printer* printer) { printer->Indent(); // Initialize all fields to default. - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "this();\n"); - } else { - // LITE_RUNTIME only has one constructor. - GenerateInitializers(printer); - } + printer->Print( + "this();\n"); // Use builder bits to track mutable repeated fields. int totalBuilderBits = 0; @@ -1697,15 +1077,9 @@ GenerateParsingConstructor(io::Printer* printer) { } if (PreserveUnknownFields(descriptor_)) { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n" - " com.google.protobuf.UnknownFieldSet.newBuilder();\n"); - } else { - printer->Print( - "com.google.protobuf.UnknownFieldSetLite.Builder unknownFields =\n" - " com.google.protobuf.UnknownFieldSetLite.newBuilder();\n"); - } + printer->Print( + "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n" + " com.google.protobuf.UnknownFieldSet.newBuilder();\n"); } printer->Print( @@ -1728,29 +1102,14 @@ GenerateParsingConstructor(io::Printer* printer) { " break;\n"); if (PreserveUnknownFields(descriptor_)) { - if (!HasDescriptorMethods(descriptor_) - && descriptor_->extension_range_count() > 0) { - // Lite runtime directly invokes parseUnknownField to reduce method - // counts. - printer->Print( - "default: {\n" - " if (!parseUnknownField(extensions, getDefaultInstanceForType(),\n" - " input, unknownFields,\n" - " extensionRegistry, tag)) {\n" - " done = true;\n" // it's an endgroup tag - " }\n" - " break;\n" - "}\n"); - } else { - printer->Print( - "default: {\n" - " if (!parseUnknownField(input, unknownFields,\n" - " extensionRegistry, tag)) {\n" - " done = true;\n" // it's an endgroup tag - " }\n" - " break;\n" - "}\n"); - } + printer->Print( + "default: {\n" + " if (!parseUnknownField(input, unknownFields,\n" + " extensionRegistry, tag)) {\n" + " done = true;\n" // it's an endgroup tag + " }\n" + " break;\n" + "}\n"); } else { printer->Print( "default: {\n" @@ -1825,18 +1184,9 @@ GenerateParsingConstructor(io::Printer* printer) { printer->Print("this.unknownFields = unknownFields.build();\n"); } - if (!HasDescriptorMethods(descriptor_)) { - // LITE runtime uses a static method to reduce method count. - if (descriptor_->extension_range_count() > 0) { - // Make extensions immutable. - printer->Print( - "makeExtensionsImmutable(extensions);\n"); - } - } else { - // Make extensions immutable. - printer->Print( - "makeExtensionsImmutable();\n"); - } + // Make extensions immutable. + printer->Print( + "makeExtensionsImmutable();\n"); printer->Outdent(); printer->Outdent(); @@ -1874,9 +1224,9 @@ void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) { " }\n", "classname", descriptor_->name()); } else { - // When parsing constructor isn't generated, use builder to parse messages. - // Note, will fallback to use reflection based mergeFieldFrom() in - // AbstractMessage.Builder. + // When parsing constructor isn't generated, use builder to parse + // messages. Note, will fallback to use reflection based mergeFieldFrom() + // in AbstractMessage.Builder. printer->Indent(); printer->Print( "Builder builder = newBuilder();\n" @@ -1886,7 +1236,8 @@ void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) { " throw e.setUnfinishedMessage(builder.buildPartial());\n" "} catch (java.io.IOException e) {\n" " throw new com.google.protobuf.InvalidProtocolBufferException(\n" - " e.getMessage()).setUnfinishedMessage(builder.buildPartial());\n" + " e.getMessage()).setUnfinishedMessage(\n" + " builder.buildPartial());\n" "}\n" "return builder.buildPartial();\n"); printer->Outdent(); @@ -1898,16 +1249,13 @@ void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) { "};\n" "\n"); - if (HasDescriptorMethods(descriptor_)) { - // LITE_RUNTIME implements this at the GeneratedMessageLite level. - printer->Print( - "@java.lang.Override\n" - "public com.google.protobuf.Parser<$classname$> getParserForType() {\n" - " return PARSER;\n" - "}\n" - "\n", - "classname", descriptor_->name()); - } + printer->Print( + "@java.lang.Override\n" + "public com.google.protobuf.Parser<$classname$> getParserForType() {\n" + " return PARSER;\n" + "}\n" + "\n", + "classname", descriptor_->name()); } // =================================================================== diff --git a/src/google/protobuf/compiler/java/java_message.h b/src/google/protobuf/compiler/java/java_message.h index 58dd5f99..c3c37765 100644 --- a/src/google/protobuf/compiler/java/java_message.h +++ b/src/google/protobuf/compiler/java/java_message.h @@ -102,10 +102,6 @@ class ImmutableMessageGenerator : public MessageGenerator { virtual int GenerateStaticVariableInitializers(io::Printer* printer); private: - enum UseMemoization { - MEMOIZE, - DONT_MEMOIZE - }; void GenerateFieldAccessorTable(io::Printer* printer); @@ -120,11 +116,8 @@ class ImmutableMessageGenerator : public MessageGenerator { io::Printer* printer, const Descriptor::ExtensionRange* range); void GenerateBuilder(io::Printer* printer); - void GenerateCommonBuilderMethods(io::Printer* printer); - void GenerateDescriptorMethods(io::Printer* printer, bool is_builder); - void GenerateBuilderParsingMethods(io::Printer* printer); - void GenerateIsInitialized(io::Printer* printer, - UseMemoization useMemoization); + void GenerateIsInitialized(io::Printer* printer); + void GenerateDescriptorMethods(io::Printer* printer); void GenerateInitializers(io::Printer* printer); void GenerateEqualsAndHashCode(io::Printer* printer); void GenerateParser(io::Printer* printer); diff --git a/src/google/protobuf/compiler/java/java_message_builder.cc b/src/google/protobuf/compiler/java/java_message_builder.cc new file mode 100644 index 00000000..72694119 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_builder.cc @@ -0,0 +1,661 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: dweis@google.com (Daniel Weis) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include +#include +#ifndef _SHARED_PTR_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { +bool GenerateHasBits(const Descriptor* descriptor) { + return SupportFieldPresence(descriptor->file()) || + HasRepeatedFields(descriptor); +} + +string MapValueImmutableClassdName(const Descriptor* descriptor, + ClassNameResolver* name_resolver) { + const FieldDescriptor* value_field = descriptor->FindFieldByName("value"); + GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, value_field->type()); + return name_resolver->GetImmutableClassName(value_field->message_type()); +} +} // namespace + +MessageBuilderGenerator::MessageBuilderGenerator( + const Descriptor* descriptor, Context* context) + : descriptor_(descriptor), context_(context), + name_resolver_(context->GetNameResolver()), + field_generators_(descriptor, context_) { + GOOGLE_CHECK_NE( + FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); +} + +MessageBuilderGenerator::~MessageBuilderGenerator() {} + +void MessageBuilderGenerator:: +Generate(io::Printer* printer) { + WriteMessageDocComment(printer, descriptor_); + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "public static final class Builder extends\n" + " com.google.protobuf.GeneratedMessage.ExtendableBuilder<\n" + " $classname$, Builder> implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_), + "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); + } else { + printer->Print( + "public static final class Builder extends\n" + " com.google.protobuf.GeneratedMessage.Builder implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_), + "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); + } + printer->Indent(); + + GenerateDescriptorMethods(printer); + GenerateCommonBuilderMethods(printer); + + if (HasGeneratedMethods(descriptor_)) { + GenerateIsInitialized(printer); + GenerateBuilderParsingMethods(printer); + } + + // oneof + map vars; + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + vars["oneof_name"] = context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name; + vars["oneof_capitalized_name"] = context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->capitalized_name; + vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); + // oneofCase_ and oneof_ + printer->Print(vars, + "private int $oneof_name$Case_ = 0;\n" + "private java.lang.Object $oneof_name$_;\n"); + // oneofCase() and clearOneof() + printer->Print(vars, + "public $oneof_capitalized_name$Case\n" + " get$oneof_capitalized_name$Case() {\n" + " return $oneof_capitalized_name$Case.valueOf(\n" + " $oneof_name$Case_);\n" + "}\n" + "\n" + "public Builder clear$oneof_capitalized_name$() {\n" + " $oneof_name$Case_ = 0;\n" + " $oneof_name$_ = null;\n"); + printer->Print(" onChanged();\n"); + printer->Print( + " return this;\n" + "}\n" + "\n"); + } + + if (GenerateHasBits(descriptor_)) { + // 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"); + field_generators_.get(descriptor_->field(i)) + .GenerateBuilderMembers(printer); + } + + if (!PreserveUnknownFields(descriptor_)) { + printer->Print( + "public final Builder setUnknownFields(\n" + " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" + " return this;\n" + "}\n" + "\n" + "public final Builder mergeUnknownFields(\n" + " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" + " return this;\n" + "}\n" + "\n"); + } + + printer->Print( + "\n" + "// @@protoc_insertion_point(builder_scope:$full_name$)\n", + "full_name", descriptor_->full_name()); + + printer->Outdent(); + printer->Print("}\n"); +} + +// =================================================================== + +void MessageBuilderGenerator:: +GenerateDescriptorMethods(io::Printer* printer) { + if (!descriptor_->options().no_standard_descriptor_accessor()) { + printer->Print( + "public static final com.google.protobuf.Descriptors.Descriptor\n" + " getDescriptor() {\n" + " return $fileclass$.internal_$identifier$_descriptor;\n" + "}\n" + "\n", + "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); + } + vector map_fields; + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (GetJavaType(field) == JAVATYPE_MESSAGE && + IsMapEntry(field->message_type())) { + map_fields.push_back(field); + } + } + if (!map_fields.empty()) { + printer->Print( + "@SuppressWarnings({\"rawtypes\"})\n" + "protected com.google.protobuf.MapField internalGetMapField(\n" + " int number) {\n" + " switch (number) {\n"); + printer->Indent(); + printer->Indent(); + for (int i = 0; i < map_fields.size(); ++i) { + const FieldDescriptor* field = map_fields[i]; + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + printer->Print( + "case $number$:\n" + " return internalGet$capitalized_name$();\n", + "number", SimpleItoa(field->number()), + "capitalized_name", info->capitalized_name); + } + printer->Print( + "default:\n" + " throw new RuntimeException(\n" + " \"Invalid map field number: \" + number);\n"); + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" + "}\n"); + printer->Print( + "@SuppressWarnings({\"rawtypes\"})\n" + "protected com.google.protobuf.MapField internalGetMutableMapField(\n" + " int number) {\n" + " switch (number) {\n"); + printer->Indent(); + printer->Indent(); + for (int i = 0; i < map_fields.size(); ++i) { + const FieldDescriptor* field = map_fields[i]; + const FieldGeneratorInfo* info = + context_->GetFieldGeneratorInfo(field); + printer->Print( + "case $number$:\n" + " return internalGetMutable$capitalized_name$();\n", + "number", SimpleItoa(field->number()), + "capitalized_name", info->capitalized_name); + } + printer->Print( + "default:\n" + " throw new RuntimeException(\n" + " \"Invalid map field number: \" + number);\n"); + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" + "}\n"); + } + printer->Print( + "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" + " internalGetFieldAccessorTable() {\n" + " return $fileclass$.internal_$identifier$_fieldAccessorTable\n" + " .ensureFieldAccessorsInitialized(\n" + " $classname$.class, $classname$.Builder.class);\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_), + "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); +} + +// =================================================================== + +void MessageBuilderGenerator:: +GenerateCommonBuilderMethods(io::Printer* printer) { + printer->Print( + "// Construct using $classname$.newBuilder()\n" + "private Builder() {\n" + " maybeForceBuilderInitialization();\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "private Builder(\n" + " com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n" + " super(parent);\n" + " maybeForceBuilderInitialization();\n" + "}\n", + "classname", name_resolver_->GetImmutableClassName(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++) { + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get(descriptor_->field(i)) + .GenerateFieldBuilderInitializationCode(printer); + } + } + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\n" + "}\n"); + + printer->Print( + "public Builder clear() {\n" + " super.clear();\n"); + + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get(descriptor_->field(i)) + .GenerateBuilderClearCode(printer); + } + } + + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "$oneof_name$Case_ = 0;\n" + "$oneof_name$_ = null;\n", + "oneof_name", context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name); + } + + printer->Outdent(); + + printer->Print( + " return this;\n" + "}\n" + "\n"); + + printer->Print( + "public com.google.protobuf.Descriptors.Descriptor\n" + " getDescriptorForType() {\n" + " return $fileclass$.internal_$identifier$_descriptor;\n" + "}\n" + "\n", + "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); + + // LITE runtime implements this in GeneratedMessageLite. + printer->Print( + "public $classname$ getDefaultInstanceForType() {\n" + " return $classname$.getDefaultInstance();\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "public $classname$ build() {\n" + " $classname$ result = buildPartial();\n" + " if (!result.isInitialized()) {\n" + " throw newUninitializedMessageException(result);\n" + " }\n" + " return result;\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "public $classname$ buildPartial() {\n" + " $classname$ result = new $classname$(this);\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Indent(); + + int totalBuilderBits = 0; + int totalMessageBits = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + const ImmutableFieldGenerator& field = + field_generators_.get(descriptor_->field(i)); + totalBuilderBits += field.GetNumBitsForBuilder(); + totalMessageBits += field.GetNumBitsForMessage(); + } + int totalBuilderInts = (totalBuilderBits + 31) / 32; + int totalMessageInts = (totalMessageBits + 31) / 32; + + if (GenerateHasBits(descriptor_)) { + // 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. + 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); + } + + if (GenerateHasBits(descriptor_)) { + // 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)); + } + } + + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print("result.$oneof_name$Case_ = $oneof_name$Case_;\n", + "oneof_name", context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name); + } + + printer->Outdent(); + + printer->Print( + " onBuilt();\n"); + + printer->Print( + " return result;\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + // ----------------------------------------------------------------- + + if (HasGeneratedMethods(descriptor_)) { + printer->Print( + "public Builder mergeFrom(com.google.protobuf.Message other) {\n" + " if (other instanceof $classname$) {\n" + " return mergeFrom(($classname$)other);\n" + " } else {\n" + " super.mergeFrom(other);\n" + " return this;\n" + " }\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "public Builder mergeFrom($classname$ other) {\n" + // Optimization: If other is the default instance, we know none of its + // fields are set so we can skip the merge. + " if (other == $classname$.getDefaultInstance()) return this;\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get( + descriptor_->field(i)).GenerateMergingCode(printer); + } + } + + // Merge oneof fields. + for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) { + printer->Print( + "switch (other.get$oneof_capitalized_name$Case()) {\n", + "oneof_capitalized_name", + context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->capitalized_name); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + printer->Print( + "case $field_name$: {\n", + "field_name", + ToUpper(field->name())); + printer->Indent(); + field_generators_.get(field).GenerateMergingCode(printer); + printer->Print( + "break;\n"); + printer->Outdent(); + printer->Print( + "}\n"); + } + printer->Print( + "case $cap_oneof_name$_NOT_SET: {\n" + " break;\n" + "}\n", + "cap_oneof_name", + ToUpper(context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name)); + printer->Outdent(); + printer->Print( + "}\n"); + } + + printer->Outdent(); + + // if message type has extensions + if (descriptor_->extension_range_count() > 0) { + printer->Print( + " this.mergeExtensionFields(other);\n"); + } + + if (PreserveUnknownFields(descriptor_)) { + printer->Print( + " this.mergeUnknownFields(other.unknownFields);\n"); + } + + printer->Print( + " onChanged();\n"); + + printer->Print( + " return this;\n" + "}\n" + "\n"); + } +} + +// =================================================================== + +void MessageBuilderGenerator:: +GenerateBuilderParsingMethods(io::Printer* printer) { + printer->Print( + "public Builder mergeFrom(\n" + " com.google.protobuf.CodedInputStream input,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws java.io.IOException {\n" + " $classname$ parsedMessage = null;\n" + " try {\n" + " parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n" + " } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" + " parsedMessage = ($classname$) e.getUnfinishedMessage();\n" + " throw e;\n" + " } finally {\n" + " if (parsedMessage != null) {\n" + " mergeFrom(parsedMessage);\n" + " }\n" + " }\n" + " return this;\n" + "}\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); +} + +// =================================================================== + +void MessageBuilderGenerator::GenerateIsInitialized( + io::Printer* printer) { + printer->Print( + "public final boolean isInitialized() {\n"); + printer->Indent(); + + // 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. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + + if (field->is_required()) { + printer->Print( + "if (!has$name$()) {\n" + " return false;\n" + "}\n", + "name", info->capitalized_name); + } + } + + // Now check that all embedded messages are initialized. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + if (GetJavaType(field) == JAVATYPE_MESSAGE && + HasRequiredFields(field->message_type())) { + switch (field->label()) { + case FieldDescriptor::LABEL_REQUIRED: + printer->Print( + "if (!get$name$().isInitialized()) {\n" + " return false;\n" + "}\n", + "type", name_resolver_->GetImmutableClassName( + field->message_type()), + "name", info->capitalized_name); + break; + case FieldDescriptor::LABEL_OPTIONAL: + if (!SupportFieldPresence(descriptor_->file()) && + field->containing_oneof() != NULL) { + const OneofDescriptor* oneof = field->containing_oneof(); + const OneofGeneratorInfo* oneof_info = + context_->GetOneofGeneratorInfo(oneof); + printer->Print( + "if ($oneof_name$Case_ == $field_number$) {\n", + "oneof_name", oneof_info->name, + "field_number", SimpleItoa(field->number())); + } else { + printer->Print( + "if (has$name$()) {\n", + "name", info->capitalized_name); + } + printer->Print( + " if (!get$name$().isInitialized()) {\n" + " return false;\n" + " }\n" + "}\n", + "name", info->capitalized_name); + break; + case FieldDescriptor::LABEL_REPEATED: + if (IsMapEntry(field->message_type())) { + printer->Print( + "for ($type$ item : get$name$().values()) {\n" + " if (!item.isInitialized()) {\n" + " return false;\n" + " }\n" + "}\n", + "type", MapValueImmutableClassdName(field->message_type(), + name_resolver_), + "name", info->capitalized_name); + } else { + printer->Print( + "for (int i = 0; i < get$name$Count(); i++) {\n" + " if (!get$name$(i).isInitialized()) {\n" + " return false;\n" + " }\n" + "}\n", + "type", name_resolver_->GetImmutableClassName( + field->message_type()), + "name", info->capitalized_name); + } + break; + } + } + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "if (!extensionsAreInitialized()) {\n" + " return false;\n" + "}\n"); + } + + printer->Outdent(); + + printer->Print( + " return true;\n" + "}\n" + "\n"); +} + +// =================================================================== + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_message_builder.h b/src/google/protobuf/compiler/java/java_message_builder.h new file mode 100644 index 00000000..015ea062 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_builder.h @@ -0,0 +1,86 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: dweis@google.com (Daniel Weis) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_BUILDER_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_BUILDER_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class MessageBuilderGenerator { + public: + explicit MessageBuilderGenerator(const Descriptor* descriptor, + Context* context); + virtual ~MessageBuilderGenerator(); + + virtual void Generate(io::Printer* printer); + + private: + void GenerateCommonBuilderMethods(io::Printer* printer); + void GenerateDescriptorMethods(io::Printer* printer); + void GenerateBuilderParsingMethods(io::Printer* printer); + void GenerateIsInitialized(io::Printer* printer); + + const Descriptor* descriptor_; + Context* context_; + ClassNameResolver* name_resolver_; + FieldGeneratorMap field_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageBuilderGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_BUILDER_H__ diff --git a/src/google/protobuf/compiler/java/java_message_builder_lite.cc b/src/google/protobuf/compiler/java/java_message_builder_lite.cc new file mode 100644 index 00000000..8719d00d --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_builder_lite.cc @@ -0,0 +1,192 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: dweis@google.com (Daniel Weis) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include +#include +#ifndef _SHARED_PTR_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { +bool GenerateHasBits(const Descriptor* descriptor) { + return SupportFieldPresence(descriptor->file()) || + HasRepeatedFields(descriptor); +} + +string MapValueImmutableClassdName(const Descriptor* descriptor, + ClassNameResolver* name_resolver) { + const FieldDescriptor* value_field = descriptor->FindFieldByName("value"); + GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, value_field->type()); + return name_resolver->GetImmutableClassName(value_field->message_type()); +} +} // namespace + +MessageBuilderLiteGenerator::MessageBuilderLiteGenerator( + const Descriptor* descriptor, Context* context) + : descriptor_(descriptor), context_(context), + name_resolver_(context->GetNameResolver()), + field_generators_(descriptor, context_) { + GOOGLE_CHECK_EQ( + FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); +} + +MessageBuilderLiteGenerator::~MessageBuilderLiteGenerator() {} + +void MessageBuilderLiteGenerator:: +Generate(io::Printer* printer) { + WriteMessageDocComment(printer, descriptor_); + printer->Print( + "public static final class Builder extends\n" + " com.google.protobuf.GeneratedMessageLite.$extendible$Builder<\n" + " $classname$, Builder> implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_), + "extra_interfaces", ExtraBuilderInterfaces(descriptor_), + "extendible", + descriptor_->extension_range_count() > 0 ? "Extendable" : ""); + printer->Indent(); + + GenerateCommonBuilderMethods(printer); + + // oneof + map vars; + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + vars["oneof_name"] = context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name; + vars["oneof_capitalized_name"] = context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->capitalized_name; + vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); + + // oneofCase() and clearOneof() + printer->Print(vars, + "public $oneof_capitalized_name$Case\n" + " get$oneof_capitalized_name$Case() {\n" + " return instance.get$oneof_capitalized_name$Case();\n" + "}\n" + "\n" + "public Builder clear$oneof_capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$oneof_capitalized_name$();\n" + " return this;\n" + "}\n" + "\n"); + } + + if (GenerateHasBits(descriptor_)) { + // 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"); + field_generators_.get(descriptor_->field(i)) + .GenerateBuilderMembers(printer); + } + + if (!PreserveUnknownFields(descriptor_)) { + printer->Print( + "public final Builder setUnknownFields(\n" + " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" + " return this;\n" + "}\n" + "\n" + "public final Builder mergeUnknownFields(\n" + " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" + " return this;\n" + "}\n" + "\n"); + } + + printer->Print( + "\n" + "// @@protoc_insertion_point(builder_scope:$full_name$)\n", + "full_name", descriptor_->full_name()); + + printer->Outdent(); + printer->Print("}\n"); +} + +// =================================================================== + +void MessageBuilderLiteGenerator:: +GenerateCommonBuilderMethods(io::Printer* printer) { + printer->Print( + "// Construct using $classname$.newBuilder()\n" + "private Builder() {\n" + " super(DEFAULT_INSTANCE);\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); +} + +// =================================================================== + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_message_builder_lite.h b/src/google/protobuf/compiler/java/java_message_builder_lite.h new file mode 100644 index 00000000..8597b2e6 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_builder_lite.h @@ -0,0 +1,83 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: dweis@google.com (Daniel Weis) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_BUILDER_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_BUILDER_LITE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class MessageBuilderLiteGenerator { + public: + explicit MessageBuilderLiteGenerator(const Descriptor* descriptor, + Context* context); + virtual ~MessageBuilderLiteGenerator(); + + virtual void Generate(io::Printer* printer); + + private: + void GenerateCommonBuilderMethods(io::Printer* printer); + + const Descriptor* descriptor_; + Context* context_; + ClassNameResolver* name_resolver_; + FieldGeneratorMap field_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageBuilderLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_BUILDER_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc index a2d12a38..b180b4a7 100644 --- a/src/google/protobuf/compiler/java/java_message_field.cc +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -157,11 +157,9 @@ GenerateInterfaceMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$$type$ get$capitalized_name$();\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder();\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder();\n"); } void ImmutableMessageFieldGenerator:: @@ -182,14 +180,12 @@ GenerateMembers(io::Printer* printer) const { " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n" "}\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder " - "get$capitalized_name$OrBuilder() {\n" - " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder " + "get$capitalized_name$OrBuilder() {\n" + " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n" + "}\n"); } else { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, @@ -202,14 +198,12 @@ GenerateMembers(io::Printer* printer) const { " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n" "}\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder " - "get$capitalized_name$OrBuilder() {\n" - " return get$capitalized_name$();\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder " + "get$capitalized_name$OrBuilder() {\n" + " return get$capitalized_name$();\n" + "}\n"); } } @@ -217,19 +211,15 @@ void ImmutableMessageFieldGenerator::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); - } + 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"); } void ImmutableMessageFieldGenerator::PrintNestedBuilderFunction( @@ -260,14 +250,12 @@ GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, "private $type$ $name$_ = null;\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - printer->Print(variables_, + 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". @@ -368,40 +356,38 @@ GenerateBuilderMembers(io::Printer* printer) const { "$clear_has_field_bit_builder$\n" "return this;\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - 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"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" - " if ($name$Builder_ != null) {\n" - " return $name$Builder_.getMessageOrBuilder();\n" - " } else {\n" - " return $name$_ == null ?\n" - " $type$.getDefaultInstance() : $name$_;\n" - " }\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "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" - " get$capitalized_name$(),\n" - " getParentForChildren(),\n" - " isClean());\n" - " $name$_ = null;\n" - " }\n" - " return $name$Builder_;\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + 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"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + " if ($name$Builder_ != null) {\n" + " return $name$Builder_.getMessageOrBuilder();\n" + " } else {\n" + " return $name$_ == null ?\n" + " $type$.getDefaultInstance() : $name$_;\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "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" + " get$capitalized_name$(),\n" + " getParentForChildren(),\n" + " isClean());\n" + " $name$_ = null;\n" + " }\n" + " return $name$Builder_;\n" + "}\n"); } void ImmutableMessageFieldGenerator:: @@ -557,16 +543,14 @@ GenerateMembers(io::Printer* printer) const { " return $type$.getDefaultInstance();\n" "}\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" - " if ($has_oneof_case_message$) {\n" - " return ($type$) $oneof_name$_;\n" - " }\n" - " return $type$.getDefaultInstance();\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + " if ($has_oneof_case_message$) {\n" + " return ($type$) $oneof_name$_;\n" + " }\n" + " return $type$.getDefaultInstance();\n" + "}\n"); } void ImmutableMessageOneofFieldGenerator:: @@ -574,14 +558,12 @@ 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. - 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"); - } + 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". @@ -683,45 +665,43 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n" - " return get$capitalized_name$FieldBuilder().getBuilder();\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" - " if (($has_oneof_case_message$) && ($name$Builder_ != null)) {\n" - " return $name$Builder_.getMessageOrBuilder();\n" - " } else {\n" - " if ($has_oneof_case_message$) {\n" - " return ($type$) $oneof_name$_;\n" - " }\n" - " return $type$.getDefaultInstance();\n" - " }\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "private com.google.protobuf.SingleFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder> \n" - " get$capitalized_name$FieldBuilder() {\n" - " if ($name$Builder_ == null) {\n" - " if (!($has_oneof_case_message$)) {\n" - " $oneof_name$_ = $type$.getDefaultInstance();\n" - " }\n" - " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder>(\n" - " ($type$) $oneof_name$_,\n" - " getParentForChildren(),\n" - " isClean());\n" - " $oneof_name$_ = null;\n" - " }\n" - " $set_oneof_case_message$;\n" - " $on_changed$;\n" - " return $name$Builder_;\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n" + " return get$capitalized_name$FieldBuilder().getBuilder();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + " if (($has_oneof_case_message$) && ($name$Builder_ != null)) {\n" + " return $name$Builder_.getMessageOrBuilder();\n" + " } else {\n" + " if ($has_oneof_case_message$) {\n" + " return ($type$) $oneof_name$_;\n" + " }\n" + " return $type$.getDefaultInstance();\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> \n" + " get$capitalized_name$FieldBuilder() {\n" + " if ($name$Builder_ == null) {\n" + " if (!($has_oneof_case_message$)) {\n" + " $oneof_name$_ = $type$.getDefaultInstance();\n" + " }\n" + " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder>(\n" + " ($type$) $oneof_name$_,\n" + " getParentForChildren(),\n" + " isClean());\n" + " $oneof_name$_ = null;\n" + " }\n" + " $set_oneof_case_message$;\n" + " $on_changed$;\n" + " return $name$Builder_;\n" + "}\n"); } void ImmutableMessageOneofFieldGenerator:: @@ -831,16 +811,15 @@ GenerateInterfaceMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$int get$capitalized_name$Count();\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$java.util.List \n" - " get$capitalized_name$OrBuilderList();\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder(\n" - " int index);\n"); - } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.util.List \n" + " get$capitalized_name$OrBuilderList();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder(\n" + " int index);\n"); } void RepeatedImmutableMessageFieldGenerator:: @@ -882,19 +861,15 @@ void RepeatedImmutableMessageFieldGenerator::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); - } + 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"); } void RepeatedImmutableMessageFieldGenerator::PrintNestedBuilderFunction( @@ -942,14 +917,12 @@ GenerateBuilderMembers(io::Printer* printer) const { "}\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"); - } + 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". @@ -1116,70 +1089,68 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder get$capitalized_name$Builder(\n" - " int index) {\n" - " return get$capitalized_name$FieldBuilder().getBuilder(index);\n" - "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder get$capitalized_name$Builder(\n" + " int index) {\n" + " return get$capitalized_name$FieldBuilder().getBuilder(index);\n" + "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$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"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$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"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public java.util.List \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"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List \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"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder add$capitalized_name$Builder() {\n" - " return get$capitalized_name$FieldBuilder().addBuilder(\n" - " $type$.getDefaultInstance());\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder add$capitalized_name$Builder(\n" - " int index) {\n" - " return get$capitalized_name$FieldBuilder().addBuilder(\n" - " index, $type$.getDefaultInstance());\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$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"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder add$capitalized_name$Builder() {\n" + " return get$capitalized_name$FieldBuilder().addBuilder(\n" + " $type$.getDefaultInstance());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder add$capitalized_name$Builder(\n" + " int index) {\n" + " return get$capitalized_name$FieldBuilder().addBuilder(\n" + " index, $type$.getDefaultInstance());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$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 RepeatedImmutableMessageFieldGenerator:: diff --git a/src/google/protobuf/compiler/java/java_message_field_lite.cc b/src/google/protobuf/compiler/java/java_message_field_lite.cc new file mode 100644 index 00000000..8332202c --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_field_lite.cc @@ -0,0 +1,944 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +void SetMessageVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, + map* variables) { + SetCommonFieldVariables(descriptor, info, variables); + + (*variables)["type"] = + name_resolver->GetImmutableClassName(descriptor->message_type()); + (*variables)["mutable_type"] = + name_resolver->GetMutableClassName(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();" : ""; + + if (SupportFieldPresence(descriptor->file())) { + // For singular messages and builders, one bit is used for the hasField bit. + (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); + + // Note that these have a trailing ";". + (*variables)["set_has_field_bit_message"] = + GenerateSetBit(messageBitIndex) + ";"; + (*variables)["clear_has_field_bit_message"] = + GenerateClearBit(messageBitIndex) + ";"; + + (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex); + } else { + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["clear_has_field_bit_message"] = ""; + + (*variables)["is_field_present_message"] = + (*variables)["name"] + "_ != null"; + } + + // For repeated builders, the underlying list tracks mutability state. + (*variables)["is_mutable"] = (*variables)["name"] + "_.isModifiable()"; + + (*variables)["get_has_field_bit_from_local"] = + GenerateGetBitFromLocal(builderBitIndex); + (*variables)["set_has_field_bit_to_local"] = + GenerateSetBitToLocal(messageBitIndex); +} + +} // namespace + +// =================================================================== + +ImmutableMessageFieldLiteGenerator:: +ImmutableMessageFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +ImmutableMessageFieldLiteGenerator::~ImmutableMessageFieldLiteGenerator() {} + +int ImmutableMessageFieldLiteGenerator::GetNumBitsForMessage() const { + return 1; +} + +int ImmutableMessageFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void ImmutableMessageFieldLiteGenerator:: +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. + if (SupportFieldPresence(descriptor_->file()) || + descriptor_->containing_oneof() == NULL) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$();\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private $type$ $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n" + "}\n"); + } else { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $name$_ != null;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n" + "}\n"); + } + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " $name$_ = value;\n" + " $set_has_field_bit_message$\n" + " }\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " $name$_ = builderForValue.build();\n" + " $set_has_field_bit_message$\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void merge$capitalized_name$($type$ value) {\n" + " if ($name$_ != null &&\n" + " $name$_ != $type$.getDefaultInstance()) {\n" + " $name$_ =\n" + " $type$.newBuilder($name$_).mergeFrom(value).buildPartial();\n" + " } else {\n" + " $name$_ = value;\n" + " }\n" + " $set_has_field_bit_message$\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {" + " $name$_ = null;\n" + " $clear_has_field_bit_message$\n" + "}\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // The comments above the methods below are based on a hypothetical + // field of type "Field" called "Field". + + // boolean hasField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + + // Field getField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + " }\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(builderForValue);\n" + " return this;\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder merge$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.merge$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + printer->Print(variables_, + "get$capitalized_name$FieldBuilder();\n"); + } +} + + +void ImmutableMessageFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const {} + +void ImmutableMessageFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " merge$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + // noop for scalars +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$type$.Builder subBuilder = null;\n" + "if ($is_field_present_message$) {\n" + " subBuilder = $name$_.toBuilder();\n" + "}\n"); + + if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "$name$_ = input.readGroup($number$, $type$.PARSER,\n" + " extensionRegistry);\n"); + } else { + printer->Print(variables_, + "$name$_ = input.readMessage($type$.PARSER, extensionRegistry);\n"); + } + + printer->Print(variables_, + "if (subBuilder != null) {\n" + " subBuilder.mergeFrom($name$_);\n" + " $name$_ = subBuilder.buildPartial();\n" + "}\n" + "$set_has_field_bit_message$\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // noop for messages. +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " output.write$group_or_message$($number$, get$capitalized_name$());\n" + "}\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$group_or_message$Size($number$, get$capitalized_name$());\n" + "}\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$()\n" + " .equals(other.get$capitalized_name$());\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n" + "hash = (53 * hash) + get$capitalized_name$().hashCode();\n"); +} + +string ImmutableMessageFieldLiteGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->message_type()); +} + +// =================================================================== + +ImmutableMessageOneofFieldLiteGenerator:: +ImmutableMessageOneofFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableMessageFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); +} + +ImmutableMessageOneofFieldLiteGenerator:: +~ImmutableMessageOneofFieldLiteGenerator() {} + +void ImmutableMessageOneofFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + PrintExtraFieldInfo(variables_, printer); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $has_oneof_case_message$;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " return ($type$) $oneof_name$_;\n" + " }\n" + " return $type$.getDefaultInstance();\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " $oneof_name$_ = value;\n" + " $set_oneof_case_message$;\n" + "}\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " $oneof_name$_ = builderForValue.build();\n" + " $set_oneof_case_message$;\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void merge$capitalized_name$($type$ value) {\n" + " if ($has_oneof_case_message$ &&\n" + " $oneof_name$_ != $type$.getDefaultInstance()) {\n" + " $oneof_name$_ = $type$.newBuilder(($type$) $oneof_name$_)\n" + " .mergeFrom(value).buildPartial();\n" + " } else {\n" + " $oneof_name$_ = value;\n" + " }\n" + " $set_oneof_case_message$;\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " }\n" + "}\n"); +} + +void ImmutableMessageOneofFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // The comments above the methods below are based on a hypothetical + // field of type "Field" called "Field". + + if (SupportFieldPresence(descriptor_->file())) { + // boolean hasField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + + // Field getField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(builderForValue);\n" + " return this;\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder merge$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.merge$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutableMessageOneofFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "merge$capitalized_name$(other.get$capitalized_name$());\n"); +} + +void ImmutableMessageOneofFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$type$.Builder subBuilder = null;\n" + "if ($has_oneof_case_message$) {\n" + " subBuilder = (($type$) $oneof_name$_).toBuilder();\n" + "}\n"); + + if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "$oneof_name$_ = input.readGroup($number$, $type$.PARSER,\n" + " extensionRegistry);\n"); + } else { + printer->Print(variables_, + "$oneof_name$_ = input.readMessage($type$.PARSER, extensionRegistry);\n"); + } + + printer->Print(variables_, + "if (subBuilder != null) {\n" + " subBuilder.mergeFrom(($type$) $oneof_name$_);\n" + " $oneof_name$_ = subBuilder.buildPartial();\n" + "}\n"); + printer->Print(variables_, + "$set_oneof_case_message$;\n"); +} + +void ImmutableMessageOneofFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " output.write$group_or_message$($number$, ($type$) $oneof_name$_);\n" + "}\n"); +} + +void ImmutableMessageOneofFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$group_or_message$Size($number$, ($type$) $oneof_name$_);\n" + "}\n"); +} + +// =================================================================== + +RepeatedImmutableMessageFieldLiteGenerator:: +RepeatedImmutableMessageFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +RepeatedImmutableMessageFieldLiteGenerator:: +~RepeatedImmutableMessageFieldLiteGenerator() {} + +int RepeatedImmutableMessageFieldLiteGenerator::GetNumBitsForMessage() const { + return 0; +} + +int RepeatedImmutableMessageFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +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. + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.util.List<$type$> \n" + " get$capitalized_name$List();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$(int index);\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Count();\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private com.google.protobuf.Internal.ProtobufList<$type$> $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List \n" + " get$capitalized_name$OrBuilderList() {\n" + " return $name$_;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n" + " int index) {\n" + " return $name$_.get(index);\n" + "}\n"); + + printer->Print(variables_, + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$is_mutable$) {\n" + " $name$_ = newProtobufList($name$_);\n" + " }\n" + "}\n" + "\n"); + + // Builder setRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, value);\n" + "}\n"); + + // Builder setRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, builderForValue.build());\n" + "}\n"); + + // Builder addRepeatedField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value);\n" + "}\n"); + + // Builder addRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " int index, $type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(index, value);\n" + "}\n"); + // Builder addRepeatedField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(builderForValue.build());\n" + "}\n"); + + // Builder addRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(index, builderForValue.build());\n" + "}\n"); + + // Builder addAllRepeatedField(Iterable values) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " com.google.protobuf.AbstractMessageLite.addAll(\n" + " values, $name$_);\n" + "}\n"); + + // Builder clearAllRepeatedField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $name$_ = emptyProtobufList();\n" + "}\n"); + + // Builder removeRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void remove$capitalized_name$(int index) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.remove(index);\n" + "}\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // The comments above the methods below are based on a hypothetical + // repeated field of type "Field" called "RepeatedField". + + // List getRepeatedFieldList() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList(\n" + " instance.get$capitalized_name$List());\n" + "}\n"); + + // int getRepeatedFieldCount() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return instance.get$capitalized_name$Count();\n" + "}"); + + // Field getRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return instance.get$capitalized_name$(index);\n" + "}\n"); + + // Builder setRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + + // Builder setRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, builderForValue);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " int index, $type$ value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + // Builder addRepeatedField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(builderForValue);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(index, builderForValue);\n" + " return this;\n" + "}\n"); + + // Builder addAllRepeatedField(Iterable values) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " copyOnWrite();\n" + " instance.addAll$capitalized_name$(values);\n" + " return this;\n" + "}\n"); + + // Builder clearAllRepeatedField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); + + // Builder removeRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder remove$capitalized_name$(int index) {\n" + " copyOnWrite();\n" + " instance.remove$capitalized_name$(index);\n" + " return this;\n" + "}\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + printer->Print(variables_, + "get$capitalized_name$FieldBuilder();\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = emptyProtobufList();\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + // 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. + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\n" + " if ($name$_.isEmpty()) {\n" + " $name$_ = other.$name$_;\n" + " } else {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.addAll(other.$name$_);\n" + " }\n" + " $on_changed$\n" + "}\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.makeImmutable();\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!$is_mutable$) {\n" + " $name$_ = newProtobufList();\n" + "}\n"); + + if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "$name$_.add(input.readGroup($number$, $type$.PARSER,\n" + " extensionRegistry));\n"); + } else { + printer->Print(variables_, + "$name$_.add(input.readMessage($type$.PARSER, extensionRegistry));\n"); + } +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_mutable$) {\n" + " $name$_.makeImmutable();\n" + "}\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.write$group_or_message$($number$, $name$_.get(i));\n" + "}\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$group_or_message$Size($number$, $name$_.get(i));\n" + "}\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$List()\n" + " .equals(other.get$capitalized_name$List());\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +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 RepeatedImmutableMessageFieldLiteGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->message_type()); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_message_field_lite.h b/src/google/protobuf/compiler/java/java_message_field_lite.h new file mode 100644 index 00000000..ae26c06a --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_field_lite.h @@ -0,0 +1,157 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_FIELD_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_FIELD_LITE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableMessageFieldLiteGenerator : public ImmutableFieldLiteGenerator { + public: + explicit ImmutableMessageFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableMessageFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + 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 GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(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; + + protected: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableMessageFieldLiteGenerator); +}; + +class ImmutableMessageOneofFieldLiteGenerator + : public ImmutableMessageFieldLiteGenerator { + public: + ImmutableMessageOneofFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableMessageOneofFieldLiteGenerator(); + + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableMessageOneofFieldLiteGenerator); +}; + +class RepeatedImmutableMessageFieldLiteGenerator + : public ImmutableFieldLiteGenerator { + public: + explicit RepeatedImmutableMessageFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~RepeatedImmutableMessageFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + 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 GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(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; + + protected: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableMessageFieldLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_FIELD_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_message_lite.cc b/src/google/protobuf/compiler/java/java_message_lite.cc new file mode 100644 index 00000000..3accee92 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_lite.cc @@ -0,0 +1,1174 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: dweis@google.com (Daniel Weis) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include +#include +#ifndef _SHARED_PTR_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { +bool GenerateHasBits(const Descriptor* descriptor) { + return SupportFieldPresence(descriptor->file()) || + HasRepeatedFields(descriptor); +} + +string MapValueImmutableClassdName(const Descriptor* descriptor, + ClassNameResolver* name_resolver) { + const FieldDescriptor* value_field = descriptor->FindFieldByName("value"); + GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, value_field->type()); + return name_resolver->GetImmutableClassName(value_field->message_type()); +} +} // namespace + +// =================================================================== +ImmutableMessageLiteGenerator::ImmutableMessageLiteGenerator( + const Descriptor* descriptor, Context* context) + : MessageGenerator(descriptor), context_(context), + name_resolver_(context->GetNameResolver()), + field_generators_(descriptor, context_) { + GOOGLE_CHECK_EQ( + FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); +} + +ImmutableMessageLiteGenerator::~ImmutableMessageLiteGenerator() {} + +void ImmutableMessageLiteGenerator::GenerateStaticVariables( + io::Printer* printer) { + // Generate static members for all nested types. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + // TODO(kenton): Reuse MessageGenerator objects? + ImmutableMessageLiteGenerator(descriptor_->nested_type(i), context_) + .GenerateStaticVariables(printer); + } +} + +int ImmutableMessageLiteGenerator::GenerateStaticVariableInitializers( + io::Printer* printer) { + int bytecode_estimate = 0; + // Generate static member initializers for all nested types. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + // TODO(kenton): Reuse MessageGenerator objects? + bytecode_estimate += + ImmutableMessageLiteGenerator(descriptor_->nested_type(i), context_) + .GenerateStaticVariableInitializers(printer); + } + return bytecode_estimate; +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::GenerateInterface(io::Printer* printer) { + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "public interface $classname$OrBuilder extends \n" + " $extra_interfaces$\n" + " com.google.protobuf.GeneratedMessageLite.\n" + " ExtendableMessageOrBuilder<\n" + " $classname$, $classname$.Builder> {\n", + "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), + "classname", descriptor_->name()); + } else { + printer->Print( + "public interface $classname$OrBuilder extends\n" + " $extra_interfaces$\n" + " com.google.protobuf.MessageLiteOrBuilder {\n", + "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), + "classname", descriptor_->name()); + } + + printer->Indent(); + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print("\n"); + field_generators_.get(descriptor_->field(i)) + .GenerateInterfaceMembers(printer); + } + printer->Outdent(); + + printer->Print("}\n"); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { + bool is_own_file = + descriptor_->containing_type() == NULL && + MultipleJavaFiles(descriptor_->file(), /* immutable = */ true); + + map variables; + variables["static"] = is_own_file ? " " : " static "; + variables["classname"] = descriptor_->name(); + variables["extra_interfaces"] = ExtraMessageInterfaces(descriptor_); + + WriteMessageDocComment(printer, descriptor_); + + // The builder_type stores the super type name of the nested Builder class. + string builder_type; + if (descriptor_->extension_range_count() > 0) { + printer->Print(variables, + "public $static$final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessageLite.ExtendableMessage<\n" + " $classname$, $classname$.Builder> implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n"); + builder_type = strings::Substitute( + "com.google.protobuf.GeneratedMessageLite.ExtendableBuilder<$0, ?>", + name_resolver_->GetImmutableClassName(descriptor_)); + } else { + printer->Print(variables, + "public $static$final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessageLite<\n" + " $classname$, $classname$.Builder> implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n"); + + builder_type = "com.google.protobuf.GeneratedMessageLite.Builder"; + } + printer->Indent(); + + GenerateParsingConstructor(printer); + + // Nested types + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + EnumGenerator(descriptor_->enum_type(i), true, context_) + .Generate(printer); + } + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + // Don't generate Java classes for map entry messages. + if (IsMapEntry(descriptor_->nested_type(i))) continue; + ImmutableMessageLiteGenerator messageGenerator( + descriptor_->nested_type(i), context_); + messageGenerator.GenerateInterface(printer); + messageGenerator.Generate(printer); + } + + if (GenerateHasBits(descriptor_)) { + // 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)); + } + } + + // oneof + map vars; + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + vars["oneof_name"] = context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name; + vars["oneof_capitalized_name"] = context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->capitalized_name; + vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); + // oneofCase_ and oneof_ + printer->Print(vars, + "private int $oneof_name$Case_ = 0;\n" + "private java.lang.Object $oneof_name$_;\n"); + // OneofCase enum + printer->Print(vars, + "public enum $oneof_capitalized_name$Case\n" + " implements com.google.protobuf.Internal.EnumLite {\n"); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + printer->Print( + "$field_name$($field_number$),\n", + "field_name", + ToUpper(field->name()), + "field_number", + SimpleItoa(field->number())); + } + printer->Print( + "$cap_oneof_name$_NOT_SET(0);\n", + "cap_oneof_name", + ToUpper(vars["oneof_name"])); + printer->Print(vars, + "private int value = 0;\n" + "private $oneof_capitalized_name$Case(int value) {\n" + " this.value = value;\n" + "}\n"); + printer->Print(vars, + "public static $oneof_capitalized_name$Case valueOf(int value) {\n" + " switch (value) {\n"); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + printer->Print( + " case $field_number$: return $field_name$;\n", + "field_number", + SimpleItoa(field->number()), + "field_name", + ToUpper(field->name())); + } + printer->Print( + " case 0: return $cap_oneof_name$_NOT_SET;\n" + " default: throw new java.lang.IllegalArgumentException(\n" + " \"Value is undefined for this oneof enum.\");\n" + " }\n" + "}\n" + "public int getNumber() {\n" + " return this.value;\n" + "}\n", + "cap_oneof_name", ToUpper(vars["oneof_name"])); + printer->Outdent(); + printer->Print("};\n\n"); + // oneofCase() + printer->Print(vars, + "public $oneof_capitalized_name$Case\n" + "get$oneof_capitalized_name$Case() {\n" + " return $oneof_capitalized_name$Case.valueOf(\n" + " $oneof_name$Case_);\n" + "}\n" + "\n" + "private void clear$oneof_capitalized_name$() {\n" + " $oneof_name$Case_ = 0;\n" + " $oneof_name$_ = null;\n" + "}\n" + "\n"); + } + + // Fields + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print("public static final int $constant_name$ = $number$;\n", + "constant_name", FieldConstantName(descriptor_->field(i)), + "number", SimpleItoa(descriptor_->field(i)->number())); + field_generators_.get(descriptor_->field(i)).GenerateMembers(printer); + printer->Print("\n"); + } + + GenerateMessageSerializationMethods(printer); + + if (HasEqualsAndHashCode(descriptor_)) { + GenerateEqualsAndHashCode(printer); + } + + + GenerateParseFromMethods(printer); + GenerateBuilder(printer); + + if (HasRequiredFields(descriptor_)) { + // 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( + "protected final Object dynamicMethod(\n" + " com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n" + " Object... args) {\n" + " switch (method) {\n" + " case PARSE_PARTIAL_FROM: {\n" + " return new $classname$(" + " (com.google.protobuf.CodedInputStream) args[0],\n" + " (com.google.protobuf.ExtensionRegistryLite) args[1]);\n" + " }\n" + " case NEW_INSTANCE: {\n" + " return new $classname$(\n" + " com.google.protobuf.Internal.EMPTY_CODED_INPUT_STREAM,\n" + " com.google.protobuf.ExtensionRegistryLite\n" + " .getEmptyRegistry());\n" + " }\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Indent(); + printer->Indent(); + + printer->Print( + "case IS_INITIALIZED: {\n"); + printer->Indent(); + GenerateDynamicMethodIsInitialized(printer); + printer->Outdent(); + + printer->Print( + "}\n" + "case MAKE_IMMUTABLE: {\n"); + + printer->Indent(); + GenerateDynamicMethodMakeImmutable(printer); + printer->Outdent(); + + printer->Print( + "}\n" + "case NEW_BUILDER: {\n"); + + printer->Indent(); + GenerateDynamicMethodNewBuilder(printer); + printer->Outdent(); + + printer->Print( + "}\n" + "case MERGE_FROM: {\n"); + + printer->Indent(); + GenerateDynamicMethodMergeFrom(printer); + printer->Outdent(); + + printer->Print( + "}\n"); + + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\n" + " throw new UnsupportedOperationException();\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "\n" + "// @@protoc_insertion_point(class_scope:$full_name$)\n", + "full_name", descriptor_->full_name()); + + + // Carefully initialize the default instance in such a way that it doesn't + // conflict with other initialization. + printer->Print( + "private static final $classname$ DEFAULT_INSTANCE;\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "static {\n" + " DEFAULT_INSTANCE = new $classname$(\n" + " com.google.protobuf.Internal\n" + " .EMPTY_CODED_INPUT_STREAM,\n" + " com.google.protobuf.ExtensionRegistryLite\n" + " .getEmptyRegistry());\n" + "}\n" + "\n", + "classname", descriptor_->name()); + printer->Print( + "public static $classname$ getDefaultInstance() {\n" + " return DEFAULT_INSTANCE;\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + GenerateParser(printer); + + // LITE_RUNTIME uses this to implement the *ForType methods at the + // GeneratedMessageLite level. + printer->Print( + "static {\n" + " com.google.protobuf.GeneratedMessageLite.onLoad(\n" + " $classname$.class, new com.google.protobuf.GeneratedMessageLite\n" + " .PrototypeHolder<$classname$, Builder>(\n" + " DEFAULT_INSTANCE, PARSER));" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + // Extensions must be declared after the DEFAULT_INSTANCE is initialized + // because the DEFAULT_INSTANCE is used by the extension to lazily retrieve + // the outer class's FileDescriptor. + for (int i = 0; i < descriptor_->extension_count(); i++) { + ImmutableExtensionGenerator(descriptor_->extension(i), context_) + .Generate(printer); + } + + printer->Outdent(); + printer->Print("}\n\n"); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator:: +GenerateMessageSerializationMethods(io::Printer* printer) { + google::protobuf::scoped_array sorted_fields( + SortFieldsByNumber(descriptor_)); + + vector sorted_extensions; + for (int i = 0; i < descriptor_->extension_range_count(); ++i) { + sorted_extensions.push_back(descriptor_->extension_range(i)); + } + std::sort(sorted_extensions.begin(), sorted_extensions.end(), + ExtensionRangeOrdering()); + + printer->Print( + "public void writeTo(com.google.protobuf.CodedOutputStream output)\n" + " throws java.io.IOException {\n"); + printer->Indent(); + if (HasPackedFields(descriptor_)) { + // writeTo(CodedOutputStream output) might be invoked without + // getSerializedSize() ever being called, but we need the memoized + // sizes in case this message has packed fields. Rather than emit checks for + // each packed field, just call getSerializedSize() up front. + // In most cases, getSerializedSize() will have already been called anyway + // by one of the wrapper writeTo() methods, making this call cheap. + printer->Print( + "getSerializedSize();\n"); + } + + if (descriptor_->extension_range_count() > 0) { + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "com.google.protobuf.GeneratedMessageLite\n" + " .ExtendableMessage<$classname$, $classname$.Builder>\n" + " .ExtensionWriter extensionWriter =\n" + " newMessageSetExtensionWriter();\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + } else { + printer->Print( + "com.google.protobuf.GeneratedMessageLite\n" + " .ExtendableMessage<$classname$, $classname$.Builder>\n" + " .ExtensionWriter extensionWriter =\n" + " newExtensionWriter();\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + } + } + + // Merge the fields and the extension ranges, both sorted by field number. + for (int i = 0, j = 0; + i < descriptor_->field_count() || j < sorted_extensions.size(); + ) { + if (i == descriptor_->field_count()) { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } else if (j == sorted_extensions.size()) { + GenerateSerializeOneField(printer, sorted_fields[i++]); + } else if (sorted_fields[i]->number() < sorted_extensions[j]->start) { + GenerateSerializeOneField(printer, sorted_fields[i++]); + } else { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } + } + + if (PreserveUnknownFields(descriptor_)) { + printer->Print( + "unknownFields.writeTo(output);\n"); + } + + printer->Outdent(); + printer->Print( + "}\n" + "\n" + "public int getSerializedSize() {\n" + " int size = memoizedSerializedSize;\n" + " if (size != -1) return size;\n" + "\n" + " size = 0;\n"); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer); + } + + if (descriptor_->extension_range_count() > 0) { + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "size += extensionsSerializedSizeAsMessageSet();\n"); + } else { + printer->Print( + "size += extensionsSerializedSize();\n"); + } + } + + if (PreserveUnknownFields(descriptor_)) { + printer->Print( + "size += unknownFields.getSerializedSize();\n"); + } + + printer->Outdent(); + printer->Print( + " memoizedSerializedSize = size;\n" + " return size;\n" + "}\n" + "\n"); + + printer->Print( + "private static final long serialVersionUID = 0L;\n"); +} + +void ImmutableMessageLiteGenerator:: +GenerateParseFromMethods(io::Printer* printer) { + // Note: These are separate from GenerateMessageSerializationMethods() + // because they need to be generated even for messages that are optimized + // for code size. + printer->Print( + "public static $classname$ parseFrom(\n" + " com.google.protobuf.ByteString data)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return PARSER.parseFrom(data);\n" + "}\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.ByteString data,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return PARSER.parseFrom(data, extensionRegistry);\n" + "}\n" + "public static $classname$ parseFrom(byte[] data)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return PARSER.parseFrom(data);\n" + "}\n" + "public static $classname$ parseFrom(\n" + " byte[] data,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return PARSER.parseFrom(data, extensionRegistry);\n" + "}\n" + "public static $classname$ parseFrom(java.io.InputStream input)\n" + " throws java.io.IOException {\n" + " return PARSER.parseFrom(input);\n" + "}\n" + "public static $classname$ parseFrom(\n" + " java.io.InputStream input,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws java.io.IOException {\n" + " return PARSER.parseFrom(input, extensionRegistry);\n" + "}\n" + "public static $classname$ parseDelimitedFrom(java.io.InputStream input)\n" + " throws java.io.IOException {\n" + " return PARSER.parseDelimitedFrom(input);\n" + "}\n" + "public static $classname$ parseDelimitedFrom(\n" + " java.io.InputStream input,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws java.io.IOException {\n" + " return PARSER.parseDelimitedFrom(input, extensionRegistry);\n" + "}\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.CodedInputStream input)\n" + " throws java.io.IOException {\n" + " return PARSER.parseFrom(input);\n" + "}\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.CodedInputStream input,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws java.io.IOException {\n" + " return PARSER.parseFrom(input, extensionRegistry);\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); +} + +void ImmutableMessageLiteGenerator::GenerateSerializeOneField( + io::Printer* printer, const FieldDescriptor* field) { + field_generators_.get(field).GenerateSerializationCode(printer); +} + +void ImmutableMessageLiteGenerator::GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range) { + printer->Print( + "extensionWriter.writeUntil($end$, output);\n", + "end", SimpleItoa(range->end)); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::GenerateBuilder(io::Printer* printer) { + printer->Print( + "public static Builder newBuilder() {\n" + " return DEFAULT_INSTANCE.toBuilder();\n" + "}\n" + "public static Builder newBuilder($classname$ prototype) {\n" + " return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + MessageBuilderLiteGenerator builderGenerator(descriptor_, context_); + builderGenerator.Generate(printer); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::GenerateDynamicMethodIsInitialized( + io::Printer* printer) { + // Returns null for false, DEFAULT_INSTANCE for true. + if (!HasRequiredFields(descriptor_)) { + printer->Print("return DEFAULT_INSTANCE;\n"); + return; + } + + // Don't directly compare to -1 to avoid an Android x86 JIT bug. + printer->Print( + "byte isInitialized = memoizedIsInitialized;\n" + "if (isInitialized == 1) return DEFAULT_INSTANCE;\n" + "if (isInitialized == 0) return null;\n" + "\n" + "boolean shouldMemoize = ((Boolean) args[0]).booleanValue();\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. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + + if (field->is_required()) { + printer->Print( + "if (!has$name$()) {\n" + " if (shouldMemoize) {\n" + " memoizedIsInitialized = 0;\n" + " }\n" + " return null;\n" + "}\n", + "name", info->capitalized_name); + } + } + + // Now check that all embedded messages are initialized. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + if (GetJavaType(field) == JAVATYPE_MESSAGE && + HasRequiredFields(field->message_type())) { + switch (field->label()) { + case FieldDescriptor::LABEL_REQUIRED: + printer->Print( + "if (!get$name$().isInitialized()) {\n" + " if (shouldMemoize) {\n" + " memoizedIsInitialized = 0;\n" + " }\n" + " return null;\n" + "}\n", + "type", name_resolver_->GetImmutableClassName( + field->message_type()), + "name", info->capitalized_name); + break; + case FieldDescriptor::LABEL_OPTIONAL: + if (!SupportFieldPresence(descriptor_->file()) && + field->containing_oneof() != NULL) { + const OneofDescriptor* oneof = field->containing_oneof(); + const OneofGeneratorInfo* oneof_info = + context_->GetOneofGeneratorInfo(oneof); + printer->Print( + "if ($oneof_name$Case_ == $field_number$) {\n", + "oneof_name", oneof_info->name, + "field_number", SimpleItoa(field->number())); + } else { + printer->Print( + "if (has$name$()) {\n", + "name", info->capitalized_name); + } + printer->Print( + " if (!get$name$().isInitialized()) {\n" + " if (shouldMemoize) {\n" + " memoizedIsInitialized = 0;\n" + " }\n" + " return null;\n" + " }\n" + "}\n", + "name", info->capitalized_name); + break; + case FieldDescriptor::LABEL_REPEATED: + if (IsMapEntry(field->message_type())) { + printer->Print( + "for ($type$ item : get$name$().values()) {\n" + " if (!item.isInitialized()) {\n" + " if (shouldMemoize) {\n" + " memoizedIsInitialized = 0;\n" + " }\n" + " return null;\n" + " }\n" + "}\n", + "type", MapValueImmutableClassdName(field->message_type(), + name_resolver_), + "name", info->capitalized_name); + } else { + printer->Print( + "for (int i = 0; i < get$name$Count(); i++) {\n" + " if (!get$name$(i).isInitialized()) {\n" + " if (shouldMemoize) {\n" + " memoizedIsInitialized = 0;\n" + " }\n" + " return null;\n" + " }\n" + "}\n", + "type", name_resolver_->GetImmutableClassName( + field->message_type()), + "name", info->capitalized_name); + } + break; + } + } + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "if (!extensionsAreInitialized()) {\n" + " if (shouldMemoize) {\n" + " memoizedIsInitialized = 0;\n" + " }\n" + " return null;\n" + "}\n"); + } + + printer->Print( + "if (shouldMemoize) memoizedIsInitialized = 1;\n"); + + printer->Print( + "return DEFAULT_INSTANCE;\n" + "\n"); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::GenerateDynamicMethodMakeImmutable( + io::Printer* printer) { + // Output generation code for each field. + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateDynamicMethodMakeImmutableCode(printer); + } + printer->Print( + "return null;"); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::GenerateDynamicMethodNewBuilder( + io::Printer* printer) { + printer->Print( + "return new Builder();"); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::GenerateDynamicMethodMergeFrom( + io::Printer* printer) { + printer->Print( + // Optimization: If other is the default instance, we know none of its + // fields are set so we can skip the merge. + "Object arg = args[0];\n" + "if (arg == $classname$.getDefaultInstance()) return this;\n" + "$classname$ other = ($classname$) arg;\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + for (int i = 0; i < descriptor_->field_count(); i++) { + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get( + descriptor_->field(i)).GenerateMergingCode(printer); + } + } + + // Merge oneof fields. + for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) { + printer->Print( + "switch (other.get$oneof_capitalized_name$Case()) {\n", + "oneof_capitalized_name", + context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->capitalized_name); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + printer->Print( + "case $field_name$: {\n", + "field_name", + ToUpper(field->name())); + printer->Indent(); + field_generators_.get(field).GenerateMergingCode(printer); + printer->Print( + "break;\n"); + printer->Outdent(); + printer->Print( + "}\n"); + } + printer->Print( + "case $cap_oneof_name$_NOT_SET: {\n" + " break;\n" + "}\n", + "cap_oneof_name", + ToUpper(context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name)); + printer->Outdent(); + printer->Print( + "}\n"); + } + + // if message type has extensions + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "this.mergeExtensionFields(other);\n"); + } + + if (PreserveUnknownFields(descriptor_)) { + printer->Print( + "this.mergeUnknownFields(other.unknownFields);\n"); + } + + printer->Print( + "return this;\n"); +} + +// =================================================================== + +namespace { +bool CheckHasBitsForEqualsAndHashCode(const FieldDescriptor* field) { + if (field->is_repeated()) { + return false; + } + if (SupportFieldPresence(field->file())) { + return true; + } + return GetJavaType(field) == JAVATYPE_MESSAGE && + field->containing_oneof() == NULL; +} +} // namespace + +void ImmutableMessageLiteGenerator:: +GenerateEqualsAndHashCode(io::Printer* printer) { + printer->Print( + "@java.lang.Override\n" + "public boolean equals(final java.lang.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", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print("boolean result = true;\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); + if (check_has_bits) { + printer->Print( + "result = result && (has$name$() == other.has$name$());\n" + "if (has$name$()) {\n", + "name", info->capitalized_name); + printer->Indent(); + } + field_generators_.get(field).GenerateEqualsCode(printer); + if (check_has_bits) { + printer->Outdent(); + printer->Print( + "}\n"); + } + } + if (PreserveUnknownFields(descriptor_)) { + // Always consider unknown fields for equality. This will sometimes return + // false for non-canonical ordering when running in LITE_RUNTIME but it's + // the best we can do. + printer->Print( + "result = result && unknownFields.equals(other.unknownFields);\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( + "if (memoizedHashCode != 0) {\n"); + printer->Indent(); + printer->Print( + "return memoizedHashCode;\n"); + printer->Outdent(); + printer->Print( + "}\n" + "int hash = 41;\n"); + + // Include the hash of the class so that two objects with different types + // but the same field values will probably have different hashes. + printer->Print("hash = (19 * hash) + $classname$.class.hashCode();\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); + if (check_has_bits) { + printer->Print( + "if (has$name$()) {\n", + "name", info->capitalized_name); + printer->Indent(); + } + field_generators_.get(field).GenerateHashCode(printer); + if (check_has_bits) { + printer->Outdent(); + printer->Print("}\n"); + } + } + + printer->Print( + "hash = (29 * hash) + unknownFields.hashCode();\n"); + printer->Print( + "memoizedHashCode = hash;\n" + "return hash;\n"); + printer->Outdent(); + printer->Print( + "}\n" + "\n"); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator:: +GenerateExtensionRegistrationCode(io::Printer* printer) { + for (int i = 0; i < descriptor_->extension_count(); i++) { + ImmutableExtensionGenerator(descriptor_->extension(i), context_) + .GenerateRegistrationCode(printer); + } + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + ImmutableMessageLiteGenerator(descriptor_->nested_type(i), context_) + .GenerateExtensionRegistrationCode(printer); + } +} + +// =================================================================== +void ImmutableMessageLiteGenerator:: +GenerateParsingConstructor(io::Printer* printer) { + google::protobuf::scoped_array sorted_fields( + SortFieldsByNumber(descriptor_)); + + printer->Print( + "private $classname$(\n" + " com.google.protobuf.CodedInputStream input,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry) {\n", + "classname", descriptor_->name()); + printer->Indent(); + + // Initialize all fields to default. + GenerateInitializers(printer); + + // Use builder bits to track mutable repeated fields. + int totalBuilderBits = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + const ImmutableFieldLiteGenerator& field = + field_generators_.get(descriptor_->field(i)); + totalBuilderBits += field.GetNumBitsForBuilder(); + } + int totalBuilderInts = (totalBuilderBits + 31) / 32; + for (int i = 0; i < totalBuilderInts; i++) { + printer->Print("int mutable_$bit_field_name$ = 0;\n", + "bit_field_name", GetBitFieldName(i)); + } + + if (PreserveUnknownFields(descriptor_)) { + printer->Print( + "com.google.protobuf.UnknownFieldSetLite.Builder unknownFields =\n" + " com.google.protobuf.UnknownFieldSetLite.newBuilder();\n"); + } + + printer->Print( + "try {\n"); + printer->Indent(); + + printer->Print( + "boolean done = false;\n" + "while (!done) {\n"); + printer->Indent(); + + printer->Print( + "int tag = input.readTag();\n" + "switch (tag) {\n"); + printer->Indent(); + + printer->Print( + "case 0:\n" // zero signals EOF / limit reached + " done = true;\n" + " break;\n"); + + if (PreserveUnknownFields(descriptor_)) { + if (descriptor_->extension_range_count() > 0) { + // Lite runtime directly invokes parseUnknownField to reduce method + // counts. + printer->Print( + "default: {\n" + " if (!parseUnknownField(extensions, getDefaultInstanceForType(),\n" + " input, unknownFields,\n" + " extensionRegistry, tag)) {\n" + " done = true;\n" // it's an endgroup tag + " }\n" + " break;\n" + "}\n"); + } else { + printer->Print( + "default: {\n" + " if (!parseUnknownField(input, unknownFields,\n" + " extensionRegistry, tag)) {\n" + " done = true;\n" // it's an endgroup tag + " }\n" + " break;\n" + "}\n"); + } + } else { + printer->Print( + "default: {\n" + " if (!input.skipField(tag)) {\n" + " done = true;\n" // it's an endgroup tag + " }\n" + " break;\n" + "}\n"); + } + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = sorted_fields[i]; + uint32 tag = WireFormatLite::MakeTag(field->number(), + WireFormat::WireTypeForFieldType(field->type())); + + printer->Print( + "case $tag$: {\n", + "tag", SimpleItoa(tag)); + printer->Indent(); + + field_generators_.get(field).GenerateParsingCode(printer); + + printer->Outdent(); + printer->Print( + " break;\n" + "}\n"); + + if (field->is_packable()) { + // To make packed = true wire compatible, we generate parsing code from a + // packed version of this field regardless of field->options().packed(). + uint32 packed_tag = WireFormatLite::MakeTag(field->number(), + WireFormatLite::WIRETYPE_LENGTH_DELIMITED); + printer->Print( + "case $tag$: {\n", + "tag", SimpleItoa(packed_tag)); + printer->Indent(); + + field_generators_.get(field).GenerateParsingCodeFromPacked(printer); + + printer->Outdent(); + printer->Print( + " break;\n" + "}\n"); + } + } + + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" // switch (tag) + "}\n"); // while (!done) + + printer->Outdent(); + printer->Print( + "} catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" + " throw new RuntimeException(e.setUnfinishedMessage(this));\n" + "} catch (java.io.IOException e) {\n" + " throw new RuntimeException(\n" + " new com.google.protobuf.InvalidProtocolBufferException(\n" + " e.getMessage()).setUnfinishedMessage(this));\n" + "} finally {\n"); + printer->Indent(); + + // Make repeated field list immutable. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = sorted_fields[i]; + field_generators_.get(field).GenerateParsingDoneCode(printer); + } + + if (PreserveUnknownFields(descriptor_)) { + // Make unknown fields immutable. + printer->Print("this.unknownFields = unknownFields.build();\n"); + } + + if (descriptor_->extension_range_count() > 0) { + // Make extensions immutable. + printer->Print( + "makeExtensionsImmutable(extensions);\n"); + } + + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" // finally + "}\n"); +} + +// =================================================================== +void ImmutableMessageLiteGenerator::GenerateParser(io::Printer* printer) { + printer->Print( + "public static final com.google.protobuf.Parser<$classname$> PARSER =\n" + " new DefaultInstanceBasedParser(DEFAULT_INSTANCE);\n" + "\n", + "classname", descriptor_->name()); +} + +// =================================================================== +void ImmutableMessageLiteGenerator::GenerateInitializers(io::Printer* printer) { + for (int i = 0; i < descriptor_->field_count(); i++) { + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get(descriptor_->field(i)) + .GenerateInitializationCode(printer); + } + } +} + + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_message_lite.h b/src/google/protobuf/compiler/java/java_message_lite.h new file mode 100644 index 00000000..2bd3cdd4 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_lite.h @@ -0,0 +1,91 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: dweis@google.com (Daniel Weis) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_LITE_H__ + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableMessageLiteGenerator : public MessageGenerator { + public: + explicit ImmutableMessageLiteGenerator(const Descriptor* descriptor, + Context* context); + virtual ~ImmutableMessageLiteGenerator(); + + virtual void Generate(io::Printer* printer); + virtual void GenerateInterface(io::Printer* printer); + virtual void GenerateExtensionRegistrationCode(io::Printer* printer); + virtual void GenerateStaticVariables(io::Printer* printer); + virtual int GenerateStaticVariableInitializers(io::Printer* printer); + + private: + + void GenerateMessageSerializationMethods(io::Printer* printer); + void GenerateParseFromMethods(io::Printer* printer); + void GenerateSerializeOneField(io::Printer* printer, + const FieldDescriptor* field); + void GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range); + + void GenerateBuilder(io::Printer* printer); + void GenerateDynamicMethodIsInitialized(io::Printer* printer); + void GenerateDynamicMethodMakeImmutable(io::Printer* printer); + void GenerateDynamicMethodMergeFrom(io::Printer* printer); + void GenerateDynamicMethodNewBuilder(io::Printer* printer); + void GenerateInitializers(io::Printer* printer); + void GenerateEqualsAndHashCode(io::Printer* printer); + void GenerateParser(io::Printer* printer); + void GenerateParsingConstructor(io::Printer* printer); + + Context* context_; + ClassNameResolver* name_resolver_; + FieldGeneratorMap field_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableMessageLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc index 48757c60..7bebe12a 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -74,7 +74,12 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, "" : ("= " + ImmutableDefaultValue(descriptor, name_resolver)); (*variables)["capitalized_type"] = GetCapitalizedType(descriptor, /* immutable = */ true); - (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); + if (descriptor->is_packed()) { + (*variables)["tag"] = SimpleItoa(WireFormatLite::MakeTag( + descriptor->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED)); + } else { + (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); + } (*variables)["tag_size"] = SimpleItoa( WireFormat::TagSize(descriptor->number(), GetType(descriptor))); if (IsReferenceType(GetJavaType(descriptor))) { @@ -599,7 +604,7 @@ GenerateMembers(io::Printer* printer) const { " return $name$_.get(index);\n" "}\n"); - if (descriptor_->options().packed() && + if (descriptor_->is_packed() && HasGeneratedMethods(descriptor_->containing_type())) { printer->Print(variables_, "private int $name$MemoizedSerializedSize = -1;\n"); @@ -771,7 +776,7 @@ GenerateParsingDoneCode(io::Printer* printer) const { void RepeatedImmutablePrimitiveFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { // We invoke getSerializedSize in writeTo for messages that have packed // fields in ImmutableMessageGenerator::GenerateMessageSerializationMethods. // That makes it safe to rely on the memoized size here. @@ -812,7 +817,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print( "size += dataSize;\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "if (!get$capitalized_name$List().isEmpty()) {\n" " size += $tag_size$;\n" @@ -825,7 +830,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { } // cache the data size for packed fields. - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "$name$MemoizedSerializedSize = dataSize;\n"); } diff --git a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc new file mode 100644 index 00000000..217ff9b6 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc @@ -0,0 +1,892 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { + +void SetPrimitiveVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, + map* variables) { + SetCommonFieldVariables(descriptor, info, variables); + + (*variables)["type"] = PrimitiveTypeName(GetJavaType(descriptor)); + (*variables)["boxed_type"] = BoxedPrimitiveTypeName(GetJavaType(descriptor)); + (*variables)["field_type"] = (*variables)["type"]; + (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); + (*variables)["default_init"] = IsDefaultValueJavaDefault(descriptor) ? + "" : ("= " + ImmutableDefaultValue(descriptor, name_resolver)); + (*variables)["capitalized_type"] = + GetCapitalizedType(descriptor, /* immutable = */ true); + (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), GetType(descriptor))); + + string capitalized_type = UnderscoresToCamelCase(PrimitiveTypeName( + GetJavaType(descriptor)), true /* cap_next_letter */); + switch (GetJavaType(descriptor)) { + case JAVATYPE_INT: + case JAVATYPE_LONG: + case JAVATYPE_FLOAT: + case JAVATYPE_DOUBLE: + case JAVATYPE_BOOLEAN: + (*variables)["field_list_type"] = + "com.google.protobuf.Internal." + capitalized_type + "List"; + (*variables)["new_list"] = "new" + capitalized_type + "List"; + (*variables)["empty_list"] = "empty" + capitalized_type + "List()"; + (*variables)["make_name_unmodifiable"] = + (*variables)["name"] + "_.makeImmutable()"; + (*variables)["repeated_index_get"] = + (*variables)["name"] + "_.get" + capitalized_type + "(index)"; + (*variables)["repeated_add"] = + (*variables)["name"] + "_.add" + capitalized_type; + (*variables)["repeated_set"] = + (*variables)["name"] + "_.set" + capitalized_type; + break; + default: + (*variables)["field_list_type"] = + "com.google.protobuf.Internal.ProtobufList<" + + (*variables)["boxed_type"] + ">"; + (*variables)["new_list"] = "newProtobufList"; + (*variables)["empty_list"] = "emptyProtobufList()"; + (*variables)["make_name_unmodifiable"] = + (*variables)["name"] + "_.makeImmutable()"; + (*variables)["repeated_index_get"] = + (*variables)["name"] + "_.get(index)"; + (*variables)["repeated_add"] = (*variables)["name"] + "_.add"; + (*variables)["repeated_set"] = (*variables)["name"] + "_.set"; + } + + if (IsReferenceType(GetJavaType(descriptor))) { + (*variables)["null_check"] = + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n"; + } 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();" : ""; + + if (SupportFieldPresence(descriptor->file())) { + // For singular messages and builders, one bit is used for the hasField bit. + (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); + + // Note that these have a trailing ";". + (*variables)["set_has_field_bit_message"] = + GenerateSetBit(messageBitIndex) + ";"; + (*variables)["clear_has_field_bit_message"] = + GenerateClearBit(messageBitIndex) + ";"; + + (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex); + } else { + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["clear_has_field_bit_message"] = ""; + + if (descriptor->type() == FieldDescriptor::TYPE_BYTES) { + (*variables)["is_field_present_message"] = + "!" + (*variables)["name"] + "_.isEmpty()"; + } else { + (*variables)["is_field_present_message"] = + (*variables)["name"] + "_ != " + (*variables)["default"]; + } + } + + // For repeated builders, the underlying list tracks mutability state. + (*variables)["is_mutable"] = (*variables)["name"] + "_.isModifiable()"; + + (*variables)["get_has_field_bit_from_local"] = + GenerateGetBitFromLocal(builderBitIndex); + (*variables)["set_has_field_bit_to_local"] = + GenerateSetBitToLocal(messageBitIndex); +} + +} // namespace + +// =================================================================== + +ImmutablePrimitiveFieldLiteGenerator:: +ImmutablePrimitiveFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +ImmutablePrimitiveFieldLiteGenerator::~ImmutablePrimitiveFieldLiteGenerator() {} + +int ImmutablePrimitiveFieldLiteGenerator::GetNumBitsForMessage() const { + return 1; +} + +int ImmutablePrimitiveFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$();\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private $field_type$ $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return $name$_;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + "$null_check$" + " $set_has_field_bit_message$\n" + " $name$_ = value;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $clear_has_field_bit_message$\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_, + " $name$_ = getDefaultInstance().get$capitalized_name$();\n"); + } else { + printer->Print(variables_, + " $name$_ = $default$;\n"); + } + printer->Print(variables_, + "}\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for primitives +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default$;\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + // noop for primitives +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " set$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); + } else { + printer->Print(variables_, + "if (other.get$capitalized_name$() != $default$) {\n" + " set$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); + } +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // noop for primitives +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + // noop for scalars +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$set_has_field_bit_message$\n" + "$name$_ = input.read$capitalized_type$();\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // noop for primitives. +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " output.write$capitalized_type$($number$, $name$_);\n" + "}\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$capitalized_type$Size($number$, $name$_);\n" + "}\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +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 && (\n" + " java.lang.Float.floatToIntBits(get$capitalized_name$())\n" + " == java.lang.Float.floatToIntBits(\n" + " other.get$capitalized_name$()));\n"); + break; + + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "result = result && (\n" + " java.lang.Double.doubleToLongBits(get$capitalized_name$())\n" + " == java.lang.Double.doubleToLongBits(\n" + " 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 ImmutablePrimitiveFieldLiteGenerator:: +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) + com.google.protobuf.Internal.hashLong(\n" + " get$capitalized_name$());\n"); + break; + + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(\n" + " get$capitalized_name$());\n"); + break; + + case JAVATYPE_FLOAT: + printer->Print(variables_, + "hash = (53 * hash) + java.lang.Float.floatToIntBits(\n" + " get$capitalized_name$());\n"); + break; + + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "hash = (53 * hash) + com.google.protobuf.Internal.hashLong(\n" + " java.lang.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 ImmutablePrimitiveFieldLiteGenerator::GetBoxedType() const { + return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +} + +// =================================================================== + +ImmutablePrimitiveOneofFieldLiteGenerator:: +ImmutablePrimitiveOneofFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutablePrimitiveFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); +} + +ImmutablePrimitiveOneofFieldLiteGenerator:: +~ImmutablePrimitiveOneofFieldLiteGenerator() {} + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + PrintExtraFieldInfo(variables_, printer); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $has_oneof_case_message$;\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " return ($boxed_type$) $oneof_name$_;\n" + " }\n" + " return $default$;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + "$null_check$" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " }\n" + "}\n"); +} + + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // noop for primitives +} + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "set$capitalized_name$(other.get$capitalized_name$());\n"); +} + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$set_oneof_case_message$;\n" + "$oneof_name$_ = input.read$capitalized_type$();\n"); +} + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " output.write$capitalized_type$(\n" + " $number$, ($type$)(($boxed_type$) $oneof_name$_));\n" + "}\n"); +} + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$capitalized_type$Size(\n" + " $number$, ($type$)(($boxed_type$) $oneof_name$_));\n" + "}\n"); +} + +// =================================================================== + +RepeatedImmutablePrimitiveFieldLiteGenerator:: +RepeatedImmutablePrimitiveFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +RepeatedImmutablePrimitiveFieldLiteGenerator:: +~RepeatedImmutablePrimitiveFieldLiteGenerator() {} + +int RepeatedImmutablePrimitiveFieldLiteGenerator::GetNumBitsForMessage() const { + return 0; +} + +int RepeatedImmutablePrimitiveFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.util.List<$boxed_type$> get$capitalized_name$List();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Count();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$(int index);\n"); +} + + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private $field_list_type$ $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$boxed_type$>\n" + " get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return $repeated_index_get$;\n" + "}\n"); + + if (descriptor_->options().packed() && + HasGeneratedMethods(descriptor_->containing_type())) { + printer->Print(variables_, + "private int $name$MemoizedSerializedSize = -1;\n"); + } + + printer->Print(variables_, + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$is_mutable$) {\n" + " $name$_ = $new_list$($name$_);\n" + " }\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, $type$ value) {\n" + "$null_check$" + " ensure$capitalized_name$IsMutable();\n" + " $repeated_set$(index, value);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$($type$ value) {\n" + "$null_check$" + " ensure$capitalized_name$IsMutable();\n" + " $repeated_add$(value);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " com.google.protobuf.AbstractMessageLite.addAll(\n" + " values, $name$_);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $name$_ = $empty_list$;\n" + "}\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$boxed_type$>\n" + " get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList(\n" + " instance.get$capitalized_name$List());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return instance.get$capitalized_name$Count();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return instance.get$capitalized_name$(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " copyOnWrite();\n" + " instance.addAll$capitalized_name$(values);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for primitives +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $empty_list$;\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + // noop for primitives +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +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" + " } else {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.addAll(other.$name$_);\n" + " }\n" + " $on_changed$\n" + "}\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // noop for primitives +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.makeImmutable();\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!$is_mutable$) {\n" + " $name$_ = $new_list$();\n" + "}\n" + "$repeated_add$(input.read$capitalized_type$());\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateParsingCodeFromPacked(io::Printer* printer) const { + printer->Print(variables_, + "int length = input.readRawVarint32();\n" + "int limit = input.pushLimit(length);\n" + "if (!$is_mutable$ && input.getBytesUntilLimit() > 0) {\n" + " $name$_ = $new_list$();\n" + "}\n" + "while (input.getBytesUntilLimit() > 0) {\n" + " $repeated_add$(input.read$capitalized_type$());\n" + "}\n" + "input.popLimit(limit);\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_mutable$) {\n" + " $make_name_unmodifiable$;\n" + "}\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + if (descriptor_->options().packed()) { + // We invoke getSerializedSize in writeTo for messages that have packed + // fields in ImmutableMessageGenerator::GenerateMessageSerializationMethods. + // That makes it safe to rely on the memoized size here. + 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.write$capitalized_type$($number$, $name$_.get(i));\n" + "}\n"); + } +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "{\n" + " int dataSize = 0;\n"); + printer->Indent(); + + if (FixedSize(GetType(descriptor_)) == -1) { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " dataSize += com.google.protobuf.CodedOutputStream\n" + " .compute$capitalized_type$SizeNoTag($name$_.get(i));\n" + "}\n"); + } else { + printer->Print(variables_, + "dataSize = $fixed_size$ * get$capitalized_name$List().size();\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 RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$List()\n" + " .equals(other.get$capitalized_name$List());\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +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 RepeatedImmutablePrimitiveFieldLiteGenerator::GetBoxedType() const { + return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_primitive_field_lite.h b/src/google/protobuf/compiler/java/java_primitive_field_lite.h new file mode 100644 index 00000000..ad603c2a --- /dev/null +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.h @@ -0,0 +1,163 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_PRIMITIVE_FIELD_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_PRIMITIVE_FIELD_LITE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutablePrimitiveFieldLiteGenerator + : public ImmutableFieldLiteGenerator { + public: + explicit ImmutablePrimitiveFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutablePrimitiveFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + 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 GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(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; + + protected: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutablePrimitiveFieldLiteGenerator); +}; + +class ImmutablePrimitiveOneofFieldLiteGenerator + : public ImmutablePrimitiveFieldLiteGenerator { + public: + ImmutablePrimitiveOneofFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutablePrimitiveOneofFieldLiteGenerator(); + + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutablePrimitiveOneofFieldLiteGenerator); +}; + +class RepeatedImmutablePrimitiveFieldLiteGenerator + : public ImmutableFieldLiteGenerator { + public: + explicit RepeatedImmutablePrimitiveFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + virtual ~RepeatedImmutablePrimitiveFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + 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 GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingCodeFromPacked(io::Printer* printer) const; + void GenerateParsingDoneCode(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 variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutablePrimitiveFieldLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_PRIMITIVE_FIELD_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc index 8aa5699c..68e863cc 100644 --- a/src/google/protobuf/compiler/java/java_string_field.cc +++ b/src/google/protobuf/compiler/java/java_string_field.cc @@ -208,7 +208,7 @@ GenerateInterfaceMembers(io::Printer* printer) const { void ImmutableStringFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private java.lang.Object $name$_;\n"); + "private volatile java.lang.Object $name$_;\n"); PrintExtraFieldInfo(variables_, printer); if (SupportFieldPresence(descriptor_->file())) { diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.cc b/src/google/protobuf/compiler/java/java_string_field_lite.cc new file mode 100644 index 00000000..51bb245c --- /dev/null +++ b/src/google/protobuf/compiler/java/java_string_field_lite.cc @@ -0,0 +1,1013 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { + +void SetPrimitiveVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, + map* variables) { + SetCommonFieldVariables(descriptor, info, variables); + + (*variables)["empty_list"] = "emptyLazyStringArrayList()"; + + (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); + (*variables)["default_init"] = + "= " + ImmutableDefaultValue(descriptor, name_resolver); + (*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();" : ""; + + if (SupportFieldPresence(descriptor->file())) { + // For singular messages and builders, one bit is used for the hasField bit. + (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); + + // Note that these have a trailing ";". + (*variables)["set_has_field_bit_message"] = + GenerateSetBit(messageBitIndex) + ";"; + (*variables)["clear_has_field_bit_message"] = + GenerateClearBit(messageBitIndex) + ";"; + + (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex); + } else { + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["clear_has_field_bit_message"] = ""; + + (*variables)["is_field_present_message"] = + "!get" + (*variables)["capitalized_name"] + "Bytes().isEmpty()"; + } + + // For repeated builders, the underlying list tracks mutability state. + (*variables)["is_mutable"] = (*variables)["name"] + "_.isModifiable()"; + + (*variables)["get_has_field_bit_from_local"] = + GenerateGetBitFromLocal(builderBitIndex); + (*variables)["set_has_field_bit_to_local"] = + GenerateSetBitToLocal(messageBitIndex); +} + +bool CheckUtf8(const FieldDescriptor* descriptor) { + return descriptor->file()->options().java_string_check_utf8(); +} + +} // namespace + +// =================================================================== + +ImmutableStringFieldLiteGenerator:: +ImmutableStringFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +ImmutableStringFieldLiteGenerator::~ImmutableStringFieldLiteGenerator() {} + +int ImmutableStringFieldLiteGenerator::GetNumBitsForMessage() const { + return 1; +} + +int ImmutableStringFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +// 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 ImmutableStringFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.lang.String get$capitalized_name$();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes();\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private java.lang.Object $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.lang.String get$capitalized_name$() {\n" + " java.lang.Object ref = $name$_;\n" + " if (ref instanceof java.lang.String) {\n" + " return (java.lang.String) ref;\n" + " } else {\n" + " com.google.protobuf.ByteString bs = \n" + " (com.google.protobuf.ByteString) ref;\n" + " java.lang.String s = bs.toStringUtf8();\n"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " $name$_ = s;\n"); + } else { + printer->Print(variables_, + " if (bs.isValidUtf8()) {\n" + " $name$_ = s;\n" + " }\n"); + } + printer->Print(variables_, + " return s;\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes() {\n" + " java.lang.Object ref = $name$_;\n" + " if (ref instanceof java.lang.String) {\n" + " com.google.protobuf.ByteString b = \n" + " com.google.protobuf.ByteString.copyFromUtf8(\n" + " (java.lang.String) ref);\n" + " $name$_ = b;\n" + " return b;\n" + " } else {\n" + " return (com.google.protobuf.ByteString) ref;\n" + " }\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " java.lang.String value) {\n" + "$null_check$" + " $set_has_field_bit_message$\n" + " $name$_ = value;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $clear_has_field_bit_message$\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. + " $name$_ = getDefaultInstance().get$capitalized_name$();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + "$null_check$"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " checkByteStringIsUtf8(value);\n"); + } + printer->Print(variables_, + " $set_has_field_bit_message$\n" + " $name$_ = value;\n" + "}\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.lang.String get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes() {\n" + " return instance.get$capitalized_name$Bytes();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " java.lang.String value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$Bytes(value);\n" + " return this;\n" + "}\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for strings +} + +void ImmutableStringFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default$;\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + // Allow a slight breach of abstraction here in order to avoid forcing + // all string fields to Strings when copying fields from a Message. + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " $set_has_field_bit_message$\n" + " $name$_ = other.$name$_;\n" + " $on_changed$\n" + "}\n"); + } else { + printer->Print(variables_, + "if (!other.get$capitalized_name$().isEmpty()) {\n" + " $name$_ = other.$name$_;\n" + " $on_changed$\n" + "}\n"); + } +} + +void ImmutableStringFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + // noop for scalars +} + +void ImmutableStringFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + "String s = input.readStringRequireUtf8();\n" + "$set_has_field_bit_message$\n" + "$name$_ = s;\n"); + } else if (!HasDescriptorMethods(descriptor_->file())) { + // Lite runtime should attempt to reduce allocations by attempting to + // construct the string directly from the input stream buffer. This avoids + // spurious intermediary ByteString allocations, cutting overall allocations + // in half. + printer->Print(variables_, + "String s = input.readString();\n" + "$set_has_field_bit_message$\n" + "$name$_ = s;\n"); + } else { + printer->Print(variables_, + "com.google.protobuf.ByteString bs = input.readBytes();\n" + "$set_has_field_bit_message$\n" + "$name$_ = bs;\n"); + } +} + +void ImmutableStringFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // noop for strings +} + +void ImmutableStringFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " output.writeBytes($number$, get$capitalized_name$Bytes());\n" + "}\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeBytesSize($number$, get$capitalized_name$Bytes());\n" + "}\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$()\n" + " .equals(other.get$capitalized_name$());\n"); +} + +void ImmutableStringFieldLiteGenerator:: +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 ImmutableStringFieldLiteGenerator::GetBoxedType() const { + return "java.lang.String"; +} + +// =================================================================== + +ImmutableStringOneofFieldLiteGenerator:: +ImmutableStringOneofFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableStringFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); +} + +ImmutableStringOneofFieldLiteGenerator:: +~ImmutableStringOneofFieldLiteGenerator() {} + +void ImmutableStringOneofFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + PrintExtraFieldInfo(variables_, printer); + + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $has_oneof_case_message$;\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.lang.String get$capitalized_name$() {\n" + " java.lang.Object ref $default_init$;\n" + " if ($has_oneof_case_message$) {\n" + " ref = $oneof_name$_;\n" + " }\n" + " if (ref instanceof java.lang.String) {\n" + " return (java.lang.String) ref;\n" + " } else {\n" + " com.google.protobuf.ByteString bs = \n" + " (com.google.protobuf.ByteString) ref;\n" + " java.lang.String s = bs.toStringUtf8();\n"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " if ($has_oneof_case_message$) {\n" + " $oneof_name$_ = s;\n" + " }\n"); + } else { + printer->Print(variables_, + " if (bs.isValidUtf8() && ($has_oneof_case_message$)) {\n" + " $oneof_name$_ = s;\n" + " }\n"); + } + printer->Print(variables_, + " return s;\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes() {\n" + " java.lang.Object ref $default_init$;\n" + " if ($has_oneof_case_message$) {\n" + " ref = $oneof_name$_;\n" + " }\n" + " if (ref instanceof java.lang.String) {\n" + " com.google.protobuf.ByteString b = \n" + " com.google.protobuf.ByteString.copyFromUtf8(\n" + " (java.lang.String) ref);\n" + " if ($has_oneof_case_message$) {\n" + " $oneof_name$_ = b;\n" + " }\n" + " return b;\n" + " } else {\n" + " return (com.google.protobuf.ByteString) ref;\n" + " }\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " java.lang.String value) {\n" + "$null_check$" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " }\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + "$null_check$"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " checkByteStringIsUtf8(value);\n"); + } + printer->Print(variables_, + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + "}\n"); +} + +void ImmutableStringOneofFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.lang.String get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes() {\n" + " return instance.get$capitalized_name$Bytes();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " java.lang.String value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$Bytes(value);\n" + " return this;\n" + "}\n"); +} + +void ImmutableStringOneofFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + // Allow a slight breach of abstraction here in order to avoid forcing + // all string fields to Strings when copying fields from a Message. + printer->Print(variables_, + "$set_oneof_case_message$;\n" + "$oneof_name$_ = other.$oneof_name$_;\n" + "$on_changed$\n"); +} + +void ImmutableStringOneofFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + "String s = input.readStringRequireUtf8();\n" + "$set_oneof_case_message$;\n" + "$oneof_name$_ = s;\n"); + } else if (!HasDescriptorMethods(descriptor_->file())) { + // Lite runtime should attempt to reduce allocations by attempting to + // construct the string directly from the input stream buffer. This avoids + // spurious intermediary ByteString allocations, cutting overall allocations + // in half. + printer->Print(variables_, + "String s = input.readString();\n" + "$set_oneof_case_message$;\n" + "$oneof_name$_ = s;\n"); + } else { + printer->Print(variables_, + "com.google.protobuf.ByteString bs = input.readBytes();\n" + "$set_oneof_case_message$;\n" + "$oneof_name$_ = bs;\n"); + } +} + +void ImmutableStringOneofFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " output.writeBytes($number$, get$capitalized_name$Bytes());\n" + "}\n"); +} + +void ImmutableStringOneofFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeBytesSize($number$, get$capitalized_name$Bytes());\n" + "}\n"); +} + +// =================================================================== + +RepeatedImmutableStringFieldLiteGenerator:: +RepeatedImmutableStringFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +RepeatedImmutableStringFieldLiteGenerator:: +~RepeatedImmutableStringFieldLiteGenerator() {} + +int RepeatedImmutableStringFieldLiteGenerator::GetNumBitsForMessage() const { + return 0; +} + +int RepeatedImmutableStringFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$com.google.protobuf.ProtocolStringList\n" + " get$capitalized_name$List();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Count();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.lang.String get$capitalized_name$(int index);\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes(int index);\n"); +} + + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private com.google.protobuf.LazyStringArrayList $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ProtocolStringList\n" + " get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.lang.String get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes(int index) {\n" + " return $name$_.getByteString(index);\n" + "}\n"); + + if (descriptor_->options().packed() && + HasGeneratedMethods(descriptor_->containing_type())) { + printer->Print(variables_, + "private int $name$MemoizedSerializedSize = -1;\n"); + } + + printer->Print(variables_, + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$is_mutable$) {\n" + " $name$_ = new com.google.protobuf.LazyStringArrayList($name$_);\n" + " }\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, java.lang.String value) {\n" + "$null_check$" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, value);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " java.lang.String value) {\n" + "$null_check$" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " com.google.protobuf.AbstractMessageLite.addAll(\n" + " values, $name$_);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $name$_ = $empty_list$;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + "$null_check$"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " checkByteStringIsUtf8(value);\n"); + } + printer->Print(variables_, + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value);\n" + "}\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ProtocolStringList\n" + " get$capitalized_name$List() {\n" + " return ((com.google.protobuf.LazyStringList)\n" + " instance.get$capitalized_name$List()).getUnmodifiableView();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return instance.get$capitalized_name$Count();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.lang.String get$capitalized_name$(int index) {\n" + " return instance.get$capitalized_name$(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes(int index) {\n" + " return instance.get$capitalized_name$Bytes(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, java.lang.String value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " java.lang.String value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " copyOnWrite();\n" + " instance.addAll$capitalized_name$(values);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$Bytes(value);\n" + " return this;\n" + "}\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for strings +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $empty_list$;\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +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" + " } else {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.addAll(other.$name$_);\n" + " }\n" + " $on_changed$\n" + "}\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.makeImmutable();\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + "String s = input.readStringRequireUtf8();\n"); + } else if (!HasDescriptorMethods(descriptor_->file())) { + // Lite runtime should attempt to reduce allocations by attempting to + // construct the string directly from the input stream buffer. This avoids + // spurious intermediary ByteString allocations, cutting overall allocations + // in half. + printer->Print(variables_, + "String s = input.readString();\n"); + } else { + printer->Print(variables_, + "com.google.protobuf.ByteString bs = input.readBytes();\n"); + } + printer->Print(variables_, + "if (!$is_mutable$) {\n" + " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" + "}\n"); + if (CheckUtf8(descriptor_) || !HasDescriptorMethods(descriptor_->file())) { + printer->Print(variables_, + "$name$_.add(s);\n"); + } else { + printer->Print(variables_, + "$name$_.add(bs);\n"); + } +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateParsingCodeFromPacked(io::Printer* printer) const { + printer->Print(variables_, + "int length = input.readRawVarint32();\n" + "int limit = input.pushLimit(length);\n" + "if (!$is_mutable$ && input.getBytesUntilLimit() > 0) {\n" + " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" + "}\n" + "while (input.getBytesUntilLimit() > 0) {\n"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " String s = input.readStringRequireUtf8();\n"); + } else { + printer->Print(variables_, + " String s = input.readString();\n"); + } + printer->Print(variables_, + " $name$.add(s);\n"); + printer->Print(variables_, + "}\n" + "input.popLimit(limit);\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_mutable$) {\n" + " $name$_.makeImmutable();\n" + "}\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +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 RepeatedImmutableStringFieldLiteGenerator:: +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 RepeatedImmutableStringFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$List()\n" + " .equals(other.get$capitalized_name$List());\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +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 RepeatedImmutableStringFieldLiteGenerator::GetBoxedType() const { + return "String"; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.h b/src/google/protobuf/compiler/java/java_string_field_lite.h new file mode 100644 index 00000000..9d93b307 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_string_field_lite.h @@ -0,0 +1,158 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// 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_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_STRING_FIELD_LITE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableStringFieldLiteGenerator : public ImmutableFieldLiteGenerator { + public: + explicit ImmutableStringFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableStringFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + 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 GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(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; + + protected: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableStringFieldLiteGenerator); +}; + +class ImmutableStringOneofFieldLiteGenerator + : public ImmutableStringFieldLiteGenerator { + public: + ImmutableStringOneofFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableStringOneofFieldLiteGenerator(); + + private: + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableStringOneofFieldLiteGenerator); +}; + +class RepeatedImmutableStringFieldLiteGenerator + : public ImmutableFieldLiteGenerator { + public: + explicit RepeatedImmutableStringFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~RepeatedImmutableStringFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + 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 GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingCodeFromPacked(io::Printer* printer) const; + void GenerateParsingDoneCode(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 variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableStringFieldLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_STRING_FIELD_LITE_H__ diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc index c50cdf54..a2eeee2d 100644 --- a/src/google/protobuf/compiler/parser.cc +++ b/src/google/protobuf/compiler/parser.cc @@ -686,6 +686,8 @@ bool Parser::ParseMessageStatement(DescriptorProto* message, LocationRecorder location(message_location, DescriptorProto::kExtensionRangeFieldNumber); return ParseExtensions(message, location, containing_file); + } else if (LookingAt("reserved")) { + return ParseReserved(message, message_location); } else if (LookingAt("extend")) { LocationRecorder location(message_location, DescriptorProto::kExtensionFieldNumber); @@ -733,6 +735,13 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field, FieldDescriptorProto::Label label; if (ParseLabel(&label, containing_file)) { field->set_label(label); + if (label == FieldDescriptorProto::LABEL_OPTIONAL && + syntax_identifier_ == "proto3") { + AddError( + "Explicit 'optional' labels are disallowed in the Proto3 syntax. " + "To define 'optional' fields in Proto3, simply remove the " + "'optional' label, as fields are 'optional' by default."); + } } } @@ -1353,6 +1362,77 @@ bool Parser::ParseExtensions(DescriptorProto* message, return true; } +// This is similar to extension range parsing, except that "max" is not +// supported, and accepts field name literals. +bool Parser::ParseReserved(DescriptorProto* message, + const LocationRecorder& message_location) { + // Parse the declaration. + DO(Consume("reserved")); + if (LookingAtType(io::Tokenizer::TYPE_STRING)) { + LocationRecorder location(message_location, + DescriptorProto::kReservedNameFieldNumber); + return ParseReservedNames(message, location); + } else { + LocationRecorder location(message_location, + DescriptorProto::kReservedRangeFieldNumber); + return ParseReservedNumbers(message, location); + } +} + + +bool Parser::ParseReservedNames(DescriptorProto* message, + const LocationRecorder& parent_location) { + do { + LocationRecorder location(parent_location, message->reserved_name_size()); + DO(ConsumeString(message->add_reserved_name(), "Expected field name.")); + } while (TryConsume(",")); + DO(ConsumeEndOfDeclaration(";", &parent_location)); + return true; +} + +bool Parser::ParseReservedNumbers(DescriptorProto* message, + const LocationRecorder& parent_location) { + bool first = true; + do { + LocationRecorder location(parent_location, message->reserved_range_size()); + + DescriptorProto::ReservedRange* range = message->add_reserved_range(); + int start, end; + io::Tokenizer::Token start_token; + { + LocationRecorder start_location( + location, DescriptorProto::ReservedRange::kStartFieldNumber); + start_token = input_->current(); + DO(ConsumeInteger(&start, (first ? + "Expected field name or number range." : + "Expected field number range."))); + } + + if (TryConsume("to")) { + LocationRecorder end_location( + location, DescriptorProto::ReservedRange::kEndFieldNumber); + DO(ConsumeInteger(&end, "Expected integer.")); + } else { + LocationRecorder end_location( + location, DescriptorProto::ReservedRange::kEndFieldNumber); + end_location.StartAt(start_token); + end_location.EndAt(start_token); + end = start; + } + + // Users like to specify inclusive ranges, but in code we like the end + // number to be exclusive. + ++end; + + range->set_start(start); + range->set_end(end); + first = false; + } while (TryConsume(",")); + + DO(ConsumeEndOfDeclaration(";", &parent_location)); + return true; +} + bool Parser::ParseExtend(RepeatedPtrField* extensions, RepeatedPtrField* messages, const LocationRecorder& parent_location, diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h index 7cb1678a..16012e96 100644 --- a/src/google/protobuf/compiler/parser.h +++ b/src/google/protobuf/compiler/parser.h @@ -364,6 +364,14 @@ class LIBPROTOBUF_EXPORT Parser { const LocationRecorder& extensions_location, const FileDescriptorProto* containing_file); + // Parse an "reserved" declaration. + bool ParseReserved(DescriptorProto* message, + const LocationRecorder& message_location); + bool ParseReservedNames(DescriptorProto* message, + const LocationRecorder& parent_location); + bool ParseReservedNumbers(DescriptorProto* message, + const LocationRecorder& parent_location); + // Parse an "extend" declaration. (See also comments for // ParseMessageField().) bool ParseExtend(RepeatedPtrField* extensions, diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc index 1684bebe..ddf34bfa 100644 --- a/src/google/protobuf/compiler/parser_unittest.cc +++ b/src/google/protobuf/compiler/parser_unittest.cc @@ -620,6 +620,36 @@ TEST_F(ParseMessageTest, NestedEnum) { "}"); } +TEST_F(ParseMessageTest, ReservedRange) { + ExpectParsesTo( + "message TestMessage {\n" + " required int32 foo = 1;\n" + " reserved 2, 15, 9 to 11, 3;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" + " reserved_range { start:2 end:3 }" + " reserved_range { start:15 end:16 }" + " reserved_range { start:9 end:12 }" + " reserved_range { start:3 end:4 }" + "}"); +} + +TEST_F(ParseMessageTest, ReservedNames) { + ExpectParsesTo( + "message TestMessage {\n" + " reserved \"foo\", \"bar\";\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " reserved_name: \"foo\"" + " reserved_name: \"bar\"" + "}"); +} + TEST_F(ParseMessageTest, ExtensionRange) { ExpectParsesTo( "message TestMessage {\n" @@ -715,19 +745,17 @@ TEST_F(ParseMessageTest, MultipleExtensionsOneExtendee) { " type_name:\"TestMessage\" extendee: \"Extendee1\" }"); } -TEST_F(ParseMessageTest, OptionalOptionalLabelProto3) { +TEST_F(ParseMessageTest, OptionalLabelProto3) { ExpectParsesTo( "syntax = \"proto3\";\n" "message TestMessage {\n" " int32 foo = 1;\n" - " optional int32 bar = 2;\n" "}\n", "syntax: \"proto3\" " "message_type {" " name: \"TestMessage\"" - " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 }" - " field { name:\"bar\" label:LABEL_OPTIONAL type:TYPE_INT32 number:2 } }"); + " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 } }"); } // =================================================================== @@ -1230,6 +1258,18 @@ TEST_F(ParseErrorTest, EofInAggregateValue) { "1:0: Unexpected end of stream while parsing aggregate value.\n"); } +TEST_F(ParseErrorTest, ExplicitOptionalLabelProto3) { + ExpectHasErrors( + "syntax = 'proto3';\n" + "message TestMessage {\n" + " optional int32 foo = 1;\n" + "}\n", + "2:11: Explicit 'optional' labels are disallowed in the Proto3 syntax. " + "To define 'optional' fields in Proto3, simply remove the 'optional' " + "label, as fields are 'optional' by default.\n"); +} + + // ------------------------------------------------------------------- // Enum errors @@ -1247,6 +1287,33 @@ TEST_F(ParseErrorTest, EnumValueMissingNumber) { "1:5: Missing numeric value for enum constant.\n"); } +// ------------------------------------------------------------------- +// Reserved field number errors + +TEST_F(ParseErrorTest, ReservedMaxNotAllowed) { + ExpectHasErrors( + "message Foo {\n" + " reserved 10 to max;\n" + "}\n", + "1:17: Expected integer.\n"); +} + +TEST_F(ParseErrorTest, ReservedMixNameAndNumber) { + ExpectHasErrors( + "message Foo {\n" + " reserved 10, \"foo\";\n" + "}\n", + "1:15: Expected field number range.\n"); +} + +TEST_F(ParseErrorTest, ReservedMissingQuotes) { + ExpectHasErrors( + "message Foo {\n" + " reserved foo;\n" + "}\n", + "1:11: Expected field name or number range.\n"); +} + // ------------------------------------------------------------------- // Service errors diff --git a/src/google/protobuf/compiler/plugin.cc b/src/google/protobuf/compiler/plugin.cc index ad501acf..cdcaffde 100644 --- a/src/google/protobuf/compiler/plugin.cc +++ b/src/google/protobuf/compiler/plugin.cc @@ -134,20 +134,34 @@ int PluginMain(int argc, char* argv[], const CodeGenerator* generator) { CodeGeneratorResponse response; GeneratorResponseContext context(&response, parsed_files); - for (int i = 0; i < parsed_files.size(); i++) { - const FileDescriptor* file = parsed_files[i]; - + if (generator->HasGenerateAll()) { string error; - bool succeeded = generator->Generate( - file, request.parameter(), &context, &error); + bool succeeded = generator->GenerateAll( + parsed_files, request.parameter(), &context, &error); if (!succeeded && error.empty()) { error = "Code generator returned false but provided no error " "description."; } if (!error.empty()) { - response.set_error(file->name() + ": " + error); - break; + response.set_error(error); + } + } else { + for (int i = 0; i < parsed_files.size(); i++) { + const FileDescriptor* file = parsed_files[i]; + + string error; + bool succeeded = generator->Generate( + file, request.parameter(), &context, &error); + + if (!succeeded && error.empty()) { + error = "Code generator returned false but provided no error " + "description."; + } + if (!error.empty()) { + response.set_error(file->name() + ": " + error); + break; + } } } diff --git a/src/google/protobuf/compiler/plugin.pb.cc b/src/google/protobuf/compiler/plugin.pb.cc index d78b715e..344ed450 100644 --- a/src/google/protobuf/compiler/plugin.pb.cc +++ b/src/google/protobuf/compiler/plugin.pb.cc @@ -179,7 +179,7 @@ const int CodeGeneratorRequest::kProtoFileFieldNumber; #endif // !_MSC_VER CodeGeneratorRequest::CodeGeneratorRequest() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.compiler.CodeGeneratorRequest) } @@ -300,12 +300,15 @@ bool CodeGeneratorRequest::MergePartialFromCodedStream( case 15: { if (tag == 122) { parse_proto_file: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_proto_file: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_proto_file())); } else { goto handle_unusual; } - if (input->ExpectTag(122)) goto parse_proto_file; + if (input->ExpectTag(122)) goto parse_loop_proto_file; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -445,9 +448,9 @@ int CodeGeneratorRequest::ByteSize() const { void CodeGeneratorRequest::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const CodeGeneratorRequest* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const CodeGeneratorRequest* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -513,10 +516,10 @@ void CodeGeneratorRequest::InternalSwap(CodeGeneratorRequest* other) { // CodeGeneratorRequest // repeated string file_to_generate = 1; - int CodeGeneratorRequest::file_to_generate_size() const { +int CodeGeneratorRequest::file_to_generate_size() const { return file_to_generate_.size(); } - void CodeGeneratorRequest::clear_file_to_generate() { +void CodeGeneratorRequest::clear_file_to_generate() { file_to_generate_.Clear(); } const ::std::string& CodeGeneratorRequest::file_to_generate(int index) const { @@ -567,16 +570,16 @@ CodeGeneratorRequest::mutable_file_to_generate() { } // optional string parameter = 2; - bool CodeGeneratorRequest::has_parameter() const { +bool CodeGeneratorRequest::has_parameter() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void CodeGeneratorRequest::set_has_parameter() { +void CodeGeneratorRequest::set_has_parameter() { _has_bits_[0] |= 0x00000002u; } - void CodeGeneratorRequest::clear_has_parameter() { +void CodeGeneratorRequest::clear_has_parameter() { _has_bits_[0] &= ~0x00000002u; } - void CodeGeneratorRequest::clear_parameter() { +void CodeGeneratorRequest::clear_parameter() { parameter_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_parameter(); } @@ -620,10 +623,10 @@ CodeGeneratorRequest::mutable_file_to_generate() { } // repeated .google.protobuf.FileDescriptorProto proto_file = 15; - int CodeGeneratorRequest::proto_file_size() const { +int CodeGeneratorRequest::proto_file_size() const { return proto_file_.size(); } - void CodeGeneratorRequest::clear_proto_file() { +void CodeGeneratorRequest::clear_proto_file() { proto_file_.Clear(); } const ::google::protobuf::FileDescriptorProto& CodeGeneratorRequest::proto_file(int index) const { @@ -660,7 +663,7 @@ const int CodeGeneratorResponse_File::kContentFieldNumber; #endif // !_MSC_VER CodeGeneratorResponse_File::CodeGeneratorResponse_File() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.compiler.CodeGeneratorResponse.File) } @@ -946,9 +949,9 @@ int CodeGeneratorResponse_File::ByteSize() const { void CodeGeneratorResponse_File::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const CodeGeneratorResponse_File* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const CodeGeneratorResponse_File* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1024,7 +1027,7 @@ const int CodeGeneratorResponse::kFileFieldNumber; #endif // !_MSC_VER CodeGeneratorResponse::CodeGeneratorResponse() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.compiler.CodeGeneratorResponse) } @@ -1124,12 +1127,15 @@ bool CodeGeneratorResponse::MergePartialFromCodedStream( case 15: { if (tag == 122) { parse_file: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_file: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_file())); } else { goto handle_unusual; } - if (input->ExpectTag(122)) goto parse_file; + if (input->ExpectTag(122)) goto parse_loop_file; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -1242,9 +1248,9 @@ int CodeGeneratorResponse::ByteSize() const { void CodeGeneratorResponse::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const CodeGeneratorResponse* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const CodeGeneratorResponse* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1307,16 +1313,16 @@ void CodeGeneratorResponse::InternalSwap(CodeGeneratorResponse* other) { // CodeGeneratorResponse_File // optional string name = 1; - bool CodeGeneratorResponse_File::has_name() const { +bool CodeGeneratorResponse_File::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void CodeGeneratorResponse_File::set_has_name() { +void CodeGeneratorResponse_File::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void CodeGeneratorResponse_File::clear_has_name() { +void CodeGeneratorResponse_File::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void CodeGeneratorResponse_File::clear_name() { +void CodeGeneratorResponse_File::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -1360,16 +1366,16 @@ void CodeGeneratorResponse::InternalSwap(CodeGeneratorResponse* other) { } // optional string insertion_point = 2; - bool CodeGeneratorResponse_File::has_insertion_point() const { +bool CodeGeneratorResponse_File::has_insertion_point() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void CodeGeneratorResponse_File::set_has_insertion_point() { +void CodeGeneratorResponse_File::set_has_insertion_point() { _has_bits_[0] |= 0x00000002u; } - void CodeGeneratorResponse_File::clear_has_insertion_point() { +void CodeGeneratorResponse_File::clear_has_insertion_point() { _has_bits_[0] &= ~0x00000002u; } - void CodeGeneratorResponse_File::clear_insertion_point() { +void CodeGeneratorResponse_File::clear_insertion_point() { insertion_point_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_insertion_point(); } @@ -1413,16 +1419,16 @@ void CodeGeneratorResponse::InternalSwap(CodeGeneratorResponse* other) { } // optional string content = 15; - bool CodeGeneratorResponse_File::has_content() const { +bool CodeGeneratorResponse_File::has_content() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void CodeGeneratorResponse_File::set_has_content() { +void CodeGeneratorResponse_File::set_has_content() { _has_bits_[0] |= 0x00000004u; } - void CodeGeneratorResponse_File::clear_has_content() { +void CodeGeneratorResponse_File::clear_has_content() { _has_bits_[0] &= ~0x00000004u; } - void CodeGeneratorResponse_File::clear_content() { +void CodeGeneratorResponse_File::clear_content() { content_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_content(); } @@ -1470,16 +1476,16 @@ void CodeGeneratorResponse::InternalSwap(CodeGeneratorResponse* other) { // CodeGeneratorResponse // optional string error = 1; - bool CodeGeneratorResponse::has_error() const { +bool CodeGeneratorResponse::has_error() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void CodeGeneratorResponse::set_has_error() { +void CodeGeneratorResponse::set_has_error() { _has_bits_[0] |= 0x00000001u; } - void CodeGeneratorResponse::clear_has_error() { +void CodeGeneratorResponse::clear_has_error() { _has_bits_[0] &= ~0x00000001u; } - void CodeGeneratorResponse::clear_error() { +void CodeGeneratorResponse::clear_error() { error_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_error(); } @@ -1523,10 +1529,10 @@ void CodeGeneratorResponse::InternalSwap(CodeGeneratorResponse* other) { } // repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15; - int CodeGeneratorResponse::file_size() const { +int CodeGeneratorResponse::file_size() const { return file_.size(); } - void CodeGeneratorResponse::clear_file() { +void CodeGeneratorResponse::clear_file() { file_.Clear(); } const ::google::protobuf::compiler::CodeGeneratorResponse_File& CodeGeneratorResponse::file(int index) const { diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h index ea48b8b5..6fcaea2e 100644 --- a/src/google/protobuf/compiler/plugin.pb.h +++ b/src/google/protobuf/compiler/plugin.pb.h @@ -796,6 +796,10 @@ CodeGeneratorResponse::mutable_file() { } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index bfdacd95..2855c377 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -1518,6 +1518,18 @@ Descriptor::FindExtensionRangeContainingNumber(int number) const { return NULL; } +const Descriptor::ReservedRange* +Descriptor::FindReservedRangeContainingNumber(int number) const { + // TODO(chrisn): Consider a non-linear search. + for (int i = 0; i < reserved_range_count(); i++) { + if (number >= reserved_range(i)->start && + number < reserved_range(i)->end) { + return reserved_range(i); + } + } + return NULL; +} + // ------------------------------------------------------------------- bool DescriptorPool::TryFindFileInFallbackDatabase(const string& name) const { @@ -1741,6 +1753,14 @@ void Descriptor::CopyTo(DescriptorProto* proto) const { for (int i = 0; i < extension_count(); i++) { extension(i)->CopyTo(proto->add_extension()); } + for (int i = 0; i < reserved_range_count(); i++) { + DescriptorProto::ReservedRange* range = proto->add_reserved_range(); + range->set_start(reserved_range(i)->start); + range->set_end(reserved_range(i)->end); + } + for (int i = 0; i < reserved_name_count(); i++) { + proto->add_reserved_name(reserved_name(i)); + } if (&options() != &MessageOptions::default_instance()) { proto->mutable_options()->CopyFrom(options()); @@ -2186,6 +2206,29 @@ void Descriptor::DebugString(int depth, string *contents, if (extension_count() > 0) strings::SubstituteAndAppend(contents, "$0 }\n", prefix); + if (reserved_range_count() > 0) { + strings::SubstituteAndAppend(contents, "$0 reserved ", prefix); + for (int i = 0; i < reserved_range_count(); i++) { + const Descriptor::ReservedRange* range = reserved_range(i); + if (range->end == range->start + 1) { + strings::SubstituteAndAppend(contents, "$0, ", range->start); + } else { + strings::SubstituteAndAppend(contents, "$0 to $1, ", + range->start, range->end - 1); + } + } + contents->replace(contents->size() - 2, 2, ";\n"); + } + + if (reserved_name_count() > 0) { + strings::SubstituteAndAppend(contents, "$0 reserved ", prefix); + for (int i = 0; i < reserved_name_count(); i++) { + strings::SubstituteAndAppend(contents, "\"$0\", ", + CEscape(reserved_name(i))); + } + contents->replace(contents->size() - 2, 2, ";\n"); + } + strings::SubstituteAndAppend(contents, "$0}\n", prefix); comment_printer.AddPostComment(contents); } @@ -2278,8 +2321,12 @@ void FieldDescriptor::DebugString(int depth, } if (type() == TYPE_GROUP) { - message_type()->DebugString(depth, contents, debug_string_options, - /* include_opening_clause */ false); + if (debug_string_options.elide_group_body) { + contents->append(" { ... };\n"); + } else { + message_type()->DebugString(depth, contents, debug_string_options, + /* include_opening_clause */ false); + } } else { contents->append(";\n"); } @@ -2308,12 +2355,16 @@ void OneofDescriptor::DebugString(int depth, string* contents, comment_printer(this, prefix, debug_string_options); comment_printer.AddPreComment(contents); strings::SubstituteAndAppend( - contents, "$0 oneof $1 {\n", prefix, name()); - for (int i = 0; i < field_count(); i++) { - field(i)->DebugString(depth, FieldDescriptor::OMIT_LABEL, contents, - debug_string_options); + contents, "$0 oneof $1 {", prefix, name()); + if (debug_string_options.elide_oneof_body) { + contents->append(" ... }\n"); + } else { + for (int i = 0; i < field_count(); i++) { + field(i)->DebugString(depth, FieldDescriptor::OMIT_LABEL, contents, + debug_string_options); + } + strings::SubstituteAndAppend(contents, "$0}\n", prefix); } - strings::SubstituteAndAppend(contents, "$0}\n", prefix); comment_printer.AddPostComment(contents); } @@ -2491,7 +2542,12 @@ bool FileDescriptor::GetSourceLocation(SourceLocation* out_location) const { } bool FieldDescriptor::is_packed() const { - return is_packable() && (options_ != NULL) && options_->packed(); + if (!is_packable()) return false; + if (file_->syntax() == FileDescriptor::SYNTAX_PROTO2) { + return (options_ != NULL) && options_->packed(); + } else { + return options_ == NULL || !options_->has_packed() || options_->packed(); + } } bool Descriptor::GetSourceLocation(SourceLocation* out_location) const { @@ -2604,7 +2660,7 @@ void MethodDescriptor::GetLocationPath(vector* output) const { namespace { // Represents an options message to interpret. Extension names in the option -// name are respolved relative to name_scope. element_name and orig_opt are +// name are resolved relative to name_scope. element_name and orig_opt are // used only for error reporting (since the parser records locations against // pointers in the original options, not the mutable copy). The Message must be // one of the Options messages in descriptor.proto. @@ -2830,6 +2886,9 @@ class DescriptorBuilder { void BuildExtensionRange(const DescriptorProto::ExtensionRange& proto, const Descriptor* parent, Descriptor::ExtensionRange* result); + void BuildReservedRange(const DescriptorProto::ReservedRange& proto, + const Descriptor* parent, + Descriptor::ReservedRange* result); void BuildOneof(const OneofDescriptorProto& proto, Descriptor* parent, OneofDescriptor* result); @@ -3920,6 +3979,17 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, BUILD_ARRAY(proto, result, enum_type , BuildEnum , result); BUILD_ARRAY(proto, result, extension_range, BuildExtensionRange, result); BUILD_ARRAY(proto, result, extension , BuildExtension , result); + BUILD_ARRAY(proto, result, reserved_range , BuildReservedRange , result); + + // Copy reserved names. + int reserved_name_count = proto.reserved_name_size(); + result->reserved_name_count_ = reserved_name_count; + result->reserved_names_ = + tables_->AllocateArray(reserved_name_count); + for (int i = 0; i < reserved_name_count; ++i) { + result->reserved_names_[i] = + tables_->AllocateString(proto.reserved_name(i)); + } // Copy options. if (!proto.has_options()) { @@ -3931,7 +4001,34 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, AddSymbol(result->full_name(), parent, result->name(), proto, Symbol(result)); - // Check that no fields have numbers in extension ranges. + for (int i = 0; i < proto.reserved_range_size(); i++) { + const DescriptorProto_ReservedRange& range1 = proto.reserved_range(i); + for (int j = i + 1; j < proto.reserved_range_size(); j++) { + const DescriptorProto_ReservedRange& range2 = proto.reserved_range(j); + if (range1.end() > range2.start() && range2.end() > range1.start()) { + AddError(result->full_name(), proto.reserved_range(i), + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Reserved range $0 to $1 overlaps with " + "already-defined range $2 to $3.", + range2.start(), range2.end() - 1, + range1.start(), range1.end() - 1)); + } + } + } + + hash_set reserved_name_set; + for (int i = 0; i < proto.reserved_name_size(); i++) { + const string& name = proto.reserved_name(i); + if (reserved_name_set.find(name) == reserved_name_set.end()) { + reserved_name_set.insert(name); + } else { + AddError(name, proto, DescriptorPool::ErrorCollector::NAME, + strings::Substitute( + "Field name \"$0\" is reserved multiple times.", + name)); + } + } + for (int i = 0; i < result->field_count(); i++) { const FieldDescriptor* field = result->field(i); for (int j = 0; j < result->extension_range_count(); j++) { @@ -3945,11 +4042,39 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, field->name(), field->number())); } } + for (int j = 0; j < result->reserved_range_count(); j++) { + const Descriptor::ReservedRange* range = result->reserved_range(j); + if (range->start <= field->number() && field->number() < range->end) { + AddError(field->full_name(), proto.reserved_range(j), + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute( + "Field \"$0\" uses reserved number $1.", + field->name(), field->number())); + } + } + if (reserved_name_set.find(field->name()) != reserved_name_set.end()) { + AddError(field->full_name(), proto.field(i), + DescriptorPool::ErrorCollector::NAME, + strings::Substitute( + "Field name \"$0\" is reserved.", field->name())); + } } - // Check that extension ranges don't overlap. + // Check that extension ranges don't overlap and don't include + // reserved field numbers. for (int i = 0; i < result->extension_range_count(); i++) { const Descriptor::ExtensionRange* range1 = result->extension_range(i); + for (int j = 0; j < result->reserved_range_count(); j++) { + const Descriptor::ReservedRange* range2 = result->reserved_range(j); + if (range1->end > range2->start && range2->end > range1->start) { + AddError(result->full_name(), proto.extension_range(j), + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Extension range $0 to $1 overlaps with " + "reserved range $2 to $3.", + range1->start, range1->end - 1, + range2->start, range2->end - 1)); + } + } for (int j = i + 1; j < result->extension_range_count(); j++) { const Descriptor::ExtensionRange* range2 = result->extension_range(j); if (range1->end > range2->start && range2->end > range1->start) { @@ -4262,6 +4387,19 @@ void DescriptorBuilder::BuildExtensionRange( } } +void DescriptorBuilder::BuildReservedRange( + const DescriptorProto::ReservedRange& proto, + const Descriptor* parent, + Descriptor::ReservedRange* result) { + result->start = proto.start(); + result->end = proto.end(); + if (result->start <= 0) { + AddError(parent->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + "Reserved numbers must be positive integers."); + } +} + void DescriptorBuilder::BuildOneof(const OneofDescriptorProto& proto, Descriptor* parent, OneofDescriptor* result) { @@ -4503,6 +4641,23 @@ void DescriptorBuilder::CrossLinkMessage( for (int i = 0; i < message->field_count(); i++) { const OneofDescriptor* oneof_decl = message->field(i)->containing_oneof(); if (oneof_decl != NULL) { + // Make sure fields belonging to the same oneof are defined consecutively. + // This enables optimizations in codegens and reflection libraries to + // skip fields in the oneof group, as only one of the field can be set. + // Note that field_count() returns how many fields in this oneof we have + // seen so far. field_count() > 0 guarantees that i > 0, so field(i-1) is + // safe. + if (oneof_decl->field_count() > 0 && + message->field(i - 1)->containing_oneof() != oneof_decl) { + AddWarning( + message->full_name() + "." + message->field(i - 1)->name(), + proto.field(i - 1), DescriptorPool::ErrorCollector::OTHER, + strings::Substitute( + "Fields in the same oneof must be defined consecutively. " + "\"$0\" cannot be defined before the completion of the " + "\"$1\" oneof definition.", + message->field(i - 1)->name(), oneof_decl->name())); + } // Must go through oneof_decls_ array to get a non-const version of the // OneofDescriptor. ++message->oneof_decls_[oneof_decl->index()].field_count_; @@ -4933,6 +5088,12 @@ void DescriptorBuilder::ValidateProto3Field( field->containing_type()->full_name() + "\" which is a proto3 message type."); } + bool allow_groups = false; + if (field->type() == FieldDescriptor::TYPE_GROUP && !allow_groups) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + "Groups are not supported in proto3 syntax."); + } } void DescriptorBuilder::ValidateProto3Enum( diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h index c0a48cea..ca87d634 100644 --- a/src/google/protobuf/descriptor.h +++ b/src/google/protobuf/descriptor.h @@ -135,10 +135,14 @@ struct DebugStringOptions { // example, the C++ code generation for fields in the proto compiler) rely on // DebugString() output being unobstructed by user comments. bool include_comments; + // If true, elide the braced body in the debug string. + bool elide_group_body; + bool elide_oneof_body; DebugStringOptions() - : include_comments(false) - {} + : include_comments(false), + elide_group_body(false), + elide_oneof_body(false) {} }; // Describes a type of protocol message, or a particular group within a @@ -298,6 +302,36 @@ class LIBPROTOBUF_EXPORT Descriptor { // this message type's scope. const FieldDescriptor* FindExtensionByCamelcaseName(const string& name) const; + // Reserved fields ------------------------------------------------- + + // A range of reserved field numbers. + struct ReservedRange { + int start; // inclusive + int end; // exclusive + }; + + // The number of reserved ranges in this message type. + int reserved_range_count() const; + // Gets an reserved range by index, where 0 <= index < + // reserved_range_count(). These are returned in the order they were defined + // in the .proto file. + const ReservedRange* reserved_range(int index) const; + + // Returns true if the number is in one of the reserved ranges. + bool IsReservedNumber(int number) const; + + // Returns NULL if no reserved range contains the given number. + const ReservedRange* FindReservedRangeContainingNumber(int number) const; + + // The number of reserved field names in this message type. + int reserved_name_count() const; + + // Gets a reserved name by index, where 0 <= index < reserved_name_count(). + const string& reserved_name(int index) const; + + // Returns true if the field name is reserved. + bool IsReservedName(const string& name) const; + // Source Location --------------------------------------------------- // Updates |*out_location| to the source location of the complete @@ -343,6 +377,10 @@ class LIBPROTOBUF_EXPORT Descriptor { ExtensionRange* extension_ranges_; int extension_count_; FieldDescriptor* extensions_; + int reserved_range_count_; + ReservedRange* reserved_ranges_; + int reserved_name_count_; + const string** reserved_names_; // IMPORTANT: If you add a new field, make sure to search for all instances // of Allocate() and AllocateArray() in descriptor.cc // and update them to initialize the field. @@ -1567,6 +1605,12 @@ PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, extension_range, const Descriptor::ExtensionRange*) PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, extension, const FieldDescriptor*) + +PROTOBUF_DEFINE_ACCESSOR(Descriptor, reserved_range_count, int) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, reserved_range, + const Descriptor::ReservedRange*) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, reserved_name_count, int) + PROTOBUF_DEFINE_OPTIONS_ACCESSOR(Descriptor, MessageOptions); PROTOBUF_DEFINE_ACCESSOR(Descriptor, is_placeholder, bool) @@ -1667,6 +1711,25 @@ inline bool Descriptor::IsExtensionNumber(int number) const { return FindExtensionRangeContainingNumber(number) != NULL; } +inline bool Descriptor::IsReservedNumber(int number) const { + return FindReservedRangeContainingNumber(number) != NULL; +} + +inline bool Descriptor::IsReservedName(const string& name) const { + for (int i = 0; i < reserved_name_count(); i++) { + if (name == reserved_name(i)) { + return true; + } + } + return false; +} + +// Can't use PROTOBUF_DEFINE_ARRAY_ACCESSOR because reserved_names_ is actually +// an array of pointers rather than the usual array of objects. +inline const string& Descriptor::reserved_name(int index) const { + return *reserved_names_[index]; +} + inline bool FieldDescriptor::is_required() const { return label() == LABEL_REQUIRED; } diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc index 8968f362..755c2617 100644 --- a/src/google/protobuf/descriptor.pb.cc +++ b/src/google/protobuf/descriptor.pb.cc @@ -33,6 +33,9 @@ const ::google::protobuf::internal::GeneratedMessageReflection* const ::google::protobuf::Descriptor* DescriptorProto_ExtensionRange_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* DescriptorProto_ExtensionRange_reflection_ = NULL; +const ::google::protobuf::Descriptor* DescriptorProto_ReservedRange_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + DescriptorProto_ReservedRange_reflection_ = NULL; const ::google::protobuf::Descriptor* FieldDescriptorProto_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* FieldDescriptorProto_reflection_ = NULL; @@ -64,6 +67,7 @@ const ::google::protobuf::Descriptor* FieldOptions_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* FieldOptions_reflection_ = NULL; const ::google::protobuf::EnumDescriptor* FieldOptions_CType_descriptor_ = NULL; +const ::google::protobuf::EnumDescriptor* FieldOptions_JSType_descriptor_ = NULL; const ::google::protobuf::Descriptor* EnumOptions_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* EnumOptions_reflection_ = NULL; @@ -140,7 +144,7 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, _internal_metadata_), -1); DescriptorProto_descriptor_ = file->message_type(2); - static const int DescriptorProto_offsets_[8] = { + static const int DescriptorProto_offsets_[10] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, name_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, field_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, extension_), @@ -149,6 +153,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, extension_range_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, oneof_decl_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, options_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, reserved_range_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, reserved_name_), }; DescriptorProto_reflection_ = ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( @@ -177,6 +183,22 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { sizeof(DescriptorProto_ExtensionRange), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ExtensionRange, _internal_metadata_), -1); + DescriptorProto_ReservedRange_descriptor_ = DescriptorProto_descriptor_->nested_type(1); + static const int DescriptorProto_ReservedRange_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ReservedRange, start_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ReservedRange, end_), + }; + DescriptorProto_ReservedRange_reflection_ = + ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( + DescriptorProto_ReservedRange_descriptor_, + DescriptorProto_ReservedRange::default_instance_, + DescriptorProto_ReservedRange_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ReservedRange, _has_bits_[0]), + -1, + -1, + sizeof(DescriptorProto_ReservedRange), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ReservedRange, _internal_metadata_), + -1); FieldDescriptorProto_descriptor_ = file->message_type(3); static const int FieldDescriptorProto_offsets_[9] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, name_), @@ -338,9 +360,10 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MessageOptions, _internal_metadata_), -1); FieldOptions_descriptor_ = file->message_type(11); - static const int FieldOptions_offsets_[6] = { + static const int FieldOptions_offsets_[7] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, ctype_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, packed_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, jstype_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, lazy_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, deprecated_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, weak_), @@ -358,6 +381,7 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, _internal_metadata_), -1); FieldOptions_CType_descriptor_ = FieldOptions_descriptor_->enum_type(0); + FieldOptions_JSType_descriptor_ = FieldOptions_descriptor_->enum_type(1); EnumOptions_descriptor_ = file->message_type(12); static const int EnumOptions_offsets_[3] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumOptions, allow_alias_), @@ -514,6 +538,8 @@ void protobuf_RegisterTypes(const ::std::string&) { DescriptorProto_descriptor_, &DescriptorProto::default_instance()); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( DescriptorProto_ExtensionRange_descriptor_, &DescriptorProto_ExtensionRange::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + DescriptorProto_ReservedRange_descriptor_, &DescriptorProto_ReservedRange::default_instance()); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( FieldDescriptorProto_descriptor_, &FieldDescriptorProto::default_instance()); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( @@ -561,6 +587,8 @@ void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto() { delete DescriptorProto_reflection_; delete DescriptorProto_ExtensionRange::default_instance_; delete DescriptorProto_ExtensionRange_reflection_; + delete DescriptorProto_ReservedRange::default_instance_; + delete DescriptorProto_ReservedRange_reflection_; delete FieldDescriptorProto::default_instance_; delete FieldDescriptorProto_reflection_; delete OneofDescriptorProto::default_instance_; @@ -619,7 +647,7 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { "\022-\n\007options\030\010 \001(\0132\034.google.protobuf.File" "Options\0229\n\020source_code_info\030\t \001(\0132\037.goog" "le.protobuf.SourceCodeInfo\022\016\n\006syntax\030\014 \001" - "(\t\"\344\003\n\017DescriptorProto\022\014\n\004name\030\001 \001(\t\0224\n\005" + "(\t\"\360\004\n\017DescriptorProto\022\014\n\004name\030\001 \001(\t\0224\n\005" "field\030\002 \003(\0132%.google.protobuf.FieldDescr" "iptorProto\0228\n\textension\030\006 \003(\0132%.google.p" "rotobuf.FieldDescriptorProto\0225\n\013nested_t" @@ -630,104 +658,111 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { "ExtensionRange\0229\n\noneof_decl\030\010 \003(\0132%.goo" "gle.protobuf.OneofDescriptorProto\0220\n\007opt" "ions\030\007 \001(\0132\037.google.protobuf.MessageOpti" - "ons\032,\n\016ExtensionRange\022\r\n\005start\030\001 \001(\005\022\013\n\003" - "end\030\002 \001(\005\"\251\005\n\024FieldDescriptorProto\022\014\n\004na" - "me\030\001 \001(\t\022\016\n\006number\030\003 \001(\005\022:\n\005label\030\004 \001(\0162" - "+.google.protobuf.FieldDescriptorProto.L" - "abel\0228\n\004type\030\005 \001(\0162*.google.protobuf.Fie" - "ldDescriptorProto.Type\022\021\n\ttype_name\030\006 \001(" - "\t\022\020\n\010extendee\030\002 \001(\t\022\025\n\rdefault_value\030\007 \001" - "(\t\022\023\n\013oneof_index\030\t \001(\005\022.\n\007options\030\010 \001(\013" - "2\035.google.protobuf.FieldOptions\"\266\002\n\004Type" - "\022\017\n\013TYPE_DOUBLE\020\001\022\016\n\nTYPE_FLOAT\020\002\022\016\n\nTYP" - "E_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_G" - "ROUP\020\n\022\020\n\014TYPE_MESSAGE\020\013\022\016\n\nTYPE_BYTES\020\014" - "\022\017\n\013TYPE_UINT32\020\r\022\r\n\tTYPE_ENUM\020\016\022\021\n\rTYPE" - "_SFIXED32\020\017\022\021\n\rTYPE_SFIXED64\020\020\022\017\n\013TYPE_S" - "INT32\020\021\022\017\n\013TYPE_SINT64\020\022\"C\n\005Label\022\022\n\016LAB" - "EL_OPTIONAL\020\001\022\022\n\016LABEL_REQUIRED\020\002\022\022\n\016LAB" - "EL_REPEATED\020\003\"$\n\024OneofDescriptorProto\022\014\n" - "\004name\030\001 \001(\t\"\214\001\n\023EnumDescriptorProto\022\014\n\004n" - "ame\030\001 \001(\t\0228\n\005value\030\002 \003(\0132).google.protob" - "uf.EnumValueDescriptorProto\022-\n\007options\030\003" - " \001(\0132\034.google.protobuf.EnumOptions\"l\n\030En" - "umValueDescriptorProto\022\014\n\004name\030\001 \001(\t\022\016\n\006" - "number\030\002 \001(\005\0222\n\007options\030\003 \001(\0132!.google.p" - "rotobuf.EnumValueOptions\"\220\001\n\026ServiceDesc" - "riptorProto\022\014\n\004name\030\001 \001(\t\0226\n\006method\030\002 \003(" - "\0132&.google.protobuf.MethodDescriptorProt" - "o\0220\n\007options\030\003 \001(\0132\037.google.protobuf.Ser" - "viceOptions\"\301\001\n\025MethodDescriptorProto\022\014\n" - "\004name\030\001 \001(\t\022\022\n\ninput_type\030\002 \001(\t\022\023\n\013outpu" - "t_type\030\003 \001(\t\022/\n\007options\030\004 \001(\0132\036.google.p" - "rotobuf.MethodOptions\022\037\n\020client_streamin" - "g\030\005 \001(\010:\005false\022\037\n\020server_streaming\030\006 \001(\010" - ":\005false\"\201\005\n\013FileOptions\022\024\n\014java_package\030" - "\001 \001(\t\022\034\n\024java_outer_classname\030\010 \001(\t\022\"\n\023j" - "ava_multiple_files\030\n \001(\010:\005false\022,\n\035java_" - "generate_equals_and_hash\030\024 \001(\010:\005false\022%\n" - "\026java_string_check_utf8\030\033 \001(\010:\005false\022F\n\014" - "optimize_for\030\t \001(\0162).google.protobuf.Fil" - "eOptions.OptimizeMode:\005SPEED\022\022\n\ngo_packa" - "ge\030\013 \001(\t\022\"\n\023cc_generic_services\030\020 \001(\010:\005f" - "alse\022$\n\025java_generic_services\030\021 \001(\010:\005fal" - "se\022\"\n\023py_generic_services\030\022 \001(\010:\005false\022\031" - "\n\ndeprecated\030\027 \001(\010:\005false\022\037\n\020cc_enable_a" - "renas\030\037 \001(\010:\005false\022\031\n\021objc_class_prefix\030" - "$ \001(\t\022\030\n\020csharp_namespace\030% \001(\t\022C\n\024unint" - "erpreted_option\030\347\007 \003(\0132$.google.protobuf" - ".UninterpretedOption\":\n\014OptimizeMode\022\t\n\005" - "SPEED\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\"\346\001\n\016MessageOptions\022&\n\027messag" - "e_set_wire_format\030\001 \001(\010:\005false\022.\n\037no_sta" - "ndard_descriptor_accessor\030\002 \001(\010:\005false\022\031" - "\n\ndeprecated\030\003 \001(\010:\005false\022\021\n\tmap_entry\030\007" - " \001(\010\022C\n\024uninterpreted_option\030\347\007 \003(\0132$.go" - "ogle.protobuf.UninterpretedOption*\t\010\350\007\020\200" - "\200\200\200\002\"\240\002\n\014FieldOptions\022:\n\005ctype\030\001 \001(\0162#.g" - "oogle.protobuf.FieldOptions.CType:\006STRIN" - "G\022\016\n\006packed\030\002 \001(\010\022\023\n\004lazy\030\005 \001(\010:\005false\022\031" - "\n\ndeprecated\030\003 \001(\010:\005false\022\023\n\004weak\030\n \001(\010:" - "\005false\022C\n\024uninterpreted_option\030\347\007 \003(\0132$." - "google.protobuf.UninterpretedOption\"/\n\005C" - "Type\022\n\n\006STRING\020\000\022\010\n\004CORD\020\001\022\020\n\014STRING_PIE" - "CE\020\002*\t\010\350\007\020\200\200\200\200\002\"\215\001\n\013EnumOptions\022\023\n\013allow" - "_alias\030\002 \001(\010\022\031\n\ndeprecated\030\003 \001(\010:\005false\022" - "C\n\024uninterpreted_option\030\347\007 \003(\0132$.google." - "protobuf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\"" - "}\n\020EnumValueOptions\022\031\n\ndeprecated\030\001 \001(\010:" - "\005false\022C\n\024uninterpreted_option\030\347\007 \003(\0132$." - "google.protobuf.UninterpretedOption*\t\010\350\007" - "\020\200\200\200\200\002\"{\n\016ServiceOptions\022\031\n\ndeprecated\030!" + "ons\022F\n\016reserved_range\030\t \003(\0132..google.pro" + "tobuf.DescriptorProto.ReservedRange\022\025\n\rr" + "eserved_name\030\n \003(\t\032,\n\016ExtensionRange\022\r\n\005" + "start\030\001 \001(\005\022\013\n\003end\030\002 \001(\005\032+\n\rReservedRang" + "e\022\r\n\005start\030\001 \001(\005\022\013\n\003end\030\002 \001(\005\"\251\005\n\024FieldD" + "escriptorProto\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.Fi" + "eldDescriptorProto.Label\0228\n\004type\030\005 \001(\0162*" + ".google.protobuf.FieldDescriptorProto.Ty" + "pe\022\021\n\ttype_name\030\006 \001(\t\022\020\n\010extendee\030\002 \001(\t\022" + "\025\n\rdefault_value\030\007 \001(\t\022\023\n\013oneof_index\030\t " + "\001(\005\022.\n\007options\030\010 \001(\0132\035.google.protobuf.F" + "ieldOptions\"\266\002\n\004Type\022\017\n\013TYPE_DOUBLE\020\001\022\016\n" + "\nTYPE_FLOAT\020\002\022\016\n\nTYPE_INT64\020\003\022\017\n\013TYPE_UI" + "NT64\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\013TYP" + "E_STRING\020\t\022\016\n\nTYPE_GROUP\020\n\022\020\n\014TYPE_MESSA" + "GE\020\013\022\016\n\nTYPE_BYTES\020\014\022\017\n\013TYPE_UINT32\020\r\022\r\n" + "\tTYPE_ENUM\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_SINT" + "64\020\022\"C\n\005Label\022\022\n\016LABEL_OPTIONAL\020\001\022\022\n\016LAB" + "EL_REQUIRED\020\002\022\022\n\016LABEL_REPEATED\020\003\"$\n\024One" + "ofDescriptorProto\022\014\n\004name\030\001 \001(\t\"\214\001\n\023Enum" + "DescriptorProto\022\014\n\004name\030\001 \001(\t\0228\n\005value\030\002" + " \003(\0132).google.protobuf.EnumValueDescript" + "orProto\022-\n\007options\030\003 \001(\0132\034.google.protob" + "uf.EnumOptions\"l\n\030EnumValueDescriptorPro" + "to\022\014\n\004name\030\001 \001(\t\022\016\n\006number\030\002 \001(\005\0222\n\007opti" + "ons\030\003 \001(\0132!.google.protobuf.EnumValueOpt" + "ions\"\220\001\n\026ServiceDescriptorProto\022\014\n\004name\030" + "\001 \001(\t\0226\n\006method\030\002 \003(\0132&.google.protobuf." + "MethodDescriptorProto\0220\n\007options\030\003 \001(\0132\037" + ".google.protobuf.ServiceOptions\"\301\001\n\025Meth" + "odDescriptorProto\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\007opti" + "ons\030\004 \001(\0132\036.google.protobuf.MethodOption" + "s\022\037\n\020client_streaming\030\005 \001(\010:\005false\022\037\n\020se" + "rver_streaming\030\006 \001(\010:\005false\"\201\005\n\013FileOpti" + "ons\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_equals_and_" + "hash\030\024 \001(\010:\005false\022%\n\026java_string_check_u" + "tf8\030\033 \001(\010:\005false\022F\n\014optimize_for\030\t \001(\0162)" + ".google.protobuf.FileOptions.OptimizeMod" + "e:\005SPEED\022\022\n\ngo_package\030\013 \001(\t\022\"\n\023cc_gener" + "ic_services\030\020 \001(\010:\005false\022$\n\025java_generic" + "_services\030\021 \001(\010:\005false\022\"\n\023py_generic_ser" + "vices\030\022 \001(\010:\005false\022\031\n\ndeprecated\030\027 \001(\010:\005" + "false\022\037\n\020cc_enable_arenas\030\037 \001(\010:\005false\022\031" + "\n\021objc_class_prefix\030$ \001(\t\022\030\n\020csharp_name" + "space\030% \001(\t\022C\n\024uninterpreted_option\030\347\007 \003" + "(\0132$.google.protobuf.UninterpretedOption" + "\":\n\014OptimizeMode\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\"\346\001\n\016Mess" + "ageOptions\022&\n\027message_set_wire_format\030\001 " + "\001(\010:\005false\022.\n\037no_standard_descriptor_acc" + "essor\030\002 \001(\010:\005false\022\031\n\ndeprecated\030\003 \001(\010:\005" + "false\022\021\n\tmap_entry\030\007 \001(\010\022C\n\024uninterprete" + "d_option\030\347\007 \003(\0132$.google.protobuf.Uninte" + "rpretedOption*\t\010\350\007\020\200\200\200\200\002\"\230\003\n\014FieldOption" + "s\022:\n\005ctype\030\001 \001(\0162#.google.protobuf.Field" + "Options.CType:\006STRING\022\016\n\006packed\030\002 \001(\010\022\?\n" + "\006jstype\030\006 \001(\0162$.google.protobuf.FieldOpt" + "ions.JSType:\tJS_NORMAL\022\023\n\004lazy\030\005 \001(\010:\005fa" + "lse\022\031\n\ndeprecated\030\003 \001(\010:\005false\022\023\n\004weak\030\n" " \001(\010:\005false\022C\n\024uninterpreted_option\030\347\007 \003" "(\0132$.google.protobuf.UninterpretedOption" - "*\t\010\350\007\020\200\200\200\200\002\"z\n\rMethodOptions\022\031\n\ndeprecat" - "ed\030! \001(\010:\005false\022C\n\024uninterpreted_option\030" - "\347\007 \003(\0132$.google.protobuf.UninterpretedOp" - "tion*\t\010\350\007\020\200\200\200\200\002\"\236\002\n\023UninterpretedOption\022" - ";\n\004name\030\002 \003(\0132-.google.protobuf.Uninterp" - "retedOption.NamePart\022\030\n\020identifier_value" - "\030\003 \001(\t\022\032\n\022positive_int_value\030\004 \001(\004\022\032\n\022ne" - "gative_int_value\030\005 \001(\003\022\024\n\014double_value\030\006" - " \001(\001\022\024\n\014string_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_extension\030\002 \002(\010\"\325\001\n\016SourceCodeI" - "nfo\022:\n\010location\030\001 \003(\0132(.google.protobuf." - "SourceCodeInfo.Location\032\206\001\n\010Location\022\020\n\004" - "path\030\001 \003(\005B\002\020\001\022\020\n\004span\030\002 \003(\005B\002\020\001\022\030\n\020lead" - "ing_comments\030\003 \001(\t\022\031\n\021trailing_comments\030" - "\004 \001(\t\022!\n\031leading_detached_comments\030\006 \003(\t" - "BY\n\023com.google.protobufB\020DescriptorProto" - "sH\001\242\002\003GPB\252\002\'Google.ProtocolBuffers.Descr" - "iptorProtos", 4691); + "\"/\n\005CType\022\n\n\006STRING\020\000\022\010\n\004CORD\020\001\022\020\n\014STRIN" + "G_PIECE\020\002\"5\n\006JSType\022\r\n\tJS_NORMAL\020\000\022\r\n\tJS" + "_STRING\020\001\022\r\n\tJS_NUMBER\020\002*\t\010\350\007\020\200\200\200\200\002\"\215\001\n\013" + "EnumOptions\022\023\n\013allow_alias\030\002 \001(\010\022\031\n\ndepr" + "ecated\030\003 \001(\010:\005false\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\020EnumValueOptions\022" + "\031\n\ndeprecated\030\001 \001(\010:\005false\022C\n\024uninterpre" + "ted_option\030\347\007 \003(\0132$.google.protobuf.Unin" + "terpretedOption*\t\010\350\007\020\200\200\200\200\002\"{\n\016ServiceOpt" + "ions\022\031\n\ndeprecated\030! \001(\010:\005false\022C\n\024unint" + "erpreted_option\030\347\007 \003(\0132$.google.protobuf" + ".UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\"z\n\rMetho" + "dOptions\022\031\n\ndeprecated\030! \001(\010:\005false\022C\n\024u" + "ninterpreted_option\030\347\007 \003(\0132$.google.prot" + "obuf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\"\236\002\n\023" + "UninterpretedOption\022;\n\004name\030\002 \003(\0132-.goog" + "le.protobuf.UninterpretedOption.NamePart" + "\022\030\n\020identifier_value\030\003 \001(\t\022\032\n\022positive_i" + "nt_value\030\004 \001(\004\022\032\n\022negative_int_value\030\005 \001" + "(\003\022\024\n\014double_value\030\006 \001(\001\022\024\n\014string_value" + "\030\007 \001(\014\022\027\n\017aggregate_value\030\010 \001(\t\0323\n\010NameP" + "art\022\021\n\tname_part\030\001 \002(\t\022\024\n\014is_extension\030\002" + " \002(\010\"\325\001\n\016SourceCodeInfo\022:\n\010location\030\001 \003(" + "\0132(.google.protobuf.SourceCodeInfo.Locat" + "ion\032\206\001\n\010Location\022\020\n\004path\030\001 \003(\005B\002\020\001\022\020\n\004sp" + "an\030\002 \003(\005B\002\020\001\022\030\n\020leading_comments\030\003 \001(\t\022\031" + "\n\021trailing_comments\030\004 \001(\t\022!\n\031leading_det" + "ached_comments\030\006 \003(\tBY\n\023com.google.proto" + "bufB\020DescriptorProtosH\001\242\002\003GPB\252\002\'Google.P" + "rotocolBuffers.DescriptorProtos", 4951); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/descriptor.proto", &protobuf_RegisterTypes); FileDescriptorSet::default_instance_ = new FileDescriptorSet(); FileDescriptorProto::default_instance_ = new FileDescriptorProto(); DescriptorProto::default_instance_ = new DescriptorProto(); DescriptorProto_ExtensionRange::default_instance_ = new DescriptorProto_ExtensionRange(); + DescriptorProto_ReservedRange::default_instance_ = new DescriptorProto_ReservedRange(); FieldDescriptorProto::default_instance_ = new FieldDescriptorProto(); OneofDescriptorProto::default_instance_ = new OneofDescriptorProto(); EnumDescriptorProto::default_instance_ = new EnumDescriptorProto(); @@ -749,6 +784,7 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { FileDescriptorProto::default_instance_->InitAsDefaultInstance(); DescriptorProto::default_instance_->InitAsDefaultInstance(); DescriptorProto_ExtensionRange::default_instance_->InitAsDefaultInstance(); + DescriptorProto_ReservedRange::default_instance_->InitAsDefaultInstance(); FieldDescriptorProto::default_instance_->InitAsDefaultInstance(); OneofDescriptorProto::default_instance_->InitAsDefaultInstance(); EnumDescriptorProto::default_instance_->InitAsDefaultInstance(); @@ -793,7 +829,7 @@ const int FileDescriptorSet::kFileFieldNumber; #endif // !_MSC_VER FileDescriptorSet::FileDescriptorSet() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FileDescriptorSet) } @@ -870,13 +906,15 @@ bool FileDescriptorSet::MergePartialFromCodedStream( // repeated .google.protobuf.FileDescriptorProto file = 1; case 1: { if (tag == 10) { - parse_file: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_file: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_file())); } else { goto handle_unusual; } - if (input->ExpectTag(10)) goto parse_file; + if (input->ExpectTag(10)) goto parse_loop_file; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -961,9 +999,9 @@ int FileDescriptorSet::ByteSize() const { void FileDescriptorSet::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FileDescriptorSet* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FileDescriptorSet* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1020,10 +1058,10 @@ void FileDescriptorSet::InternalSwap(FileDescriptorSet* other) { // FileDescriptorSet // repeated .google.protobuf.FileDescriptorProto file = 1; - int FileDescriptorSet::file_size() const { +int FileDescriptorSet::file_size() const { return file_.size(); } - void FileDescriptorSet::clear_file() { +void FileDescriptorSet::clear_file() { file_.Clear(); } const ::google::protobuf::FileDescriptorProto& FileDescriptorSet::file(int index) const { @@ -1069,7 +1107,7 @@ const int FileDescriptorProto::kSyntaxFieldNumber; #endif // !_MSC_VER FileDescriptorProto::FileDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FileDescriptorProto) } @@ -1237,54 +1275,63 @@ bool FileDescriptorProto::MergePartialFromCodedStream( case 4: { if (tag == 34) { parse_message_type: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_message_type: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_message_type())); } else { goto handle_unusual; } - if (input->ExpectTag(34)) goto parse_message_type; - if (input->ExpectTag(42)) goto parse_enum_type; + if (input->ExpectTag(34)) goto parse_loop_message_type; + if (input->ExpectTag(42)) goto parse_loop_enum_type; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; case 5: { if (tag == 42) { - parse_enum_type: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_enum_type: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_enum_type())); } else { goto handle_unusual; } - if (input->ExpectTag(42)) goto parse_enum_type; - if (input->ExpectTag(50)) goto parse_service; + if (input->ExpectTag(42)) goto parse_loop_enum_type; + if (input->ExpectTag(50)) goto parse_loop_service; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.ServiceDescriptorProto service = 6; case 6: { if (tag == 50) { - parse_service: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_service: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_service())); } else { goto handle_unusual; } - if (input->ExpectTag(50)) goto parse_service; - if (input->ExpectTag(58)) goto parse_extension; + if (input->ExpectTag(50)) goto parse_loop_service; + if (input->ExpectTag(58)) goto parse_loop_extension; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.FieldDescriptorProto extension = 7; case 7: { if (tag == 58) { - parse_extension: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_extension: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_extension())); } else { goto handle_unusual; } - if (input->ExpectTag(58)) goto parse_extension; + if (input->ExpectTag(58)) goto parse_loop_extension; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(66)) goto parse_options; break; } @@ -1712,9 +1759,9 @@ int FileDescriptorProto::ByteSize() const { void FileDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FileDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FileDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1816,16 +1863,16 @@ void FileDescriptorProto::InternalSwap(FileDescriptorProto* other) { // FileDescriptorProto // optional string name = 1; - bool FileDescriptorProto::has_name() const { +bool FileDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void FileDescriptorProto::set_has_name() { +void FileDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void FileDescriptorProto::clear_has_name() { +void FileDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void FileDescriptorProto::clear_name() { +void FileDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -1869,16 +1916,16 @@ void FileDescriptorProto::InternalSwap(FileDescriptorProto* other) { } // optional string package = 2; - bool FileDescriptorProto::has_package() const { +bool FileDescriptorProto::has_package() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void FileDescriptorProto::set_has_package() { +void FileDescriptorProto::set_has_package() { _has_bits_[0] |= 0x00000002u; } - void FileDescriptorProto::clear_has_package() { +void FileDescriptorProto::clear_has_package() { _has_bits_[0] &= ~0x00000002u; } - void FileDescriptorProto::clear_package() { +void FileDescriptorProto::clear_package() { package_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_package(); } @@ -1922,10 +1969,10 @@ void FileDescriptorProto::InternalSwap(FileDescriptorProto* other) { } // repeated string dependency = 3; - int FileDescriptorProto::dependency_size() const { +int FileDescriptorProto::dependency_size() const { return dependency_.size(); } - void FileDescriptorProto::clear_dependency() { +void FileDescriptorProto::clear_dependency() { dependency_.Clear(); } const ::std::string& FileDescriptorProto::dependency(int index) const { @@ -1976,10 +2023,10 @@ FileDescriptorProto::mutable_dependency() { } // repeated int32 public_dependency = 10; - int FileDescriptorProto::public_dependency_size() const { +int FileDescriptorProto::public_dependency_size() const { return public_dependency_.size(); } - void FileDescriptorProto::clear_public_dependency() { +void FileDescriptorProto::clear_public_dependency() { public_dependency_.Clear(); } ::google::protobuf::int32 FileDescriptorProto::public_dependency(int index) const { @@ -2006,10 +2053,10 @@ FileDescriptorProto::mutable_public_dependency() { } // repeated int32 weak_dependency = 11; - int FileDescriptorProto::weak_dependency_size() const { +int FileDescriptorProto::weak_dependency_size() const { return weak_dependency_.size(); } - void FileDescriptorProto::clear_weak_dependency() { +void FileDescriptorProto::clear_weak_dependency() { weak_dependency_.Clear(); } ::google::protobuf::int32 FileDescriptorProto::weak_dependency(int index) const { @@ -2036,10 +2083,10 @@ FileDescriptorProto::mutable_weak_dependency() { } // repeated .google.protobuf.DescriptorProto message_type = 4; - int FileDescriptorProto::message_type_size() const { +int FileDescriptorProto::message_type_size() const { return message_type_.size(); } - void FileDescriptorProto::clear_message_type() { +void FileDescriptorProto::clear_message_type() { message_type_.Clear(); } const ::google::protobuf::DescriptorProto& FileDescriptorProto::message_type(int index) const { @@ -2066,10 +2113,10 @@ FileDescriptorProto::mutable_message_type() { } // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; - int FileDescriptorProto::enum_type_size() const { +int FileDescriptorProto::enum_type_size() const { return enum_type_.size(); } - void FileDescriptorProto::clear_enum_type() { +void FileDescriptorProto::clear_enum_type() { enum_type_.Clear(); } const ::google::protobuf::EnumDescriptorProto& FileDescriptorProto::enum_type(int index) const { @@ -2096,10 +2143,10 @@ FileDescriptorProto::mutable_enum_type() { } // repeated .google.protobuf.ServiceDescriptorProto service = 6; - int FileDescriptorProto::service_size() const { +int FileDescriptorProto::service_size() const { return service_.size(); } - void FileDescriptorProto::clear_service() { +void FileDescriptorProto::clear_service() { service_.Clear(); } const ::google::protobuf::ServiceDescriptorProto& FileDescriptorProto::service(int index) const { @@ -2126,10 +2173,10 @@ FileDescriptorProto::mutable_service() { } // repeated .google.protobuf.FieldDescriptorProto extension = 7; - int FileDescriptorProto::extension_size() const { +int FileDescriptorProto::extension_size() const { return extension_.size(); } - void FileDescriptorProto::clear_extension() { +void FileDescriptorProto::clear_extension() { extension_.Clear(); } const ::google::protobuf::FieldDescriptorProto& FileDescriptorProto::extension(int index) const { @@ -2156,16 +2203,16 @@ FileDescriptorProto::mutable_extension() { } // optional .google.protobuf.FileOptions options = 8; - bool FileDescriptorProto::has_options() const { +bool FileDescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000200u) != 0; } - void FileDescriptorProto::set_has_options() { +void FileDescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000200u; } - void FileDescriptorProto::clear_has_options() { +void FileDescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000200u; } - void FileDescriptorProto::clear_options() { +void FileDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::FileOptions::Clear(); clear_has_options(); } @@ -2199,16 +2246,16 @@ FileDescriptorProto::mutable_extension() { } // optional .google.protobuf.SourceCodeInfo source_code_info = 9; - bool FileDescriptorProto::has_source_code_info() const { +bool FileDescriptorProto::has_source_code_info() const { return (_has_bits_[0] & 0x00000400u) != 0; } - void FileDescriptorProto::set_has_source_code_info() { +void FileDescriptorProto::set_has_source_code_info() { _has_bits_[0] |= 0x00000400u; } - void FileDescriptorProto::clear_has_source_code_info() { +void FileDescriptorProto::clear_has_source_code_info() { _has_bits_[0] &= ~0x00000400u; } - void FileDescriptorProto::clear_source_code_info() { +void FileDescriptorProto::clear_source_code_info() { if (source_code_info_ != NULL) source_code_info_->::google::protobuf::SourceCodeInfo::Clear(); clear_has_source_code_info(); } @@ -2242,16 +2289,16 @@ FileDescriptorProto::mutable_extension() { } // optional string syntax = 12; - bool FileDescriptorProto::has_syntax() const { +bool FileDescriptorProto::has_syntax() const { return (_has_bits_[0] & 0x00000800u) != 0; } - void FileDescriptorProto::set_has_syntax() { +void FileDescriptorProto::set_has_syntax() { _has_bits_[0] |= 0x00000800u; } - void FileDescriptorProto::clear_has_syntax() { +void FileDescriptorProto::clear_has_syntax() { _has_bits_[0] &= ~0x00000800u; } - void FileDescriptorProto::clear_syntax() { +void FileDescriptorProto::clear_syntax() { syntax_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_syntax(); } @@ -2304,7 +2351,7 @@ const int DescriptorProto_ExtensionRange::kEndFieldNumber; #endif // !_MSC_VER DescriptorProto_ExtensionRange::DescriptorProto_ExtensionRange() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.DescriptorProto.ExtensionRange) } @@ -2516,9 +2563,9 @@ int DescriptorProto_ExtensionRange::ByteSize() const { void DescriptorProto_ExtensionRange::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const DescriptorProto_ExtensionRange* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const DescriptorProto_ExtensionRange* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -2579,6 +2626,289 @@ void DescriptorProto_ExtensionRange::InternalSwap(DescriptorProto_ExtensionRange } +// ------------------------------------------------------------------- + +#ifndef _MSC_VER +const int DescriptorProto_ReservedRange::kStartFieldNumber; +const int DescriptorProto_ReservedRange::kEndFieldNumber; +#endif // !_MSC_VER + +DescriptorProto_ReservedRange::DescriptorProto_ReservedRange() + : ::google::protobuf::Message(), _internal_metadata_(NULL) { + SharedCtor(); + // @@protoc_insertion_point(constructor:google.protobuf.DescriptorProto.ReservedRange) +} + +void DescriptorProto_ReservedRange::InitAsDefaultInstance() { +} + +DescriptorProto_ReservedRange::DescriptorProto_ReservedRange(const DescriptorProto_ReservedRange& from) + : ::google::protobuf::Message(), + _internal_metadata_(NULL) { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:google.protobuf.DescriptorProto.ReservedRange) +} + +void DescriptorProto_ReservedRange::SharedCtor() { + _cached_size_ = 0; + start_ = 0; + end_ = 0; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +DescriptorProto_ReservedRange::~DescriptorProto_ReservedRange() { + // @@protoc_insertion_point(destructor:google.protobuf.DescriptorProto.ReservedRange) + SharedDtor(); +} + +void DescriptorProto_ReservedRange::SharedDtor() { + if (this != default_instance_) { + } +} + +void DescriptorProto_ReservedRange::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* DescriptorProto_ReservedRange::descriptor() { + protobuf_AssignDescriptorsOnce(); + return DescriptorProto_ReservedRange_descriptor_; +} + +const DescriptorProto_ReservedRange& DescriptorProto_ReservedRange::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return *default_instance_; +} + +DescriptorProto_ReservedRange* DescriptorProto_ReservedRange::default_instance_ = NULL; + +DescriptorProto_ReservedRange* DescriptorProto_ReservedRange::New(::google::protobuf::Arena* arena) const { + DescriptorProto_ReservedRange* n = new DescriptorProto_ReservedRange; + if (arena != NULL) { + arena->Own(n); + } + return n; +} + +void DescriptorProto_ReservedRange::Clear() { +#define ZR_HELPER_(f) reinterpret_cast(\ + &reinterpret_cast(16)->f) + +#define ZR_(first, last) do {\ + ::memset(&first, 0,\ + ZR_HELPER_(last) - ZR_HELPER_(first) + sizeof(last));\ +} while (0) + + ZR_(start_, end_); + +#undef ZR_HELPER_ +#undef ZR_ + + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (_internal_metadata_.have_unknown_fields()) { + mutable_unknown_fields()->Clear(); + } +} + +bool DescriptorProto_ReservedRange::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:google.protobuf.DescriptorProto.ReservedRange) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional int32 start = 1; + case 1: { + if (tag == 8) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &start_))); + set_has_start(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(16)) goto parse_end; + break; + } + + // optional int32 end = 2; + case 2: { + if (tag == 16) { + parse_end: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &end_))); + set_has_end(); + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:google.protobuf.DescriptorProto.ReservedRange) + return true; +failure: + // @@protoc_insertion_point(parse_failure:google.protobuf.DescriptorProto.ReservedRange) + return false; +#undef DO_ +} + +void DescriptorProto_ReservedRange::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:google.protobuf.DescriptorProto.ReservedRange) + // optional int32 start = 1; + if (has_start()) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->start(), output); + } + + // optional int32 end = 2; + if (has_end()) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->end(), output); + } + + if (_internal_metadata_.have_unknown_fields()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } + // @@protoc_insertion_point(serialize_end:google.protobuf.DescriptorProto.ReservedRange) +} + +::google::protobuf::uint8* DescriptorProto_ReservedRange::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.DescriptorProto.ReservedRange) + // optional int32 start = 1; + if (has_start()) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(1, this->start(), target); + } + + // optional int32 end = 2; + if (has_end()) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(2, this->end(), target); + } + + if (_internal_metadata_.have_unknown_fields()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.DescriptorProto.ReservedRange) + return target; +} + +int DescriptorProto_ReservedRange::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & 3) { + // optional int32 start = 1; + if (has_start()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->start()); + } + + // optional int32 end = 2; + if (has_end()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->end()); + } + + } + if (_internal_metadata_.have_unknown_fields()) { + 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 DescriptorProto_ReservedRange::MergeFrom(const ::google::protobuf::Message& from) { + if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); + const DescriptorProto_ReservedRange* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void DescriptorProto_ReservedRange::MergeFrom(const DescriptorProto_ReservedRange& from) { + if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_start()) { + set_start(from.start()); + } + if (from.has_end()) { + set_end(from.end()); + } + } + if (from._internal_metadata_.have_unknown_fields()) { + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); + } +} + +void DescriptorProto_ReservedRange::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void DescriptorProto_ReservedRange::CopyFrom(const DescriptorProto_ReservedRange& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool DescriptorProto_ReservedRange::IsInitialized() const { + + return true; +} + +void DescriptorProto_ReservedRange::Swap(DescriptorProto_ReservedRange* other) { + if (other == this) return; + InternalSwap(other); +} +void DescriptorProto_ReservedRange::InternalSwap(DescriptorProto_ReservedRange* other) { + std::swap(start_, other->start_); + std::swap(end_, other->end_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _internal_metadata_.Swap(&other->_internal_metadata_); + std::swap(_cached_size_, other->_cached_size_); +} + +::google::protobuf::Metadata DescriptorProto_ReservedRange::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = DescriptorProto_ReservedRange_descriptor_; + metadata.reflection = DescriptorProto_ReservedRange_reflection_; + return metadata; +} + + // ------------------------------------------------------------------- #ifndef _MSC_VER @@ -2590,10 +2920,12 @@ const int DescriptorProto::kEnumTypeFieldNumber; const int DescriptorProto::kExtensionRangeFieldNumber; const int DescriptorProto::kOneofDeclFieldNumber; const int DescriptorProto::kOptionsFieldNumber; +const int DescriptorProto::kReservedRangeFieldNumber; +const int DescriptorProto::kReservedNameFieldNumber; #endif // !_MSC_VER DescriptorProto::DescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.DescriptorProto) } @@ -2670,6 +3002,8 @@ void DescriptorProto::Clear() { enum_type_.Clear(); extension_range_.Clear(); oneof_decl_.Clear(); + reserved_range_.Clear(); + reserved_name_.Clear(); ::memset(_has_bits_, 0, sizeof(_has_bits_)); if (_internal_metadata_.have_unknown_fields()) { mutable_unknown_fields()->Clear(); @@ -2706,68 +3040,79 @@ bool DescriptorProto::MergePartialFromCodedStream( case 2: { if (tag == 18) { parse_field: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_field: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_field())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_field; - if (input->ExpectTag(26)) goto parse_nested_type; + if (input->ExpectTag(18)) goto parse_loop_field; + if (input->ExpectTag(26)) goto parse_loop_nested_type; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.DescriptorProto nested_type = 3; case 3: { if (tag == 26) { - parse_nested_type: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_nested_type: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_nested_type())); } else { goto handle_unusual; } - if (input->ExpectTag(26)) goto parse_nested_type; - if (input->ExpectTag(34)) goto parse_enum_type; + if (input->ExpectTag(26)) goto parse_loop_nested_type; + if (input->ExpectTag(34)) goto parse_loop_enum_type; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; case 4: { if (tag == 34) { - parse_enum_type: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_enum_type: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_enum_type())); } else { goto handle_unusual; } - if (input->ExpectTag(34)) goto parse_enum_type; - if (input->ExpectTag(42)) goto parse_extension_range; + if (input->ExpectTag(34)) goto parse_loop_enum_type; + if (input->ExpectTag(42)) goto parse_loop_extension_range; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; case 5: { if (tag == 42) { - parse_extension_range: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_extension_range: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_extension_range())); } else { goto handle_unusual; } - if (input->ExpectTag(42)) goto parse_extension_range; - if (input->ExpectTag(50)) goto parse_extension; + if (input->ExpectTag(42)) goto parse_loop_extension_range; + if (input->ExpectTag(50)) goto parse_loop_extension; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.FieldDescriptorProto extension = 6; case 6: { if (tag == 50) { - parse_extension: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_extension: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_extension())); } else { goto handle_unusual; } - if (input->ExpectTag(50)) goto parse_extension; + if (input->ExpectTag(50)) goto parse_loop_extension; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(58)) goto parse_options; break; } @@ -2789,12 +3134,50 @@ bool DescriptorProto::MergePartialFromCodedStream( case 8: { if (tag == 66) { parse_oneof_decl: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_oneof_decl: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_oneof_decl())); } else { goto handle_unusual; } - if (input->ExpectTag(66)) goto parse_oneof_decl; + if (input->ExpectTag(66)) goto parse_loop_oneof_decl; + if (input->ExpectTag(74)) goto parse_loop_reserved_range; + input->UnsafeDecrementRecursionDepth(); + break; + } + + // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; + case 9: { + if (tag == 74) { + DO_(input->IncrementRecursionDepth()); + parse_loop_reserved_range: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( + input, add_reserved_range())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(74)) goto parse_loop_reserved_range; + input->UnsafeDecrementRecursionDepth(); + if (input->ExpectTag(82)) goto parse_reserved_name; + break; + } + + // repeated string reserved_name = 10; + case 10: { + if (tag == 82) { + parse_reserved_name: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->add_reserved_name())); + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( + this->reserved_name(this->reserved_name_size() - 1).data(), + this->reserved_name(this->reserved_name_size() - 1).length(), + ::google::protobuf::internal::WireFormat::PARSE, + "google.protobuf.DescriptorProto.reserved_name"); + } else { + goto handle_unusual; + } + if (input->ExpectTag(82)) goto parse_reserved_name; if (input->ExpectAtEnd()) goto success; break; } @@ -2876,6 +3259,22 @@ void DescriptorProto::SerializeWithCachedSizes( 8, this->oneof_decl(i), output); } + // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; + for (unsigned int i = 0, n = this->reserved_range_size(); i < n; i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 9, this->reserved_range(i), output); + } + + // repeated string reserved_name = 10; + for (int i = 0; i < this->reserved_name_size(); i++) { + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( + this->reserved_name(i).data(), this->reserved_name(i).length(), + ::google::protobuf::internal::WireFormat::SERIALIZE, + "google.protobuf.DescriptorProto.reserved_name"); + ::google::protobuf::internal::WireFormatLite::WriteString( + 10, this->reserved_name(i), output); + } + if (_internal_metadata_.have_unknown_fields()) { ::google::protobuf::internal::WireFormat::SerializeUnknownFields( unknown_fields(), output); @@ -2946,6 +3345,23 @@ void DescriptorProto::SerializeWithCachedSizes( 8, this->oneof_decl(i), target); } + // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; + for (unsigned int i = 0, n = this->reserved_range_size(); i < n; i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 9, this->reserved_range(i), target); + } + + // repeated string reserved_name = 10; + for (int i = 0; i < this->reserved_name_size(); i++) { + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( + this->reserved_name(i).data(), this->reserved_name(i).length(), + ::google::protobuf::internal::WireFormat::SERIALIZE, + "google.protobuf.DescriptorProto.reserved_name"); + target = ::google::protobuf::internal::WireFormatLite:: + WriteStringToArray(10, this->reserved_name(i), target); + } + if (_internal_metadata_.have_unknown_fields()) { target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( unknown_fields(), target); @@ -3021,6 +3437,21 @@ int DescriptorProto::ByteSize() const { this->oneof_decl(i)); } + // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; + total_size += 1 * this->reserved_range_size(); + for (int i = 0; i < this->reserved_range_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->reserved_range(i)); + } + + // repeated string reserved_name = 10; + total_size += 1 * this->reserved_name_size(); + for (int i = 0; i < this->reserved_name_size(); i++) { + total_size += ::google::protobuf::internal::WireFormatLite::StringSize( + this->reserved_name(i)); + } + if (_internal_metadata_.have_unknown_fields()) { total_size += ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( @@ -3034,9 +3465,9 @@ int DescriptorProto::ByteSize() const { void DescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const DescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const DescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -3052,6 +3483,8 @@ void DescriptorProto::MergeFrom(const DescriptorProto& from) { enum_type_.MergeFrom(from.enum_type_); extension_range_.MergeFrom(from.extension_range_); oneof_decl_.MergeFrom(from.oneof_decl_); + reserved_range_.MergeFrom(from.reserved_range_); + reserved_name_.MergeFrom(from.reserved_name_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { if (from.has_name()) { set_has_name(); @@ -3103,6 +3536,8 @@ void DescriptorProto::InternalSwap(DescriptorProto* other) { extension_range_.UnsafeArenaSwap(&other->extension_range_); oneof_decl_.UnsafeArenaSwap(&other->oneof_decl_); std::swap(options_, other->options_); + reserved_range_.UnsafeArenaSwap(&other->reserved_range_); + reserved_name_.UnsafeArenaSwap(&other->reserved_name_); std::swap(_has_bits_[0], other->_has_bits_[0]); _internal_metadata_.Swap(&other->_internal_metadata_); std::swap(_cached_size_, other->_cached_size_); @@ -3120,16 +3555,16 @@ void DescriptorProto::InternalSwap(DescriptorProto* other) { // DescriptorProto_ExtensionRange // optional int32 start = 1; - bool DescriptorProto_ExtensionRange::has_start() const { +bool DescriptorProto_ExtensionRange::has_start() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void DescriptorProto_ExtensionRange::set_has_start() { +void DescriptorProto_ExtensionRange::set_has_start() { _has_bits_[0] |= 0x00000001u; } - void DescriptorProto_ExtensionRange::clear_has_start() { +void DescriptorProto_ExtensionRange::clear_has_start() { _has_bits_[0] &= ~0x00000001u; } - void DescriptorProto_ExtensionRange::clear_start() { +void DescriptorProto_ExtensionRange::clear_start() { start_ = 0; clear_has_start(); } @@ -3144,16 +3579,16 @@ void DescriptorProto::InternalSwap(DescriptorProto* other) { } // optional int32 end = 2; - bool DescriptorProto_ExtensionRange::has_end() const { +bool DescriptorProto_ExtensionRange::has_end() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void DescriptorProto_ExtensionRange::set_has_end() { +void DescriptorProto_ExtensionRange::set_has_end() { _has_bits_[0] |= 0x00000002u; } - void DescriptorProto_ExtensionRange::clear_has_end() { +void DescriptorProto_ExtensionRange::clear_has_end() { _has_bits_[0] &= ~0x00000002u; } - void DescriptorProto_ExtensionRange::clear_end() { +void DescriptorProto_ExtensionRange::clear_end() { end_ = 0; clear_has_end(); } @@ -3169,19 +3604,71 @@ void DescriptorProto::InternalSwap(DescriptorProto* other) { // ------------------------------------------------------------------- +// DescriptorProto_ReservedRange + +// optional int32 start = 1; +bool DescriptorProto_ReservedRange::has_start() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +void DescriptorProto_ReservedRange::set_has_start() { + _has_bits_[0] |= 0x00000001u; +} +void DescriptorProto_ReservedRange::clear_has_start() { + _has_bits_[0] &= ~0x00000001u; +} +void DescriptorProto_ReservedRange::clear_start() { + start_ = 0; + clear_has_start(); +} + ::google::protobuf::int32 DescriptorProto_ReservedRange::start() const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ReservedRange.start) + return start_; +} + void DescriptorProto_ReservedRange::set_start(::google::protobuf::int32 value) { + set_has_start(); + start_ = value; + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.ReservedRange.start) +} + +// optional int32 end = 2; +bool DescriptorProto_ReservedRange::has_end() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +void DescriptorProto_ReservedRange::set_has_end() { + _has_bits_[0] |= 0x00000002u; +} +void DescriptorProto_ReservedRange::clear_has_end() { + _has_bits_[0] &= ~0x00000002u; +} +void DescriptorProto_ReservedRange::clear_end() { + end_ = 0; + clear_has_end(); +} + ::google::protobuf::int32 DescriptorProto_ReservedRange::end() const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ReservedRange.end) + return end_; +} + void DescriptorProto_ReservedRange::set_end(::google::protobuf::int32 value) { + set_has_end(); + end_ = value; + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.ReservedRange.end) +} + +// ------------------------------------------------------------------- + // DescriptorProto // optional string name = 1; - bool DescriptorProto::has_name() const { +bool DescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void DescriptorProto::set_has_name() { +void DescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void DescriptorProto::clear_has_name() { +void DescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void DescriptorProto::clear_name() { +void DescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -3225,10 +3712,10 @@ void DescriptorProto::InternalSwap(DescriptorProto* other) { } // repeated .google.protobuf.FieldDescriptorProto field = 2; - int DescriptorProto::field_size() const { +int DescriptorProto::field_size() const { return field_.size(); } - void DescriptorProto::clear_field() { +void DescriptorProto::clear_field() { field_.Clear(); } const ::google::protobuf::FieldDescriptorProto& DescriptorProto::field(int index) const { @@ -3255,10 +3742,10 @@ DescriptorProto::mutable_field() { } // repeated .google.protobuf.FieldDescriptorProto extension = 6; - int DescriptorProto::extension_size() const { +int DescriptorProto::extension_size() const { return extension_.size(); } - void DescriptorProto::clear_extension() { +void DescriptorProto::clear_extension() { extension_.Clear(); } const ::google::protobuf::FieldDescriptorProto& DescriptorProto::extension(int index) const { @@ -3285,10 +3772,10 @@ DescriptorProto::mutable_extension() { } // repeated .google.protobuf.DescriptorProto nested_type = 3; - int DescriptorProto::nested_type_size() const { +int DescriptorProto::nested_type_size() const { return nested_type_.size(); } - void DescriptorProto::clear_nested_type() { +void DescriptorProto::clear_nested_type() { nested_type_.Clear(); } const ::google::protobuf::DescriptorProto& DescriptorProto::nested_type(int index) const { @@ -3315,10 +3802,10 @@ DescriptorProto::mutable_nested_type() { } // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; - int DescriptorProto::enum_type_size() const { +int DescriptorProto::enum_type_size() const { return enum_type_.size(); } - void DescriptorProto::clear_enum_type() { +void DescriptorProto::clear_enum_type() { enum_type_.Clear(); } const ::google::protobuf::EnumDescriptorProto& DescriptorProto::enum_type(int index) const { @@ -3345,10 +3832,10 @@ DescriptorProto::mutable_enum_type() { } // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; - int DescriptorProto::extension_range_size() const { +int DescriptorProto::extension_range_size() const { return extension_range_.size(); } - void DescriptorProto::clear_extension_range() { +void DescriptorProto::clear_extension_range() { extension_range_.Clear(); } const ::google::protobuf::DescriptorProto_ExtensionRange& DescriptorProto::extension_range(int index) const { @@ -3375,10 +3862,10 @@ DescriptorProto::mutable_extension_range() { } // repeated .google.protobuf.OneofDescriptorProto oneof_decl = 8; - int DescriptorProto::oneof_decl_size() const { +int DescriptorProto::oneof_decl_size() const { return oneof_decl_.size(); } - void DescriptorProto::clear_oneof_decl() { +void DescriptorProto::clear_oneof_decl() { oneof_decl_.Clear(); } const ::google::protobuf::OneofDescriptorProto& DescriptorProto::oneof_decl(int index) const { @@ -3405,16 +3892,16 @@ DescriptorProto::mutable_oneof_decl() { } // optional .google.protobuf.MessageOptions options = 7; - bool DescriptorProto::has_options() const { +bool DescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000080u) != 0; } - void DescriptorProto::set_has_options() { +void DescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000080u; } - void DescriptorProto::clear_has_options() { +void DescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000080u; } - void DescriptorProto::clear_options() { +void DescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::MessageOptions::Clear(); clear_has_options(); } @@ -3447,6 +3934,90 @@ DescriptorProto::mutable_oneof_decl() { // @@protoc_insertion_point(field_set_allocated:google.protobuf.DescriptorProto.options) } +// repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; +int DescriptorProto::reserved_range_size() const { + return reserved_range_.size(); +} +void DescriptorProto::clear_reserved_range() { + reserved_range_.Clear(); +} + const ::google::protobuf::DescriptorProto_ReservedRange& DescriptorProto::reserved_range(int index) const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_.Get(index); +} + ::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::mutable_reserved_range(int index) { + // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_.Mutable(index); +} + ::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::add_reserved_range() { + // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_.Add(); +} + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >& +DescriptorProto::reserved_range() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_; +} + ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >* +DescriptorProto::mutable_reserved_range() { + // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.reserved_range) + return &reserved_range_; +} + +// repeated string reserved_name = 10; +int DescriptorProto::reserved_name_size() const { + return reserved_name_.size(); +} +void DescriptorProto::clear_reserved_name() { + reserved_name_.Clear(); +} + const ::std::string& DescriptorProto::reserved_name(int index) const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.reserved_name) + return reserved_name_.Get(index); +} + ::std::string* DescriptorProto::mutable_reserved_name(int index) { + // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.reserved_name) + return reserved_name_.Mutable(index); +} + void DescriptorProto::set_reserved_name(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.reserved_name) + reserved_name_.Mutable(index)->assign(value); +} + void DescriptorProto::set_reserved_name(int index, const char* value) { + reserved_name_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:google.protobuf.DescriptorProto.reserved_name) +} + void DescriptorProto::set_reserved_name(int index, const char* value, size_t size) { + reserved_name_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:google.protobuf.DescriptorProto.reserved_name) +} + ::std::string* DescriptorProto::add_reserved_name() { + return reserved_name_.Add(); +} + void DescriptorProto::add_reserved_name(const ::std::string& value) { + reserved_name_.Add()->assign(value); + // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_name) +} + void DescriptorProto::add_reserved_name(const char* value) { + reserved_name_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:google.protobuf.DescriptorProto.reserved_name) +} + void DescriptorProto::add_reserved_name(const char* value, size_t size) { + reserved_name_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:google.protobuf.DescriptorProto.reserved_name) +} + const ::google::protobuf::RepeatedPtrField< ::std::string>& +DescriptorProto::reserved_name() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_name) + return reserved_name_; +} + ::google::protobuf::RepeatedPtrField< ::std::string>* +DescriptorProto::mutable_reserved_name() { + // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.reserved_name) + return &reserved_name_; +} + #endif // PROTOBUF_INLINE_NOT_IN_HEADERS // =================================================================== @@ -3540,7 +4111,7 @@ const int FieldDescriptorProto::kOptionsFieldNumber; #endif // !_MSC_VER FieldDescriptorProto::FieldDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FieldDescriptorProto) } @@ -4063,9 +4634,9 @@ int FieldDescriptorProto::ByteSize() const { void FieldDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FieldDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FieldDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -4166,16 +4737,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { // FieldDescriptorProto // optional string name = 1; - bool FieldDescriptorProto::has_name() const { +bool FieldDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void FieldDescriptorProto::set_has_name() { +void FieldDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void FieldDescriptorProto::clear_has_name() { +void FieldDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void FieldDescriptorProto::clear_name() { +void FieldDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -4219,16 +4790,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional int32 number = 3; - bool FieldDescriptorProto::has_number() const { +bool FieldDescriptorProto::has_number() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void FieldDescriptorProto::set_has_number() { +void FieldDescriptorProto::set_has_number() { _has_bits_[0] |= 0x00000002u; } - void FieldDescriptorProto::clear_has_number() { +void FieldDescriptorProto::clear_has_number() { _has_bits_[0] &= ~0x00000002u; } - void FieldDescriptorProto::clear_number() { +void FieldDescriptorProto::clear_number() { number_ = 0; clear_has_number(); } @@ -4243,16 +4814,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional .google.protobuf.FieldDescriptorProto.Label label = 4; - bool FieldDescriptorProto::has_label() const { +bool FieldDescriptorProto::has_label() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void FieldDescriptorProto::set_has_label() { +void FieldDescriptorProto::set_has_label() { _has_bits_[0] |= 0x00000004u; } - void FieldDescriptorProto::clear_has_label() { +void FieldDescriptorProto::clear_has_label() { _has_bits_[0] &= ~0x00000004u; } - void FieldDescriptorProto::clear_label() { +void FieldDescriptorProto::clear_label() { label_ = 1; clear_has_label(); } @@ -4268,16 +4839,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional .google.protobuf.FieldDescriptorProto.Type type = 5; - bool FieldDescriptorProto::has_type() const { +bool FieldDescriptorProto::has_type() const { return (_has_bits_[0] & 0x00000008u) != 0; } - void FieldDescriptorProto::set_has_type() { +void FieldDescriptorProto::set_has_type() { _has_bits_[0] |= 0x00000008u; } - void FieldDescriptorProto::clear_has_type() { +void FieldDescriptorProto::clear_has_type() { _has_bits_[0] &= ~0x00000008u; } - void FieldDescriptorProto::clear_type() { +void FieldDescriptorProto::clear_type() { type_ = 1; clear_has_type(); } @@ -4293,16 +4864,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional string type_name = 6; - bool FieldDescriptorProto::has_type_name() const { +bool FieldDescriptorProto::has_type_name() const { return (_has_bits_[0] & 0x00000010u) != 0; } - void FieldDescriptorProto::set_has_type_name() { +void FieldDescriptorProto::set_has_type_name() { _has_bits_[0] |= 0x00000010u; } - void FieldDescriptorProto::clear_has_type_name() { +void FieldDescriptorProto::clear_has_type_name() { _has_bits_[0] &= ~0x00000010u; } - void FieldDescriptorProto::clear_type_name() { +void FieldDescriptorProto::clear_type_name() { type_name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_type_name(); } @@ -4346,16 +4917,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional string extendee = 2; - bool FieldDescriptorProto::has_extendee() const { +bool FieldDescriptorProto::has_extendee() const { return (_has_bits_[0] & 0x00000020u) != 0; } - void FieldDescriptorProto::set_has_extendee() { +void FieldDescriptorProto::set_has_extendee() { _has_bits_[0] |= 0x00000020u; } - void FieldDescriptorProto::clear_has_extendee() { +void FieldDescriptorProto::clear_has_extendee() { _has_bits_[0] &= ~0x00000020u; } - void FieldDescriptorProto::clear_extendee() { +void FieldDescriptorProto::clear_extendee() { extendee_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_extendee(); } @@ -4399,16 +4970,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional string default_value = 7; - bool FieldDescriptorProto::has_default_value() const { +bool FieldDescriptorProto::has_default_value() const { return (_has_bits_[0] & 0x00000040u) != 0; } - void FieldDescriptorProto::set_has_default_value() { +void FieldDescriptorProto::set_has_default_value() { _has_bits_[0] |= 0x00000040u; } - void FieldDescriptorProto::clear_has_default_value() { +void FieldDescriptorProto::clear_has_default_value() { _has_bits_[0] &= ~0x00000040u; } - void FieldDescriptorProto::clear_default_value() { +void FieldDescriptorProto::clear_default_value() { default_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_default_value(); } @@ -4452,16 +5023,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional int32 oneof_index = 9; - bool FieldDescriptorProto::has_oneof_index() const { +bool FieldDescriptorProto::has_oneof_index() const { return (_has_bits_[0] & 0x00000080u) != 0; } - void FieldDescriptorProto::set_has_oneof_index() { +void FieldDescriptorProto::set_has_oneof_index() { _has_bits_[0] |= 0x00000080u; } - void FieldDescriptorProto::clear_has_oneof_index() { +void FieldDescriptorProto::clear_has_oneof_index() { _has_bits_[0] &= ~0x00000080u; } - void FieldDescriptorProto::clear_oneof_index() { +void FieldDescriptorProto::clear_oneof_index() { oneof_index_ = 0; clear_has_oneof_index(); } @@ -4476,16 +5047,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional .google.protobuf.FieldOptions options = 8; - bool FieldDescriptorProto::has_options() const { +bool FieldDescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000100u) != 0; } - void FieldDescriptorProto::set_has_options() { +void FieldDescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000100u; } - void FieldDescriptorProto::clear_has_options() { +void FieldDescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000100u; } - void FieldDescriptorProto::clear_options() { +void FieldDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::FieldOptions::Clear(); clear_has_options(); } @@ -4527,7 +5098,7 @@ const int OneofDescriptorProto::kNameFieldNumber; #endif // !_MSC_VER OneofDescriptorProto::OneofDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.OneofDescriptorProto) } @@ -4709,9 +5280,9 @@ int OneofDescriptorProto::ByteSize() const { void OneofDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const OneofDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const OneofDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -4772,16 +5343,16 @@ void OneofDescriptorProto::InternalSwap(OneofDescriptorProto* other) { // OneofDescriptorProto // optional string name = 1; - bool OneofDescriptorProto::has_name() const { +bool OneofDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void OneofDescriptorProto::set_has_name() { +void OneofDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void OneofDescriptorProto::clear_has_name() { +void OneofDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void OneofDescriptorProto::clear_name() { +void OneofDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -4835,7 +5406,7 @@ const int EnumDescriptorProto::kOptionsFieldNumber; #endif // !_MSC_VER EnumDescriptorProto::EnumDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.EnumDescriptorProto) } @@ -4943,12 +5514,15 @@ bool EnumDescriptorProto::MergePartialFromCodedStream( case 2: { if (tag == 18) { parse_value: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_value: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_value())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_value; + if (input->ExpectTag(18)) goto parse_loop_value; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(26)) goto parse_options; break; } @@ -5096,9 +5670,9 @@ int EnumDescriptorProto::ByteSize() const { void EnumDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const EnumDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const EnumDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -5169,16 +5743,16 @@ void EnumDescriptorProto::InternalSwap(EnumDescriptorProto* other) { // EnumDescriptorProto // optional string name = 1; - bool EnumDescriptorProto::has_name() const { +bool EnumDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void EnumDescriptorProto::set_has_name() { +void EnumDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void EnumDescriptorProto::clear_has_name() { +void EnumDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void EnumDescriptorProto::clear_name() { +void EnumDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -5222,10 +5796,10 @@ void EnumDescriptorProto::InternalSwap(EnumDescriptorProto* other) { } // repeated .google.protobuf.EnumValueDescriptorProto value = 2; - int EnumDescriptorProto::value_size() const { +int EnumDescriptorProto::value_size() const { return value_.size(); } - void EnumDescriptorProto::clear_value() { +void EnumDescriptorProto::clear_value() { value_.Clear(); } const ::google::protobuf::EnumValueDescriptorProto& EnumDescriptorProto::value(int index) const { @@ -5252,16 +5826,16 @@ EnumDescriptorProto::mutable_value() { } // optional .google.protobuf.EnumOptions options = 3; - bool EnumDescriptorProto::has_options() const { +bool EnumDescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void EnumDescriptorProto::set_has_options() { +void EnumDescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000004u; } - void EnumDescriptorProto::clear_has_options() { +void EnumDescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000004u; } - void EnumDescriptorProto::clear_options() { +void EnumDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::EnumOptions::Clear(); clear_has_options(); } @@ -5305,7 +5879,7 @@ const int EnumValueDescriptorProto::kOptionsFieldNumber; #endif // !_MSC_VER EnumValueDescriptorProto::EnumValueDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.EnumValueDescriptorProto) } @@ -5564,9 +6138,9 @@ int EnumValueDescriptorProto::ByteSize() const { void EnumValueDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const EnumValueDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const EnumValueDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -5638,16 +6212,16 @@ void EnumValueDescriptorProto::InternalSwap(EnumValueDescriptorProto* other) { // EnumValueDescriptorProto // optional string name = 1; - bool EnumValueDescriptorProto::has_name() const { +bool EnumValueDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void EnumValueDescriptorProto::set_has_name() { +void EnumValueDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void EnumValueDescriptorProto::clear_has_name() { +void EnumValueDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void EnumValueDescriptorProto::clear_name() { +void EnumValueDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -5691,16 +6265,16 @@ void EnumValueDescriptorProto::InternalSwap(EnumValueDescriptorProto* other) { } // optional int32 number = 2; - bool EnumValueDescriptorProto::has_number() const { +bool EnumValueDescriptorProto::has_number() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void EnumValueDescriptorProto::set_has_number() { +void EnumValueDescriptorProto::set_has_number() { _has_bits_[0] |= 0x00000002u; } - void EnumValueDescriptorProto::clear_has_number() { +void EnumValueDescriptorProto::clear_has_number() { _has_bits_[0] &= ~0x00000002u; } - void EnumValueDescriptorProto::clear_number() { +void EnumValueDescriptorProto::clear_number() { number_ = 0; clear_has_number(); } @@ -5715,16 +6289,16 @@ void EnumValueDescriptorProto::InternalSwap(EnumValueDescriptorProto* other) { } // optional .google.protobuf.EnumValueOptions options = 3; - bool EnumValueDescriptorProto::has_options() const { +bool EnumValueDescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void EnumValueDescriptorProto::set_has_options() { +void EnumValueDescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000004u; } - void EnumValueDescriptorProto::clear_has_options() { +void EnumValueDescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000004u; } - void EnumValueDescriptorProto::clear_options() { +void EnumValueDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::EnumValueOptions::Clear(); clear_has_options(); } @@ -5768,7 +6342,7 @@ const int ServiceDescriptorProto::kOptionsFieldNumber; #endif // !_MSC_VER ServiceDescriptorProto::ServiceDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.ServiceDescriptorProto) } @@ -5876,12 +6450,15 @@ bool ServiceDescriptorProto::MergePartialFromCodedStream( case 2: { if (tag == 18) { parse_method: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_method: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_method())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_method; + if (input->ExpectTag(18)) goto parse_loop_method; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(26)) goto parse_options; break; } @@ -6029,9 +6606,9 @@ int ServiceDescriptorProto::ByteSize() const { void ServiceDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const ServiceDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const ServiceDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -6102,16 +6679,16 @@ void ServiceDescriptorProto::InternalSwap(ServiceDescriptorProto* other) { // ServiceDescriptorProto // optional string name = 1; - bool ServiceDescriptorProto::has_name() const { +bool ServiceDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void ServiceDescriptorProto::set_has_name() { +void ServiceDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void ServiceDescriptorProto::clear_has_name() { +void ServiceDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void ServiceDescriptorProto::clear_name() { +void ServiceDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -6155,10 +6732,10 @@ void ServiceDescriptorProto::InternalSwap(ServiceDescriptorProto* other) { } // repeated .google.protobuf.MethodDescriptorProto method = 2; - int ServiceDescriptorProto::method_size() const { +int ServiceDescriptorProto::method_size() const { return method_.size(); } - void ServiceDescriptorProto::clear_method() { +void ServiceDescriptorProto::clear_method() { method_.Clear(); } const ::google::protobuf::MethodDescriptorProto& ServiceDescriptorProto::method(int index) const { @@ -6185,16 +6762,16 @@ ServiceDescriptorProto::mutable_method() { } // optional .google.protobuf.ServiceOptions options = 3; - bool ServiceDescriptorProto::has_options() const { +bool ServiceDescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void ServiceDescriptorProto::set_has_options() { +void ServiceDescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000004u; } - void ServiceDescriptorProto::clear_has_options() { +void ServiceDescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000004u; } - void ServiceDescriptorProto::clear_options() { +void ServiceDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::ServiceOptions::Clear(); clear_has_options(); } @@ -6241,7 +6818,7 @@ const int MethodDescriptorProto::kServerStreamingFieldNumber; #endif // !_MSC_VER MethodDescriptorProto::MethodDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.MethodDescriptorProto) } @@ -6641,9 +7218,9 @@ int MethodDescriptorProto::ByteSize() const { void MethodDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const MethodDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const MethodDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -6729,16 +7306,16 @@ void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) { // MethodDescriptorProto // optional string name = 1; - bool MethodDescriptorProto::has_name() const { +bool MethodDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void MethodDescriptorProto::set_has_name() { +void MethodDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void MethodDescriptorProto::clear_has_name() { +void MethodDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void MethodDescriptorProto::clear_name() { +void MethodDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -6782,16 +7359,16 @@ void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) { } // optional string input_type = 2; - bool MethodDescriptorProto::has_input_type() const { +bool MethodDescriptorProto::has_input_type() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void MethodDescriptorProto::set_has_input_type() { +void MethodDescriptorProto::set_has_input_type() { _has_bits_[0] |= 0x00000002u; } - void MethodDescriptorProto::clear_has_input_type() { +void MethodDescriptorProto::clear_has_input_type() { _has_bits_[0] &= ~0x00000002u; } - void MethodDescriptorProto::clear_input_type() { +void MethodDescriptorProto::clear_input_type() { input_type_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_input_type(); } @@ -6835,16 +7412,16 @@ void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) { } // optional string output_type = 3; - bool MethodDescriptorProto::has_output_type() const { +bool MethodDescriptorProto::has_output_type() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void MethodDescriptorProto::set_has_output_type() { +void MethodDescriptorProto::set_has_output_type() { _has_bits_[0] |= 0x00000004u; } - void MethodDescriptorProto::clear_has_output_type() { +void MethodDescriptorProto::clear_has_output_type() { _has_bits_[0] &= ~0x00000004u; } - void MethodDescriptorProto::clear_output_type() { +void MethodDescriptorProto::clear_output_type() { output_type_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_output_type(); } @@ -6888,16 +7465,16 @@ void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) { } // optional .google.protobuf.MethodOptions options = 4; - bool MethodDescriptorProto::has_options() const { +bool MethodDescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000008u) != 0; } - void MethodDescriptorProto::set_has_options() { +void MethodDescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000008u; } - void MethodDescriptorProto::clear_has_options() { +void MethodDescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000008u; } - void MethodDescriptorProto::clear_options() { +void MethodDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::MethodOptions::Clear(); clear_has_options(); } @@ -6931,16 +7508,16 @@ void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) { } // optional bool client_streaming = 5 [default = false]; - bool MethodDescriptorProto::has_client_streaming() const { +bool MethodDescriptorProto::has_client_streaming() const { return (_has_bits_[0] & 0x00000010u) != 0; } - void MethodDescriptorProto::set_has_client_streaming() { +void MethodDescriptorProto::set_has_client_streaming() { _has_bits_[0] |= 0x00000010u; } - void MethodDescriptorProto::clear_has_client_streaming() { +void MethodDescriptorProto::clear_has_client_streaming() { _has_bits_[0] &= ~0x00000010u; } - void MethodDescriptorProto::clear_client_streaming() { +void MethodDescriptorProto::clear_client_streaming() { client_streaming_ = false; clear_has_client_streaming(); } @@ -6955,16 +7532,16 @@ void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) { } // optional bool server_streaming = 6 [default = false]; - bool MethodDescriptorProto::has_server_streaming() const { +bool MethodDescriptorProto::has_server_streaming() const { return (_has_bits_[0] & 0x00000020u) != 0; } - void MethodDescriptorProto::set_has_server_streaming() { +void MethodDescriptorProto::set_has_server_streaming() { _has_bits_[0] |= 0x00000020u; } - void MethodDescriptorProto::clear_has_server_streaming() { +void MethodDescriptorProto::clear_has_server_streaming() { _has_bits_[0] &= ~0x00000020u; } - void MethodDescriptorProto::clear_server_streaming() { +void MethodDescriptorProto::clear_server_streaming() { server_streaming_ = false; clear_has_server_streaming(); } @@ -7024,7 +7601,7 @@ const int FileOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER FileOptions::FileOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FileOptions) } @@ -7381,12 +7958,15 @@ bool FileOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -7768,9 +8348,9 @@ int FileOptions::ByteSize() const { void FileOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FileOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FileOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -7895,16 +8475,16 @@ void FileOptions::InternalSwap(FileOptions* other) { // FileOptions // optional string java_package = 1; - bool FileOptions::has_java_package() const { +bool FileOptions::has_java_package() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void FileOptions::set_has_java_package() { +void FileOptions::set_has_java_package() { _has_bits_[0] |= 0x00000001u; } - void FileOptions::clear_has_java_package() { +void FileOptions::clear_has_java_package() { _has_bits_[0] &= ~0x00000001u; } - void FileOptions::clear_java_package() { +void FileOptions::clear_java_package() { java_package_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_java_package(); } @@ -7948,16 +8528,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional string java_outer_classname = 8; - bool FileOptions::has_java_outer_classname() const { +bool FileOptions::has_java_outer_classname() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void FileOptions::set_has_java_outer_classname() { +void FileOptions::set_has_java_outer_classname() { _has_bits_[0] |= 0x00000002u; } - void FileOptions::clear_has_java_outer_classname() { +void FileOptions::clear_has_java_outer_classname() { _has_bits_[0] &= ~0x00000002u; } - void FileOptions::clear_java_outer_classname() { +void FileOptions::clear_java_outer_classname() { java_outer_classname_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_java_outer_classname(); } @@ -8001,16 +8581,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool java_multiple_files = 10 [default = false]; - bool FileOptions::has_java_multiple_files() const { +bool FileOptions::has_java_multiple_files() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void FileOptions::set_has_java_multiple_files() { +void FileOptions::set_has_java_multiple_files() { _has_bits_[0] |= 0x00000004u; } - void FileOptions::clear_has_java_multiple_files() { +void FileOptions::clear_has_java_multiple_files() { _has_bits_[0] &= ~0x00000004u; } - void FileOptions::clear_java_multiple_files() { +void FileOptions::clear_java_multiple_files() { java_multiple_files_ = false; clear_has_java_multiple_files(); } @@ -8025,16 +8605,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool java_generate_equals_and_hash = 20 [default = false]; - bool FileOptions::has_java_generate_equals_and_hash() const { +bool FileOptions::has_java_generate_equals_and_hash() const { return (_has_bits_[0] & 0x00000008u) != 0; } - void FileOptions::set_has_java_generate_equals_and_hash() { +void FileOptions::set_has_java_generate_equals_and_hash() { _has_bits_[0] |= 0x00000008u; } - void FileOptions::clear_has_java_generate_equals_and_hash() { +void FileOptions::clear_has_java_generate_equals_and_hash() { _has_bits_[0] &= ~0x00000008u; } - void FileOptions::clear_java_generate_equals_and_hash() { +void FileOptions::clear_java_generate_equals_and_hash() { java_generate_equals_and_hash_ = false; clear_has_java_generate_equals_and_hash(); } @@ -8049,16 +8629,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool java_string_check_utf8 = 27 [default = false]; - bool FileOptions::has_java_string_check_utf8() const { +bool FileOptions::has_java_string_check_utf8() const { return (_has_bits_[0] & 0x00000010u) != 0; } - void FileOptions::set_has_java_string_check_utf8() { +void FileOptions::set_has_java_string_check_utf8() { _has_bits_[0] |= 0x00000010u; } - void FileOptions::clear_has_java_string_check_utf8() { +void FileOptions::clear_has_java_string_check_utf8() { _has_bits_[0] &= ~0x00000010u; } - void FileOptions::clear_java_string_check_utf8() { +void FileOptions::clear_java_string_check_utf8() { java_string_check_utf8_ = false; clear_has_java_string_check_utf8(); } @@ -8073,16 +8653,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; - bool FileOptions::has_optimize_for() const { +bool FileOptions::has_optimize_for() const { return (_has_bits_[0] & 0x00000020u) != 0; } - void FileOptions::set_has_optimize_for() { +void FileOptions::set_has_optimize_for() { _has_bits_[0] |= 0x00000020u; } - void FileOptions::clear_has_optimize_for() { +void FileOptions::clear_has_optimize_for() { _has_bits_[0] &= ~0x00000020u; } - void FileOptions::clear_optimize_for() { +void FileOptions::clear_optimize_for() { optimize_for_ = 1; clear_has_optimize_for(); } @@ -8098,16 +8678,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional string go_package = 11; - bool FileOptions::has_go_package() const { +bool FileOptions::has_go_package() const { return (_has_bits_[0] & 0x00000040u) != 0; } - void FileOptions::set_has_go_package() { +void FileOptions::set_has_go_package() { _has_bits_[0] |= 0x00000040u; } - void FileOptions::clear_has_go_package() { +void FileOptions::clear_has_go_package() { _has_bits_[0] &= ~0x00000040u; } - void FileOptions::clear_go_package() { +void FileOptions::clear_go_package() { go_package_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_go_package(); } @@ -8151,16 +8731,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool cc_generic_services = 16 [default = false]; - bool FileOptions::has_cc_generic_services() const { +bool FileOptions::has_cc_generic_services() const { return (_has_bits_[0] & 0x00000080u) != 0; } - void FileOptions::set_has_cc_generic_services() { +void FileOptions::set_has_cc_generic_services() { _has_bits_[0] |= 0x00000080u; } - void FileOptions::clear_has_cc_generic_services() { +void FileOptions::clear_has_cc_generic_services() { _has_bits_[0] &= ~0x00000080u; } - void FileOptions::clear_cc_generic_services() { +void FileOptions::clear_cc_generic_services() { cc_generic_services_ = false; clear_has_cc_generic_services(); } @@ -8175,16 +8755,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool java_generic_services = 17 [default = false]; - bool FileOptions::has_java_generic_services() const { +bool FileOptions::has_java_generic_services() const { return (_has_bits_[0] & 0x00000100u) != 0; } - void FileOptions::set_has_java_generic_services() { +void FileOptions::set_has_java_generic_services() { _has_bits_[0] |= 0x00000100u; } - void FileOptions::clear_has_java_generic_services() { +void FileOptions::clear_has_java_generic_services() { _has_bits_[0] &= ~0x00000100u; } - void FileOptions::clear_java_generic_services() { +void FileOptions::clear_java_generic_services() { java_generic_services_ = false; clear_has_java_generic_services(); } @@ -8199,16 +8779,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool py_generic_services = 18 [default = false]; - bool FileOptions::has_py_generic_services() const { +bool FileOptions::has_py_generic_services() const { return (_has_bits_[0] & 0x00000200u) != 0; } - void FileOptions::set_has_py_generic_services() { +void FileOptions::set_has_py_generic_services() { _has_bits_[0] |= 0x00000200u; } - void FileOptions::clear_has_py_generic_services() { +void FileOptions::clear_has_py_generic_services() { _has_bits_[0] &= ~0x00000200u; } - void FileOptions::clear_py_generic_services() { +void FileOptions::clear_py_generic_services() { py_generic_services_ = false; clear_has_py_generic_services(); } @@ -8223,16 +8803,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool deprecated = 23 [default = false]; - bool FileOptions::has_deprecated() const { +bool FileOptions::has_deprecated() const { return (_has_bits_[0] & 0x00000400u) != 0; } - void FileOptions::set_has_deprecated() { +void FileOptions::set_has_deprecated() { _has_bits_[0] |= 0x00000400u; } - void FileOptions::clear_has_deprecated() { +void FileOptions::clear_has_deprecated() { _has_bits_[0] &= ~0x00000400u; } - void FileOptions::clear_deprecated() { +void FileOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -8247,16 +8827,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool cc_enable_arenas = 31 [default = false]; - bool FileOptions::has_cc_enable_arenas() const { +bool FileOptions::has_cc_enable_arenas() const { return (_has_bits_[0] & 0x00000800u) != 0; } - void FileOptions::set_has_cc_enable_arenas() { +void FileOptions::set_has_cc_enable_arenas() { _has_bits_[0] |= 0x00000800u; } - void FileOptions::clear_has_cc_enable_arenas() { +void FileOptions::clear_has_cc_enable_arenas() { _has_bits_[0] &= ~0x00000800u; } - void FileOptions::clear_cc_enable_arenas() { +void FileOptions::clear_cc_enable_arenas() { cc_enable_arenas_ = false; clear_has_cc_enable_arenas(); } @@ -8271,16 +8851,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional string objc_class_prefix = 36; - bool FileOptions::has_objc_class_prefix() const { +bool FileOptions::has_objc_class_prefix() const { return (_has_bits_[0] & 0x00001000u) != 0; } - void FileOptions::set_has_objc_class_prefix() { +void FileOptions::set_has_objc_class_prefix() { _has_bits_[0] |= 0x00001000u; } - void FileOptions::clear_has_objc_class_prefix() { +void FileOptions::clear_has_objc_class_prefix() { _has_bits_[0] &= ~0x00001000u; } - void FileOptions::clear_objc_class_prefix() { +void FileOptions::clear_objc_class_prefix() { objc_class_prefix_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_objc_class_prefix(); } @@ -8324,16 +8904,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional string csharp_namespace = 37; - bool FileOptions::has_csharp_namespace() const { +bool FileOptions::has_csharp_namespace() const { return (_has_bits_[0] & 0x00002000u) != 0; } - void FileOptions::set_has_csharp_namespace() { +void FileOptions::set_has_csharp_namespace() { _has_bits_[0] |= 0x00002000u; } - void FileOptions::clear_has_csharp_namespace() { +void FileOptions::clear_has_csharp_namespace() { _has_bits_[0] &= ~0x00002000u; } - void FileOptions::clear_csharp_namespace() { +void FileOptions::clear_csharp_namespace() { csharp_namespace_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_csharp_namespace(); } @@ -8377,10 +8957,10 @@ void FileOptions::InternalSwap(FileOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int FileOptions::uninterpreted_option_size() const { +int FileOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void FileOptions::clear_uninterpreted_option() { +void FileOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& FileOptions::uninterpreted_option(int index) const { @@ -8419,7 +8999,7 @@ const int MessageOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER MessageOptions::MessageOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.MessageOptions) } @@ -8574,12 +9154,15 @@ bool MessageOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -8741,9 +9324,9 @@ int MessageOptions::ByteSize() const { void MessageOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const MessageOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const MessageOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -8821,16 +9404,16 @@ void MessageOptions::InternalSwap(MessageOptions* other) { // MessageOptions // optional bool message_set_wire_format = 1 [default = false]; - bool MessageOptions::has_message_set_wire_format() const { +bool MessageOptions::has_message_set_wire_format() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void MessageOptions::set_has_message_set_wire_format() { +void MessageOptions::set_has_message_set_wire_format() { _has_bits_[0] |= 0x00000001u; } - void MessageOptions::clear_has_message_set_wire_format() { +void MessageOptions::clear_has_message_set_wire_format() { _has_bits_[0] &= ~0x00000001u; } - void MessageOptions::clear_message_set_wire_format() { +void MessageOptions::clear_message_set_wire_format() { message_set_wire_format_ = false; clear_has_message_set_wire_format(); } @@ -8845,16 +9428,16 @@ void MessageOptions::InternalSwap(MessageOptions* other) { } // optional bool no_standard_descriptor_accessor = 2 [default = false]; - bool MessageOptions::has_no_standard_descriptor_accessor() const { +bool MessageOptions::has_no_standard_descriptor_accessor() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void MessageOptions::set_has_no_standard_descriptor_accessor() { +void MessageOptions::set_has_no_standard_descriptor_accessor() { _has_bits_[0] |= 0x00000002u; } - void MessageOptions::clear_has_no_standard_descriptor_accessor() { +void MessageOptions::clear_has_no_standard_descriptor_accessor() { _has_bits_[0] &= ~0x00000002u; } - void MessageOptions::clear_no_standard_descriptor_accessor() { +void MessageOptions::clear_no_standard_descriptor_accessor() { no_standard_descriptor_accessor_ = false; clear_has_no_standard_descriptor_accessor(); } @@ -8869,16 +9452,16 @@ void MessageOptions::InternalSwap(MessageOptions* other) { } // optional bool deprecated = 3 [default = false]; - bool MessageOptions::has_deprecated() const { +bool MessageOptions::has_deprecated() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void MessageOptions::set_has_deprecated() { +void MessageOptions::set_has_deprecated() { _has_bits_[0] |= 0x00000004u; } - void MessageOptions::clear_has_deprecated() { +void MessageOptions::clear_has_deprecated() { _has_bits_[0] &= ~0x00000004u; } - void MessageOptions::clear_deprecated() { +void MessageOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -8893,16 +9476,16 @@ void MessageOptions::InternalSwap(MessageOptions* other) { } // optional bool map_entry = 7; - bool MessageOptions::has_map_entry() const { +bool MessageOptions::has_map_entry() const { return (_has_bits_[0] & 0x00000008u) != 0; } - void MessageOptions::set_has_map_entry() { +void MessageOptions::set_has_map_entry() { _has_bits_[0] |= 0x00000008u; } - void MessageOptions::clear_has_map_entry() { +void MessageOptions::clear_has_map_entry() { _has_bits_[0] &= ~0x00000008u; } - void MessageOptions::clear_map_entry() { +void MessageOptions::clear_map_entry() { map_entry_ = false; clear_has_map_entry(); } @@ -8917,10 +9500,10 @@ void MessageOptions::InternalSwap(MessageOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int MessageOptions::uninterpreted_option_size() const { +int MessageOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void MessageOptions::clear_uninterpreted_option() { +void MessageOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& MessageOptions::uninterpreted_option(int index) const { @@ -8973,9 +9556,33 @@ const FieldOptions_CType FieldOptions::CType_MIN; const FieldOptions_CType FieldOptions::CType_MAX; const int FieldOptions::CType_ARRAYSIZE; #endif // _MSC_VER +const ::google::protobuf::EnumDescriptor* FieldOptions_JSType_descriptor() { + protobuf_AssignDescriptorsOnce(); + return FieldOptions_JSType_descriptor_; +} +bool FieldOptions_JSType_IsValid(int value) { + switch(value) { + case 0: + case 1: + case 2: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const FieldOptions_JSType FieldOptions::JS_NORMAL; +const FieldOptions_JSType FieldOptions::JS_STRING; +const FieldOptions_JSType FieldOptions::JS_NUMBER; +const FieldOptions_JSType FieldOptions::JSType_MIN; +const FieldOptions_JSType FieldOptions::JSType_MAX; +const int FieldOptions::JSType_ARRAYSIZE; +#endif // _MSC_VER #ifndef _MSC_VER const int FieldOptions::kCtypeFieldNumber; const int FieldOptions::kPackedFieldNumber; +const int FieldOptions::kJstypeFieldNumber; const int FieldOptions::kLazyFieldNumber; const int FieldOptions::kDeprecatedFieldNumber; const int FieldOptions::kWeakFieldNumber; @@ -8983,7 +9590,7 @@ const int FieldOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER FieldOptions::FieldOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FieldOptions) } @@ -9003,6 +9610,7 @@ void FieldOptions::SharedCtor() { _cached_size_ = 0; ctype_ = 0; packed_ = false; + jstype_ = 0; lazy_ = false; deprecated_ = false; weak_ = false; @@ -9054,8 +9662,9 @@ void FieldOptions::Clear() { ZR_HELPER_(last) - ZR_HELPER_(first) + sizeof(last));\ } while (0) - if (_has_bits_[0 / 32] & 31u) { - ZR_(ctype_, weak_); + if (_has_bits_[0 / 32] & 63u) { + ZR_(ctype_, jstype_); + ZR_(packed_, weak_); } #undef ZR_HELPER_ @@ -9138,6 +9747,26 @@ bool FieldOptions::MergePartialFromCodedStream( } else { goto handle_unusual; } + if (input->ExpectTag(48)) goto parse_jstype; + break; + } + + // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; + case 6: { + if (tag == 48) { + parse_jstype: + int value; + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>( + input, &value))); + if (::google::protobuf::FieldOptions_JSType_IsValid(value)) { + set_jstype(static_cast< ::google::protobuf::FieldOptions_JSType >(value)); + } else { + mutable_unknown_fields()->AddVarint(6, value); + } + } else { + goto handle_unusual; + } if (input->ExpectTag(80)) goto parse_weak; break; } @@ -9161,12 +9790,15 @@ bool FieldOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -9222,6 +9854,12 @@ void FieldOptions::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteBool(5, this->lazy(), output); } + // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; + if (has_jstype()) { + ::google::protobuf::internal::WireFormatLite::WriteEnum( + 6, this->jstype(), output); + } + // optional bool weak = 10 [default = false]; if (has_weak()) { ::google::protobuf::internal::WireFormatLite::WriteBool(10, this->weak(), output); @@ -9268,6 +9906,12 @@ void FieldOptions::SerializeWithCachedSizes( target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(5, this->lazy(), target); } + // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; + if (has_jstype()) { + target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( + 6, this->jstype(), target); + } + // optional bool weak = 10 [default = false]; if (has_weak()) { target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(10, this->weak(), target); @@ -9295,7 +9939,7 @@ void FieldOptions::SerializeWithCachedSizes( int FieldOptions::ByteSize() const { int total_size = 0; - if (_has_bits_[0 / 32] & 31) { + if (_has_bits_[0 / 32] & 63) { // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING]; if (has_ctype()) { total_size += 1 + @@ -9307,6 +9951,12 @@ int FieldOptions::ByteSize() const { total_size += 1 + 1; } + // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; + if (has_jstype()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::EnumSize(this->jstype()); + } + // optional bool lazy = 5 [default = false]; if (has_lazy()) { total_size += 1 + 1; @@ -9346,9 +9996,9 @@ int FieldOptions::ByteSize() const { void FieldOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FieldOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FieldOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -9366,6 +10016,9 @@ void FieldOptions::MergeFrom(const FieldOptions& from) { if (from.has_packed()) { set_packed(from.packed()); } + if (from.has_jstype()) { + set_jstype(from.jstype()); + } if (from.has_lazy()) { set_lazy(from.lazy()); } @@ -9408,6 +10061,7 @@ void FieldOptions::Swap(FieldOptions* other) { void FieldOptions::InternalSwap(FieldOptions* other) { std::swap(ctype_, other->ctype_); std::swap(packed_, other->packed_); + std::swap(jstype_, other->jstype_); std::swap(lazy_, other->lazy_); std::swap(deprecated_, other->deprecated_); std::swap(weak_, other->weak_); @@ -9430,16 +10084,16 @@ void FieldOptions::InternalSwap(FieldOptions* other) { // FieldOptions // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING]; - bool FieldOptions::has_ctype() const { +bool FieldOptions::has_ctype() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void FieldOptions::set_has_ctype() { +void FieldOptions::set_has_ctype() { _has_bits_[0] |= 0x00000001u; } - void FieldOptions::clear_has_ctype() { +void FieldOptions::clear_has_ctype() { _has_bits_[0] &= ~0x00000001u; } - void FieldOptions::clear_ctype() { +void FieldOptions::clear_ctype() { ctype_ = 0; clear_has_ctype(); } @@ -9455,16 +10109,16 @@ void FieldOptions::InternalSwap(FieldOptions* other) { } // optional bool packed = 2; - bool FieldOptions::has_packed() const { +bool FieldOptions::has_packed() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void FieldOptions::set_has_packed() { +void FieldOptions::set_has_packed() { _has_bits_[0] |= 0x00000002u; } - void FieldOptions::clear_has_packed() { +void FieldOptions::clear_has_packed() { _has_bits_[0] &= ~0x00000002u; } - void FieldOptions::clear_packed() { +void FieldOptions::clear_packed() { packed_ = false; clear_has_packed(); } @@ -9478,17 +10132,42 @@ void FieldOptions::InternalSwap(FieldOptions* other) { // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.packed) } -// optional bool lazy = 5 [default = false]; - bool FieldOptions::has_lazy() const { +// optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; +bool FieldOptions::has_jstype() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void FieldOptions::set_has_lazy() { +void FieldOptions::set_has_jstype() { _has_bits_[0] |= 0x00000004u; } - void FieldOptions::clear_has_lazy() { +void FieldOptions::clear_has_jstype() { _has_bits_[0] &= ~0x00000004u; } - void FieldOptions::clear_lazy() { +void FieldOptions::clear_jstype() { + jstype_ = 0; + clear_has_jstype(); +} + ::google::protobuf::FieldOptions_JSType FieldOptions::jstype() const { + // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.jstype) + return static_cast< ::google::protobuf::FieldOptions_JSType >(jstype_); +} + void FieldOptions::set_jstype(::google::protobuf::FieldOptions_JSType value) { + assert(::google::protobuf::FieldOptions_JSType_IsValid(value)); + set_has_jstype(); + jstype_ = value; + // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.jstype) +} + +// optional bool lazy = 5 [default = false]; +bool FieldOptions::has_lazy() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +void FieldOptions::set_has_lazy() { + _has_bits_[0] |= 0x00000008u; +} +void FieldOptions::clear_has_lazy() { + _has_bits_[0] &= ~0x00000008u; +} +void FieldOptions::clear_lazy() { lazy_ = false; clear_has_lazy(); } @@ -9503,16 +10182,16 @@ void FieldOptions::InternalSwap(FieldOptions* other) { } // optional bool deprecated = 3 [default = false]; - bool FieldOptions::has_deprecated() const { - return (_has_bits_[0] & 0x00000008u) != 0; +bool FieldOptions::has_deprecated() const { + return (_has_bits_[0] & 0x00000010u) != 0; } - void FieldOptions::set_has_deprecated() { - _has_bits_[0] |= 0x00000008u; +void FieldOptions::set_has_deprecated() { + _has_bits_[0] |= 0x00000010u; } - void FieldOptions::clear_has_deprecated() { - _has_bits_[0] &= ~0x00000008u; +void FieldOptions::clear_has_deprecated() { + _has_bits_[0] &= ~0x00000010u; } - void FieldOptions::clear_deprecated() { +void FieldOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -9527,16 +10206,16 @@ void FieldOptions::InternalSwap(FieldOptions* other) { } // optional bool weak = 10 [default = false]; - bool FieldOptions::has_weak() const { - return (_has_bits_[0] & 0x00000010u) != 0; +bool FieldOptions::has_weak() const { + return (_has_bits_[0] & 0x00000020u) != 0; } - void FieldOptions::set_has_weak() { - _has_bits_[0] |= 0x00000010u; +void FieldOptions::set_has_weak() { + _has_bits_[0] |= 0x00000020u; } - void FieldOptions::clear_has_weak() { - _has_bits_[0] &= ~0x00000010u; +void FieldOptions::clear_has_weak() { + _has_bits_[0] &= ~0x00000020u; } - void FieldOptions::clear_weak() { +void FieldOptions::clear_weak() { weak_ = false; clear_has_weak(); } @@ -9551,10 +10230,10 @@ void FieldOptions::InternalSwap(FieldOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int FieldOptions::uninterpreted_option_size() const { +int FieldOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void FieldOptions::clear_uninterpreted_option() { +void FieldOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& FieldOptions::uninterpreted_option(int index) const { @@ -9591,7 +10270,7 @@ const int EnumOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER EnumOptions::EnumOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.EnumOptions) } @@ -9714,12 +10393,15 @@ bool EnumOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -9851,9 +10533,9 @@ int EnumOptions::ByteSize() const { void EnumOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const EnumOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const EnumOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -9923,16 +10605,16 @@ void EnumOptions::InternalSwap(EnumOptions* other) { // EnumOptions // optional bool allow_alias = 2; - bool EnumOptions::has_allow_alias() const { +bool EnumOptions::has_allow_alias() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void EnumOptions::set_has_allow_alias() { +void EnumOptions::set_has_allow_alias() { _has_bits_[0] |= 0x00000001u; } - void EnumOptions::clear_has_allow_alias() { +void EnumOptions::clear_has_allow_alias() { _has_bits_[0] &= ~0x00000001u; } - void EnumOptions::clear_allow_alias() { +void EnumOptions::clear_allow_alias() { allow_alias_ = false; clear_has_allow_alias(); } @@ -9947,16 +10629,16 @@ void EnumOptions::InternalSwap(EnumOptions* other) { } // optional bool deprecated = 3 [default = false]; - bool EnumOptions::has_deprecated() const { +bool EnumOptions::has_deprecated() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void EnumOptions::set_has_deprecated() { +void EnumOptions::set_has_deprecated() { _has_bits_[0] |= 0x00000002u; } - void EnumOptions::clear_has_deprecated() { +void EnumOptions::clear_has_deprecated() { _has_bits_[0] &= ~0x00000002u; } - void EnumOptions::clear_deprecated() { +void EnumOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -9971,10 +10653,10 @@ void EnumOptions::InternalSwap(EnumOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int EnumOptions::uninterpreted_option_size() const { +int EnumOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void EnumOptions::clear_uninterpreted_option() { +void EnumOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& EnumOptions::uninterpreted_option(int index) const { @@ -10010,7 +10692,7 @@ const int EnumValueOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER EnumValueOptions::EnumValueOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.EnumValueOptions) } @@ -10105,12 +10787,15 @@ bool EnumValueOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -10225,9 +10910,9 @@ int EnumValueOptions::ByteSize() const { void EnumValueOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const EnumValueOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const EnumValueOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -10293,16 +10978,16 @@ void EnumValueOptions::InternalSwap(EnumValueOptions* other) { // EnumValueOptions // optional bool deprecated = 1 [default = false]; - bool EnumValueOptions::has_deprecated() const { +bool EnumValueOptions::has_deprecated() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void EnumValueOptions::set_has_deprecated() { +void EnumValueOptions::set_has_deprecated() { _has_bits_[0] |= 0x00000001u; } - void EnumValueOptions::clear_has_deprecated() { +void EnumValueOptions::clear_has_deprecated() { _has_bits_[0] &= ~0x00000001u; } - void EnumValueOptions::clear_deprecated() { +void EnumValueOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -10317,10 +11002,10 @@ void EnumValueOptions::InternalSwap(EnumValueOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int EnumValueOptions::uninterpreted_option_size() const { +int EnumValueOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void EnumValueOptions::clear_uninterpreted_option() { +void EnumValueOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& EnumValueOptions::uninterpreted_option(int index) const { @@ -10356,7 +11041,7 @@ const int ServiceOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER ServiceOptions::ServiceOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.ServiceOptions) } @@ -10451,12 +11136,15 @@ bool ServiceOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -10571,9 +11259,9 @@ int ServiceOptions::ByteSize() const { void ServiceOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const ServiceOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const ServiceOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -10639,16 +11327,16 @@ void ServiceOptions::InternalSwap(ServiceOptions* other) { // ServiceOptions // optional bool deprecated = 33 [default = false]; - bool ServiceOptions::has_deprecated() const { +bool ServiceOptions::has_deprecated() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void ServiceOptions::set_has_deprecated() { +void ServiceOptions::set_has_deprecated() { _has_bits_[0] |= 0x00000001u; } - void ServiceOptions::clear_has_deprecated() { +void ServiceOptions::clear_has_deprecated() { _has_bits_[0] &= ~0x00000001u; } - void ServiceOptions::clear_deprecated() { +void ServiceOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -10663,10 +11351,10 @@ void ServiceOptions::InternalSwap(ServiceOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int ServiceOptions::uninterpreted_option_size() const { +int ServiceOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void ServiceOptions::clear_uninterpreted_option() { +void ServiceOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& ServiceOptions::uninterpreted_option(int index) const { @@ -10702,7 +11390,7 @@ const int MethodOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER MethodOptions::MethodOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.MethodOptions) } @@ -10797,12 +11485,15 @@ bool MethodOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -10917,9 +11608,9 @@ int MethodOptions::ByteSize() const { void MethodOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const MethodOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const MethodOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -10985,16 +11676,16 @@ void MethodOptions::InternalSwap(MethodOptions* other) { // MethodOptions // optional bool deprecated = 33 [default = false]; - bool MethodOptions::has_deprecated() const { +bool MethodOptions::has_deprecated() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void MethodOptions::set_has_deprecated() { +void MethodOptions::set_has_deprecated() { _has_bits_[0] |= 0x00000001u; } - void MethodOptions::clear_has_deprecated() { +void MethodOptions::clear_has_deprecated() { _has_bits_[0] &= ~0x00000001u; } - void MethodOptions::clear_deprecated() { +void MethodOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -11009,10 +11700,10 @@ void MethodOptions::InternalSwap(MethodOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int MethodOptions::uninterpreted_option_size() const { +int MethodOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void MethodOptions::clear_uninterpreted_option() { +void MethodOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& MethodOptions::uninterpreted_option(int index) const { @@ -11048,7 +11739,7 @@ const int UninterpretedOption_NamePart::kIsExtensionFieldNumber; #endif // !_MSC_VER UninterpretedOption_NamePart::UninterpretedOption_NamePart() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.UninterpretedOption.NamePart) } @@ -11281,9 +11972,9 @@ int UninterpretedOption_NamePart::ByteSize() const { void UninterpretedOption_NamePart::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const UninterpretedOption_NamePart* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const UninterpretedOption_NamePart* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -11359,7 +12050,7 @@ const int UninterpretedOption::kAggregateValueFieldNumber; #endif // !_MSC_VER UninterpretedOption::UninterpretedOption() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.UninterpretedOption) } @@ -11470,13 +12161,15 @@ bool UninterpretedOption::MergePartialFromCodedStream( // repeated .google.protobuf.UninterpretedOption.NamePart name = 2; case 2: { if (tag == 18) { - parse_name: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_name: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_name())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_name; + if (input->ExpectTag(18)) goto parse_loop_name; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(26)) goto parse_identifier_value; break; } @@ -11780,9 +12473,9 @@ int UninterpretedOption::ByteSize() const { void UninterpretedOption::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const UninterpretedOption* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const UninterpretedOption* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -11868,16 +12561,16 @@ void UninterpretedOption::InternalSwap(UninterpretedOption* other) { // UninterpretedOption_NamePart // required string name_part = 1; - bool UninterpretedOption_NamePart::has_name_part() const { +bool UninterpretedOption_NamePart::has_name_part() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void UninterpretedOption_NamePart::set_has_name_part() { +void UninterpretedOption_NamePart::set_has_name_part() { _has_bits_[0] |= 0x00000001u; } - void UninterpretedOption_NamePart::clear_has_name_part() { +void UninterpretedOption_NamePart::clear_has_name_part() { _has_bits_[0] &= ~0x00000001u; } - void UninterpretedOption_NamePart::clear_name_part() { +void UninterpretedOption_NamePart::clear_name_part() { name_part_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name_part(); } @@ -11921,16 +12614,16 @@ void UninterpretedOption::InternalSwap(UninterpretedOption* other) { } // required bool is_extension = 2; - bool UninterpretedOption_NamePart::has_is_extension() const { +bool UninterpretedOption_NamePart::has_is_extension() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void UninterpretedOption_NamePart::set_has_is_extension() { +void UninterpretedOption_NamePart::set_has_is_extension() { _has_bits_[0] |= 0x00000002u; } - void UninterpretedOption_NamePart::clear_has_is_extension() { +void UninterpretedOption_NamePart::clear_has_is_extension() { _has_bits_[0] &= ~0x00000002u; } - void UninterpretedOption_NamePart::clear_is_extension() { +void UninterpretedOption_NamePart::clear_is_extension() { is_extension_ = false; clear_has_is_extension(); } @@ -11949,10 +12642,10 @@ void UninterpretedOption::InternalSwap(UninterpretedOption* other) { // UninterpretedOption // repeated .google.protobuf.UninterpretedOption.NamePart name = 2; - int UninterpretedOption::name_size() const { +int UninterpretedOption::name_size() const { return name_.size(); } - void UninterpretedOption::clear_name() { +void UninterpretedOption::clear_name() { name_.Clear(); } const ::google::protobuf::UninterpretedOption_NamePart& UninterpretedOption::name(int index) const { @@ -11979,16 +12672,16 @@ UninterpretedOption::mutable_name() { } // optional string identifier_value = 3; - bool UninterpretedOption::has_identifier_value() const { +bool UninterpretedOption::has_identifier_value() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void UninterpretedOption::set_has_identifier_value() { +void UninterpretedOption::set_has_identifier_value() { _has_bits_[0] |= 0x00000002u; } - void UninterpretedOption::clear_has_identifier_value() { +void UninterpretedOption::clear_has_identifier_value() { _has_bits_[0] &= ~0x00000002u; } - void UninterpretedOption::clear_identifier_value() { +void UninterpretedOption::clear_identifier_value() { identifier_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_identifier_value(); } @@ -12032,16 +12725,16 @@ UninterpretedOption::mutable_name() { } // optional uint64 positive_int_value = 4; - bool UninterpretedOption::has_positive_int_value() const { +bool UninterpretedOption::has_positive_int_value() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void UninterpretedOption::set_has_positive_int_value() { +void UninterpretedOption::set_has_positive_int_value() { _has_bits_[0] |= 0x00000004u; } - void UninterpretedOption::clear_has_positive_int_value() { +void UninterpretedOption::clear_has_positive_int_value() { _has_bits_[0] &= ~0x00000004u; } - void UninterpretedOption::clear_positive_int_value() { +void UninterpretedOption::clear_positive_int_value() { positive_int_value_ = GOOGLE_ULONGLONG(0); clear_has_positive_int_value(); } @@ -12056,16 +12749,16 @@ UninterpretedOption::mutable_name() { } // optional int64 negative_int_value = 5; - bool UninterpretedOption::has_negative_int_value() const { +bool UninterpretedOption::has_negative_int_value() const { return (_has_bits_[0] & 0x00000008u) != 0; } - void UninterpretedOption::set_has_negative_int_value() { +void UninterpretedOption::set_has_negative_int_value() { _has_bits_[0] |= 0x00000008u; } - void UninterpretedOption::clear_has_negative_int_value() { +void UninterpretedOption::clear_has_negative_int_value() { _has_bits_[0] &= ~0x00000008u; } - void UninterpretedOption::clear_negative_int_value() { +void UninterpretedOption::clear_negative_int_value() { negative_int_value_ = GOOGLE_LONGLONG(0); clear_has_negative_int_value(); } @@ -12080,16 +12773,16 @@ UninterpretedOption::mutable_name() { } // optional double double_value = 6; - bool UninterpretedOption::has_double_value() const { +bool UninterpretedOption::has_double_value() const { return (_has_bits_[0] & 0x00000010u) != 0; } - void UninterpretedOption::set_has_double_value() { +void UninterpretedOption::set_has_double_value() { _has_bits_[0] |= 0x00000010u; } - void UninterpretedOption::clear_has_double_value() { +void UninterpretedOption::clear_has_double_value() { _has_bits_[0] &= ~0x00000010u; } - void UninterpretedOption::clear_double_value() { +void UninterpretedOption::clear_double_value() { double_value_ = 0; clear_has_double_value(); } @@ -12104,16 +12797,16 @@ UninterpretedOption::mutable_name() { } // optional bytes string_value = 7; - bool UninterpretedOption::has_string_value() const { +bool UninterpretedOption::has_string_value() const { return (_has_bits_[0] & 0x00000020u) != 0; } - void UninterpretedOption::set_has_string_value() { +void UninterpretedOption::set_has_string_value() { _has_bits_[0] |= 0x00000020u; } - void UninterpretedOption::clear_has_string_value() { +void UninterpretedOption::clear_has_string_value() { _has_bits_[0] &= ~0x00000020u; } - void UninterpretedOption::clear_string_value() { +void UninterpretedOption::clear_string_value() { string_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_string_value(); } @@ -12157,16 +12850,16 @@ UninterpretedOption::mutable_name() { } // optional string aggregate_value = 8; - bool UninterpretedOption::has_aggregate_value() const { +bool UninterpretedOption::has_aggregate_value() const { return (_has_bits_[0] & 0x00000040u) != 0; } - void UninterpretedOption::set_has_aggregate_value() { +void UninterpretedOption::set_has_aggregate_value() { _has_bits_[0] |= 0x00000040u; } - void UninterpretedOption::clear_has_aggregate_value() { +void UninterpretedOption::clear_has_aggregate_value() { _has_bits_[0] &= ~0x00000040u; } - void UninterpretedOption::clear_aggregate_value() { +void UninterpretedOption::clear_aggregate_value() { aggregate_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_aggregate_value(); } @@ -12222,7 +12915,7 @@ const int SourceCodeInfo_Location::kLeadingDetachedCommentsFieldNumber; #endif // !_MSC_VER SourceCodeInfo_Location::SourceCodeInfo_Location() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.SourceCodeInfo.Location) } @@ -12625,9 +13318,9 @@ int SourceCodeInfo_Location::ByteSize() const { void SourceCodeInfo_Location::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const SourceCodeInfo_Location* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const SourceCodeInfo_Location* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -12703,7 +13396,7 @@ const int SourceCodeInfo::kLocationFieldNumber; #endif // !_MSC_VER SourceCodeInfo::SourceCodeInfo() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.SourceCodeInfo) } @@ -12780,13 +13473,15 @@ bool SourceCodeInfo::MergePartialFromCodedStream( // repeated .google.protobuf.SourceCodeInfo.Location location = 1; case 1: { if (tag == 10) { - parse_location: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_location: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_location())); } else { goto handle_unusual; } - if (input->ExpectTag(10)) goto parse_location; + if (input->ExpectTag(10)) goto parse_loop_location; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -12871,9 +13566,9 @@ int SourceCodeInfo::ByteSize() const { void SourceCodeInfo::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const SourceCodeInfo* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const SourceCodeInfo* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -12929,10 +13624,10 @@ void SourceCodeInfo::InternalSwap(SourceCodeInfo* other) { // SourceCodeInfo_Location // repeated int32 path = 1 [packed = true]; - int SourceCodeInfo_Location::path_size() const { +int SourceCodeInfo_Location::path_size() const { return path_.size(); } - void SourceCodeInfo_Location::clear_path() { +void SourceCodeInfo_Location::clear_path() { path_.Clear(); } ::google::protobuf::int32 SourceCodeInfo_Location::path(int index) const { @@ -12959,10 +13654,10 @@ SourceCodeInfo_Location::mutable_path() { } // repeated int32 span = 2 [packed = true]; - int SourceCodeInfo_Location::span_size() const { +int SourceCodeInfo_Location::span_size() const { return span_.size(); } - void SourceCodeInfo_Location::clear_span() { +void SourceCodeInfo_Location::clear_span() { span_.Clear(); } ::google::protobuf::int32 SourceCodeInfo_Location::span(int index) const { @@ -12989,16 +13684,16 @@ SourceCodeInfo_Location::mutable_span() { } // optional string leading_comments = 3; - bool SourceCodeInfo_Location::has_leading_comments() const { +bool SourceCodeInfo_Location::has_leading_comments() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void SourceCodeInfo_Location::set_has_leading_comments() { +void SourceCodeInfo_Location::set_has_leading_comments() { _has_bits_[0] |= 0x00000004u; } - void SourceCodeInfo_Location::clear_has_leading_comments() { +void SourceCodeInfo_Location::clear_has_leading_comments() { _has_bits_[0] &= ~0x00000004u; } - void SourceCodeInfo_Location::clear_leading_comments() { +void SourceCodeInfo_Location::clear_leading_comments() { leading_comments_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_leading_comments(); } @@ -13042,16 +13737,16 @@ SourceCodeInfo_Location::mutable_span() { } // optional string trailing_comments = 4; - bool SourceCodeInfo_Location::has_trailing_comments() const { +bool SourceCodeInfo_Location::has_trailing_comments() const { return (_has_bits_[0] & 0x00000008u) != 0; } - void SourceCodeInfo_Location::set_has_trailing_comments() { +void SourceCodeInfo_Location::set_has_trailing_comments() { _has_bits_[0] |= 0x00000008u; } - void SourceCodeInfo_Location::clear_has_trailing_comments() { +void SourceCodeInfo_Location::clear_has_trailing_comments() { _has_bits_[0] &= ~0x00000008u; } - void SourceCodeInfo_Location::clear_trailing_comments() { +void SourceCodeInfo_Location::clear_trailing_comments() { trailing_comments_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_trailing_comments(); } @@ -13095,10 +13790,10 @@ SourceCodeInfo_Location::mutable_span() { } // repeated string leading_detached_comments = 6; - int SourceCodeInfo_Location::leading_detached_comments_size() const { +int SourceCodeInfo_Location::leading_detached_comments_size() const { return leading_detached_comments_.size(); } - void SourceCodeInfo_Location::clear_leading_detached_comments() { +void SourceCodeInfo_Location::clear_leading_detached_comments() { leading_detached_comments_.Clear(); } const ::std::string& SourceCodeInfo_Location::leading_detached_comments(int index) const { @@ -13153,10 +13848,10 @@ SourceCodeInfo_Location::mutable_leading_detached_comments() { // SourceCodeInfo // repeated .google.protobuf.SourceCodeInfo.Location location = 1; - int SourceCodeInfo::location_size() const { +int SourceCodeInfo::location_size() const { return location_.size(); } - void SourceCodeInfo::clear_location() { +void SourceCodeInfo::clear_location() { location_.Clear(); } const ::google::protobuf::SourceCodeInfo_Location& SourceCodeInfo::location(int index) const { diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h index 5bebf4fd..b887b8cb 100644 --- a/src/google/protobuf/descriptor.pb.h +++ b/src/google/protobuf/descriptor.pb.h @@ -42,6 +42,7 @@ class FileDescriptorSet; class FileDescriptorProto; class DescriptorProto; class DescriptorProto_ExtensionRange; +class DescriptorProto_ReservedRange; class FieldDescriptorProto; class OneofDescriptorProto; class EnumDescriptorProto; @@ -155,6 +156,26 @@ inline bool FieldOptions_CType_Parse( return ::google::protobuf::internal::ParseNamedEnum( FieldOptions_CType_descriptor(), name, value); } +enum FieldOptions_JSType { + FieldOptions_JSType_JS_NORMAL = 0, + FieldOptions_JSType_JS_STRING = 1, + FieldOptions_JSType_JS_NUMBER = 2 +}; +LIBPROTOBUF_EXPORT bool FieldOptions_JSType_IsValid(int value); +const FieldOptions_JSType FieldOptions_JSType_JSType_MIN = FieldOptions_JSType_JS_NORMAL; +const FieldOptions_JSType FieldOptions_JSType_JSType_MAX = FieldOptions_JSType_JS_NUMBER; +const int FieldOptions_JSType_JSType_ARRAYSIZE = FieldOptions_JSType_JSType_MAX + 1; + +LIBPROTOBUF_EXPORT const ::google::protobuf::EnumDescriptor* FieldOptions_JSType_descriptor(); +inline const ::std::string& FieldOptions_JSType_Name(FieldOptions_JSType value) { + return ::google::protobuf::internal::NameOfEnum( + FieldOptions_JSType_descriptor(), value); +} +inline bool FieldOptions_JSType_Parse( + const ::std::string& name, FieldOptions_JSType* value) { + return ::google::protobuf::internal::ParseNamedEnum( + FieldOptions_JSType_descriptor(), name, value); +} // =================================================================== class LIBPROTOBUF_EXPORT FileDescriptorSet : public ::google::protobuf::Message { @@ -591,6 +612,105 @@ class LIBPROTOBUF_EXPORT DescriptorProto_ExtensionRange : public ::google::proto }; // ------------------------------------------------------------------- +class LIBPROTOBUF_EXPORT DescriptorProto_ReservedRange : public ::google::protobuf::Message { + public: + DescriptorProto_ReservedRange(); + virtual ~DescriptorProto_ReservedRange(); + + DescriptorProto_ReservedRange(const DescriptorProto_ReservedRange& from); + + inline DescriptorProto_ReservedRange& operator=(const DescriptorProto_ReservedRange& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const DescriptorProto_ReservedRange& default_instance(); + + void Swap(DescriptorProto_ReservedRange* other); + + // implements Message ---------------------------------------------- + + inline DescriptorProto_ReservedRange* New() const { return New(NULL); } + + DescriptorProto_ReservedRange* New(::google::protobuf::Arena* arena) const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const DescriptorProto_ReservedRange& from); + void MergeFrom(const DescriptorProto_ReservedRange& 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; + void InternalSwap(DescriptorProto_ReservedRange* other); + private: + inline ::google::protobuf::Arena* GetArenaNoVirtual() const { + return _internal_metadata_.arena(); + } + inline void* MaybeArenaPtr() const { + return _internal_metadata_.raw_arena_ptr(); + } + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional int32 start = 1; + bool has_start() const; + void clear_start(); + static const int kStartFieldNumber = 1; + ::google::protobuf::int32 start() const; + void set_start(::google::protobuf::int32 value); + + // optional int32 end = 2; + bool has_end() const; + void clear_end(); + static const int kEndFieldNumber = 2; + ::google::protobuf::int32 end() const; + void set_end(::google::protobuf::int32 value); + + // @@protoc_insertion_point(class_scope:google.protobuf.DescriptorProto.ReservedRange) + private: + inline void set_has_start(); + inline void clear_has_start(); + inline void set_has_end(); + inline void clear_has_end(); + + ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + ::google::protobuf::uint32 _has_bits_[1]; + 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(); + + void InitAsDefaultInstance(); + static DescriptorProto_ReservedRange* default_instance_; +}; +// ------------------------------------------------------------------- + class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { public: DescriptorProto(); @@ -654,6 +774,7 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { // nested types ---------------------------------------------------- typedef DescriptorProto_ExtensionRange ExtensionRange; + typedef DescriptorProto_ReservedRange ReservedRange; // accessors ------------------------------------------------------- @@ -750,6 +871,34 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { ::google::protobuf::MessageOptions* release_options(); void set_allocated_options(::google::protobuf::MessageOptions* options); + // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; + int reserved_range_size() const; + void clear_reserved_range(); + static const int kReservedRangeFieldNumber = 9; + const ::google::protobuf::DescriptorProto_ReservedRange& reserved_range(int index) const; + ::google::protobuf::DescriptorProto_ReservedRange* mutable_reserved_range(int index); + ::google::protobuf::DescriptorProto_ReservedRange* add_reserved_range(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >& + reserved_range() const; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >* + mutable_reserved_range(); + + // repeated string reserved_name = 10; + int reserved_name_size() const; + void clear_reserved_name(); + static const int kReservedNameFieldNumber = 10; + const ::std::string& reserved_name(int index) const; + ::std::string* mutable_reserved_name(int index); + void set_reserved_name(int index, const ::std::string& value); + void set_reserved_name(int index, const char* value); + void set_reserved_name(int index, const char* value, size_t size); + ::std::string* add_reserved_name(); + void add_reserved_name(const ::std::string& value); + void add_reserved_name(const char* value); + void add_reserved_name(const char* value, size_t size); + const ::google::protobuf::RepeatedPtrField< ::std::string>& reserved_name() const; + ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_reserved_name(); + // @@protoc_insertion_point(class_scope:google.protobuf.DescriptorProto) private: inline void set_has_name(); @@ -768,6 +917,8 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange > extension_range_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::OneofDescriptorProto > oneof_decl_; ::google::protobuf::MessageOptions* options_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange > reserved_range_; + ::google::protobuf::RepeatedPtrField< ::std::string> reserved_name_; 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(); @@ -2144,6 +2295,31 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { return FieldOptions_CType_Parse(name, value); } + typedef FieldOptions_JSType JSType; + static const JSType JS_NORMAL = FieldOptions_JSType_JS_NORMAL; + static const JSType JS_STRING = FieldOptions_JSType_JS_STRING; + static const JSType JS_NUMBER = FieldOptions_JSType_JS_NUMBER; + static inline bool JSType_IsValid(int value) { + return FieldOptions_JSType_IsValid(value); + } + static const JSType JSType_MIN = + FieldOptions_JSType_JSType_MIN; + static const JSType JSType_MAX = + FieldOptions_JSType_JSType_MAX; + static const int JSType_ARRAYSIZE = + FieldOptions_JSType_JSType_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* + JSType_descriptor() { + return FieldOptions_JSType_descriptor(); + } + static inline const ::std::string& JSType_Name(JSType value) { + return FieldOptions_JSType_Name(value); + } + static inline bool JSType_Parse(const ::std::string& name, + JSType* value) { + return FieldOptions_JSType_Parse(name, value); + } + // accessors ------------------------------------------------------- // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING]; @@ -2160,6 +2336,13 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { bool packed() const; void set_packed(bool value); + // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; + bool has_jstype() const; + void clear_jstype(); + static const int kJstypeFieldNumber = 6; + ::google::protobuf::FieldOptions_JSType jstype() const; + void set_jstype(::google::protobuf::FieldOptions_JSType value); + // optional bool lazy = 5 [default = false]; bool has_lazy() const; void clear_lazy(); @@ -2200,6 +2383,8 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { inline void clear_has_ctype(); inline void set_has_packed(); inline void clear_has_packed(); + inline void set_has_jstype(); + inline void clear_has_jstype(); inline void set_has_lazy(); inline void clear_has_lazy(); inline void set_has_deprecated(); @@ -2213,11 +2398,12 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { ::google::protobuf::uint32 _has_bits_[1]; mutable int _cached_size_; int ctype_; + int jstype_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; bool packed_; bool lazy_; bool deprecated_; bool weak_; - ::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(); @@ -3754,6 +3940,58 @@ inline void DescriptorProto_ExtensionRange::set_end(::google::protobuf::int32 va // ------------------------------------------------------------------- +// DescriptorProto_ReservedRange + +// optional int32 start = 1; +inline bool DescriptorProto_ReservedRange::has_start() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void DescriptorProto_ReservedRange::set_has_start() { + _has_bits_[0] |= 0x00000001u; +} +inline void DescriptorProto_ReservedRange::clear_has_start() { + _has_bits_[0] &= ~0x00000001u; +} +inline void DescriptorProto_ReservedRange::clear_start() { + start_ = 0; + clear_has_start(); +} +inline ::google::protobuf::int32 DescriptorProto_ReservedRange::start() const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ReservedRange.start) + return start_; +} +inline void DescriptorProto_ReservedRange::set_start(::google::protobuf::int32 value) { + set_has_start(); + start_ = value; + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.ReservedRange.start) +} + +// optional int32 end = 2; +inline bool DescriptorProto_ReservedRange::has_end() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void DescriptorProto_ReservedRange::set_has_end() { + _has_bits_[0] |= 0x00000002u; +} +inline void DescriptorProto_ReservedRange::clear_has_end() { + _has_bits_[0] &= ~0x00000002u; +} +inline void DescriptorProto_ReservedRange::clear_end() { + end_ = 0; + clear_has_end(); +} +inline ::google::protobuf::int32 DescriptorProto_ReservedRange::end() const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ReservedRange.end) + return end_; +} +inline void DescriptorProto_ReservedRange::set_end(::google::protobuf::int32 value) { + set_has_end(); + end_ = value; + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.ReservedRange.end) +} + +// ------------------------------------------------------------------- + // DescriptorProto // optional string name = 1; @@ -4032,6 +4270,90 @@ inline void DescriptorProto::set_allocated_options(::google::protobuf::MessageOp // @@protoc_insertion_point(field_set_allocated:google.protobuf.DescriptorProto.options) } +// repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; +inline int DescriptorProto::reserved_range_size() const { + return reserved_range_.size(); +} +inline void DescriptorProto::clear_reserved_range() { + reserved_range_.Clear(); +} +inline const ::google::protobuf::DescriptorProto_ReservedRange& DescriptorProto::reserved_range(int index) const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_.Get(index); +} +inline ::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::mutable_reserved_range(int index) { + // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_.Mutable(index); +} +inline ::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::add_reserved_range() { + // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_.Add(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >& +DescriptorProto::reserved_range() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >* +DescriptorProto::mutable_reserved_range() { + // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.reserved_range) + return &reserved_range_; +} + +// repeated string reserved_name = 10; +inline int DescriptorProto::reserved_name_size() const { + return reserved_name_.size(); +} +inline void DescriptorProto::clear_reserved_name() { + reserved_name_.Clear(); +} +inline const ::std::string& DescriptorProto::reserved_name(int index) const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.reserved_name) + return reserved_name_.Get(index); +} +inline ::std::string* DescriptorProto::mutable_reserved_name(int index) { + // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.reserved_name) + return reserved_name_.Mutable(index); +} +inline void DescriptorProto::set_reserved_name(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.reserved_name) + reserved_name_.Mutable(index)->assign(value); +} +inline void DescriptorProto::set_reserved_name(int index, const char* value) { + reserved_name_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:google.protobuf.DescriptorProto.reserved_name) +} +inline void DescriptorProto::set_reserved_name(int index, const char* value, size_t size) { + reserved_name_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:google.protobuf.DescriptorProto.reserved_name) +} +inline ::std::string* DescriptorProto::add_reserved_name() { + return reserved_name_.Add(); +} +inline void DescriptorProto::add_reserved_name(const ::std::string& value) { + reserved_name_.Add()->assign(value); + // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_name) +} +inline void DescriptorProto::add_reserved_name(const char* value) { + reserved_name_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:google.protobuf.DescriptorProto.reserved_name) +} +inline void DescriptorProto::add_reserved_name(const char* value, size_t size) { + reserved_name_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:google.protobuf.DescriptorProto.reserved_name) +} +inline const ::google::protobuf::RepeatedPtrField< ::std::string>& +DescriptorProto::reserved_name() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_name) + return reserved_name_; +} +inline ::google::protobuf::RepeatedPtrField< ::std::string>* +DescriptorProto::mutable_reserved_name() { + // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.reserved_name) + return &reserved_name_; +} + // ------------------------------------------------------------------- // FieldDescriptorProto @@ -5783,15 +6105,40 @@ inline void FieldOptions::set_packed(bool value) { // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.packed) } +// optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; +inline bool FieldOptions::has_jstype() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void FieldOptions::set_has_jstype() { + _has_bits_[0] |= 0x00000004u; +} +inline void FieldOptions::clear_has_jstype() { + _has_bits_[0] &= ~0x00000004u; +} +inline void FieldOptions::clear_jstype() { + jstype_ = 0; + clear_has_jstype(); +} +inline ::google::protobuf::FieldOptions_JSType FieldOptions::jstype() const { + // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.jstype) + return static_cast< ::google::protobuf::FieldOptions_JSType >(jstype_); +} +inline void FieldOptions::set_jstype(::google::protobuf::FieldOptions_JSType value) { + assert(::google::protobuf::FieldOptions_JSType_IsValid(value)); + set_has_jstype(); + jstype_ = value; + // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.jstype) +} + // optional bool lazy = 5 [default = false]; inline bool FieldOptions::has_lazy() const { - return (_has_bits_[0] & 0x00000004u) != 0; + return (_has_bits_[0] & 0x00000008u) != 0; } inline void FieldOptions::set_has_lazy() { - _has_bits_[0] |= 0x00000004u; + _has_bits_[0] |= 0x00000008u; } inline void FieldOptions::clear_has_lazy() { - _has_bits_[0] &= ~0x00000004u; + _has_bits_[0] &= ~0x00000008u; } inline void FieldOptions::clear_lazy() { lazy_ = false; @@ -5809,13 +6156,13 @@ inline void FieldOptions::set_lazy(bool value) { // optional bool deprecated = 3 [default = false]; inline bool FieldOptions::has_deprecated() const { - return (_has_bits_[0] & 0x00000008u) != 0; + return (_has_bits_[0] & 0x00000010u) != 0; } inline void FieldOptions::set_has_deprecated() { - _has_bits_[0] |= 0x00000008u; + _has_bits_[0] |= 0x00000010u; } inline void FieldOptions::clear_has_deprecated() { - _has_bits_[0] &= ~0x00000008u; + _has_bits_[0] &= ~0x00000010u; } inline void FieldOptions::clear_deprecated() { deprecated_ = false; @@ -5833,13 +6180,13 @@ inline void FieldOptions::set_deprecated(bool value) { // optional bool weak = 10 [default = false]; inline bool FieldOptions::has_weak() const { - return (_has_bits_[0] & 0x00000010u) != 0; + return (_has_bits_[0] & 0x00000020u) != 0; } inline void FieldOptions::set_has_weak() { - _has_bits_[0] |= 0x00000010u; + _has_bits_[0] |= 0x00000020u; } inline void FieldOptions::clear_has_weak() { - _has_bits_[0] &= ~0x00000010u; + _has_bits_[0] &= ~0x00000020u; } inline void FieldOptions::clear_weak() { weak_ = false; @@ -6746,6 +7093,48 @@ SourceCodeInfo::mutable_location() { } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) @@ -6776,6 +7165,11 @@ template <> inline const EnumDescriptor* GetEnumDescriptor< ::google::protobuf::FieldOptions_CType>() { return ::google::protobuf::FieldOptions_CType_descriptor(); } +template <> struct is_proto_enum< ::google::protobuf::FieldOptions_JSType> : ::google::protobuf::internal::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::google::protobuf::FieldOptions_JSType>() { + return ::google::protobuf::FieldOptions_JSType_descriptor(); +} } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto index 7099135d..20a60080 100644 --- a/src/google/protobuf/descriptor.proto +++ b/src/google/protobuf/descriptor.proto @@ -106,6 +106,18 @@ message DescriptorProto { repeated OneofDescriptorProto oneof_decl = 8; optional MessageOptions options = 7; + + // Range of reserved tag numbers. Reserved tag numbers may not be used by + // fields or extension ranges in the same message. Reserved ranges may + // not overlap. + message ReservedRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. + } + repeated ReservedRange reserved_range = 9; + // Reserved field names, which may not be used by fields in the same message. + // A given name may only be reserved once. + repeated string reserved_name = 10; } // Describes a field within a message. @@ -251,11 +263,11 @@ message MethodDescriptorProto { // * For options which will be published and used publicly by multiple // independent entities, e-mail protobuf-global-extension-registry@google.com // to reserve extension numbers. Simply provide your project name (e.g. -// Object-C plugin) and your porject website (if available) -- there's no need -// to explain how you intend to use them. Usually you only need one extension -// number. You can declare multiple options with only one extension number by -// putting them in a sub-message. See the Custom Options section of the docs -// for examples: +// Objective-C plugin) and your project website (if available) -- there's no +// need to explain how you intend to use them. Usually you only need one +// extension number. You can declare multiple options with only one extension +// number by putting them in a sub-message. See the Custom Options section of +// the docs for examples: // https://developers.google.com/protocol-buffers/docs/proto#options // If this turns out to be popular, a web service will be set up // to automatically assign option numbers. @@ -442,10 +454,31 @@ message FieldOptions { // The packed option can be enabled for repeated primitive fields to enable // a more efficient representation on the wire. Rather than repeatedly // writing the tag and type for each element, the entire array is encoded as - // a single length-delimited blob. + // a single length-delimited blob. In proto3, only explicit setting it to + // false will avoid using packed encoding. optional bool packed = 2; + // The jstype option determines the JavaScript type used for values of the + // field. The option is permitted only for 64 bit integral and fixed types + // (int64, uint64, sint64, fixed64, sfixed64). By default these types are + // represented as JavaScript strings. This avoids loss of precision that can + // happen when a large value is converted to a floating point JavaScript + // numbers. Specifying JS_NUMBER for the jstype causes the generated + // JavaScript code to use the JavaScript "number" type instead of strings. + // This option is an enum to permit additional types to be added, + // e.g. goog.math.Integer. + optional JSType jstype = 6 [default = JS_NORMAL]; + enum JSType { + // Use the default type. + JS_NORMAL = 0; + + // Use JavaScript strings. + JS_STRING = 1; + + // Use JavaScript numbers. + JS_NUMBER = 2; + } // Should this field be parsed lazily? Lazy applies only to message-type // fields. It means that when the outer message is initially parsed, the diff --git a/src/google/protobuf/descriptor_database.h b/src/google/protobuf/descriptor_database.h index 934e4022..86002d56 100644 --- a/src/google/protobuf/descriptor_database.h +++ b/src/google/protobuf/descriptor_database.h @@ -312,7 +312,7 @@ class LIBPROTOBUF_EXPORT EncodedDescriptorDatabase : public DescriptorDatabase { // A DescriptorDatabase that fetches files from a given pool. class LIBPROTOBUF_EXPORT DescriptorPoolDatabase : public DescriptorDatabase { public: - DescriptorPoolDatabase(const DescriptorPool& pool); + explicit DescriptorPoolDatabase(const DescriptorPool& pool); ~DescriptorPoolDatabase(); // implements DescriptorDatabase ----------------------------------- @@ -341,7 +341,7 @@ class LIBPROTOBUF_EXPORT MergedDescriptorDatabase : public DescriptorDatabase { // Merge more than two databases. The sources remain property of the caller. // The vector may be deleted after the constructor returns but the // DescriptorDatabases need to stick around. - MergedDescriptorDatabase(const vector& sources); + explicit MergedDescriptorDatabase(const vector& sources); ~MergedDescriptorDatabase(); // implements DescriptorDatabase ----------------------------------- diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc index fdce3d78..760df097 100644 --- a/src/google/protobuf/descriptor_unittest.cc +++ b/src/google/protobuf/descriptor_unittest.cc @@ -139,6 +139,14 @@ DescriptorProto::ExtensionRange* AddExtensionRange(DescriptorProto* parent, return result; } +DescriptorProto::ReservedRange* AddReservedRange(DescriptorProto* parent, + int start, int end) { + DescriptorProto::ReservedRange* result = parent->add_reserved_range(); + result->set_start(start); + result->set_end(end); + return result; +} + EnumValueDescriptorProto* AddEnumValue(EnumDescriptorProto* enum_proto, const string& name, int number) { EnumValueDescriptorProto* result = enum_proto->add_value(); @@ -1720,6 +1728,84 @@ TEST_F(ExtensionDescriptorTest, DuplicateFieldNumber) { // =================================================================== +// Test reserved fields. +class ReservedDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // message Foo { + // reserved 2, 9 to 11, 15; + // reserved "foo", "bar"; + // } + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + + DescriptorProto* foo = AddMessage(&foo_file, "Foo"); + AddReservedRange(foo, 2, 3); + AddReservedRange(foo, 9, 12); + AddReservedRange(foo, 15, 16); + + foo->add_reserved_name("foo"); + foo->add_reserved_name("bar"); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + ASSERT_EQ(1, foo_file_->message_type_count()); + foo_ = foo_file_->message_type(0); + } + + DescriptorPool pool_; + const FileDescriptor* foo_file_; + const Descriptor* foo_; +}; + +TEST_F(ReservedDescriptorTest, ReservedRanges) { + ASSERT_EQ(3, foo_->reserved_range_count()); + + EXPECT_EQ(2, foo_->reserved_range(0)->start); + EXPECT_EQ(3, foo_->reserved_range(0)->end); + + EXPECT_EQ(9, foo_->reserved_range(1)->start); + EXPECT_EQ(12, foo_->reserved_range(1)->end); + + EXPECT_EQ(15, foo_->reserved_range(2)->start); + EXPECT_EQ(16, foo_->reserved_range(2)->end); +}; + +TEST_F(ReservedDescriptorTest, IsReservedNumber) { + EXPECT_FALSE(foo_->IsReservedNumber(1)); + EXPECT_TRUE (foo_->IsReservedNumber(2)); + EXPECT_FALSE(foo_->IsReservedNumber(3)); + EXPECT_FALSE(foo_->IsReservedNumber(8)); + EXPECT_TRUE (foo_->IsReservedNumber(9)); + EXPECT_TRUE (foo_->IsReservedNumber(10)); + EXPECT_TRUE (foo_->IsReservedNumber(11)); + EXPECT_FALSE(foo_->IsReservedNumber(12)); + EXPECT_FALSE(foo_->IsReservedNumber(13)); + EXPECT_FALSE(foo_->IsReservedNumber(14)); + EXPECT_TRUE (foo_->IsReservedNumber(15)); + EXPECT_FALSE(foo_->IsReservedNumber(16)); +}; + +TEST_F(ReservedDescriptorTest, ReservedNames) { + ASSERT_EQ(2, foo_->reserved_name_count()); + + EXPECT_EQ("foo", foo_->reserved_name(0)); + EXPECT_EQ("bar", foo_->reserved_name(1)); +}; + +TEST_F(ReservedDescriptorTest, IsReservedName) { + EXPECT_TRUE (foo_->IsReservedName("foo")); + EXPECT_TRUE (foo_->IsReservedName("bar")); + EXPECT_FALSE(foo_->IsReservedName("baz")); +}; + +// =================================================================== + class MiscTest : public testing::Test { protected: // Function which makes a field descriptor of the given type. @@ -2997,10 +3083,10 @@ class ValidationErrorTest : public testing::Test { protected: // Parse file_text as a FileDescriptorProto in text format and add it // to the DescriptorPool. Expect no errors. - void BuildFile(const string& file_text) { + const FileDescriptor* BuildFile(const string& file_text) { FileDescriptorProto file_proto; - ASSERT_TRUE(TextFormat::ParseFromString(file_text, &file_proto)); - ASSERT_TRUE(pool_.BuildFile(file_proto) != NULL); + EXPECT_TRUE(TextFormat::ParseFromString(file_text, &file_proto)); + return GOOGLE_CHECK_NOTNULL(pool_.BuildFile(file_proto)); } // Parse file_text as a FileDescriptorProto in text format and add it @@ -3251,6 +3337,102 @@ TEST_F(ValidationErrorTest, OverlappingExtensionRanges) { "already-defined range 20 to 29.\n"); } +TEST_F(ValidationErrorTest, ReservedFieldError) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: 15 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " reserved_range { start: 10 end: 20 }" + "}", + + "foo.proto: Foo.foo: NUMBER: Field \"foo\" uses reserved number 15.\n"); +} + +TEST_F(ValidationErrorTest, ReservedExtensionRangeError) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension_range { start: 10 end: 20 }" + " reserved_range { start: 5 end: 15 }" + "}", + + "foo.proto: Foo: NUMBER: Extension range 10 to 19" + " overlaps with reserved range 5 to 14.\n"); +} + +TEST_F(ValidationErrorTest, ReservedExtensionRangeAdjacent) { + BuildFile( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension_range { start: 10 end: 20 }" + " reserved_range { start: 5 end: 10 }" + "}"); +} + +TEST_F(ValidationErrorTest, ReservedRangeOverlap) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " reserved_range { start: 10 end: 20 }" + " reserved_range { start: 5 end: 15 }" + "}", + + "foo.proto: Foo: NUMBER: Reserved range 5 to 14" + " overlaps with already-defined range 10 to 19.\n"); +} + +TEST_F(ValidationErrorTest, ReservedNameError) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: 15 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name: \"bar\" number: 16 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name: \"baz\" number: 17 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " reserved_name: \"foo\"" + " reserved_name: \"bar\"" + "}", + + "foo.proto: Foo.foo: NAME: Field name \"foo\" is reserved.\n" + "foo.proto: Foo.bar: NAME: Field name \"bar\" is reserved.\n"); +} + +TEST_F(ValidationErrorTest, ReservedNameRedundant) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " reserved_name: \"foo\"" + " reserved_name: \"foo\"" + "}", + + "foo.proto: foo: NAME: Field name \"foo\" is reserved multiple times.\n"); +} + +TEST_F(ValidationErrorTest, ReservedFieldsDebugString) { + const FileDescriptor* file = BuildFile( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " reserved_name: \"foo\"" + " reserved_name: \"bar\"" + " reserved_range { start: 5 end: 6 }" + " reserved_range { start: 10 end: 20 }" + "}"); + + ASSERT_EQ( + "syntax = \"proto2\";\n\n" + "message Foo {\n" + " reserved 5, 10 to 19;\n" + " reserved \"foo\", \"bar\";\n" + "}\n\n", + file->DebugString()); +} + TEST_F(ValidationErrorTest, InvalidDefaults) { BuildFileWithErrors( "name: \"foo.proto\" " @@ -3399,6 +3581,48 @@ TEST_F(ValidationErrorTest, FieldOneofIndexNegative) { "range for type \"Foo\".\n"); } +TEST_F(ValidationErrorTest, OneofFieldsConsecutiveDefinition) { + // Fields belonging to the same oneof must be defined consecutively. + BuildFileWithWarnings( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo1\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 0 }" + " field { name:\"bar\" number: 2 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name:\"foo2\" number: 3 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 0 }" + " oneof_decl { name:\"foos\" }" + "}", + + "foo.proto: Foo.bar: OTHER: Fields in the same oneof must be defined " + "consecutively. \"bar\" cannot be defined before the completion of the " + "\"foos\" oneof definition.\n"); + + // Prevent interleaved fields, which belong to different oneofs. + BuildFileWithWarnings( + "name: \"foo2.proto\" " + "message_type {" + " name: \"Foo2\"" + " field { name:\"foo1\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 0 }" + " field { name:\"bar1\" number: 2 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 1 }" + " field { name:\"foo2\" number: 3 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 0 }" + " field { name:\"bar2\" number: 4 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 1 }" + " oneof_decl { name:\"foos\" }" + " oneof_decl { name:\"bars\" }" + "}", + "foo2.proto: Foo2.bar1: OTHER: Fields in the same oneof must be defined " + "consecutively. \"bar1\" cannot be defined before the completion of the " + "\"foos\" oneof definition.\n" + "foo2.proto: Foo2.foo2: OTHER: Fields in the same oneof must be defined " + "consecutively. \"foo2\" cannot be defined before the completion of the " + "\"bars\" oneof definition.\n"); +} + TEST_F(ValidationErrorTest, FieldNumberConflict) { BuildFileWithErrors( "name: \"foo.proto\" " @@ -5293,6 +5517,22 @@ TEST_F(ValidationErrorTest, ValidateProto3LiteRuntime) { "in proto3.\n"); } +TEST_F(ValidationErrorTest, ValidateProto3Group) { + BuildFileWithErrors( + "name: 'foo.proto' " + "syntax: 'proto3' " + "message_type { " + " name: 'Foo' " + " nested_type { " + " name: 'FooGroup' " + " } " + " field { name:'foo_group' number: 1 label:LABEL_OPTIONAL " + " type: TYPE_GROUP type_name:'FooGroup' } " + "}", + "foo.proto: Foo.foo_group: TYPE: Groups are not supported in proto3 " + "syntax.\n"); +} + TEST_F(ValidationErrorTest, ValidateProto3EnumFromProto2) { // Define an enum in a proto2 file. diff --git a/src/google/protobuf/duration.pb.cc b/src/google/protobuf/duration.pb.cc index 4a11209e..f78fce21 100644 --- a/src/google/protobuf/duration.pb.cc +++ b/src/google/protobuf/duration.pb.cc @@ -117,7 +117,7 @@ const int Duration::kNanosFieldNumber; #endif // !_MSC_VER Duration::Duration() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Duration) } @@ -310,9 +310,9 @@ int Duration::ByteSize() const { void Duration::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Duration* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Duration* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -370,7 +370,7 @@ void Duration::InternalSwap(Duration* other) { // Duration // optional int64 seconds = 1; - void Duration::clear_seconds() { +void Duration::clear_seconds() { seconds_ = GOOGLE_LONGLONG(0); } ::google::protobuf::int64 Duration::seconds() const { @@ -384,7 +384,7 @@ void Duration::InternalSwap(Duration* other) { } // optional int32 nanos = 2; - void Duration::clear_nanos() { +void Duration::clear_nanos() { nanos_ = 0; } ::google::protobuf::int32 Duration::nanos() const { diff --git a/src/google/protobuf/empty.pb.cc b/src/google/protobuf/empty.pb.cc index f7ca5013..1a9a6661 100644 --- a/src/google/protobuf/empty.pb.cc +++ b/src/google/protobuf/empty.pb.cc @@ -111,7 +111,7 @@ static void MergeFromFail(int line) { #endif // !_MSC_VER Empty::Empty() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Empty) } @@ -221,9 +221,9 @@ int Empty::ByteSize() const { void Empty::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Empty* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Empty* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { diff --git a/src/google/protobuf/extension_set.cc b/src/google/protobuf/extension_set.cc index 03b38dd0..649ae184 100644 --- a/src/google/protobuf/extension_set.cc +++ b/src/google/protobuf/extension_set.cc @@ -314,7 +314,7 @@ void ExtensionSet::Add##CAMELCASE(int number, FieldType type, \ extension->is_repeated = true; \ extension->is_packed = packed; \ extension->repeated_##LOWERCASE##_value = \ - Arena::Create >(arena_, arena_); \ + Arena::CreateMessage >(arena_); \ } else { \ GOOGLE_DCHECK_TYPE(*extension, REPEATED, UPPERCASE); \ GOOGLE_DCHECK_EQ(extension->is_packed, packed); \ @@ -359,43 +359,43 @@ void* ExtensionSet::MutableRawRepeatedField(int number, FieldType field_type, static_cast(field_type))) { case WireFormatLite::CPPTYPE_INT32: extension->repeated_int32_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_INT64: extension->repeated_int64_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_UINT32: extension->repeated_uint32_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_UINT64: extension->repeated_uint64_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_DOUBLE: extension->repeated_double_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_FLOAT: extension->repeated_float_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_BOOL: extension->repeated_bool_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_ENUM: extension->repeated_enum_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_STRING: extension->repeated_string_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_MESSAGE: extension->repeated_message_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; } } @@ -468,7 +468,7 @@ void ExtensionSet::AddEnum(int number, FieldType type, extension->is_repeated = true; extension->is_packed = packed; extension->repeated_enum_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); } else { GOOGLE_DCHECK_TYPE(*extension, REPEATED, ENUM); GOOGLE_DCHECK_EQ(extension->is_packed, packed); @@ -529,7 +529,7 @@ string* ExtensionSet::AddString(int number, FieldType type, extension->is_repeated = true; extension->is_packed = false; extension->repeated_string_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); } else { GOOGLE_DCHECK_TYPE(*extension, REPEATED, STRING); } @@ -632,6 +632,35 @@ void ExtensionSet::SetAllocatedMessage(int number, FieldType type, extension->is_cleared = false; } +void ExtensionSet::UnsafeArenaSetAllocatedMessage( + int number, FieldType type, const FieldDescriptor* descriptor, + MessageLite* message) { + if (message == NULL) { + ClearExtension(number); + return; + } + Extension* extension; + if (MaybeNewExtension(number, descriptor, &extension)) { + extension->type = type; + GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE); + extension->is_repeated = false; + extension->is_lazy = false; + extension->message_value = message; + } else { + GOOGLE_DCHECK_TYPE(*extension, OPTIONAL, MESSAGE); + if (extension->is_lazy) { + extension->lazymessage_value->UnsafeArenaSetAllocatedMessage(message); + } else { + if (arena_ == NULL) { + delete extension->message_value; + } + extension->message_value = message; + } + } + extension->is_cleared = false; +} + + MessageLite* ExtensionSet::ReleaseMessage(int number, const MessageLite& prototype) { map::iterator iter = extensions_.find(number); @@ -712,7 +741,7 @@ MessageLite* ExtensionSet::AddMessage(int number, FieldType type, GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE); extension->is_repeated = true; extension->repeated_message_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); } else { GOOGLE_DCHECK_TYPE(*extension, REPEATED, MESSAGE); } @@ -866,7 +895,7 @@ void ExtensionSet::InternalExtensionMergeFrom( case WireFormatLite::CPPTYPE_##UPPERCASE: \ if (is_new) { \ extension->repeated_##LOWERCASE##_value = \ - Arena::Create(arena_, arena_); \ + Arena::CreateMessage(arena_); \ } \ extension->repeated_##LOWERCASE##_value->MergeFrom( \ *other_extension.repeated_##LOWERCASE##_value); \ @@ -886,7 +915,7 @@ void ExtensionSet::InternalExtensionMergeFrom( case WireFormatLite::CPPTYPE_MESSAGE: if (is_new) { extension->repeated_message_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); } // We can't call RepeatedPtrField::MergeFrom() because // it would attempt to allocate new objects. diff --git a/src/google/protobuf/extension_set.h b/src/google/protobuf/extension_set.h index 6d6702b3..c371e011 100644 --- a/src/google/protobuf/extension_set.h +++ b/src/google/protobuf/extension_set.h @@ -194,7 +194,7 @@ class LIBPROTOBUF_EXPORT ExtensionSet { // directly, unless you are doing low-level memory management. // // When calling any of these accessors, the extension number requested - // MUST exist in the DescriptorPool provided to the constructor. Otheriwse, + // MUST exist in the DescriptorPool provided to the constructor. Otherwise, // the method will fail an assert. Normally, though, you would not call // these directly; you would either call the generated accessors of your // message class (e.g. GetExtension()) or you would call the accessors @@ -262,6 +262,9 @@ class LIBPROTOBUF_EXPORT ExtensionSet { void SetAllocatedMessage(int number, FieldType type, const FieldDescriptor* descriptor, MessageLite* message); + void UnsafeArenaSetAllocatedMessage(int number, FieldType type, + const FieldDescriptor* descriptor, + MessageLite* message); MessageLite* ReleaseMessage(int number, const MessageLite& prototype); MessageLite* UnsafeArenaReleaseMessage( int number, const MessageLite& prototype); @@ -432,6 +435,7 @@ class LIBPROTOBUF_EXPORT ExtensionSet { const MessageLite& prototype) const = 0; virtual MessageLite* MutableMessage(const MessageLite& prototype) = 0; virtual void SetAllocatedMessage(MessageLite *message) = 0; + virtual void UnsafeArenaSetAllocatedMessage(MessageLite *message) = 0; virtual MessageLite* ReleaseMessage(const MessageLite& prototype) = 0; virtual MessageLite* UnsafeArenaReleaseMessage( const MessageLite& prototype) = 0; diff --git a/src/google/protobuf/extension_set_heavy.cc b/src/google/protobuf/extension_set_heavy.cc index 796e7a5f..330bd828 100644 --- a/src/google/protobuf/extension_set_heavy.cc +++ b/src/google/protobuf/extension_set_heavy.cc @@ -221,7 +221,7 @@ MessageLite* ExtensionSet::AddMessage(const FieldDescriptor* descriptor, GOOGLE_DCHECK_EQ(cpp_type(extension->type), FieldDescriptor::CPPTYPE_MESSAGE); extension->is_repeated = true; extension->repeated_message_value = - ::google::protobuf::Arena::Create >(arena_, arena_); + ::google::protobuf::Arena::CreateMessage >(arena_); } else { GOOGLE_DCHECK_TYPE(*extension, REPEATED, MESSAGE); } diff --git a/src/google/protobuf/field_mask.pb.cc b/src/google/protobuf/field_mask.pb.cc index b00461d5..68314fd3 100644 --- a/src/google/protobuf/field_mask.pb.cc +++ b/src/google/protobuf/field_mask.pb.cc @@ -114,7 +114,7 @@ const int FieldMask::kPathsFieldNumber; #endif // !_MSC_VER FieldMask::FieldMask() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FieldMask) } @@ -277,9 +277,9 @@ int FieldMask::ByteSize() const { void FieldMask::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FieldMask* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FieldMask* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -331,10 +331,10 @@ void FieldMask::InternalSwap(FieldMask* other) { // FieldMask // repeated string paths = 1; - int FieldMask::paths_size() const { +int FieldMask::paths_size() const { return paths_.size(); } - void FieldMask::clear_paths() { +void FieldMask::clear_paths() { paths_.Clear(); } const ::std::string& FieldMask::paths(int index) const { diff --git a/src/google/protobuf/generated_message_reflection.h b/src/google/protobuf/generated_message_reflection.h index 4dddf6c7..dc8abb98 100644 --- a/src/google/protobuf/generated_message_reflection.h +++ b/src/google/protobuf/generated_message_reflection.h @@ -40,6 +40,7 @@ #include #include +#include #include // TODO(jasonh): Remove this once the compiler change to directly include this // is released to components. @@ -597,6 +598,42 @@ inline To dynamic_cast_if_available(From from) { #endif } +// Tries to downcast this message to a generated message type. +// Returns NULL if this class is not an instance of T. +// +// This is like dynamic_cast_if_available, except it works even when +// dynamic_cast is not available by using Reflection. However it only works +// with Message objects. +// +// TODO(haberman): can we remove dynamic_cast_if_available in favor of this? +template +T* DynamicCastToGenerated(const Message* from) { + // Compile-time assert that T is a generated type that has a + // default_instance() accessor, but avoid actually calling it. + const T&(*get_default_instance)() = &T::default_instance; + (void)get_default_instance; + + // Compile-time assert that T is a subclass of google::protobuf::Message. + const Message* unused = static_cast(NULL); + (void)unused; + +#if defined(GOOGLE_PROTOBUF_NO_RTTI) || \ + (defined(_MSC_VER) && !defined(_CPPRTTI)) + bool ok = &T::default_instance() == + from->GetReflection()->GetMessageFactory()->GetPrototype( + from->GetDescriptor()); + return ok ? down_cast(from) : NULL; +#else + return dynamic_cast(from); +#endif +} + +template +T* DynamicCastToGenerated(Message* from) { + const Message* message_const = from; + return const_cast(DynamicCastToGenerated(message_const)); +} + } // namespace internal } // namespace protobuf diff --git a/src/google/protobuf/generated_message_util.h b/src/google/protobuf/generated_message_util.h index 678f92a7..6357e27d 100644 --- a/src/google/protobuf/generated_message_util.h +++ b/src/google/protobuf/generated_message_util.h @@ -47,6 +47,10 @@ namespace google { namespace protobuf { + +class Arena; +namespace io { class CodedInputStream; } + namespace internal { @@ -106,6 +110,15 @@ template bool AllAreInitialized(const Type& t) { return true; } +class ArenaString; + +// Read a length (varint32), followed by a string, from *input. Return a +// pointer to a copy of the string that resides in *arena. Requires both +// args to be non-NULL. If something goes wrong while reading the data +// then NULL is returned (e.g., input does not start with a valid varint). +ArenaString* ReadArenaString(::google::protobuf::io::CodedInputStream* input, + ::google::protobuf::Arena* arena); + } // namespace internal } // namespace protobuf diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc index 93e1a22e..3b8650d6 100644 --- a/src/google/protobuf/io/coded_stream.cc +++ b/src/google/protobuf/io/coded_stream.cc @@ -156,6 +156,11 @@ CodedInputStream::IncrementRecursionDepthAndPushLimit(int byte_limit) { return std::make_pair(PushLimit(byte_limit), --recursion_budget_); } +CodedInputStream::Limit CodedInputStream::ReadLengthAndPushLimit() { + uint32 length; + return PushLimit(ReadVarint32(&length) ? length : 0); +} + bool CodedInputStream::DecrementRecursionDepthAndPopLimit(Limit limit) { bool result = ConsumedEntireMessage(); PopLimit(limit); @@ -164,6 +169,12 @@ bool CodedInputStream::DecrementRecursionDepthAndPopLimit(Limit limit) { return result; } +bool CodedInputStream::CheckEntireMessageConsumedAndPopLimit(Limit limit) { + bool result = ConsumedEntireMessage(); + PopLimit(limit); + return result; +} + int CodedInputStream::BytesUntilLimit() const { if (current_limit_ == INT_MAX) return -1; int current_position = CurrentPosition(); @@ -245,20 +256,7 @@ bool CodedInputStream::GetDirectBufferPointer(const void** data, int* size) { } bool CodedInputStream::ReadRaw(void* buffer, int size) { - int current_buffer_size; - while ((current_buffer_size = BufferSize()) < size) { - // Reading past end of buffer. Copy what we have, then refresh. - memcpy(buffer, buffer_, current_buffer_size); - buffer = reinterpret_cast(buffer) + current_buffer_size; - size -= current_buffer_size; - Advance(current_buffer_size); - if (!Refresh()) return false; - } - - memcpy(buffer, buffer_, size); - Advance(size); - - return true; + return InternalReadRawInline(buffer, size); } bool CodedInputStream::ReadString(string* buffer, int size) { @@ -336,17 +334,23 @@ bool CodedInputStream::ReadLittleEndian64Fallback(uint64* value) { namespace { -inline const uint8* ReadVarint32FromArray( - const uint8* buffer, uint32* value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; -inline const uint8* ReadVarint32FromArray(const uint8* buffer, uint32* value) { +// Read a varint from the given buffer, write it to *value, and return a pair. +// The first part of the pair is true iff the read was successful. The second +// part is buffer + (number of bytes read). This function is always inlined, +// so returning a pair is costless. +inline ::std::pair ReadVarint32FromArray( + uint32 first_byte, const uint8* buffer, + uint32* value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; +inline ::std::pair ReadVarint32FromArray( + uint32 first_byte, const uint8* buffer, uint32* value) { // Fast path: We have enough bytes left in the buffer to guarantee that // this read won't cross the end, so we can skip the checks. + GOOGLE_DCHECK_EQ(*buffer, first_byte); + GOOGLE_DCHECK_EQ(first_byte & 0x80, 0x80) << first_byte; const uint8* ptr = buffer; uint32 b; - uint32 result; - - b = *(ptr++); result = b ; if (!(b & 0x80)) goto done; - result -= 0x80; + uint32 result = first_byte - 0x80; + ++ptr; // We just processed the first byte. Move on to the second. b = *(ptr++); result += b << 7; if (!(b & 0x80)) goto done; result -= 0x80 << 7; b = *(ptr++); result += b << 14; if (!(b & 0x80)) goto done; @@ -364,38 +368,42 @@ inline const uint8* ReadVarint32FromArray(const uint8* buffer, uint32* value) { // We have overrun the maximum size of a varint (10 bytes). Assume // the data is corrupt. - return NULL; + return std::make_pair(false, ptr); done: *value = result; - return ptr; + return std::make_pair(true, ptr); } } // namespace bool CodedInputStream::ReadVarint32Slow(uint32* value) { - uint64 result; // Directly invoke ReadVarint64Fallback, since we already tried to optimize // for one-byte varints. - if (!ReadVarint64Fallback(&result)) return false; - *value = (uint32)result; - return true; + std::pair p = ReadVarint64Fallback(); + *value = static_cast(p.first); + return p.second; } -bool CodedInputStream::ReadVarint32Fallback(uint32* value) { +int64 CodedInputStream::ReadVarint32Fallback(uint32 first_byte_or_zero) { if (BufferSize() >= kMaxVarintBytes || // Optimization: We're also safe if the buffer is non-empty and it ends // with a byte that would terminate a varint. (buffer_end_ > buffer_ && !(buffer_end_[-1] & 0x80))) { - const uint8* end = ReadVarint32FromArray(buffer_, value); - if (end == NULL) return false; - buffer_ = end; - return true; + GOOGLE_DCHECK_NE(first_byte_or_zero, 0) + << "Caller should provide us with *buffer_ when buffer is non-empty"; + uint32 temp; + ::std::pair p = + ReadVarint32FromArray(first_byte_or_zero, buffer_, &temp); + if (!p.first) return -1; + buffer_ = p.second; + return temp; } else { // Really slow case: we will incur the cost of an extra function call here, // but moving this out of line reduces the size of this function, which // improves the common case. In micro benchmarks, this is worth about 10-15% - return ReadVarint32Slow(value); + uint32 temp; + return ReadVarint32Slow(&temp) ? static_cast(temp) : -1; } } @@ -425,18 +433,24 @@ uint32 CodedInputStream::ReadTagSlow() { return static_cast(result); } -uint32 CodedInputStream::ReadTagFallback() { +uint32 CodedInputStream::ReadTagFallback(uint32 first_byte_or_zero) { const int buf_size = BufferSize(); if (buf_size >= kMaxVarintBytes || // Optimization: We're also safe if the buffer is non-empty and it ends // with a byte that would terminate a varint. (buf_size > 0 && !(buffer_end_[-1] & 0x80))) { + GOOGLE_DCHECK_EQ(first_byte_or_zero, buffer_[0]); + if (first_byte_or_zero == 0) { + ++buffer_; + return 0; + } uint32 tag; - const uint8* end = ReadVarint32FromArray(buffer_, &tag); - if (end == NULL) { + ::std::pair p = + ReadVarint32FromArray(first_byte_or_zero, buffer_, &tag); + if (!p.first) { return 0; } - buffer_ = end; + buffer_ = p.second; return tag; } else { // We are commonly at a limit when attempting to read tags. Try to quickly @@ -479,7 +493,7 @@ bool CodedInputStream::ReadVarint64Slow(uint64* value) { return true; } -bool CodedInputStream::ReadVarint64Fallback(uint64* value) { +std::pair CodedInputStream::ReadVarint64Fallback() { if (BufferSize() >= kMaxVarintBytes || // Optimization: We're also safe if the buffer is non-empty and it ends // with a byte that would terminate a varint. @@ -517,16 +531,18 @@ bool CodedInputStream::ReadVarint64Fallback(uint64* value) { // We have overrun the maximum size of a varint (10 bytes). The data // must be corrupt. - return false; + return std::make_pair(0, false); done: Advance(ptr - buffer_); - *value = (static_cast(part0) ) | - (static_cast(part1) << 28) | - (static_cast(part2) << 56); - return true; + return std::make_pair((static_cast(part0)) | + (static_cast(part1) << 28) | + (static_cast(part2) << 56), + true); } else { - return ReadVarint64Slow(value); + uint64 temp; + bool success = ReadVarint64Slow(&temp); + return std::make_pair(temp, success); } } diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h index dea4b650..961c1a3f 100644 --- a/src/google/protobuf/io/coded_stream.h +++ b/src/google/protobuf/io/coded_stream.h @@ -197,6 +197,11 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // Read raw bytes, copying them into the given buffer. bool ReadRaw(void* buffer, int size); + // Like the above, with inlined optimizations. This should only be used + // by the protobuf implementation. + inline bool InternalReadRawInline(void* buffer, + int size) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + // Like ReadRaw, but reads into a string. // // Implementation Note: ReadString() grows the string gradually as it @@ -387,9 +392,14 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // under the limit, false if it has gone over. bool IncrementRecursionDepth(); - // Decrements the recursion depth. + // Decrements the recursion depth if possible. void DecrementRecursionDepth(); + // Decrements the recursion depth blindly. This is faster than + // DecrementRecursionDepth(). It should be used only if all previous + // increments to recursion depth were successful. + void UnsafeDecrementRecursionDepth(); + // Shorthand for make_pair(PushLimit(byte_limit), --recursion_budget_). // Using this can reduce code size and complexity in some cases. The caller // is expected to check that the second part of the result is non-negative (to @@ -398,15 +408,25 @@ class LIBPROTOBUF_EXPORT CodedInputStream { std::pair IncrementRecursionDepthAndPushLimit( int byte_limit); + // Shorthand for PushLimit(ReadVarint32(&length) ? length : 0). + Limit ReadLengthAndPushLimit(); + // Helper that is equivalent to: { // bool result = ConsumedEntireMessage(); // PopLimit(limit); - // DecrementRecursionDepth(); + // UnsafeDecrementRecursionDepth(); // return result; } // Using this can reduce code size and complexity in some cases. // Do not use unless the current recursion depth is greater than zero. bool DecrementRecursionDepthAndPopLimit(Limit limit); + // Helper that is equivalent to: { + // bool result = ConsumedEntireMessage(); + // PopLimit(limit); + // return result; } + // Using this can reduce code size and complexity in some cases. + bool CheckEntireMessageConsumedAndPopLimit(Limit limit); + // Extension Registry ---------------------------------------------- // ADVANCED USAGE: 99.9% of people can ignore this section. // @@ -568,9 +588,13 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // optimization. The Slow method is yet another fallback when the buffer is // not large enough. Making the slow path out-of-line speeds up the common // case by 10-15%. The slow path is fairly uncommon: it only triggers when a - // message crosses multiple buffers. - bool ReadVarint32Fallback(uint32* value); - bool ReadVarint64Fallback(uint64* value); + // message crosses multiple buffers. Note: ReadVarint32Fallback() and + // ReadVarint64Fallback() are called frequently and generally not inlined, so + // they have been optimized to avoid "out" parameters. The former returns -1 + // if it fails and the uint32 it read otherwise. The latter has a bool + // indicating success or failure as part of its return type. + int64 ReadVarint32Fallback(uint32 first_byte_or_zero); + std::pair ReadVarint64Fallback(); bool ReadVarint32Slow(uint32* value); bool ReadVarint64Slow(uint64* value); bool ReadLittleEndian32Fallback(uint32* value); @@ -578,7 +602,7 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // Fallback/slow methods for reading tags. These do not update last_tag_, // but will set legitimate_message_end_ if we are at the end of the input // stream. - uint32 ReadTagFallback(); + uint32 ReadTagFallback(uint32 first_byte_or_zero); uint32 ReadTagSlow(); bool ReadStringFallback(string* buffer, int size); @@ -818,13 +842,18 @@ class LIBPROTOBUF_EXPORT CodedOutputStream { // methods optimize for that case. inline bool CodedInputStream::ReadVarint32(uint32* value) { - if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_) && *buffer_ < 0x80) { - *value = *buffer_; - Advance(1); - return true; - } else { - return ReadVarint32Fallback(value); + uint32 v = 0; + if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_)) { + v = *buffer_; + if (v < 0x80) { + *value = v; + Advance(1); + return true; + } } + int64 result = ReadVarint32Fallback(v); + *value = static_cast(result); + return result >= 0; } inline bool CodedInputStream::ReadVarint64(uint64* value) { @@ -832,9 +861,10 @@ inline bool CodedInputStream::ReadVarint64(uint64* value) { *value = *buffer_; Advance(1); return true; - } else { - return ReadVarint64Fallback(value); } + std::pair p = ReadVarint64Fallback(); + *value = p.first; + return p.second; } // static @@ -903,14 +933,17 @@ inline bool CodedInputStream::ReadLittleEndian64(uint64* value) { } inline uint32 CodedInputStream::ReadTag() { - if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_) && buffer_[0] < 0x80) { - last_tag_ = buffer_[0]; - Advance(1); - return last_tag_; - } else { - last_tag_ = ReadTagFallback(); - return last_tag_; + uint32 v = 0; + if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_)) { + v = *buffer_; + if (v < 0x80) { + last_tag_ = v; + Advance(1); + return v; + } } + last_tag_ = ReadTagFallback(v); + return last_tag_; } inline std::pair CodedInputStream::ReadTagWithCutoff( @@ -918,10 +951,12 @@ inline std::pair CodedInputStream::ReadTagWithCutoff( // In performance-sensitive code we can expect cutoff to be a compile-time // constant, and things like "cutoff >= kMax1ByteVarint" to be evaluated at // compile time. + uint32 first_byte_or_zero = 0; if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_)) { // Hot case: buffer_ non_empty, buffer_[0] in [1, 128). // TODO(gpike): Is it worth rearranging this? E.g., if the number of fields // is large enough then is it better to check for the two-byte case first? + first_byte_or_zero = buffer_[0]; if (static_cast(buffer_[0]) > 0) { const uint32 kMax1ByteVarint = 0x7f; uint32 tag = last_tag_ = buffer_[0]; @@ -948,7 +983,7 @@ inline std::pair CodedInputStream::ReadTagWithCutoff( } } // Slow path - last_tag_ = ReadTagFallback(); + last_tag_ = ReadTagFallback(first_byte_or_zero); return std::make_pair(last_tag_, static_cast(last_tag_ - 1) < cutoff); } @@ -1177,6 +1212,11 @@ inline void CodedInputStream::DecrementRecursionDepth() { if (recursion_budget_ < recursion_limit_) ++recursion_budget_; } +inline void CodedInputStream::UnsafeDecrementRecursionDepth() { + assert(recursion_budget_ < recursion_limit_); + ++recursion_budget_; +} + inline void CodedInputStream::SetExtensionRegistry(const DescriptorPool* pool, MessageFactory* factory) { extension_pool_ = pool; diff --git a/src/google/protobuf/io/coded_stream_inl.h b/src/google/protobuf/io/coded_stream_inl.h index cd8d1746..fa20f208 100644 --- a/src/google/protobuf/io/coded_stream_inl.h +++ b/src/google/protobuf/io/coded_stream_inl.h @@ -66,6 +66,23 @@ inline bool CodedInputStream::InternalReadStringInline(string* buffer, return ReadStringFallback(buffer, size); } +inline bool CodedInputStream::InternalReadRawInline(void* buffer, int size) { + int current_buffer_size; + while ((current_buffer_size = BufferSize()) < size) { + // Reading past end of buffer. Copy what we have, then refresh. + memcpy(buffer, buffer_, current_buffer_size); + buffer = reinterpret_cast(buffer) + current_buffer_size; + size -= current_buffer_size; + Advance(current_buffer_size); + if (!Refresh()) return false; + } + + memcpy(buffer, buffer_, size); + Advance(size); + + return true; +} + } // namespace io } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/io/printer.cc b/src/google/protobuf/io/printer.cc index e621ba1d..3ae8c268 100644 --- a/src/google/protobuf/io/printer.cc +++ b/src/google/protobuf/io/printer.cc @@ -155,6 +155,78 @@ 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, + const char* variable4, const string& value4, + const char* variable5, const string& value5) { + map vars; + vars[variable1] = value1; + vars[variable2] = value2; + vars[variable3] = value3; + vars[variable4] = value4; + vars[variable5] = value5; + 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, + const char* variable4, const string& value4, + const char* variable5, const string& value5, + const char* variable6, const string& value6) { + map vars; + vars[variable1] = value1; + vars[variable2] = value2; + vars[variable3] = value3; + vars[variable4] = value4; + vars[variable5] = value5; + vars[variable6] = value6; + 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, + const char* variable4, const string& value4, + const char* variable5, const string& value5, + const char* variable6, const string& value6, + const char* variable7, const string& value7) { + map vars; + vars[variable1] = value1; + vars[variable2] = value2; + vars[variable3] = value3; + vars[variable4] = value4; + vars[variable5] = value5; + vars[variable6] = value6; + vars[variable7] = value7; + 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, + const char* variable4, const string& value4, + const char* variable5, const string& value5, + const char* variable6, const string& value6, + const char* variable7, const string& value7, + const char* variable8, const string& value8) { + map vars; + vars[variable1] = value1; + vars[variable2] = value2; + vars[variable3] = value3; + vars[variable4] = value4; + vars[variable5] = value5; + vars[variable6] = value6; + vars[variable7] = value7; + vars[variable8] = value8; + Print(vars, text); +} + void Printer::Indent() { indent_ += " "; } diff --git a/src/google/protobuf/io/printer.h b/src/google/protobuf/io/printer.h index 92ce3409..f1490bbe 100644 --- a/src/google/protobuf/io/printer.h +++ b/src/google/protobuf/io/printer.h @@ -91,8 +91,36 @@ class LIBPROTOBUF_EXPORT Printer { const char* variable2, const string& value2, const char* variable3, const string& value3, const char* variable4, const string& value4); - // TODO(kenton): Overloaded versions with more variables? Three seems - // to be enough. + // 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, + const char* variable4, const string& value4, + const char* variable5, const string& value5); + // 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, + const char* variable4, const string& value4, + const char* variable5, const string& value5, + const char* variable6, const string& value6); + // 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, + const char* variable4, const string& value4, + const char* variable5, const string& value5, + const char* variable6, const string& value6, + const char* variable7, const string& value7); + // 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, + const char* variable4, const string& value4, + const char* variable5, const string& value5, + const char* variable6, const string& value6, + const char* variable7, const string& value7, + const char* variable8, const string& value8); // Indent text by two spaces. After calling Indent(), two spaces will be // inserted at the beginning of each line of text. Indent() may be called diff --git a/src/google/protobuf/lite_unittest.cc b/src/google/protobuf/lite_unittest.cc index 4ea21007..1995cf08 100644 --- a/src/google/protobuf/lite_unittest.cc +++ b/src/google/protobuf/lite_unittest.cc @@ -34,12 +34,16 @@ #include #include +#include +#include +#include #include #include #include #include #include #include +#include using namespace std; @@ -84,6 +88,12 @@ void SetSomeTypesInEmptyMessageUnknownFields( } // namespace +#define EXPECT_TRUE GOOGLE_CHECK +#define ASSERT_TRUE GOOGLE_CHECK +#define EXPECT_FALSE(COND) GOOGLE_CHECK(!(COND)) +#define EXPECT_EQ GOOGLE_CHECK_EQ +#define ASSERT_EQ GOOGLE_CHECK_EQ + int main(int argc, char* argv[]) { string data, data2, packed_data; @@ -345,6 +355,374 @@ int main(int argc, char* argv[]) { GOOGLE_CHECK_EQ(0, empty_message.unknown_fields().size()); } + // Tests for map lite ============================================= + + { + // Accessors + protobuf_unittest::TestMapLite message; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message); + + google::protobuf::MapLiteTestUtil::ModifyMapFields(&message); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsModified(message); + } + + { + // SetMapFieldsInitialized + protobuf_unittest::TestMapLite message; + + google::protobuf::MapLiteTestUtil::SetMapFieldsInitialized(&message); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSetInitialized(message); + } + + { + // Proto2SetMapFieldsInitialized + protobuf_unittest::TestEnumStartWithNonZeroMapLite message; + EXPECT_EQ(protobuf_unittest::PROTO2_NON_ZERO_MAP_ENUM_FOO_LITE, + (*message.mutable_map_field())[0]); + } + + { + // Clear + protobuf_unittest::TestMapLite message; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message); + message.Clear(); + google::protobuf::MapLiteTestUtil::ExpectClear(message); + } + + { + // ClearMessageMap + protobuf_unittest::TestMessageMapLite message; + + // Creates a TestAllTypes with default value + google::protobuf::TestUtilLite::ExpectClear( + (*message.mutable_map_int32_message())[0]); + } + + { + // CopyFrom + protobuf_unittest::TestMapLite message1, message2; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + message2.CopyFrom(message1); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + + // Copying from self should be a no-op. + message2.CopyFrom(message2); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + } + + { + // CopyFromMessageMap + protobuf_unittest::TestMessageMapLite message1, message2; + + (*message1.mutable_map_int32_message())[0].add_repeated_int32(100); + (*message2.mutable_map_int32_message())[0].add_repeated_int32(101); + + message1.CopyFrom(message2); + + // Checks repeated field is overwritten. + EXPECT_EQ(1, message1.map_int32_message().at(0).repeated_int32_size()); + EXPECT_EQ(101, message1.map_int32_message().at(0).repeated_int32(0)); + } + + { + // SwapWithEmpty + protobuf_unittest::TestMapLite message1, message2; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message1); + google::protobuf::MapLiteTestUtil::ExpectClear(message2); + + message1.Swap(&message2); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + google::protobuf::MapLiteTestUtil::ExpectClear(message1); + } + + { + // SwapWithSelf + protobuf_unittest::TestMapLite message; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message); + + message.Swap(&message); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message); + } + + { + // SwapWithOther + protobuf_unittest::TestMapLite message1, message2; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + google::protobuf::MapLiteTestUtil::SetMapFields(&message2); + google::protobuf::MapLiteTestUtil::ModifyMapFields(&message2); + + message1.Swap(&message2); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsModified(message1); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + } + + { + // CopyConstructor + protobuf_unittest::TestMapLite message1; + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + + protobuf_unittest::TestMapLite message2(message1); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + } + + { + // CopyAssignmentOperator + protobuf_unittest::TestMapLite message1; + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + + protobuf_unittest::TestMapLite message2; + message2 = message1; + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + + // Make sure that self-assignment does something sane. + message2.operator=(message2); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + } + + { + // NonEmptyMergeFrom + protobuf_unittest::TestMapLite message1, message2; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + + // This field will test merging into an empty spot. + (*message2.mutable_map_int32_int32())[1] = 1; + message1.mutable_map_int32_int32()->erase(1); + + // This tests overwriting. + (*message2.mutable_map_int32_double())[1] = 1; + (*message1.mutable_map_int32_double())[1] = 2; + + message1.MergeFrom(message2); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message1); + } + + { + // MergeFromMessageMap + protobuf_unittest::TestMessageMapLite message1, message2; + + (*message1.mutable_map_int32_message())[0].add_repeated_int32(100); + (*message2.mutable_map_int32_message())[0].add_repeated_int32(101); + + message1.MergeFrom(message2); + + // Checks repeated field is overwritten. + EXPECT_EQ(1, message1.map_int32_message().at(0).repeated_int32_size()); + EXPECT_EQ(101, message1.map_int32_message().at(0).repeated_int32(0)); + } + + { + // Test the generated SerializeWithCachedSizesToArray() + protobuf_unittest::TestMapLite message1, message2; + string data; + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + int size = message1.ByteSize(); + data.resize(size); + ::google::protobuf::uint8* start = reinterpret_cast< ::google::protobuf::uint8*>(::google::protobuf::string_as_array(&data)); + ::google::protobuf::uint8* end = message1.SerializeWithCachedSizesToArray(start); + EXPECT_EQ(size, end - start); + EXPECT_TRUE(message2.ParseFromString(data)); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + } + + { + // Test the generated SerializeWithCachedSizes() + protobuf_unittest::TestMapLite message1, message2; + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + int size = message1.ByteSize(); + string data; + data.resize(size); + { + // Allow the output stream to buffer only one byte at a time. + google::protobuf::io::ArrayOutputStream array_stream( + ::google::protobuf::string_as_array(&data), size, 1); + google::protobuf::io::CodedOutputStream output_stream(&array_stream); + message1.SerializeWithCachedSizes(&output_stream); + EXPECT_FALSE(output_stream.HadError()); + EXPECT_EQ(size, output_stream.ByteCount()); + } + EXPECT_TRUE(message2.ParseFromString(data)); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + } + + + { + // Proto2UnknownEnum + protobuf_unittest::TestEnumMapPlusExtraLite from; + (*from.mutable_known_map_field())[0] = + protobuf_unittest::E_PROTO2_MAP_ENUM_FOO_LITE; + (*from.mutable_unknown_map_field())[0] = + protobuf_unittest::E_PROTO2_MAP_ENUM_EXTRA_LITE; + string data; + from.SerializeToString(&data); + + protobuf_unittest::TestEnumMapLite to; + EXPECT_TRUE(to.ParseFromString(data)); + EXPECT_EQ(0, to.unknown_map_field().size()); + EXPECT_FALSE(to.mutable_unknown_fields()->empty()); + EXPECT_EQ(1, to.known_map_field().size()); + EXPECT_EQ(protobuf_unittest::PROTO2_MAP_ENUM_FOO_LITE, + to.known_map_field().at(0)); + + data.clear(); + from.Clear(); + to.SerializeToString(&data); + EXPECT_TRUE(from.ParseFromString(data)); + EXPECT_EQ(1, from.known_map_field().size()); + EXPECT_EQ(protobuf_unittest::E_PROTO2_MAP_ENUM_FOO_LITE, + from.known_map_field().at(0)); + EXPECT_EQ(1, from.unknown_map_field().size()); + EXPECT_EQ(protobuf_unittest::E_PROTO2_MAP_ENUM_EXTRA_LITE, + from.unknown_map_field().at(0)); + } + + { + // StandardWireFormat + protobuf_unittest::TestMapLite message; + string data = "\x0A\x04\x08\x01\x10\x01"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(1, message.map_int32_int32().at(1)); + } + + { + // UnorderedWireFormat + protobuf_unittest::TestMapLite message; + + // put value before key in wire format + string data = "\x0A\x04\x10\x01\x08\x02"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(1, message.map_int32_int32().at(2)); + } + + { + // DuplicatedKeyWireFormat + protobuf_unittest::TestMapLite message; + + // Two key fields in wire format + string data = "\x0A\x06\x08\x01\x08\x02\x10\x01"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(1, message.map_int32_int32().at(2)); + } + + { + // DuplicatedValueWireFormat + protobuf_unittest::TestMapLite message; + + // Two value fields in wire format + string data = "\x0A\x06\x08\x01\x10\x01\x10\x02"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(2, message.map_int32_int32().at(1)); + } + + { + // MissedKeyWireFormat + protobuf_unittest::TestMapLite message; + + // No key field in wire format + string data = "\x0A\x02\x10\x01"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(1, message.map_int32_int32().at(0)); + } + + { + // MissedValueWireFormat + protobuf_unittest::TestMapLite message; + + // No value field in wire format + string data = "\x0A\x02\x08\x01"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(0, message.map_int32_int32().at(1)); + } + + { + // UnknownFieldWireFormat + protobuf_unittest::TestMapLite message; + + // Unknown field in wire format + string data = "\x0A\x06\x08\x02\x10\x03\x18\x01"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(3, message.map_int32_int32().at(2)); + } + + { + // CorruptedWireFormat + protobuf_unittest::TestMapLite message; + + // corrupted data in wire format + string data = "\x0A\x06\x08\x02\x11\x03"; + + EXPECT_FALSE(message.ParseFromString(data)); + } + + { + // IsInitialized + protobuf_unittest::TestRequiredMessageMapLite map_message; + + // Add an uninitialized message. + (*map_message.mutable_map_field())[0]; + EXPECT_FALSE(map_message.IsInitialized()); + + // Initialize uninitialized message + (*map_message.mutable_map_field())[0].set_a(0); + (*map_message.mutable_map_field())[0].set_b(0); + (*map_message.mutable_map_field())[0].set_c(0); + EXPECT_TRUE(map_message.IsInitialized()); + } + + // arena support for map ========================================= + + { + // ParsingAndSerializingNoHeapAllocation + + // Allocate a large initial block to avoid mallocs during hooked test. + std::vector arena_block(128 * 1024); + google::protobuf::ArenaOptions options; + options.initial_block = arena_block.data(); + options.initial_block_size = arena_block.size(); + google::protobuf::Arena arena(options); + string data; + data.reserve(128 * 1024); + + { + google::protobuf::internal::NoHeapChecker no_heap; + + protobuf_unittest::TestArenaMapLite* from = + google::protobuf::Arena::CreateMessage( + &arena); + google::protobuf::MapLiteTestUtil::SetArenaMapFields(from); + from->SerializeToString(&data); + + protobuf_unittest::TestArenaMapLite* to = + google::protobuf::Arena::CreateMessage( + &arena); + to->ParseFromString(data); + google::protobuf::MapLiteTestUtil::ExpectArenaMapFieldsSet(*to); + } + } + std::cout << "PASS" << std::endl; return 0; } diff --git a/src/google/protobuf/map.h b/src/google/protobuf/map.h index e56af3fc..1858e2f9 100644 --- a/src/google/protobuf/map.h +++ b/src/google/protobuf/map.h @@ -422,6 +422,7 @@ class Map { int default_enum_value_; friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; typedef void DestructorSkippable_; template >( - arena, key, value, arena); + arena, key, value); } // Like above, but for all the other types. This avoids value copy to create // MapEntryLite from google::protobuf::Map in serialization. static MapEntryLite* Wrap(const Key& key, const Value& value, Arena* arena) { - return Arena::Create >( - arena, key, value, arena); + return Arena::CreateMessage >( + arena, key, value); } protected: @@ -308,7 +309,7 @@ class LIBPROTOBUF_EXPORT MapEntryLite : public MessageLite { typedef typename Base::ValCppType ValCppType; public: - MapEntryWrapper(const K& key, const V& value, Arena* arena) + MapEntryWrapper(Arena* arena, const K& key, const V& value) : MapEntryLite(arena), key_(key), value_(value) { @@ -323,6 +324,7 @@ class LIBPROTOBUF_EXPORT MapEntryLite : public MessageLite { const Value& value_; friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; typedef void DestructorSkippable_; }; @@ -341,7 +343,7 @@ class LIBPROTOBUF_EXPORT MapEntryLite : public MessageLite { typedef typename Base::ValCppType ValCppType; public: - MapEnumEntryWrapper(const K& key, const V& value, Arena* arena) + MapEnumEntryWrapper(Arena* arena, const K& key, const V& value) : MapEntryLite(arena), key_(key), value_(value) { @@ -355,7 +357,7 @@ class LIBPROTOBUF_EXPORT MapEntryLite : public MessageLite { const KeyCppType& key_; const ValCppType value_; - friend class ::google::protobuf::Arena; + friend class LIBPROTOBUF_EXPORT google::protobuf::Arena; typedef void DestructorSkippable_; }; diff --git a/src/google/protobuf/map_field.cc b/src/google/protobuf/map_field.cc index fd40c0d8..6ff1936e 100644 --- a/src/google/protobuf/map_field.cc +++ b/src/google/protobuf/map_field.cc @@ -104,14 +104,18 @@ void MapFieldBase::SetRepeatedDirty() { state_ = STATE_MODIFIED_REPEATED; } void* MapFieldBase::MutableRepeatedPtrField() const { return repeated_field_; } void MapFieldBase::SyncRepeatedFieldWithMap() const { - Atomic32 state = google::protobuf::internal::NoBarrier_Load(&state_); + // "Acquire" insures the operation after SyncRepeatedFieldWithMap won't get + // executed before state_ is checked. + Atomic32 state = google::protobuf::internal::Acquire_Load(&state_); if (state == STATE_MODIFIED_MAP) { mutex_.Lock(); // Double check state, because another thread may have seen the same state // and done the synchronization before the current thread. if (state_ == STATE_MODIFIED_MAP) { SyncRepeatedFieldWithMapNoLock(); - google::protobuf::internal::NoBarrier_Store(&state_, CLEAN); + // "Release" insures state_ can only be changed "after" + // SyncRepeatedFieldWithMapNoLock is finished. + google::protobuf::internal::Release_Store(&state_, CLEAN); } mutex_.Unlock(); } @@ -119,19 +123,23 @@ void MapFieldBase::SyncRepeatedFieldWithMap() const { void MapFieldBase::SyncRepeatedFieldWithMapNoLock() const { if (repeated_field_ == NULL) { - repeated_field_ = Arena::Create >(arena_, arena_); + repeated_field_ = Arena::CreateMessage >(arena_); } } void MapFieldBase::SyncMapWithRepeatedField() const { - Atomic32 state = google::protobuf::internal::NoBarrier_Load(&state_); + // "Acquire" insures the operation after SyncMapWithRepeatedField won't get + // executed before state_ is checked. + Atomic32 state = google::protobuf::internal::Acquire_Load(&state_); if (state == STATE_MODIFIED_REPEATED) { mutex_.Lock(); // Double check state, because another thread may have seen the same state // and done the synchronization before the current thread. if (state_ == STATE_MODIFIED_REPEATED) { SyncMapWithRepeatedFieldNoLock(); - google::protobuf::internal::NoBarrier_Store(&state_, CLEAN); + // "Release" insures state_ can only be changed "after" + // SyncRepeatedFieldWithMapNoLock is finished. + google::protobuf::internal::Release_Store(&state_, CLEAN); } mutex_.Unlock(); } diff --git a/src/google/protobuf/map_field.h b/src/google/protobuf/map_field.h index 6d8b6ec8..f3504f1b 100644 --- a/src/google/protobuf/map_field.h +++ b/src/google/protobuf/map_field.h @@ -208,6 +208,7 @@ class LIBPROTOBUF_EXPORT MapField : public MapFieldBase, void SetAssignDescriptorCallback(void (*callback)()); private: + typedef void InternalArenaConstructable_; typedef void DestructorSkippable_; // MapField needs MapEntry's default instance to create new MapEntry. diff --git a/src/google/protobuf/map_field_inl.h b/src/google/protobuf/map_field_inl.h index ae63c721..cbfc0c8f 100644 --- a/src/google/protobuf/map_field_inl.h +++ b/src/google/protobuf/map_field_inl.h @@ -216,7 +216,7 @@ MapField(); } else { repeated_field_ = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); } } const Map& map = GetInternalMap(); diff --git a/src/google/protobuf/map_field_lite.h b/src/google/protobuf/map_field_lite.h index 549ecc08..40322851 100644 --- a/src/google/protobuf/map_field_lite.h +++ b/src/google/protobuf/map_field_lite.h @@ -109,7 +109,7 @@ template ::MapFieldLite(Arena* arena) : arena_(arena) { - map_ = Arena::Create >(arena, arena); + map_ = Arena::CreateMessage >(arena); SetDefaultEnumValue(); } diff --git a/src/google/protobuf/map_field_test.cc b/src/google/protobuf/map_field_test.cc index 61344cbb..f4681866 100644 --- a/src/google/protobuf/map_field_test.cc +++ b/src/google/protobuf/map_field_test.cc @@ -56,6 +56,7 @@ using unittest::TestAllTypes; class MapFieldBaseStub : public MapFieldBase { public: + typedef void InternalArenaConstructable_; typedef void DestructorSkippable_; MapFieldBaseStub() {} explicit MapFieldBaseStub(Arena* arena) : MapFieldBase(arena) {} @@ -149,10 +150,12 @@ TEST_F(MapFieldBasePrimitiveTest, Arena) { Arena arena(options); { - NoHeapChecker no_heap; + // TODO(liujisi): Re-write the test to ensure the memory for the map and + // repeated fields are allocated from arenas. + // NoHeapChecker no_heap; MapFieldType* map_field = - Arena::Create(&arena, &arena, default_entry_); + Arena::CreateMessage(&arena, default_entry_); // Set content in map (*map_field->MutableMap())[100] = 101; @@ -162,10 +165,12 @@ TEST_F(MapFieldBasePrimitiveTest, Arena) { } { - NoHeapChecker no_heap; + // TODO(liujisi): Re-write the test to ensure the memory for the map and + // repeated fields are allocated from arenas. + // NoHeapChecker no_heap; MapFieldBaseStub* map_field = - Arena::Create(&arena, &arena); + Arena::CreateMessage(&arena); // Trigger conversion to repeated field. EXPECT_TRUE(map_field->MutableRepeatedField() != NULL); diff --git a/src/google/protobuf/map_test.cc b/src/google/protobuf/map_test.cc index 88cba1f2..447f52d9 100644 --- a/src/google/protobuf/map_test.cc +++ b/src/google/protobuf/map_test.cc @@ -2258,6 +2258,27 @@ TEST(TextFormatMapTest, SerializeAndParse) { MapTestUtil::ExpectMapFieldsSet(dest); } +TEST(TextFormatMapTest, Sorted) { + unittest::TestMap message; + MapTestUtil::MapReflectionTester tester(message.GetDescriptor()); + tester.SetMapFieldsViaReflection(&message); + + string expected_text; + GOOGLE_CHECK_OK(File::GetContents( + TestSourceDir() + + "/google/protobuf/" + "testdata/map_test_data.txt", + &expected_text, true)); + + EXPECT_EQ(message.DebugString(), expected_text); + + // Test again on the reverse order. + unittest::TestMap message2; + tester.SetMapFieldsViaReflection(&message2); + tester.SwapMapsViaReflection(&message2); + EXPECT_EQ(message2.DebugString(), expected_text); +} + // arena support ================================================= TEST(ArenaTest, ParsingAndSerializingNoHeapAllocation) { diff --git a/src/google/protobuf/map_type_handler.h b/src/google/protobuf/map_type_handler.h index 278b78ae..ffdb6dfb 100644 --- a/src/google/protobuf/map_type_handler.h +++ b/src/google/protobuf/map_type_handler.h @@ -129,11 +129,11 @@ class MapCppTypeHandler : public MapCommonTypeHandler { // Return bytes used by value in Map. static int SpaceUsedInMap(const Type& value) { return value.SpaceUsed(); } static inline void Clear(Type** value) { - if (*value != NULL) (*value)->Type::Clear(); + if (*value != NULL) (*value)->Clear(); } static inline void ClearMaybeByDefaultEnum(Type** value, int default_enum_value) { - if (*value != NULL) (*value)->Type::Clear(); + if (*value != NULL) (*value)->Clear(); } static inline void Merge(const Type& from, Type** to) { (*to)->MergeFrom(from); diff --git a/src/google/protobuf/map_unittest.proto b/src/google/protobuf/map_unittest.proto index b6a988b3..cbf747d9 100644 --- a/src/google/protobuf/map_unittest.proto +++ b/src/google/protobuf/map_unittest.proto @@ -62,7 +62,7 @@ message TestMap { } message TestMapSubmessage { - optional TestMap test_map = 1; + TestMap test_map = 1; } message TestMessageMap { @@ -104,3 +104,17 @@ message TestArenaMap { map map_int32_enum = 14; map map_int32_foreign_message = 15; } + +// Previously, message containing enum called Type cannot be used as value of +// map field. +message MessageContainingEnumCalledType { + enum Type { + TYPE_FOO = 0; + } + map type = 1; +} + +// Previously, message cannot contain map field called "entry". +message MessageContainingMapCalledEntry { + map entry = 1; +} diff --git a/src/google/protobuf/message.cc b/src/google/protobuf/message.cc index f58be848..276d7de5 100644 --- a/src/google/protobuf/message.cc +++ b/src/google/protobuf/message.cc @@ -465,10 +465,21 @@ struct ShutdownRepeatedFieldRegister { } // namespace internal namespace internal { -// Macro defined in repeated_field.h. We can only define the Message-specific -// GenericTypeHandler specializations here because we depend on Message, which -// is not part of proto2-lite hence is not available in repeated_field.h. -DEFINE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES_NOINLINE(Message); +template<> +Message* GenericTypeHandler::NewFromPrototype( + const Message* prototype, google::protobuf::Arena* arena) { + return prototype->New(arena); +} +template<> +google::protobuf::Arena* GenericTypeHandler::GetArena( + Message* value) { + return value->GetArena(); +} +template<> +void* GenericTypeHandler::GetMaybeArenaPointer( + Message* value) { + return value->GetMaybeArenaPointer(); +} } // namespace internal } // namespace protobuf diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h index 6e1929e5..18c092d0 100644 --- a/src/google/protobuf/message.h +++ b/src/google/protobuf/message.h @@ -98,11 +98,11 @@ // // // Use the reflection interface to examine the contents. // const Reflection* reflection = foo->GetReflection(); -// assert(reflection->GetString(foo, text_field) == "Hello World!"); -// assert(reflection->FieldSize(foo, numbers_field) == 3); -// assert(reflection->GetRepeatedInt32(foo, numbers_field, 0) == 1); -// assert(reflection->GetRepeatedInt32(foo, numbers_field, 1) == 5); -// assert(reflection->GetRepeatedInt32(foo, numbers_field, 2) == 42); +// assert(reflection->GetString(*foo, text_field) == "Hello World!"); +// assert(reflection->FieldSize(*foo, numbers_field) == 3); +// assert(reflection->GetRepeatedInt32(*foo, numbers_field, 0) == 1); +// assert(reflection->GetRepeatedInt32(*foo, numbers_field, 1) == 5); +// assert(reflection->GetRepeatedInt32(*foo, numbers_field, 2) == 42); // // delete foo; // } @@ -229,6 +229,11 @@ class LIBPROTOBUF_EXPORT Message : public MessageLite { // Computes (an estimate of) the total number of bytes currently used for // storing the message in memory. The default implementation calls the // Reflection object's SpaceUsed() method. + // + // SpaceUsed() is noticeably slower than ByteSize(), as it is implemented + // using reflection (rather than the generated code implementation for + // ByteSize()). Like ByteSize(), its CPU time is linear in the number of + // fields defined for the proto. virtual int SpaceUsed() const; // Debugging & Testing---------------------------------------------- diff --git a/src/google/protobuf/message_lite.cc b/src/google/protobuf/message_lite.cc index 63be0e9b..4f63ad2b 100644 --- a/src/google/protobuf/message_lite.cc +++ b/src/google/protobuf/message_lite.cc @@ -337,7 +337,7 @@ bool MessageLite::SerializePartialToArray(void* data, int size) const { string MessageLite::SerializeAsString() const { // If the compiler implements the (Named) Return Value Optimization, - // the local variable 'result' will not actually reside on the stack + // the local variable 'output' will not actually reside on the stack // of this function, but will be overlaid with the object that the // caller supplied for the return value to be constructed in. string output; diff --git a/src/google/protobuf/message_lite.h b/src/google/protobuf/message_lite.h index eab61c14..4c16f4c0 100644 --- a/src/google/protobuf/message_lite.h +++ b/src/google/protobuf/message_lite.h @@ -238,6 +238,9 @@ class LIBPROTOBUF_EXPORT MessageLite { // Computes the serialized size of the message. This recursively calls // ByteSize() on all embedded messages. If a subclass does not override // this, it MUST override SetCachedSize(). + // + // ByteSize() is generally linear in the number of fields defined for the + // proto. virtual int ByteSize() const = 0; // Serializes the message without recomputing the size. The message must diff --git a/src/google/protobuf/message_unittest.cc b/src/google/protobuf/message_unittest.cc index ebfb4321..75d60b8b 100644 --- a/src/google/protobuf/message_unittest.cc +++ b/src/google/protobuf/message_unittest.cc @@ -45,12 +45,13 @@ #include #include -#include +#include +#include #include -#include +#include #include -#include -#include +#include +#include #include #include @@ -205,6 +206,28 @@ TEST(MessageTest, InitializationErrorString) { EXPECT_EQ("a, b, c", message.InitializationErrorString()); } +TEST(MessageTest, DynamicCastToGenerated) { + unittest::TestAllTypes test_all_types; + + google::protobuf::Message* test_all_types_pointer = &test_all_types; + EXPECT_EQ(&test_all_types, + google::protobuf::internal::DynamicCastToGenerated( + test_all_types_pointer)); + EXPECT_EQ(NULL, + google::protobuf::internal::DynamicCastToGenerated( + test_all_types_pointer)); + + const google::protobuf::Message* test_all_types_pointer_const = &test_all_types; + EXPECT_EQ( + &test_all_types, + google::protobuf::internal::DynamicCastToGenerated( + test_all_types_pointer_const)); + EXPECT_EQ( + NULL, + google::protobuf::internal::DynamicCastToGenerated( + test_all_types_pointer_const)); +} + #ifdef PROTOBUF_HAS_DEATH_TEST // death tests do not work on Windows yet. TEST(MessageTest, SerializeFailsIfNotInitialized) { diff --git a/src/google/protobuf/proto3_arena_unittest.cc b/src/google/protobuf/proto3_arena_unittest.cc index c3b5996f..da4be673 100644 --- a/src/google/protobuf/proto3_arena_unittest.cc +++ b/src/google/protobuf/proto3_arena_unittest.cc @@ -180,6 +180,16 @@ TEST(ArenaTest, ReleaseMessage) { EXPECT_EQ(118, nested->bb()); } +TEST(ArenaTest, MessageFieldClear) { + // GitHub issue #310: https://github.com/google/protobuf/issues/310 + Arena arena; + TestAllTypes* arena_message = Arena::CreateMessage(&arena); + arena_message->mutable_optional_nested_message()->set_bb(118); + // This should not crash, but prior to the bugfix, it tried to use `operator + // delete` the nested message (which is on the arena): + arena_message->Clear(); +} + } // namespace } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/reflection.h b/src/google/protobuf/reflection.h index 03c761c1..4ff0f6b4 100755 --- a/src/google/protobuf/reflection.h +++ b/src/google/protobuf/reflection.h @@ -39,6 +39,7 @@ #endif #include +#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h index 7bfdc40a..5a2fb409 100644 --- a/src/google/protobuf/repeated_field.h +++ b/src/google/protobuf/repeated_field.h @@ -576,26 +576,21 @@ class GenericTypeHandler { } }; -// Macros for specializing GenericTypeHandler for base proto types, these are -// are defined here, to allow inlining them at their callsites. -#define DEFINE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES(Inline, TypeName) \ - template<> \ - Inline TypeName* GenericTypeHandler::NewFromPrototype( \ - const TypeName* prototype, google::protobuf::Arena* arena) { \ - return prototype->New(arena); \ - } \ - template<> \ - Inline google::protobuf::Arena* GenericTypeHandler::GetArena( \ - TypeName* value) { \ - return value->GetArena(); \ - } \ - template<> \ - Inline void* GenericTypeHandler::GetMaybeArenaPointer( \ - TypeName* value) { \ - return value->GetMaybeArenaPointer(); \ - } -#define DEFINE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES_NOINLINE(TypeName) \ - DEFINE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES(, TypeName) +template<> +inline MessageLite* GenericTypeHandler::NewFromPrototype( + const MessageLite* prototype, google::protobuf::Arena* arena) { + return prototype->New(arena); +} +template<> +inline google::protobuf::Arena* GenericTypeHandler::GetArena( + MessageLite* value) { + return value->GetArena(); +} +template<> +inline void* GenericTypeHandler::GetMaybeArenaPointer( + MessageLite* value) { + return value->GetMaybeArenaPointer(); +} // Implements GenericTypeHandler specialization required by RepeatedPtrFields // to work with MessageLite type. @@ -605,8 +600,6 @@ inline void GenericTypeHandler::Merge( to->CheckTypeAndMergeFrom(from); } -DEFINE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES(inline, MessageLite); - // Declarations of the specialization as we cannot define them here, as the // header that defines ProtocolMessage depends on types defined in this header. #define DECLARE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES(TypeName) \ @@ -1235,6 +1228,7 @@ void RepeatedField::Reserve(int new_size) { kRepHeaderSize + sizeof(Element)*new_size)); } rep_->arena = arena; + int old_total_size = total_size_; total_size_ = new_size; // Invoke placement-new on newly allocated elements. We shouldn't have to do // this, since Element is supposed to be POD, but a previous version of this @@ -1253,15 +1247,17 @@ void RepeatedField::Reserve(int new_size) { if (current_size_ > 0) { MoveArray(rep_->elements, old_rep->elements, current_size_); } - // Likewise, we need to invoke destructors on the old array. If Element has no - // destructor, this loop will disappear. - e = &old_rep->elements[0]; - limit = &old_rep->elements[current_size_]; - for (; e < limit; e++) { - e->Element::~Element(); - } - if (arena == NULL) { - delete[] reinterpret_cast(old_rep); + if (old_rep) { + // Likewise, we need to invoke destructors on the old array. If Element has + // no destructor, this loop will disappear. + e = &old_rep->elements[0]; + limit = &old_rep->elements[old_total_size]; + for (; e < limit; e++) { + e->Element::~Element(); + } + if (arena == NULL) { + delete[] reinterpret_cast(old_rep); + } } } @@ -1418,7 +1414,7 @@ void RepeatedPtrFieldBase::Clear() { const int n = current_size_; GOOGLE_DCHECK_GE(n, 0); if (n > 0) { - void* const* elements = raw_data(); + void* const* elements = rep_->elements; int i = 0; do { TypeHandler::Clear(cast(elements[i++])); diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc index 66e74523..af397932 100644 --- a/src/google/protobuf/repeated_field_unittest.cc +++ b/src/google/protobuf/repeated_field_unittest.cc @@ -456,6 +456,28 @@ TEST(RepeatedField, ExtractSubrange) { } } +TEST(RepeatedField, ClearThenReserveMore) { + // Test that Reserve properly destroys the old internal array when it's forced + // to allocate a new one, even when cleared-but-not-deleted objects are + // present. Use a 'string' and > 16 bytes length so that the elements are + // non-POD and allocate -- the leak checker will catch any skipped destructor + // calls here. + RepeatedField field; + for (int i = 0; i < 32; i++) { + field.Add(string("abcdefghijklmnopqrstuvwxyz0123456789")); + } + EXPECT_EQ(32, field.size()); + field.Clear(); + EXPECT_EQ(0, field.size()); + EXPECT_EQ(32, field.Capacity()); + + field.Reserve(1024); + EXPECT_EQ(0, field.size()); + EXPECT_EQ(1024, field.Capacity()); + // Finish test -- |field| should destroy the cleared-but-not-yet-destroyed + // strings. +} + // =================================================================== // RepeatedPtrField tests. These pretty much just mirror the RepeatedField // tests above. diff --git a/src/google/protobuf/source_context.pb.cc b/src/google/protobuf/source_context.pb.cc index 0926e747..3b3799a9 100644 --- a/src/google/protobuf/source_context.pb.cc +++ b/src/google/protobuf/source_context.pb.cc @@ -114,7 +114,7 @@ const int SourceContext::kFileNameFieldNumber; #endif // !_MSC_VER SourceContext::SourceContext() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.SourceContext) } @@ -277,9 +277,9 @@ int SourceContext::ByteSize() const { void SourceContext::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const SourceContext* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const SourceContext* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -334,7 +334,7 @@ void SourceContext::InternalSwap(SourceContext* other) { // SourceContext // optional string file_name = 1; - void SourceContext::clear_file_name() { +void SourceContext::clear_file_name() { file_name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& SourceContext::file_name() const { diff --git a/src/google/protobuf/struct.pb.cc b/src/google/protobuf/struct.pb.cc index 4405a5bf..30113cdb 100644 --- a/src/google/protobuf/struct.pb.cc +++ b/src/google/protobuf/struct.pb.cc @@ -217,7 +217,7 @@ const int Struct::kFieldsFieldNumber; #endif // !_MSC_VER Struct::Struct() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Struct) } @@ -295,7 +295,8 @@ bool Struct::MergePartialFromCodedStream( // map fields = 1; case 1: { if (tag == 10) { - parse_fields: + DO_(input->IncrementRecursionDepth()); + parse_loop_fields: ::google::protobuf::scoped_ptr entry(fields_.NewEntry()); DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( input, entry.get())); @@ -303,7 +304,8 @@ bool Struct::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(10)) goto parse_fields; + if (input->ExpectTag(10)) goto parse_loop_fields; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -336,7 +338,8 @@ void Struct::SerializeWithCachedSizes( { ::google::protobuf::scoped_ptr entry; for (::google::protobuf::Map< ::std::string, ::google::protobuf::Value >::const_iterator - it = fields().begin(); it != fields().end(); ++it) { + it = this->fields().begin(); + it != this->fields().end(); ++it) { entry.reset(fields_.NewEntryWrapper(it->first, it->second)); ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( 1, *entry, output); @@ -353,7 +356,8 @@ void Struct::SerializeWithCachedSizes( { ::google::protobuf::scoped_ptr entry; for (::google::protobuf::Map< ::std::string, ::google::protobuf::Value >::const_iterator - it = fields().begin(); it != fields().end(); ++it) { + it = this->fields().begin(); + it != this->fields().end(); ++it) { entry.reset(fields_.NewEntryWrapper(it->first, it->second)); target = ::google::protobuf::internal::WireFormatLite:: WriteMessageNoVirtualToArray( @@ -373,7 +377,8 @@ int Struct::ByteSize() const { { ::google::protobuf::scoped_ptr entry; for (::google::protobuf::Map< ::std::string, ::google::protobuf::Value >::const_iterator - it = fields().begin(); it != fields().end(); ++it) { + it = this->fields().begin(); + it != this->fields().end(); ++it) { entry.reset(fields_.NewEntryWrapper(it->first, it->second)); total_size += ::google::protobuf::internal::WireFormatLite:: MessageSizeNoVirtual(*entry); @@ -388,9 +393,9 @@ int Struct::ByteSize() const { void Struct::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Struct* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Struct* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -442,10 +447,10 @@ void Struct::InternalSwap(Struct* other) { // Struct // map fields = 1; - int Struct::fields_size() const { +int Struct::fields_size() const { return fields_.size(); } - void Struct::clear_fields() { +void Struct::clear_fields() { fields_.Clear(); } const ::google::protobuf::Map< ::std::string, ::google::protobuf::Value >& @@ -473,7 +478,7 @@ const int Value::kListValueFieldNumber; #endif // !_MSC_VER Value::Value() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Value) } @@ -845,9 +850,9 @@ int Value::ByteSize() const { void Value::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Value* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Value* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -928,13 +933,13 @@ void Value::InternalSwap(Value* other) { // Value // optional .google.protobuf.NullValue null_value = 1; - bool Value::has_null_value() const { +bool Value::has_null_value() const { return kind_case() == kNullValue; } - void Value::set_has_null_value() { +void Value::set_has_null_value() { _oneof_case_[0] = kNullValue; } - void Value::clear_null_value() { +void Value::clear_null_value() { if (has_null_value()) { kind_.null_value_ = 0; clear_has_kind(); @@ -957,13 +962,13 @@ void Value::InternalSwap(Value* other) { } // optional double number_value = 2; - bool Value::has_number_value() const { +bool Value::has_number_value() const { return kind_case() == kNumberValue; } - void Value::set_has_number_value() { +void Value::set_has_number_value() { _oneof_case_[0] = kNumberValue; } - void Value::clear_number_value() { +void Value::clear_number_value() { if (has_number_value()) { kind_.number_value_ = 0; clear_has_kind(); @@ -986,13 +991,13 @@ void Value::InternalSwap(Value* other) { } // optional string string_value = 3; - bool Value::has_string_value() const { +bool Value::has_string_value() const { return kind_case() == kStringValue; } - void Value::set_has_string_value() { +void Value::set_has_string_value() { _oneof_case_[0] = kStringValue; } - void Value::clear_string_value() { +void Value::clear_string_value() { if (has_string_value()) { kind_.string_value_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_kind(); @@ -1066,13 +1071,13 @@ void Value::InternalSwap(Value* other) { } // optional bool bool_value = 4; - bool Value::has_bool_value() const { +bool Value::has_bool_value() const { return kind_case() == kBoolValue; } - void Value::set_has_bool_value() { +void Value::set_has_bool_value() { _oneof_case_[0] = kBoolValue; } - void Value::clear_bool_value() { +void Value::clear_bool_value() { if (has_bool_value()) { kind_.bool_value_ = false; clear_has_kind(); @@ -1095,13 +1100,13 @@ void Value::InternalSwap(Value* other) { } // optional .google.protobuf.Struct struct_value = 5; - bool Value::has_struct_value() const { +bool Value::has_struct_value() const { return kind_case() == kStructValue; } - void Value::set_has_struct_value() { +void Value::set_has_struct_value() { _oneof_case_[0] = kStructValue; } - void Value::clear_struct_value() { +void Value::clear_struct_value() { if (has_struct_value()) { delete kind_.struct_value_; clear_has_kind(); @@ -1141,13 +1146,13 @@ void Value::InternalSwap(Value* other) { } // optional .google.protobuf.ListValue list_value = 6; - bool Value::has_list_value() const { +bool Value::has_list_value() const { return kind_case() == kListValue; } - void Value::set_has_list_value() { +void Value::set_has_list_value() { _oneof_case_[0] = kListValue; } - void Value::clear_list_value() { +void Value::clear_list_value() { if (has_list_value()) { delete kind_.list_value_; clear_has_kind(); @@ -1186,10 +1191,10 @@ void Value::InternalSwap(Value* other) { // @@protoc_insertion_point(field_set_allocated:google.protobuf.Value.list_value) } - bool Value::has_kind() const { +bool Value::has_kind() const { return kind_case() != KIND_NOT_SET; } - void Value::clear_has_kind() { +void Value::clear_has_kind() { _oneof_case_[0] = KIND_NOT_SET; } Value::KindCase Value::kind_case() const { @@ -1204,7 +1209,7 @@ const int ListValue::kValuesFieldNumber; #endif // !_MSC_VER ListValue::ListValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.ListValue) } @@ -1278,13 +1283,15 @@ bool ListValue::MergePartialFromCodedStream( // repeated .google.protobuf.Value values = 1; case 1: { if (tag == 10) { - parse_values: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_values: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_values())); } else { goto handle_unusual; } - if (input->ExpectTag(10)) goto parse_values; + if (input->ExpectTag(10)) goto parse_loop_values; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -1355,9 +1362,9 @@ int ListValue::ByteSize() const { void ListValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const ListValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const ListValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1409,10 +1416,10 @@ void ListValue::InternalSwap(ListValue* other) { // ListValue // repeated .google.protobuf.Value values = 1; - int ListValue::values_size() const { +int ListValue::values_size() const { return values_.size(); } - void ListValue::clear_values() { +void ListValue::clear_values() { values_.Clear(); } const ::google::protobuf::Value& ListValue::values(int index) const { diff --git a/src/google/protobuf/struct.pb.h b/src/google/protobuf/struct.pb.h index 46482142..2889c8fe 100644 --- a/src/google/protobuf/struct.pb.h +++ b/src/google/protobuf/struct.pb.h @@ -735,6 +735,10 @@ ListValue::mutable_values() { } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) diff --git a/src/google/protobuf/stubs/common.h b/src/google/protobuf/stubs/common.h index 37123c7b..3acaeba1 100644 --- a/src/google/protobuf/stubs/common.h +++ b/src/google/protobuf/stubs/common.h @@ -1141,6 +1141,14 @@ class LIBPROTOBUF_EXPORT Mutex { GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Mutex); }; +// Undefine the macros to workaround the conflicts with Google internal +// MutexLock implementation. +// TODO(liujisi): Remove the undef once internal macros are removed. +#undef MutexLock +#undef ReaderMutexLock +#undef WriterMutexLock +#undef MutexLockMaybe + // MutexLock(mu) acquires mu when constructed and releases it when destroyed. class LIBPROTOBUF_EXPORT MutexLock { public: diff --git a/src/google/protobuf/stubs/strutil.cc b/src/google/protobuf/stubs/strutil.cc index 7955d261..7ecc17ee 100644 --- a/src/google/protobuf/stubs/strutil.cc +++ b/src/google/protobuf/stubs/strutil.cc @@ -1285,24 +1285,6 @@ char* FloatToBuffer(float value, char* buffer) { return buffer; } -string ToHex(uint64 num) { - if (num == 0) { - return string("0"); - } - - // Compute hex bytes in reverse order, writing to the back of the - // buffer. - char buf[16]; // No more than 16 hex digits needed. - char* bufptr = buf + 16; - static const char kHexChars[] = "0123456789abcdef"; - while (num != 0) { - *--bufptr = kHexChars[num & 0xf]; - num >>= 4; - } - - return string(bufptr, buf + 16 - bufptr); -} - namespace strings { AlphaNum::AlphaNum(strings::Hex hex) { diff --git a/src/google/protobuf/stubs/strutil.h b/src/google/protobuf/stubs/strutil.h index 920701eb..5faa81e0 100644 --- a/src/google/protobuf/stubs/strutil.h +++ b/src/google/protobuf/stubs/strutil.h @@ -682,12 +682,6 @@ string Join(const Range& components, return result; } -// ---------------------------------------------------------------------- -// ToHex() -// Return a lower-case hex string representation of the given integer. -// ---------------------------------------------------------------------- -LIBPROTOBUF_EXPORT string ToHex(uint64 num); - // ---------------------------------------------------------------------- // GlobalReplaceSubstring() // Replaces all instances of a substring in a string. Does nothing diff --git a/src/google/protobuf/testdata/golden_message_proto3 b/src/google/protobuf/testdata/golden_message_proto3 index 934f36fa..bd646a0d 100644 Binary files a/src/google/protobuf/testdata/golden_message_proto3 and b/src/google/protobuf/testdata/golden_message_proto3 differ diff --git a/src/google/protobuf/testdata/map_test_data.txt b/src/google/protobuf/testdata/map_test_data.txt new file mode 100644 index 00000000..bc272321 --- /dev/null +++ b/src/google/protobuf/testdata/map_test_data.txt @@ -0,0 +1,140 @@ +map_int32_int32 { + key: 0 + value: 0 +} +map_int32_int32 { + key: 1 + value: 1 +} +map_int64_int64 { + key: 0 + value: 0 +} +map_int64_int64 { + key: 1 + value: 1 +} +map_uint32_uint32 { + key: 0 + value: 0 +} +map_uint32_uint32 { + key: 1 + value: 1 +} +map_uint64_uint64 { + key: 0 + value: 0 +} +map_uint64_uint64 { + key: 1 + value: 1 +} +map_sint32_sint32 { + key: 0 + value: 0 +} +map_sint32_sint32 { + key: 1 + value: 1 +} +map_sint64_sint64 { + key: 0 + value: 0 +} +map_sint64_sint64 { + key: 1 + value: 1 +} +map_fixed32_fixed32 { + key: 0 + value: 0 +} +map_fixed32_fixed32 { + key: 1 + value: 1 +} +map_fixed64_fixed64 { + key: 0 + value: 0 +} +map_fixed64_fixed64 { + key: 1 + value: 1 +} +map_sfixed32_sfixed32 { + key: 0 + value: 0 +} +map_sfixed32_sfixed32 { + key: 1 + value: 1 +} +map_sfixed64_sfixed64 { + key: 0 + value: 0 +} +map_sfixed64_sfixed64 { + key: 1 + value: 1 +} +map_int32_float { + key: 0 + value: 0 +} +map_int32_float { + key: 1 + value: 1 +} +map_int32_double { + key: 0 + value: 0 +} +map_int32_double { + key: 1 + value: 1 +} +map_bool_bool { + key: false + value: false +} +map_bool_bool { + key: true + value: true +} +map_string_string { + key: "0" + value: "0" +} +map_string_string { + key: "1" + value: "1" +} +map_int32_bytes { + key: 0 + value: "0" +} +map_int32_bytes { + key: 1 + value: "1" +} +map_int32_enum { + key: 0 + value: MAP_ENUM_BAR +} +map_int32_enum { + key: 1 + value: MAP_ENUM_BAZ +} +map_int32_foreign_message { + key: 0 + value { + c: 0 + } +} +map_int32_foreign_message { + key: 1 + value { + c: 1 + } +} diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc index ec070c51..61dfa5d6 100644 --- a/src/google/protobuf/text_format.cc +++ b/src/google/protobuf/text_format.cc @@ -43,6 +43,8 @@ #include #include +#include +#include #include #include #include @@ -50,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +73,18 @@ inline bool IsOctNumber(const string& str) { (str[1] >= '0' && str[1] < '8')); } +inline bool GetAnyFieldDescriptors(const Message& message, + const FieldDescriptor** type_url_field, + const FieldDescriptor** value_field) { + const Descriptor* descriptor = message.GetDescriptor(); + *type_url_field = descriptor->FindFieldByNumber(1); + *value_field = descriptor->FindFieldByNumber(2); + return (*type_url_field != NULL && + (*type_url_field)->type() == FieldDescriptor::TYPE_STRING && + *value_field != NULL && + (*value_field)->type() == FieldDescriptor::TYPE_BYTES); +} + } // namespace string Message::DebugString() const { @@ -330,7 +345,17 @@ class TextFormat::Parser::ParserImpl { // Confirm that we have a valid ending delimiter. DO(Consume(delimiter)); + return true; + } + // Consume either "<" or "{". + bool ConsumeMessageDelimiter(string* delimiter) { + if (TryConsume("<")) { + *delimiter = ">"; + } else { + DO(Consume("{")); + *delimiter = "}"; + } return true; } @@ -347,15 +372,28 @@ class TextFormat::Parser::ParserImpl { int start_line = tokenizer_.current().line; int start_column = tokenizer_.current().column; + const FieldDescriptor* any_type_url_field; + const FieldDescriptor* any_value_field; + if (internal::GetAnyFieldDescriptors(*message, &any_type_url_field, + &any_value_field) && + TryConsume("[")) { + string full_type_name; + DO(ConsumeAnyTypeUrl(&full_type_name)); + DO(Consume("]")); + string serialized_value; + DO(ConsumeAnyValue(full_type_name, + message->GetDescriptor()->file()->pool(), + &serialized_value)); + reflection->SetString( + message, any_type_url_field, + string(internal::kTypeGoogleApisComPrefix) + full_type_name); + reflection->SetString(message, any_value_field, serialized_value); + return true; + // Fall through. + } if (TryConsume("[")) { // Extension. - DO(ConsumeIdentifier(&field_name)); - while (TryConsume(".")) { - string part; - DO(ConsumeIdentifier(&part)); - field_name += "."; - field_name += part; - } + DO(ConsumeFullTypeName(&field_name)); DO(Consume("]")); field = (finder_ != NULL @@ -512,13 +550,7 @@ class TextFormat::Parser::ParserImpl { string field_name; if (TryConsume("[")) { // Extension name. - DO(ConsumeIdentifier(&field_name)); - while (TryConsume(".")) { - string part; - DO(ConsumeIdentifier(&part)); - field_name += "."; - field_name += part; - } + DO(ConsumeFullTypeName(&field_name)); DO(Consume("]")); } else { DO(ConsumeIdentifier(&field_name)); @@ -553,13 +585,7 @@ class TextFormat::Parser::ParserImpl { } string delimiter; - if (TryConsume("<")) { - delimiter = ">"; - } else { - DO(Consume("{")); - delimiter = "}"; - } - + DO(ConsumeMessageDelimiter(&delimiter)); if (field->is_repeated()) { DO(ConsumeMessage(reflection->AddMessage(message, field), delimiter)); } else { @@ -576,12 +602,7 @@ class TextFormat::Parser::ParserImpl { // the ending delimiter. bool SkipFieldMessage() { string delimiter; - if (TryConsume("<")) { - delimiter = ">"; - } else { - DO(Consume("{")); - delimiter = "}"; - } + DO(ConsumeMessageDelimiter(&delimiter)); while (!LookingAt(">") && !LookingAt("}")) { DO(SkipField()); } @@ -808,6 +829,18 @@ class TextFormat::Parser::ParserImpl { return false; } + // Consume a string of form ".....". + bool ConsumeFullTypeName(string* name) { + DO(ConsumeIdentifier(name)); + while (TryConsume(".")) { + string part; + DO(ConsumeIdentifier(&part)); + *name += "."; + *name += part; + } + return true; + } + // Consumes a string and saves its value in the text parameter. // Returns false if the token is not of type STRING. bool ConsumeString(string* text) { @@ -947,6 +980,54 @@ class TextFormat::Parser::ParserImpl { return true; } + // Consumes Any::type_url value, of form "type.googleapis.com/full.type.Name" + bool ConsumeAnyTypeUrl(string* full_type_name) { + // TODO(saito) Extend Consume() to consume multiple tokens at once, so that + // this code can be written as just DO(Consume(kGoogleApisTypePrefix)). + string url1, url2, url3; + DO(ConsumeIdentifier(&url1)); // type + DO(Consume(".")); + DO(ConsumeIdentifier(&url2)); // googleapis + DO(Consume(".")); + DO(ConsumeIdentifier(&url3)); // com + DO(Consume("/")); + DO(ConsumeFullTypeName(full_type_name)); + + const string prefix = url1 + "." + url2 + "." + url3 + "/"; + if (prefix != internal::kTypeGoogleApisComPrefix) { + ReportError("TextFormat::Parser for Any supports only " + "type.googleapi.com, but found \"" + prefix + "\""); + return false; + } + return true; + } + + // A helper function for reconstructing Any::value. Consumes a text of + // full_type_name, then serializes it into serialized_value. "pool" is used to + // look up and create a temporary object with full_type_name. + bool ConsumeAnyValue(const string& full_type_name, const DescriptorPool* pool, + string* serialized_value) { + const Descriptor* value_descriptor = + pool->FindMessageTypeByName(full_type_name); + if (value_descriptor == NULL) { + ReportError("Could not find type \"" + full_type_name + + "\" stored in google.protobuf.Any."); + return false; + } + DynamicMessageFactory factory; + const Message* value_prototype = factory.GetPrototype(value_descriptor); + if (value_prototype == NULL) { + return false; + } + google::protobuf::scoped_ptr value(value_prototype->New()); + string sub_delimiter; + DO(ConsumeMessageDelimiter(&sub_delimiter)); + DO(ConsumeMessage(value.get(), sub_delimiter)); + + value->AppendToString(serialized_value); + return true; + } + // Consumes a token and confirms that it matches that specified in the // value parameter. Returns false if the token found does not match that // which was specified. @@ -1338,7 +1419,8 @@ TextFormat::Printer::Printer() use_field_number_(false), use_short_repeated_primitives_(false), hide_unknown_fields_(false), - print_message_fields_in_index_order_(false) { + print_message_fields_in_index_order_(false), + expand_any_(false) { SetUseUtf8StringEscaping(false); } @@ -1413,11 +1495,63 @@ struct FieldIndexSorter { return left->index() < right->index(); } }; + } // namespace +bool TextFormat::Printer::PrintAny(const Message& message, + TextGenerator& generator) const { + const FieldDescriptor* type_url_field; + const FieldDescriptor* value_field; + if (!internal::GetAnyFieldDescriptors(message, &type_url_field, + &value_field)) { + return false; + } + + const Reflection* reflection = message.GetReflection(); + + // Extract the full type name from the type_url field. + const string& type_url = reflection->GetString(message, type_url_field); + string full_type_name; + if (!internal::ParseAnyTypeUrl(type_url, &full_type_name)) { + return false; + } + + // Print the "value" in text. + const google::protobuf::Descriptor* value_descriptor = + message.GetDescriptor()->file()->pool()->FindMessageTypeByName( + full_type_name); + if (value_descriptor == NULL) { + GOOGLE_LOG(WARNING) << "Proto type " << type_url << " not found"; + return false; + } + DynamicMessageFactory factory; + google::protobuf::scoped_ptr value_message( + factory.GetPrototype(value_descriptor)->New()); + string serialized_value = reflection->GetString(message, value_field); + if (!value_message->ParseFromString(serialized_value)) { + GOOGLE_LOG(WARNING) << type_url << ": failed to parse contents"; + return false; + } + generator.Print(StrCat("[", type_url, "]")); + const FieldValuePrinter* printer = FindWithDefault( + custom_printers_, value_field, default_field_value_printer_.get()); + generator.Print( + printer->PrintMessageStart(message, -1, 0, single_line_mode_)); + generator.Indent(); + Print(*value_message, generator); + generator.Outdent(); + generator.Print(printer->PrintMessageEnd(message, -1, 0, single_line_mode_)); + return true; +} + void TextFormat::Printer::Print(const Message& message, TextGenerator& generator) const { + const Descriptor* descriptor = message.GetDescriptor(); const Reflection* reflection = message.GetReflection(); + if (descriptor->full_name() == internal::kAnyFullTypeName && expand_any_ && + PrintAny(message, generator)) { + return; + } vector fields; reflection->ListFields(message, &fields); if (print_message_fields_in_index_order_) { @@ -1446,6 +1580,54 @@ void TextFormat::Printer::PrintFieldValueToString( PrintFieldValue(message, message.GetReflection(), field, index, generator); } +class MapEntryMessageComparator { + public: + explicit MapEntryMessageComparator(const Descriptor* descriptor) + : field_(descriptor->field(0)) {} + + bool operator()(const Message* a, const Message* b) { + const Reflection* reflection = a->GetReflection(); + switch (field_->cpp_type()) { + case FieldDescriptor::CPPTYPE_BOOL: { + bool first = reflection->GetBool(*a, field_); + bool second = reflection->GetBool(*b, field_); + return first < second; + } + case FieldDescriptor::CPPTYPE_INT32: { + int32 first = reflection->GetInt32(*a, field_); + int32 second = reflection->GetInt32(*b, field_); + return first < second; + } + case FieldDescriptor::CPPTYPE_INT64: { + int64 first = reflection->GetInt64(*a, field_); + int64 second = reflection->GetInt64(*b, field_); + return first < second; + } + case FieldDescriptor::CPPTYPE_UINT32: { + uint32 first = reflection->GetUInt32(*a, field_); + uint32 second = reflection->GetUInt32(*b, field_); + return first < second; + } + case FieldDescriptor::CPPTYPE_UINT64: { + uint64 first = reflection->GetUInt64(*a, field_); + uint64 second = reflection->GetUInt64(*b, field_); + return first < second; + } + case FieldDescriptor::CPPTYPE_STRING: { + string first = reflection->GetString(*a, field_); + string second = reflection->GetString(*b, field_); + return first < second; + } + default: + GOOGLE_LOG(DFATAL) << "Invalid key for map field."; + return true; + } + } + + private: + const FieldDescriptor* field_; +}; + void TextFormat::Printer::PrintField(const Message& message, const Reflection* reflection, const FieldDescriptor* field, @@ -1466,6 +1648,21 @@ void TextFormat::Printer::PrintField(const Message& message, count = 1; } + std::vector sorted_map_field; + if (field->is_map()) { + const RepeatedPtrField& map_field = + reflection->GetRepeatedPtrField(message, field); + for (RepeatedPtrField::const_pointer_iterator it = + map_field.pointer_begin(); + it != map_field.pointer_end(); ++it) { + sorted_map_field.push_back(*it); + } + + MapEntryMessageComparator comparator(field->message_type()); + std::stable_sort(sorted_map_field.begin(), sorted_map_field.end(), + comparator); + } + for (int j = 0; j < count; ++j) { const int field_index = field->is_repeated() ? j : -1; @@ -1475,8 +1672,10 @@ void TextFormat::Printer::PrintField(const Message& message, const FieldValuePrinter* printer = FindWithDefault( custom_printers_, field, default_field_value_printer_.get()); const Message& sub_message = - field->is_repeated() - ? reflection->GetRepeatedMessage(message, field, j) + field->is_repeated() + ? (field->is_map() + ? *sorted_map_field[j] + : reflection->GetRepeatedMessage(message, field, j)) : reflection->GetMessage(message, field); generator.Print( printer->PrintMessageStart( @@ -1680,8 +1879,8 @@ void TextFormat::Printer::PrintUnknownFields( case UnknownField::TYPE_FIXED32: { generator.Print(field_number); generator.Print(": 0x"); - char buffer[kFastToBufferSize]; - generator.Print(FastHex32ToBuffer(field.fixed32(), buffer)); + generator.Print( + StrCat(strings::Hex(field.fixed32(), strings::Hex::ZERO_PAD_8))); if (single_line_mode_) { generator.Print(" "); } else { @@ -1692,8 +1891,8 @@ void TextFormat::Printer::PrintUnknownFields( case UnknownField::TYPE_FIXED64: { generator.Print(field_number); generator.Print(": 0x"); - char buffer[kFastToBufferSize]; - generator.Print(FastHex64ToBuffer(field.fixed64(), buffer)); + generator.Print( + StrCat(strings::Hex(field.fixed64(), strings::Hex::ZERO_PAD_16))); if (single_line_mode_) { generator.Print(" "); } else { diff --git a/src/google/protobuf/text_format.h b/src/google/protobuf/text_format.h index 9e2cb070..6717aecd 100644 --- a/src/google/protobuf/text_format.h +++ b/src/google/protobuf/text_format.h @@ -208,6 +208,17 @@ class LIBPROTOBUF_EXPORT TextFormat { print_message_fields_in_index_order; } + // If expand==true, expand google.protobuf.Any payloads. The output + // will be of form + // [type_url] { } + // + // If expand==false, print Any using the default printer. The output will + // look like + // type_url: "" value: "serialized_content" + void SetExpandAny(bool expand) { + expand_any_ = expand; + } + // Register a custom field-specific FieldValuePrinter for fields // with a particular FieldDescriptor. // Returns "true" if the registration succeeded, or "false", if there is @@ -259,6 +270,8 @@ class LIBPROTOBUF_EXPORT TextFormat { void PrintUnknownFields(const UnknownFieldSet& unknown_fields, TextGenerator& generator) const; + bool PrintAny(const Message& message, TextGenerator& generator) const; + int initial_indent_level_; bool single_line_mode_; @@ -271,6 +284,8 @@ class LIBPROTOBUF_EXPORT TextFormat { bool print_message_fields_in_index_order_; + bool expand_any_; + google::protobuf::scoped_ptr default_field_value_printer_; typedef map CustomPrinterMap; diff --git a/src/google/protobuf/text_format_unittest.cc b/src/google/protobuf/text_format_unittest.cc index 477fdcbd..1b18c5ed 100644 --- a/src/google/protobuf/text_format_unittest.cc +++ b/src/google/protobuf/text_format_unittest.cc @@ -32,23 +32,24 @@ // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. +#include + #include #include #include -#include -#include -#include -#include -#include -#include - #include #include -#include -#include +#include +#include +#include +#include +#include #include #include +#include +#include + namespace google { namespace protobuf { @@ -451,7 +452,7 @@ TEST_F(TextFormatTest, ErrorCasesRegisteringFieldValuePrinterShouldFail) { class CustomMessageFieldValuePrinter : public TextFormat::FieldValuePrinter { public: virtual string PrintInt32(int32 v) const { - return StrCat(FieldValuePrinter::PrintInt32(v), " # x", ToHex(v)); + return StrCat(FieldValuePrinter::PrintInt32(v), " # x", strings::Hex(v)); } virtual string PrintMessageStart(const Message& message, diff --git a/src/google/protobuf/timestamp.pb.cc b/src/google/protobuf/timestamp.pb.cc index 6edfe056..877dc8f3 100644 --- a/src/google/protobuf/timestamp.pb.cc +++ b/src/google/protobuf/timestamp.pb.cc @@ -117,7 +117,7 @@ const int Timestamp::kNanosFieldNumber; #endif // !_MSC_VER Timestamp::Timestamp() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Timestamp) } @@ -310,9 +310,9 @@ int Timestamp::ByteSize() const { void Timestamp::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Timestamp* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Timestamp* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -370,7 +370,7 @@ void Timestamp::InternalSwap(Timestamp* other) { // Timestamp // optional int64 seconds = 1; - void Timestamp::clear_seconds() { +void Timestamp::clear_seconds() { seconds_ = GOOGLE_LONGLONG(0); } ::google::protobuf::int64 Timestamp::seconds() const { @@ -384,7 +384,7 @@ void Timestamp::InternalSwap(Timestamp* other) { } // optional int32 nanos = 2; - void Timestamp::clear_nanos() { +void Timestamp::clear_nanos() { nanos_ = 0; } ::google::protobuf::int32 Timestamp::nanos() const { diff --git a/src/google/protobuf/type.pb.cc b/src/google/protobuf/type.pb.cc index d77bc6ce..8b08909e 100644 --- a/src/google/protobuf/type.pb.cc +++ b/src/google/protobuf/type.pb.cc @@ -267,7 +267,7 @@ const int Type::kSourceContextFieldNumber; #endif // !_MSC_VER Type::Type() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Type) } @@ -332,7 +332,7 @@ Type* Type::New(::google::protobuf::Arena* arena) const { void Type::Clear() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); - if (source_context_ != NULL) delete source_context_; + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; fields_.Clear(); oneofs_.Clear(); @@ -369,12 +369,15 @@ bool Type::MergePartialFromCodedStream( case 2: { if (tag == 18) { parse_fields: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_fields: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_fields())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_fields; + if (input->ExpectTag(18)) goto parse_loop_fields; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(26)) goto parse_oneofs; break; } @@ -402,12 +405,15 @@ bool Type::MergePartialFromCodedStream( case 4: { if (tag == 34) { parse_options: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_options: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_options())); } else { goto handle_unusual; } - if (input->ExpectTag(34)) goto parse_options; + if (input->ExpectTag(34)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(42)) goto parse_source_context; break; } @@ -587,9 +593,9 @@ int Type::ByteSize() const { void Type::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Type* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Type* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -654,7 +660,7 @@ void Type::InternalSwap(Type* other) { // Type // optional string name = 1; - void Type::clear_name() { +void Type::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Type::name() const { @@ -697,10 +703,10 @@ void Type::InternalSwap(Type* other) { } // repeated .google.protobuf.Field fields = 2; - int Type::fields_size() const { +int Type::fields_size() const { return fields_.size(); } - void Type::clear_fields() { +void Type::clear_fields() { fields_.Clear(); } const ::google::protobuf::Field& Type::fields(int index) const { @@ -727,10 +733,10 @@ Type::mutable_fields() { } // repeated string oneofs = 3; - int Type::oneofs_size() const { +int Type::oneofs_size() const { return oneofs_.size(); } - void Type::clear_oneofs() { +void Type::clear_oneofs() { oneofs_.Clear(); } const ::std::string& Type::oneofs(int index) const { @@ -781,10 +787,10 @@ Type::mutable_oneofs() { } // repeated .google.protobuf.Option options = 4; - int Type::options_size() const { +int Type::options_size() const { return options_.size(); } - void Type::clear_options() { +void Type::clear_options() { options_.Clear(); } const ::google::protobuf::Option& Type::options(int index) const { @@ -811,11 +817,11 @@ Type::mutable_options() { } // optional .google.protobuf.SourceContext source_context = 5; - bool Type::has_source_context() const { +bool Type::has_source_context() const { return !_is_default_instance_ && source_context_ != NULL; } - void Type::clear_source_context() { - if (source_context_ != NULL) delete source_context_; +void Type::clear_source_context() { + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; } const ::google::protobuf::SourceContext& Type::source_context() const { @@ -941,7 +947,7 @@ const int Field::kOptionsFieldNumber; #endif // !_MSC_VER Field::Field() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Field) } @@ -1153,12 +1159,15 @@ bool Field::MergePartialFromCodedStream( case 9: { if (tag == 74) { parse_options: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_options: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_options())); } else { goto handle_unusual; } - if (input->ExpectTag(74)) goto parse_options; + if (input->ExpectTag(74)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -1370,9 +1379,9 @@ int Field::ByteSize() const { void Field::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Field* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Field* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1454,7 +1463,7 @@ void Field::InternalSwap(Field* other) { // Field // optional .google.protobuf.Field.Kind kind = 1; - void Field::clear_kind() { +void Field::clear_kind() { kind_ = 0; } ::google::protobuf::Field_Kind Field::kind() const { @@ -1468,7 +1477,7 @@ void Field::InternalSwap(Field* other) { } // optional .google.protobuf.Field.Cardinality cardinality = 2; - void Field::clear_cardinality() { +void Field::clear_cardinality() { cardinality_ = 0; } ::google::protobuf::Field_Cardinality Field::cardinality() const { @@ -1482,7 +1491,7 @@ void Field::InternalSwap(Field* other) { } // optional int32 number = 3; - void Field::clear_number() { +void Field::clear_number() { number_ = 0; } ::google::protobuf::int32 Field::number() const { @@ -1496,7 +1505,7 @@ void Field::InternalSwap(Field* other) { } // optional string name = 4; - void Field::clear_name() { +void Field::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Field::name() const { @@ -1539,7 +1548,7 @@ void Field::InternalSwap(Field* other) { } // optional string type_url = 6; - void Field::clear_type_url() { +void Field::clear_type_url() { type_url_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Field::type_url() const { @@ -1582,7 +1591,7 @@ void Field::InternalSwap(Field* other) { } // optional int32 oneof_index = 7; - void Field::clear_oneof_index() { +void Field::clear_oneof_index() { oneof_index_ = 0; } ::google::protobuf::int32 Field::oneof_index() const { @@ -1596,7 +1605,7 @@ void Field::InternalSwap(Field* other) { } // optional bool packed = 8; - void Field::clear_packed() { +void Field::clear_packed() { packed_ = false; } bool Field::packed() const { @@ -1610,10 +1619,10 @@ void Field::InternalSwap(Field* other) { } // repeated .google.protobuf.Option options = 9; - int Field::options_size() const { +int Field::options_size() const { return options_.size(); } - void Field::clear_options() { +void Field::clear_options() { options_.Clear(); } const ::google::protobuf::Option& Field::options(int index) const { @@ -1651,7 +1660,7 @@ const int Enum::kSourceContextFieldNumber; #endif // !_MSC_VER Enum::Enum() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Enum) } @@ -1716,7 +1725,7 @@ Enum* Enum::New(::google::protobuf::Arena* arena) const { void Enum::Clear() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); - if (source_context_ != NULL) delete source_context_; + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; enumvalue_.Clear(); options_.Clear(); @@ -1752,26 +1761,31 @@ bool Enum::MergePartialFromCodedStream( case 2: { if (tag == 18) { parse_enumvalue: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_enumvalue: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_enumvalue())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_enumvalue; - if (input->ExpectTag(26)) goto parse_options; + if (input->ExpectTag(18)) goto parse_loop_enumvalue; + if (input->ExpectTag(26)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.Option options = 3; case 3: { if (tag == 26) { - parse_options: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_options: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_options())); } else { goto handle_unusual; } - if (input->ExpectTag(26)) goto parse_options; + if (input->ExpectTag(26)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(34)) goto parse_source_context; break; } @@ -1924,9 +1938,9 @@ int Enum::ByteSize() const { void Enum::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Enum* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Enum* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1989,7 +2003,7 @@ void Enum::InternalSwap(Enum* other) { // Enum // optional string name = 1; - void Enum::clear_name() { +void Enum::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Enum::name() const { @@ -2032,10 +2046,10 @@ void Enum::InternalSwap(Enum* other) { } // repeated .google.protobuf.EnumValue enumvalue = 2; - int Enum::enumvalue_size() const { +int Enum::enumvalue_size() const { return enumvalue_.size(); } - void Enum::clear_enumvalue() { +void Enum::clear_enumvalue() { enumvalue_.Clear(); } const ::google::protobuf::EnumValue& Enum::enumvalue(int index) const { @@ -2062,10 +2076,10 @@ Enum::mutable_enumvalue() { } // repeated .google.protobuf.Option options = 3; - int Enum::options_size() const { +int Enum::options_size() const { return options_.size(); } - void Enum::clear_options() { +void Enum::clear_options() { options_.Clear(); } const ::google::protobuf::Option& Enum::options(int index) const { @@ -2092,11 +2106,11 @@ Enum::mutable_options() { } // optional .google.protobuf.SourceContext source_context = 4; - bool Enum::has_source_context() const { +bool Enum::has_source_context() const { return !_is_default_instance_ && source_context_ != NULL; } - void Enum::clear_source_context() { - if (source_context_ != NULL) delete source_context_; +void Enum::clear_source_context() { + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; } const ::google::protobuf::SourceContext& Enum::source_context() const { @@ -2139,7 +2153,7 @@ const int EnumValue::kOptionsFieldNumber; #endif // !_MSC_VER EnumValue::EnumValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.EnumValue) } @@ -2251,12 +2265,15 @@ bool EnumValue::MergePartialFromCodedStream( case 3: { if (tag == 26) { parse_options: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_options: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_options())); } else { goto handle_unusual; } - if (input->ExpectTag(26)) goto parse_options; + if (input->ExpectTag(26)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -2372,9 +2389,9 @@ int EnumValue::ByteSize() const { void EnumValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const EnumValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const EnumValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -2435,7 +2452,7 @@ void EnumValue::InternalSwap(EnumValue* other) { // EnumValue // optional string name = 1; - void EnumValue::clear_name() { +void EnumValue::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& EnumValue::name() const { @@ -2478,7 +2495,7 @@ void EnumValue::InternalSwap(EnumValue* other) { } // optional int32 number = 2; - void EnumValue::clear_number() { +void EnumValue::clear_number() { number_ = 0; } ::google::protobuf::int32 EnumValue::number() const { @@ -2492,10 +2509,10 @@ void EnumValue::InternalSwap(EnumValue* other) { } // repeated .google.protobuf.Option options = 3; - int EnumValue::options_size() const { +int EnumValue::options_size() const { return options_.size(); } - void EnumValue::clear_options() { +void EnumValue::clear_options() { options_.Clear(); } const ::google::protobuf::Option& EnumValue::options(int index) const { @@ -2531,7 +2548,7 @@ const int Option::kValueFieldNumber; #endif // !_MSC_VER Option::Option() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Option) } @@ -2596,7 +2613,7 @@ Option* Option::New(::google::protobuf::Arena* arena) const { void Option::Clear() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); - if (value_ != NULL) delete value_; + if (GetArenaNoVirtual() == NULL && value_ != NULL) delete value_; value_ = NULL; } @@ -2732,9 +2749,9 @@ int Option::ByteSize() const { void Option::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Option* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Option* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -2793,7 +2810,7 @@ void Option::InternalSwap(Option* other) { // Option // optional string name = 1; - void Option::clear_name() { +void Option::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Option::name() const { @@ -2836,11 +2853,11 @@ void Option::InternalSwap(Option* other) { } // optional .google.protobuf.Any value = 2; - bool Option::has_value() const { +bool Option::has_value() const { return !_is_default_instance_ && value_ != NULL; } - void Option::clear_value() { - if (value_ != NULL) delete value_; +void Option::clear_value() { + if (GetArenaNoVirtual() == NULL && value_ != NULL) delete value_; value_ = NULL; } const ::google::protobuf::Any& Option::value() const { diff --git a/src/google/protobuf/type.pb.h b/src/google/protobuf/type.pb.h index a14edd4a..c9952efa 100644 --- a/src/google/protobuf/type.pb.h +++ b/src/google/protobuf/type.pb.h @@ -936,7 +936,7 @@ inline bool Type::has_source_context() const { return !_is_default_instance_ && source_context_ != NULL; } inline void Type::clear_source_context() { - if (source_context_ != NULL) delete source_context_; + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; } inline const ::google::protobuf::SourceContext& Type::source_context() const { @@ -1270,7 +1270,7 @@ inline bool Enum::has_source_context() const { return !_is_default_instance_ && source_context_ != NULL; } inline void Enum::clear_source_context() { - if (source_context_ != NULL) delete source_context_; + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; } inline const ::google::protobuf::SourceContext& Enum::source_context() const { @@ -1445,7 +1445,7 @@ inline bool Option::has_value() const { return !_is_default_instance_ && value_ != NULL; } inline void Option::clear_value() { - if (value_ != NULL) delete value_; + if (GetArenaNoVirtual() == NULL && value_ != NULL) delete value_; value_ = NULL; } inline const ::google::protobuf::Any& Option::value() const { @@ -1478,6 +1478,14 @@ inline void Option::set_allocated_value(::google::protobuf::Any* value) { } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) diff --git a/src/google/protobuf/unittest.proto b/src/google/protobuf/unittest.proto index a32291e0..834c9b56 100644 --- a/src/google/protobuf/unittest.proto +++ b/src/google/protobuf/unittest.proto @@ -185,6 +185,7 @@ message TestAllTypes { message NestedTestAllTypes { optional NestedTestAllTypes child = 1; optional TestAllTypes payload = 2; + repeated NestedTestAllTypes repeated_child = 3; } message TestDeprecatedFields { @@ -203,6 +204,11 @@ enum ForeignEnum { FOREIGN_BAZ = 6; } +message TestReservedFields { + reserved 2, 15, 9 to 11; + reserved "bar", "baz"; +} + message TestAllExtensions { extensions 1 to max; } diff --git a/src/google/protobuf/unittest_drop_unknown_fields.proto b/src/google/protobuf/unittest_drop_unknown_fields.proto index 1b35fad0..faaddc6e 100644 --- a/src/google/protobuf/unittest_drop_unknown_fields.proto +++ b/src/google/protobuf/unittest_drop_unknown_fields.proto @@ -41,8 +41,8 @@ message Foo { BAR = 1; BAZ = 2; } - optional int32 int32_value = 1; - optional NestedEnum enum_value = 2; + int32 int32_value = 1; + NestedEnum enum_value = 2; } message FooWithExtraFields { @@ -52,7 +52,7 @@ message FooWithExtraFields { BAZ = 2; QUX = 3; } - optional int32 int32_value = 1; - optional NestedEnum enum_value = 2; - optional int32 extra_int32_value = 3; + int32 int32_value = 1; + NestedEnum enum_value = 2; + int32 extra_int32_value = 3; } diff --git a/src/google/protobuf/unittest_no_field_presence.proto b/src/google/protobuf/unittest_no_field_presence.proto index f261b58e..f5cc4cc3 100644 --- a/src/google/protobuf/unittest_no_field_presence.proto +++ b/src/google/protobuf/unittest_no_field_presence.proto @@ -43,7 +43,7 @@ option csharp_namespace = "Google.ProtocolBuffers.TestProtos.Proto3"; // forms. message TestAllTypes { message NestedMessage { - optional int32 bb = 1; + int32 bb = 1; } enum NestedEnum { @@ -55,36 +55,36 @@ message TestAllTypes { // Singular // TODO: remove 'optional' labels as soon as CL 69188077 is LGTM'd to make // 'optional' optional. - optional int32 optional_int32 = 1; - optional int64 optional_int64 = 2; - optional uint32 optional_uint32 = 3; - optional uint64 optional_uint64 = 4; - optional sint32 optional_sint32 = 5; - optional sint64 optional_sint64 = 6; - optional fixed32 optional_fixed32 = 7; - optional fixed64 optional_fixed64 = 8; - optional sfixed32 optional_sfixed32 = 9; - optional sfixed64 optional_sfixed64 = 10; - optional float optional_float = 11; - optional double optional_double = 12; - optional bool optional_bool = 13; - optional string optional_string = 14; - optional bytes optional_bytes = 15; - - optional NestedMessage optional_nested_message = 18; - optional ForeignMessage optional_foreign_message = 19; - optional protobuf_unittest.TestAllTypes optional_proto2_message = 20; - - optional NestedEnum optional_nested_enum = 21; - optional ForeignEnum optional_foreign_enum = 22; + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + + NestedMessage optional_nested_message = 18; + ForeignMessage optional_foreign_message = 19; + protobuf_unittest.TestAllTypes optional_proto2_message = 20; + + NestedEnum optional_nested_enum = 21; + ForeignEnum optional_foreign_enum = 22; // N.B.: proto2-enum-type fields not allowed, because their default values // might not be zero. //optional protobuf_unittest.ForeignEnum optional_proto2_enum = 23; - optional string optional_string_piece = 24 [ctype=STRING_PIECE]; - optional string optional_cord = 25 [ctype=CORD]; + string optional_string_piece = 24 [ctype=STRING_PIECE]; + string optional_cord = 25 [ctype=CORD]; - optional NestedMessage optional_lazy_message = 30 [lazy=true]; + NestedMessage optional_lazy_message = 30 [lazy=true]; // Repeated repeated int32 repeated_int32 = 31; @@ -124,13 +124,13 @@ message TestAllTypes { } message TestProto2Required { - optional protobuf_unittest.TestRequired proto2 = 1; + protobuf_unittest.TestRequired proto2 = 1; } // Define these after TestAllTypes to make sure the compiler can handle // that. message ForeignMessage { - optional int32 c = 1; + int32 c = 1; } enum ForeignEnum { diff --git a/src/google/protobuf/unittest_preserve_unknown_enum.proto b/src/google/protobuf/unittest_preserve_unknown_enum.proto index 24e6828f..abc3de28 100644 --- a/src/google/protobuf/unittest_preserve_unknown_enum.proto +++ b/src/google/protobuf/unittest_preserve_unknown_enum.proto @@ -49,7 +49,7 @@ enum MyEnumPlusExtra { } message MyMessage { - optional MyEnum e = 1; + MyEnum e = 1; repeated MyEnum repeated_e = 2; repeated MyEnum repeated_packed_e = 3 [packed=true]; repeated MyEnumPlusExtra repeated_packed_unexpected_e = 4; // not packed @@ -60,7 +60,7 @@ message MyMessage { } message MyMessagePlusExtra { - optional MyEnumPlusExtra e = 1; + MyEnumPlusExtra e = 1; repeated MyEnumPlusExtra repeated_e = 2; repeated MyEnumPlusExtra repeated_packed_e = 3 [packed=true]; repeated MyEnumPlusExtra repeated_packed_unexpected_e = 4 [packed=true]; diff --git a/src/google/protobuf/unittest_proto3_arena.proto b/src/google/protobuf/unittest_proto3_arena.proto index 6d7bada8..b835a6ba 100644 --- a/src/google/protobuf/unittest_proto3_arena.proto +++ b/src/google/protobuf/unittest_proto3_arena.proto @@ -43,7 +43,7 @@ message TestAllTypes { // The field name "b" fails to compile in proto1 because it conflicts with // a local variable named "b" in one of the generated methods. Doh. // This file needs to compile in proto1 to test backwards-compatibility. - optional int32 bb = 1; + int32 bb = 1; } enum NestedEnum { @@ -55,46 +55,47 @@ message TestAllTypes { } // Singular - optional int32 optional_int32 = 1; - optional int64 optional_int64 = 2; - optional uint32 optional_uint32 = 3; - optional uint64 optional_uint64 = 4; - optional sint32 optional_sint32 = 5; - optional sint64 optional_sint64 = 6; - optional fixed32 optional_fixed32 = 7; - optional fixed64 optional_fixed64 = 8; - optional sfixed32 optional_sfixed32 = 9; - optional sfixed64 optional_sfixed64 = 10; - optional float optional_float = 11; - optional double optional_double = 12; - optional bool optional_bool = 13; - optional string optional_string = 14; - optional bytes optional_bytes = 15; - - optional group OptionalGroup = 16 { - optional int32 a = 17; - } - - optional NestedMessage optional_nested_message = 18; - optional ForeignMessage optional_foreign_message = 19; - optional protobuf_unittest_import.ImportMessage optional_import_message = 20; - - optional NestedEnum optional_nested_enum = 21; - optional ForeignEnum optional_foreign_enum = 22; + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + + // Groups are not allowed in proto3. + // optional group OptionalGroup = 16 { + // optional int32 a = 17; + // } + + NestedMessage optional_nested_message = 18; + ForeignMessage optional_foreign_message = 19; + protobuf_unittest_import.ImportMessage optional_import_message = 20; + + NestedEnum optional_nested_enum = 21; + ForeignEnum optional_foreign_enum = 22; // Omitted (compared to unittest.proto) because proto2 enums are not allowed // inside proto2 messages. // // optional protobuf_unittest_import.ImportEnum optional_import_enum = 23; - optional string optional_string_piece = 24 [ctype=STRING_PIECE]; - optional string optional_cord = 25 [ctype=CORD]; + string optional_string_piece = 24 [ctype=STRING_PIECE]; + string optional_cord = 25 [ctype=CORD]; // Defined in unittest_import_public.proto - optional protobuf_unittest_import.PublicImportMessage + protobuf_unittest_import.PublicImportMessage optional_public_import_message = 26; - optional NestedMessage optional_lazy_message = 27 [lazy=true]; + NestedMessage optional_lazy_message = 27 [lazy=true]; // Repeated repeated int32 repeated_int32 = 31; @@ -113,9 +114,10 @@ message TestAllTypes { repeated string repeated_string = 44; repeated bytes repeated_bytes = 45; - repeated group RepeatedGroup = 46 { - optional int32 a = 47; - } + // Groups are not allowed in proto3. + // repeated group RepeatedGroup = 46 { + // optional int32 a = 47; + // } repeated NestedMessage repeated_nested_message = 48; repeated ForeignMessage repeated_foreign_message = 49; @@ -161,6 +163,24 @@ message TestPackedTypes { repeated ForeignEnum packed_enum = 103 [packed = true]; } +// Explicitly set packed to false +message TestUnpackedTypes { + repeated int32 repeated_int32 = 1 [packed = false]; + repeated int64 repeated_int64 = 2 [packed = false]; + repeated uint32 repeated_uint32 = 3 [packed = false]; + repeated uint64 repeated_uint64 = 4 [packed = false]; + repeated sint32 repeated_sint32 = 5 [packed = false]; + repeated sint64 repeated_sint64 = 6 [packed = false]; + repeated fixed32 repeated_fixed32 = 7 [packed = false]; + repeated fixed64 repeated_fixed64 = 8 [packed = false]; + repeated sfixed32 repeated_sfixed32 = 9 [packed = false]; + repeated sfixed64 repeated_sfixed64 = 10 [packed = false]; + repeated float repeated_float = 11 [packed = false]; + repeated double repeated_double = 12 [packed = false]; + repeated bool repeated_bool = 13 [packed = false]; + repeated TestAllTypes.NestedEnum repeated_nested_enum = 14 [packed = false]; +} + // This proto includes a recusively nested message. message NestedTestAllTypes { NestedTestAllTypes child = 1; @@ -170,7 +190,7 @@ message NestedTestAllTypes { // Define these after TestAllTypes to make sure the compiler can handle // that. message ForeignMessage { - optional int32 c = 1; + int32 c = 1; } enum ForeignEnum { diff --git a/src/google/protobuf/unknown_field_set.cc b/src/google/protobuf/unknown_field_set.cc index e15280c8..76644900 100644 --- a/src/google/protobuf/unknown_field_set.cc +++ b/src/google/protobuf/unknown_field_set.cc @@ -93,7 +93,7 @@ void UnknownFieldSet::InternalMergeFrom(const UnknownFieldSet& other) { fields_ = new vector(); for (int i = 0; i < other_field_count; i++) { fields_->push_back((*other.fields_)[i]); - fields_->back().DeepCopy(); + fields_->back().DeepCopy((*other.fields_)[i]); } } } @@ -104,7 +104,7 @@ void UnknownFieldSet::MergeFrom(const UnknownFieldSet& other) { if (fields_ == NULL) fields_ = new vector(); for (int i = 0; i < other_field_count; i++) { fields_->push_back((*other.fields_)[i]); - fields_->back().DeepCopy(); + fields_->back().DeepCopy((*other.fields_)[i]); } } } @@ -202,7 +202,7 @@ UnknownFieldSet* UnknownFieldSet::AddGroup(int number) { void UnknownFieldSet::AddField(const UnknownField& field) { if (fields_ == NULL) fields_ = new vector(); fields_->push_back(field); - fields_->back().DeepCopy(); + fields_->back().DeepCopy(field); } void UnknownFieldSet::DeleteSubrange(int start, int num) { @@ -303,7 +303,7 @@ void UnknownField::Reset() { } } -void UnknownField::DeepCopy() { +void UnknownField::DeepCopy(const UnknownField& other) { switch (type()) { case UnknownField::TYPE_LENGTH_DELIMITED: length_delimited_.string_value_ = new string( diff --git a/src/google/protobuf/unknown_field_set.h b/src/google/protobuf/unknown_field_set.h index 987b1979..6781cd0f 100644 --- a/src/google/protobuf/unknown_field_set.h +++ b/src/google/protobuf/unknown_field_set.h @@ -216,7 +216,7 @@ class LIBPROTOBUF_EXPORT UnknownField { void Reset(); // Make a deep copy of any pointers in this UnknownField. - void DeepCopy(); + void DeepCopy(const UnknownField& other); // Set the wire type of this UnknownField. Should only be used when this // UnknownField is being created. diff --git a/src/google/protobuf/wire_format.cc b/src/google/protobuf/wire_format.cc index c5bbbf2e..54cd653a 100644 --- a/src/google/protobuf/wire_format.cc +++ b/src/google/protobuf/wire_format.cc @@ -829,7 +829,7 @@ void WireFormat::SerializeFieldWithCachedSizes( count = 1; } - const bool is_packed = field->options().packed(); + const bool is_packed = field->is_packed(); if (is_packed && count > 0) { WireFormatLite::WriteTag(field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); @@ -996,7 +996,7 @@ int WireFormat::FieldByteSize( const int data_size = FieldDataOnlyByteSize(field, message); int our_size = data_size; - if (field->options().packed()) { + if (field->is_packed()) { if (data_size > 0) { // Packed fields get serialized like a string, not their native type. // Technically this doesn't really matter; the size only changes if it's diff --git a/src/google/protobuf/wire_format_lite.h b/src/google/protobuf/wire_format_lite.h index 76bc75a1..ac83abdc 100644 --- a/src/google/protobuf/wire_format_lite.h +++ b/src/google/protobuf/wire_format_lite.h @@ -330,6 +330,17 @@ class LIBPROTOBUF_EXPORT WireFormatLite { template static inline bool ReadMessageNoVirtual(input, MessageType* value); + // The same, but do not modify input's recursion depth. This is useful + // when reading a bunch of groups or messages in a loop, because then the + // recursion depth can be incremented before the loop and decremented after. + template + static inline bool ReadGroupNoVirtualNoRecursionDepth(field_number, input, + MessageType* value); + + template + static inline bool ReadMessageNoVirtualNoRecursionDepth(input, + MessageType* value); + // Write a tag. The Write*() functions typically include the tag, so // normally there's no need to call this unless using the Write*NoTag() // variants. diff --git a/src/google/protobuf/wire_format_lite_inl.h b/src/google/protobuf/wire_format_lite_inl.h index 129fc63f..d073ff92 100644 --- a/src/google/protobuf/wire_format_lite_inl.h +++ b/src/google/protobuf/wire_format_lite_inl.h @@ -470,7 +470,7 @@ inline bool WireFormatLite::ReadGroupNoVirtual( if (!value-> MessageType_WorkAroundCppLookupDefect::MergePartialFromCodedStream(input)) return false; - input->DecrementRecursionDepth(); + input->UnsafeDecrementRecursionDepth(); // Make sure the last thing read was an end tag for this group. if (!input->LastTagWas(MakeTag(field_number, WIRETYPE_END_GROUP))) { return false; @@ -478,6 +478,14 @@ inline bool WireFormatLite::ReadGroupNoVirtual( return true; } template +inline bool WireFormatLite::ReadGroupNoVirtualNoRecursionDepth( + int field_number, io::CodedInputStream* input, + MessageType_WorkAroundCppLookupDefect* value) { + return value->MessageType_WorkAroundCppLookupDefect:: + MergePartialFromCodedStream(input) && + input->LastTagWas(MakeTag(field_number, WIRETYPE_END_GROUP)); +} +template inline bool WireFormatLite::ReadMessageNoVirtual( io::CodedInputStream* input, MessageType_WorkAroundCppLookupDefect* value) { uint32 length; @@ -491,6 +499,17 @@ inline bool WireFormatLite::ReadMessageNoVirtual( // tag. return input->DecrementRecursionDepthAndPopLimit(p.first); } +template +inline bool WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( + io::CodedInputStream* input, MessageType_WorkAroundCppLookupDefect* value) { + io::CodedInputStream::Limit old_limit = input->ReadLengthAndPushLimit(); + if (!value-> + MessageType_WorkAroundCppLookupDefect::MergePartialFromCodedStream(input)) + return false; + // Make sure that parsing stopped when the limit was hit, not at an endgroup + // tag. + return input->CheckEntireMessageConsumedAndPopLimit(old_limit); +} // =================================================================== diff --git a/src/google/protobuf/wire_format_unittest.cc b/src/google/protobuf/wire_format_unittest.cc index a3062a6a..e80c5a71 100644 --- a/src/google/protobuf/wire_format_unittest.cc +++ b/src/google/protobuf/wire_format_unittest.cc @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -794,6 +795,137 @@ TEST(WireFormatTest, CompatibleTypes) { ASSERT_EQ(static_cast(data), msg5.data()); } +class Proto3PrimitiveRepeatedWireFormatTest + : public ::testing::TestWithParam { + protected: + template + void SetProto3PrimitiveRepeatedFields(Proto* message) { + message->add_repeated_int32(1); + message->add_repeated_int64(1); + message->add_repeated_uint32(1); + message->add_repeated_uint64(1); + message->add_repeated_sint32(1); + message->add_repeated_sint64(1); + message->add_repeated_fixed32(1); + message->add_repeated_fixed64(1); + message->add_repeated_sfixed32(1); + message->add_repeated_sfixed64(1); + message->add_repeated_float(1.0); + message->add_repeated_double(1.0); + message->add_repeated_bool(true); + message->add_repeated_nested_enum( + proto3_arena_unittest::TestAllTypes_NestedEnum_FOO); + } + + template + void ExpectProto3PrimitiveRepeatedFieldsSet(const Proto& message) { + EXPECT_EQ(1, message.repeated_int32(0)); + EXPECT_EQ(1, message.repeated_int64(0)); + EXPECT_EQ(1, message.repeated_uint32(0)); + EXPECT_EQ(1, message.repeated_uint64(0)); + EXPECT_EQ(1, message.repeated_sint32(0)); + EXPECT_EQ(1, message.repeated_sint64(0)); + EXPECT_EQ(1, message.repeated_fixed32(0)); + EXPECT_EQ(1, message.repeated_fixed64(0)); + EXPECT_EQ(1, message.repeated_sfixed32(0)); + EXPECT_EQ(1, message.repeated_sfixed64(0)); + EXPECT_EQ(1.0, message.repeated_float(0)); + EXPECT_EQ(1.0, message.repeated_double(0)); + EXPECT_EQ(true, message.repeated_bool(0)); + EXPECT_EQ(proto3_arena_unittest::TestAllTypes_NestedEnum_FOO, + message.repeated_nested_enum(0)); + } + + template + void TestProto3PrimitiveRepeatedFields(Proto* message, + const string& expected) { + SetProto3PrimitiveRepeatedFields(message); + + int size = message->ByteSize(); + + // Serialize using the generated code. + string generated_data; + { + io::StringOutputStream raw_output(&generated_data); + io::CodedOutputStream output(&raw_output); + message->SerializeWithCachedSizes(&output); + ASSERT_FALSE(output.HadError()); + } + + EXPECT_TRUE(expected == generated_data); + + message->Clear(); + message->ParseFromString(generated_data); + ExpectProto3PrimitiveRepeatedFieldsSet(*message); + + // Serialize using the dynamic code. + string dynamic_data; + { + io::StringOutputStream raw_output(&dynamic_data); + io::CodedOutputStream output(&raw_output); + WireFormat::SerializeWithCachedSizes(*message, size, &output); + ASSERT_FALSE(output.HadError()); + } + + EXPECT_TRUE(expected == dynamic_data); + + message->Clear(); + io::CodedInputStream input( + reinterpret_cast(dynamic_data.data()), + dynamic_data.size()); + WireFormat::ParseAndMergePartial(&input, message); + ExpectProto3PrimitiveRepeatedFieldsSet(*message); + } +}; +INSTANTIATE_TEST_CASE_P(SetPacked, + Proto3PrimitiveRepeatedWireFormatTest, + ::testing::Values(false, true)); + +TEST_P(Proto3PrimitiveRepeatedWireFormatTest, Proto3PrimitiveRepeated) { + proto3_arena_unittest::TestAllTypes packed_message; + proto3_arena_unittest::TestUnpackedTypes unpacked_message; + + const string packedExpected( + "\xFA\x01\x01\x01" + "\x82\x02\x01\x01" + "\x8A\x02\x01\x01" + "\x92\x02\x01\x01" + "\x9A\x02\x01\x02" + "\xA2\x02\x01\x02" + "\xAA\x02\x04\x01\x00\x00\x00" + "\xB2\x02\x08\x01\x00\x00\x00\x00\x00\x00\x00" + "\xBA\x02\x04\x01\x00\x00\x00" + "\xC2\x02\x08\x01\x00\x00\x00\x00\x00\x00\x00" + "\xCA\x02\x04\x00\x00\x80\x3f" + "\xD2\x02\x08\x00\x00\x00\x00\x00\x00\xf0\x3f" + "\xDA\x02\x01\x01" + "\x9A\x03\x01\x01", + 86); + + const string unpackedExpected( + "\x08\x01" + "\x10\x01" + "\x18\x01" + "\x20\x01" + "\x28\x02" + "\x30\x02" + "\x3D\x01\x00\x00\x00" + "\x41\x01\x00\x00\x00\x00\x00\x00\x00" + "\x4D\x01\x00\x00\x00" + "\x51\x01\x00\x00\x00\x00\x00\x00\x00" + "\x5D\x00\x00\x80\x3f" + "\x61\x00\x00\x00\x00\x00\x00\xf0\x3f" + "\x68\x01" + "\x70\x01", + 58); + + if (GetParam()) { + TestProto3PrimitiveRepeatedFields(&packed_message, packedExpected); + } else { + TestProto3PrimitiveRepeatedFields(&unpacked_message, unpackedExpected); + } +} + class WireFormatInvalidInputTest : public testing::Test { protected: // Make a serialized TestAllTypes in which the field optional_nested_message diff --git a/src/google/protobuf/wrappers.pb.cc b/src/google/protobuf/wrappers.pb.cc index 4d91ed56..ffc77f1c 100644 --- a/src/google/protobuf/wrappers.pb.cc +++ b/src/google/protobuf/wrappers.pb.cc @@ -312,7 +312,7 @@ const int DoubleValue::kValueFieldNumber; #endif // !_MSC_VER DoubleValue::DoubleValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.DoubleValue) } @@ -458,9 +458,9 @@ int DoubleValue::ByteSize() const { void DoubleValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const DoubleValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const DoubleValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -514,7 +514,7 @@ void DoubleValue::InternalSwap(DoubleValue* other) { // DoubleValue // optional double value = 1; - void DoubleValue::clear_value() { +void DoubleValue::clear_value() { value_ = 0; } double DoubleValue::value() const { @@ -536,7 +536,7 @@ const int FloatValue::kValueFieldNumber; #endif // !_MSC_VER FloatValue::FloatValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FloatValue) } @@ -682,9 +682,9 @@ int FloatValue::ByteSize() const { void FloatValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FloatValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FloatValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -738,7 +738,7 @@ void FloatValue::InternalSwap(FloatValue* other) { // FloatValue // optional float value = 1; - void FloatValue::clear_value() { +void FloatValue::clear_value() { value_ = 0; } float FloatValue::value() const { @@ -760,7 +760,7 @@ const int Int64Value::kValueFieldNumber; #endif // !_MSC_VER Int64Value::Int64Value() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Int64Value) } @@ -908,9 +908,9 @@ int Int64Value::ByteSize() const { void Int64Value::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Int64Value* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Int64Value* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -964,7 +964,7 @@ void Int64Value::InternalSwap(Int64Value* other) { // Int64Value // optional int64 value = 1; - void Int64Value::clear_value() { +void Int64Value::clear_value() { value_ = GOOGLE_LONGLONG(0); } ::google::protobuf::int64 Int64Value::value() const { @@ -986,7 +986,7 @@ const int UInt64Value::kValueFieldNumber; #endif // !_MSC_VER UInt64Value::UInt64Value() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.UInt64Value) } @@ -1134,9 +1134,9 @@ int UInt64Value::ByteSize() const { void UInt64Value::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const UInt64Value* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const UInt64Value* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1190,7 +1190,7 @@ void UInt64Value::InternalSwap(UInt64Value* other) { // UInt64Value // optional uint64 value = 1; - void UInt64Value::clear_value() { +void UInt64Value::clear_value() { value_ = GOOGLE_ULONGLONG(0); } ::google::protobuf::uint64 UInt64Value::value() const { @@ -1212,7 +1212,7 @@ const int Int32Value::kValueFieldNumber; #endif // !_MSC_VER Int32Value::Int32Value() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Int32Value) } @@ -1360,9 +1360,9 @@ int Int32Value::ByteSize() const { void Int32Value::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Int32Value* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Int32Value* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1416,7 +1416,7 @@ void Int32Value::InternalSwap(Int32Value* other) { // Int32Value // optional int32 value = 1; - void Int32Value::clear_value() { +void Int32Value::clear_value() { value_ = 0; } ::google::protobuf::int32 Int32Value::value() const { @@ -1438,7 +1438,7 @@ const int UInt32Value::kValueFieldNumber; #endif // !_MSC_VER UInt32Value::UInt32Value() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.UInt32Value) } @@ -1586,9 +1586,9 @@ int UInt32Value::ByteSize() const { void UInt32Value::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const UInt32Value* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const UInt32Value* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1642,7 +1642,7 @@ void UInt32Value::InternalSwap(UInt32Value* other) { // UInt32Value // optional uint32 value = 1; - void UInt32Value::clear_value() { +void UInt32Value::clear_value() { value_ = 0u; } ::google::protobuf::uint32 UInt32Value::value() const { @@ -1664,7 +1664,7 @@ const int BoolValue::kValueFieldNumber; #endif // !_MSC_VER BoolValue::BoolValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.BoolValue) } @@ -1810,9 +1810,9 @@ int BoolValue::ByteSize() const { void BoolValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const BoolValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const BoolValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1866,7 +1866,7 @@ void BoolValue::InternalSwap(BoolValue* other) { // BoolValue // optional bool value = 1; - void BoolValue::clear_value() { +void BoolValue::clear_value() { value_ = false; } bool BoolValue::value() const { @@ -1888,7 +1888,7 @@ const int StringValue::kValueFieldNumber; #endif // !_MSC_VER StringValue::StringValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.StringValue) } @@ -2051,9 +2051,9 @@ int StringValue::ByteSize() const { void StringValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const StringValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const StringValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -2108,7 +2108,7 @@ void StringValue::InternalSwap(StringValue* other) { // StringValue // optional string value = 1; - void StringValue::clear_value() { +void StringValue::clear_value() { value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& StringValue::value() const { @@ -2159,7 +2159,7 @@ const int BytesValue::kValueFieldNumber; #endif // !_MSC_VER BytesValue::BytesValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.BytesValue) } @@ -2310,9 +2310,9 @@ int BytesValue::ByteSize() const { void BytesValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const BytesValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const BytesValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -2367,7 +2367,7 @@ void BytesValue::InternalSwap(BytesValue* other) { // BytesValue // optional bytes value = 1; - void BytesValue::clear_value() { +void BytesValue::clear_value() { value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& BytesValue::value() const { diff --git a/src/google/protobuf/wrappers.pb.h b/src/google/protobuf/wrappers.pb.h index c318f950..387ebd7c 100644 --- a/src/google/protobuf/wrappers.pb.h +++ b/src/google/protobuf/wrappers.pb.h @@ -984,6 +984,22 @@ inline void BytesValue::set_allocated_value(::std::string* value) { } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) diff --git a/vsprojects/libprotoc.vcproj b/vsprojects/libprotoc.vcproj index d1f065e2..3474d21f 100644 --- a/vsprojects/libprotoc.vcproj +++ b/vsprojects/libprotoc.vcproj @@ -295,6 +295,10 @@ RelativePath="..\src\google\protobuf\compiler\java\java_enum_field.h" > + + @@ -323,14 +327,34 @@ RelativePath="..\src\google\protobuf\compiler\java\java_lazy_message_field.h" > + + + + + + + + + + @@ -339,6 +363,10 @@ RelativePath="..\src\google\protobuf\compiler\java\java_primitive_field.h" > + + @@ -351,6 +379,10 @@ RelativePath="..\src\google\protobuf\compiler\java\java_string_field.h" > + + @@ -607,18 +639,42 @@ RelativePath="..\src\google\protobuf\compiler\java\java_lazy_message_field.cc" > + + + + + + + + + + + + @@ -627,6 +683,10 @@ RelativePath="..\src\google\protobuf\compiler\java\java_primitive_field.cc" > + + @@ -639,6 +699,10 @@ RelativePath="..\src\google\protobuf\compiler\java\java_string_field.cc" > + + -- cgit v1.2.3 From 621bf8206543334bf59cda5ae7f4aade1fe337c7 Mon Sep 17 00:00:00 2001 From: Feng Xiao Date: Tue, 26 May 2015 11:40:37 -0700 Subject: Null-terminate C strings. Change-Id: I85675bfcd7697bb058b9af475445a6bfdd7fd967 --- conformance/conformance_test_runner.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'conformance') diff --git a/conformance/conformance_test_runner.cc b/conformance/conformance_test_runner.cc index ca5877bd..e0bb50a5 100644 --- a/conformance/conformance_test_runner.cc +++ b/conformance/conformance_test_runner.cc @@ -143,8 +143,9 @@ class ForkPipeRunner : public google::protobuf::ConformanceTestRunner { CHECK_SYSCALL(close(toproc_pipe_fd[1])); CHECK_SYSCALL(close(fromproc_pipe_fd[0])); - scoped_array executable(new char[executable_.size()]); + scoped_array executable(new char[executable_.size() + 1]); memcpy(executable.get(), executable_.c_str(), executable_.size()); + executable[executable_.size()] = '\0'; char *const argv[] = {executable.get(), NULL}; CHECK_SYSCALL(execv(executable.get(), argv)); // Never returns. -- cgit v1.2.3 From d2b67389b30d1bff1080be3a3d8d7cfb0b81b867 Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Wed, 3 Jun 2015 12:04:35 -0700 Subject: Conformance tests can now be excluded based on their names. This allows us to enable conformance tests even when we know that some tests are failing and need to be fixed. Change-Id: I372f43663008747db6f2b2cf06e6ffa4c6d85b2d --- conformance/Makefile.am | 2 +- conformance/conformance.proto | 8 +- conformance/conformance_test.cc | 160 ++++++++++++++++++++++++--------- conformance/conformance_test.h | 39 ++++++-- conformance/conformance_test_runner.cc | 63 +++++++++++-- conformance/failure_list_cpp.txt | 21 +++++ 6 files changed, 234 insertions(+), 59 deletions(-) create mode 100644 conformance/failure_list_cpp.txt (limited to 'conformance') diff --git a/conformance/Makefile.am b/conformance/Makefile.am index 95343f14..cccbac9e 100644 --- a/conformance/Makefile.am +++ b/conformance/Makefile.am @@ -57,7 +57,7 @@ conformance-java: javac_middleman # Targets for actually running tests. test_cpp: protoc_middleman conformance-test-runner conformance-cpp - ./conformance-test-runner ./conformance-cpp + ./conformance-test-runner --failure_list failure_list_cpp.txt ./conformance-cpp test_java: protoc_middleman conformance-test-runner conformance-java ./conformance-test-runner ./conformance-java diff --git a/conformance/conformance.proto b/conformance/conformance.proto index 39eafdbb..892db380 100644 --- a/conformance/conformance.proto +++ b/conformance/conformance.proto @@ -57,11 +57,13 @@ option java_package = "com.google.protobuf.conformance"; // 2. parse the protobuf or JSON payload in "payload" (which may fail) // 3. if the parse succeeded, serialize the message in the requested format. message ConformanceRequest { + string test_name = 1; + // The payload (whether protobuf of JSON) is always for a TestAllTypes proto // (see below). oneof payload { - bytes protobuf_payload = 1; - string json_payload = 2; + bytes protobuf_payload = 2; + string json_payload = 3; } enum RequestedOutput { @@ -71,7 +73,7 @@ message ConformanceRequest { } // Which format should the testee serialize its message to? - RequestedOutput requested_output = 3; + RequestedOutput requested_output = 4; } // Represents a single test case's output. diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc index 857f2152..d441137d 100644 --- a/conformance/conformance_test.cc +++ b/conformance/conformance_test.cc @@ -126,12 +126,11 @@ string submsg(uint32_t fn, const string& buf) { #define UNKNOWN_FIELD 666 -uint32_t GetFieldNumberForType(WireFormatLite::FieldType type, bool repeated) { +uint32_t GetFieldNumberForType(FieldDescriptor::Type type, bool repeated) { const Descriptor* d = TestAllTypes().GetDescriptor(); for (int i = 0; i < d->field_count(); i++) { const FieldDescriptor* f = d->field(i); - if (static_cast(f->type()) == type && - f->is_repeated() == repeated) { + if (f->type() == type && f->is_repeated() == repeated) { return f->number(); } } @@ -139,16 +138,37 @@ uint32_t GetFieldNumberForType(WireFormatLite::FieldType type, bool repeated) { return 0; } +string UpperCase(string str) { + for (int i = 0; i < str.size(); i++) { + str[i] = toupper(str[i]); + } + return str; +} + } // anonymous namespace namespace google { namespace protobuf { -void ConformanceTestSuite::ReportSuccess() { +void ConformanceTestSuite::ReportSuccess(const string& test_name) { + if (expected_to_fail_.erase(test_name) != 0) { + StringAppendF(&output_, + "ERROR: test %s is in the failure list, but test succeeded. " + "Remove it from the failure list.\n", + test_name.c_str()); + unexpected_succeeding_tests_.insert(test_name); + } successes_++; } -void ConformanceTestSuite::ReportFailure(const char *fmt, ...) { +void ConformanceTestSuite::ReportFailure(const string& test_name, + const char* fmt, ...) { + if (expected_to_fail_.erase(test_name) == 1) { + StringAppendF(&output_, "FAILED AS EXPECTED: "); + } else { + StringAppendF(&output_, "ERROR: "); + unexpected_failing_tests_.insert(test_name); + } va_list args; va_start(args, fmt); StringAppendV(&output_, fmt, args); @@ -158,6 +178,10 @@ void ConformanceTestSuite::ReportFailure(const char *fmt, ...) { void ConformanceTestSuite::RunTest(const ConformanceRequest& request, ConformanceResponse* response) { + if (test_names_.insert(request.test_name()).second == false) { + GOOGLE_LOG(FATAL) << "Duplicated test name: " << request.test_name(); + } + string serialized_request; string serialized_response; request.SerializeToString(&serialized_request); @@ -176,10 +200,12 @@ void ConformanceTestSuite::RunTest(const ConformanceRequest& request, } } -void ConformanceTestSuite::DoExpectParseFailureForProto(const string& proto, - int line) { +// Expect that this precise protobuf will cause a parse error. +void ConformanceTestSuite::ExpectParseFailureForProto( + const string& proto, const string& test_name) { ConformanceRequest request; ConformanceResponse response; + request.set_test_name(test_name); request.set_protobuf_payload(proto); // We don't expect output, but if the program erroneously accepts the protobuf @@ -188,29 +214,27 @@ void ConformanceTestSuite::DoExpectParseFailureForProto(const string& proto, RunTest(request, &response); if (response.result_case() == ConformanceResponse::kParseError) { - ReportSuccess(); + ReportSuccess(test_name); } else { - ReportFailure("Should have failed, but didn't. Line: %d, Request: %s, " + ReportFailure(test_name, + "Should have failed to parse, but didn't. Request: %s, " "response: %s\n", - line, request.ShortDebugString().c_str(), response.ShortDebugString().c_str()); } } -// Expect that this precise protobuf will cause a parse error. -#define ExpectParseFailureForProto(proto) DoExpectParseFailureForProto(proto, __LINE__) - // Expect that this protobuf will cause a parse error, even if it is followed // by valid protobuf data. We can try running this twice: once with this // data verbatim and once with this data followed by some valid data. // // TODO(haberman): implement the second of these. -#define ExpectHardParseFailureForProto(proto) DoExpectParseFailureForProto(proto, __LINE__) - +void ConformanceTestSuite::ExpectHardParseFailureForProto( + const string& proto, const string& test_name) { + return ExpectParseFailureForProto(proto, test_name); +} -void ConformanceTestSuite::TestPrematureEOFForType( - WireFormatLite::FieldType type) { +void ConformanceTestSuite::TestPrematureEOFForType(FieldDescriptor::Type type) { // Incomplete values for each wire type. static const string incompletes[6] = { string("\x80"), // VARINT @@ -223,45 +247,51 @@ void ConformanceTestSuite::TestPrematureEOFForType( uint32_t fieldnum = GetFieldNumberForType(type, false); uint32_t rep_fieldnum = GetFieldNumberForType(type, true); - WireFormatLite::WireType wire_type = - WireFormatLite::WireTypeForFieldType(type); + WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType( + static_cast(type)); const string& incomplete = incompletes[wire_type]; + const string type_name = + UpperCase(string(".") + FieldDescriptor::TypeName(type)); - // EOF before a known non-repeated value. - ExpectParseFailureForProto(tag(fieldnum, wire_type)); + ExpectParseFailureForProto( + tag(fieldnum, wire_type), + "PrematureEofBeforeKnownNonRepeatedValue" + type_name); - // EOF before a known repeated value. - ExpectParseFailureForProto(tag(rep_fieldnum, wire_type)); + ExpectParseFailureForProto( + tag(rep_fieldnum, wire_type), + "PrematureEofBeforeKnownRepeatedValue" + type_name); - // EOF before an unknown value. - ExpectParseFailureForProto(tag(UNKNOWN_FIELD, wire_type)); + ExpectParseFailureForProto( + tag(UNKNOWN_FIELD, wire_type), + "PrematureEofBeforeUnknownValue" + type_name); - // EOF inside a known non-repeated value. ExpectParseFailureForProto( - cat( tag(fieldnum, wire_type), incomplete )); + cat( tag(fieldnum, wire_type), incomplete ), + "PrematureEofInsideKnownNonRepeatedValue" + type_name); - // EOF inside a known repeated value. ExpectParseFailureForProto( - cat( tag(rep_fieldnum, wire_type), incomplete )); + cat( tag(rep_fieldnum, wire_type), incomplete ), + "PrematureEofInsideKnownRepeatedValue" + type_name); - // EOF inside an unknown value. ExpectParseFailureForProto( - cat( tag(UNKNOWN_FIELD, wire_type), incomplete )); + cat( tag(UNKNOWN_FIELD, wire_type), incomplete ), + "PrematureEofInsideUnknownValue" + type_name); if (wire_type == WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { - // EOF in the middle of delimited data for known non-repeated value. ExpectParseFailureForProto( - cat( tag(fieldnum, wire_type), varint(1) )); + cat( tag(fieldnum, wire_type), varint(1) ), + "PrematureEofInDelimitedDataForKnownNonRepeatedValue" + type_name); - // EOF in the middle of delimited data for known repeated value. ExpectParseFailureForProto( - cat( tag(rep_fieldnum, wire_type), varint(1) )); + cat( tag(rep_fieldnum, wire_type), varint(1) ), + "PrematureEofInDelimitedDataForKnownRepeatedValue" + type_name); // EOF in the middle of delimited data for unknown value. ExpectParseFailureForProto( - cat( tag(UNKNOWN_FIELD, wire_type), varint(1) )); + cat( tag(UNKNOWN_FIELD, wire_type), varint(1) ), + "PrematureEofInDelimitedDataForUnknownValue" + type_name); - if (type == WireFormatLite::TYPE_MESSAGE) { + if (type == FieldDescriptor::TYPE_MESSAGE) { // Submessage ends in the middle of a value. string incomplete_submsg = cat( tag(WireFormatLite::TYPE_INT32, WireFormatLite::WIRETYPE_VARINT), @@ -269,42 +299,86 @@ void ConformanceTestSuite::TestPrematureEOFForType( ExpectHardParseFailureForProto( cat( tag(fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), varint(incomplete_submsg.size()), - incomplete_submsg )); + incomplete_submsg ), + "PrematureEofInSubmessageValue" + type_name); } - } else if (type != WireFormatLite::TYPE_GROUP) { + } else if (type != FieldDescriptor::TYPE_GROUP) { // Non-delimited, non-group: eligible for packing. // Packed region ends in the middle of a value. ExpectHardParseFailureForProto( cat( tag(rep_fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), varint(incomplete.size()), - incomplete )); + incomplete ), + "PrematureEofInPackedFieldValue" + type_name); // EOF in the middle of packed region. ExpectParseFailureForProto( cat( tag(rep_fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), - varint(1) )); + varint(1) ), + "PrematureEofInPackedField" + type_name); } } -void ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, +void ConformanceTestSuite::SetFailureList(const vector& failure_list) { + expected_to_fail_.clear(); + std::copy(failure_list.begin(), failure_list.end(), + std::inserter(expected_to_fail_, expected_to_fail_.end())); +} + +bool ConformanceTestSuite::CheckSetEmpty(const set& set_to_check, + const char* msg) { + if (set_to_check.empty()) { + return true; + } else { + StringAppendF(&output_, "\n"); + StringAppendF(&output_, "ERROR: %s:\n", msg); + for (set::const_iterator iter = set_to_check.begin(); + iter != set_to_check.end(); ++iter) { + StringAppendF(&output_, "%s\n", iter->c_str()); + } + return false; + } +} + +bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, std::string* output) { runner_ = runner; output_.clear(); successes_ = 0; failures_ = 0; + test_names_.clear(); + unexpected_failing_tests_.clear(); + unexpected_succeeding_tests_.clear(); for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) { if (i == FieldDescriptor::TYPE_GROUP) continue; - TestPrematureEOFForType(static_cast(i)); + TestPrematureEOFForType(static_cast(i)); } + StringAppendF(&output_, "\n"); StringAppendF(&output_, "CONFORMANCE SUITE FINISHED: completed %d tests, %d successes, " "%d failures.\n", successes_ + failures_, successes_, failures_); + bool ok = + CheckSetEmpty(expected_to_fail_, + "These tests were listed in the failure list, but they " + "don't exist. Remove them from the failure list") && + + CheckSetEmpty(unexpected_failing_tests_, + "These tests failed. If they can't be fixed right now, " + "you can add them to the failure list so the overall " + "suite can succeed") && + + CheckSetEmpty(unexpected_succeeding_tests_, + "These tests succeeded, even though they were listed in " + "the failure list. Remove them from the failure list"); + output->assign(output_); + + return ok; } } // namespace protobuf diff --git a/conformance/conformance_test.h b/conformance/conformance_test.h index c16f9c0b..651088f7 100644 --- a/conformance/conformance_test.h +++ b/conformance/conformance_test.h @@ -83,24 +83,49 @@ class ConformanceTestSuite { public: ConformanceTestSuite() : verbose_(false) {} + // Sets the list of tests that are expected to fail when RunSuite() is called. + // RunSuite() will fail unless the set of failing tests is exactly the same + // as this list. + void SetFailureList(const std::vector& failure_list); + // Run all the conformance tests against the given test runner. // Test output will be stored in "output". - void RunSuite(ConformanceTestRunner* runner, std::string* output); + // + // Returns true if the set of failing tests was exactly the same as the + // failure list. If SetFailureList() was not called, returns true if all + // tests passed. + bool RunSuite(ConformanceTestRunner* runner, std::string* output); private: - void ReportSuccess(); - void ReportFailure(const char* fmt, ...); + void ReportSuccess(const std::string& test_name); + void ReportFailure(const std::string& test_name, const char* fmt, ...); void RunTest(const conformance::ConformanceRequest& request, conformance::ConformanceResponse* response); - void DoExpectParseFailureForProto(const std::string& proto, int line); - void TestPrematureEOFForType( - google::protobuf::internal::WireFormatLite::FieldType type); - + void ExpectParseFailureForProto(const std::string& proto, + const std::string& test_name); + void ExpectHardParseFailureForProto(const std::string& proto, + const std::string& test_name); + void TestPrematureEOFForType(google::protobuf::FieldDescriptor::Type type); + bool CheckSetEmpty(const set& set_to_check, const char* msg); ConformanceTestRunner* runner_; int successes_; int failures_; bool verbose_; std::string output_; + + // The set of test names that are expected to fail in this run, but haven't + // failed yet. + std::set expected_to_fail_; + + // The set of test names that have been run. Used to ensure that there are no + // duplicate names in the suite. + std::set test_names_; + + // The set of tests that failed, but weren't expected to. + std::set unexpected_failing_tests_; + + // The set of tests that succeeded, but weren't expected to. + std::set unexpected_succeeding_tests_; }; } // namespace protobuf diff --git a/conformance/conformance_test_runner.cc b/conformance/conformance_test_runner.cc index e0bb50a5..b56e19cf 100644 --- a/conformance/conformance_test_runner.cc +++ b/conformance/conformance_test_runner.cc @@ -55,6 +55,8 @@ #include #include +#include +#include #include "conformance.pb.h" #include "conformance_test.h" @@ -62,6 +64,8 @@ using conformance::ConformanceRequest; using conformance::ConformanceResponse; using google::protobuf::internal::scoped_array; +using std::string; +using std::vector; #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) @@ -180,18 +184,67 @@ class ForkPipeRunner : public google::protobuf::ConformanceTestRunner { std::string executable_; }; +void UsageError() { + fprintf(stderr, + "Usage: conformance-test-runner [options] \n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, + " --failure_list Use to specify list of tests\n"); + fprintf(stderr, + " that are expected to fail. File\n"); + fprintf(stderr, + " should contain one test name per\n"); + fprintf(stderr, + " line. Use '#' for comments.\n"); + exit(1); +} + +void ParseFailureList(const char *filename, vector* failure_list) { + std::ifstream infile(filename); + for (string line; getline(infile, line);) { + // Remove whitespace. + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // Remove comments. + line = line.substr(0, line.find("#")); + + if (!line.empty()) { + failure_list->push_back(line); + } + } +} int main(int argc, char *argv[]) { - if (argc < 2) { - fprintf(stderr, "Usage: conformance-test-runner \n"); - exit(1); + int arg = 1; + char *program; + vector failure_list; + + for (int arg = 1; arg < argc; ++arg) { + if (strcmp(argv[arg], "--failure_list") == 0) { + if (++arg == argc) UsageError(); + ParseFailureList(argv[arg], &failure_list); + } else if (argv[arg][0] == '-') { + fprintf(stderr, "Unknown option: %s\n", argv[arg]); + UsageError(); + } else { + if (arg != argc - 1) { + fprintf(stderr, "Too many arguments.\n"); + UsageError(); + } + program = argv[arg]; + } } - ForkPipeRunner runner(argv[1]); + ForkPipeRunner runner(program); google::protobuf::ConformanceTestSuite suite; + suite.SetFailureList(failure_list); std::string output; - suite.RunSuite(&runner, &output); + bool ok = suite.RunSuite(&runner, &output); fwrite(output.c_str(), 1, output.size(), stderr); + + return ok ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/conformance/failure_list_cpp.txt b/conformance/failure_list_cpp.txt new file mode 100644 index 00000000..cda1d4a1 --- /dev/null +++ b/conformance/failure_list_cpp.txt @@ -0,0 +1,21 @@ +# This is the list of conformance tests that are known to fail for the C++ +# implementation right now. These should be fixed. +# +# By listing them here we can keep tabs on which ones are failing and be sure +# that we don't introduce regressions in other tests. +# +# TODO(haberman): insert links to corresponding bugs tracking the issue. +# Should we use GitHub issues or the Google-internal bug tracker. + +PrematureEofBeforeKnownRepeatedValue.MESSAGE +PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE +PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE +PrematureEofInPackedField.BOOL +PrematureEofInPackedField.ENUM +PrematureEofInPackedField.INT32 +PrematureEofInPackedField.INT64 +PrematureEofInPackedField.SINT32 +PrematureEofInPackedField.SINT64 +PrematureEofInPackedField.UINT32 +PrematureEofInPackedField.UINT64 +PrematureEofInsideKnownRepeatedValue.MESSAGE -- cgit v1.2.3 From 23bf3b566f4bd1c5d73a5d093e2d0a45ee81ff27 Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Thu, 4 Jun 2015 15:04:00 -0700 Subject: Removed test_name from conformance.proto. Change-Id: I382dcda97fa123a6da4ff5faad5d7ece95853f33 --- conformance/conformance.proto | 8 +++----- conformance/conformance_test.cc | 17 +++++++++-------- conformance/conformance_test.h | 3 ++- conformance/failure_list_cpp.txt | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) (limited to 'conformance') diff --git a/conformance/conformance.proto b/conformance/conformance.proto index 892db380..39eafdbb 100644 --- a/conformance/conformance.proto +++ b/conformance/conformance.proto @@ -57,13 +57,11 @@ option java_package = "com.google.protobuf.conformance"; // 2. parse the protobuf or JSON payload in "payload" (which may fail) // 3. if the parse succeeded, serialize the message in the requested format. message ConformanceRequest { - string test_name = 1; - // The payload (whether protobuf of JSON) is always for a TestAllTypes proto // (see below). oneof payload { - bytes protobuf_payload = 2; - string json_payload = 3; + bytes protobuf_payload = 1; + string json_payload = 2; } enum RequestedOutput { @@ -73,7 +71,7 @@ message ConformanceRequest { } // Which format should the testee serialize its message to? - RequestedOutput requested_output = 4; + RequestedOutput requested_output = 3; } // Represents a single test case's output. diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc index d441137d..7a7fc6f5 100644 --- a/conformance/conformance_test.cc +++ b/conformance/conformance_test.cc @@ -164,9 +164,9 @@ void ConformanceTestSuite::ReportSuccess(const string& test_name) { void ConformanceTestSuite::ReportFailure(const string& test_name, const char* fmt, ...) { if (expected_to_fail_.erase(test_name) == 1) { - StringAppendF(&output_, "FAILED AS EXPECTED: "); + StringAppendF(&output_, "FAILED AS EXPECTED, test=%s: ", test_name.c_str()); } else { - StringAppendF(&output_, "ERROR: "); + StringAppendF(&output_, "ERROR, test=%s: ", test_name.c_str()); unexpected_failing_tests_.insert(test_name); } va_list args; @@ -176,10 +176,11 @@ void ConformanceTestSuite::ReportFailure(const string& test_name, failures_++; } -void ConformanceTestSuite::RunTest(const ConformanceRequest& request, +void ConformanceTestSuite::RunTest(const string& test_name, + const ConformanceRequest& request, ConformanceResponse* response) { - if (test_names_.insert(request.test_name()).second == false) { - GOOGLE_LOG(FATAL) << "Duplicated test name: " << request.test_name(); + if (test_names_.insert(test_name).second == false) { + GOOGLE_LOG(FATAL) << "Duplicated test name: " << test_name; } string serialized_request; @@ -194,7 +195,8 @@ void ConformanceTestSuite::RunTest(const ConformanceRequest& request, } if (verbose_) { - StringAppendF(&output_, "conformance test: request=%s, response=%s\n", + StringAppendF(&output_, "conformance test: name=%s, request=%s, response=%s\n", + test_name.c_str(), request.ShortDebugString().c_str(), response->ShortDebugString().c_str()); } @@ -205,14 +207,13 @@ void ConformanceTestSuite::ExpectParseFailureForProto( const string& proto, const string& test_name) { ConformanceRequest request; ConformanceResponse response; - request.set_test_name(test_name); request.set_protobuf_payload(proto); // We don't expect output, but if the program erroneously accepts the protobuf // we let it send its response as this. We must not leave it unspecified. request.set_requested_output(ConformanceRequest::PROTOBUF); - RunTest(request, &response); + RunTest(test_name, request, &response); if (response.result_case() == ConformanceResponse::kParseError) { ReportSuccess(test_name); } else { diff --git a/conformance/conformance_test.h b/conformance/conformance_test.h index 651088f7..764a8d33 100644 --- a/conformance/conformance_test.h +++ b/conformance/conformance_test.h @@ -99,7 +99,8 @@ class ConformanceTestSuite { private: void ReportSuccess(const std::string& test_name); void ReportFailure(const std::string& test_name, const char* fmt, ...); - void RunTest(const conformance::ConformanceRequest& request, + void RunTest(const std::string& test_name, + const conformance::ConformanceRequest& request, conformance::ConformanceResponse* response); void ExpectParseFailureForProto(const std::string& proto, const std::string& test_name); diff --git a/conformance/failure_list_cpp.txt b/conformance/failure_list_cpp.txt index cda1d4a1..d792eddd 100644 --- a/conformance/failure_list_cpp.txt +++ b/conformance/failure_list_cpp.txt @@ -5,7 +5,7 @@ # that we don't introduce regressions in other tests. # # TODO(haberman): insert links to corresponding bugs tracking the issue. -# Should we use GitHub issues or the Google-internal bug tracker. +# Should we use GitHub issues or the Google-internal bug tracker? PrematureEofBeforeKnownRepeatedValue.MESSAGE PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE -- cgit v1.2.3 From b0500b37b231b8f93a5b1b95e23d13830e11eb07 Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Fri, 10 Jul 2015 16:36:59 -0700 Subject: Added support for Json and valid input to conformance tests. This was enabled by the recent open-sourcing of JSON support and MessageDifferencer. MessageDifferencer allows the conformance suite to expand because it allows us to write tests for payloads that parse successfully. To verify the testee's output payload, we need to parse it back into a message and compare the message instances. Comparing output bytes vs. a golden message is *not* valid, because protobufs do not have a canonical encoding (especially in the presence of maps, which have no prescribed serialization order). We only add one small JSON test for now, but with the framework in place we now have the foundation to dramatically expand the coverage of the conformance test suite. Also added the ability for the testee to skip tests that exercise features that are unimplemented. This allows Java (which currently has no JSON support) to skip tests involving JSON. Change-Id: I697b4363da432b61ae3b638b4287c4cda1af4deb --- conformance/ConformanceJava.java | 6 +- conformance/conformance.proto | 18 ++-- conformance/conformance_cpp.cc | 56 ++++++++++-- conformance/conformance_test.cc | 186 +++++++++++++++++++++++++++++++++++---- conformance/conformance_test.h | 25 +++++- conformance/failure_list_cpp.txt | 24 ++--- 6 files changed, 263 insertions(+), 52 deletions(-) (limited to 'conformance') diff --git a/conformance/ConformanceJava.java b/conformance/ConformanceJava.java index c1a53141..25646b85 100644 --- a/conformance/ConformanceJava.java +++ b/conformance/ConformanceJava.java @@ -54,7 +54,7 @@ class ConformanceJava { break; } case JSON_PAYLOAD: { - return Conformance.ConformanceResponse.newBuilder().setRuntimeError("JSON not yet supported.").build(); + return Conformance.ConformanceResponse.newBuilder().setSkipped("JSON not yet supported.").build(); } case PAYLOAD_NOT_SET: { throw new RuntimeException("Request didn't have payload."); @@ -65,7 +65,7 @@ class ConformanceJava { } } - switch (request.getRequestedOutput()) { + switch (request.getRequestedOutputFormat()) { case UNSPECIFIED: throw new RuntimeException("Unspecified output format."); @@ -73,7 +73,7 @@ class ConformanceJava { return Conformance.ConformanceResponse.newBuilder().setProtobufPayload(testMessage.toByteString()).build(); case JSON: - return Conformance.ConformanceResponse.newBuilder().setRuntimeError("JSON not yet supported.").build(); + return Conformance.ConformanceResponse.newBuilder().setSkipped("JSON not yet supported.").build(); default: { throw new RuntimeException("Unexpected request output."); diff --git a/conformance/conformance.proto b/conformance/conformance.proto index 39eafdbb..714cbe78 100644 --- a/conformance/conformance.proto +++ b/conformance/conformance.proto @@ -51,6 +51,12 @@ option java_package = "com.google.protobuf.conformance"; // - running as a sub-process may be more tricky in unusual environments like // iOS apps, where fork/stdin/stdout are not available. +enum WireFormat { + UNSPECIFIED = 0; + PROTOBUF = 1; + JSON = 2; +} + // Represents a single test case's input. The testee should: // // 1. parse this proto (which should always succeed) @@ -64,14 +70,8 @@ message ConformanceRequest { string json_payload = 2; } - enum RequestedOutput { - UNSPECIFIED = 0; - PROTOBUF = 1; - JSON = 2; - } - // Which format should the testee serialize its message to? - RequestedOutput requested_output = 3; + WireFormat requested_output_format = 3; } // Represents a single test case's output. @@ -96,6 +96,10 @@ message ConformanceResponse { // If the input was successfully parsed and the requested output was JSON, // serialize to JSON and set it in this field. string json_payload = 4; + + // For when the testee skipped the test, likely because a certain feature + // wasn't supported, like JSON input/output. + string skipped = 5; } } diff --git a/conformance/conformance_cpp.cc b/conformance/conformance_cpp.cc index ff47fbbf..863b6a5b 100644 --- a/conformance/conformance_cpp.cc +++ b/conformance/conformance_cpp.cc @@ -33,14 +33,33 @@ #include #include "conformance.pb.h" +#include +#include -using std::string; using conformance::ConformanceRequest; using conformance::ConformanceResponse; using conformance::TestAllTypes; +using google::protobuf::Descriptor; +using google::protobuf::DescriptorPool; +using google::protobuf::internal::scoped_ptr; +using google::protobuf::util::BinaryToJsonString; +using google::protobuf::util::JsonToBinaryString; +using google::protobuf::util::NewTypeResolverForDescriptorPool; +using google::protobuf::util::Status; +using google::protobuf::util::TypeResolver; +using std::string; + +static const char kTypeUrlPrefix[] = "type.googleapis.com"; + +static string GetTypeUrl(const Descriptor* message) { + return string(kTypeUrlPrefix) + "/" + message->full_name(); +} int test_count = 0; bool verbose = false; +TypeResolver* type_resolver; +string* type_url; + bool CheckedRead(int fd, void *buf, size_t len) { size_t ofs = 0; @@ -79,27 +98,43 @@ void DoTest(const ConformanceRequest& request, ConformanceResponse* response) { } break; - case ConformanceRequest::kJsonPayload: - response->set_runtime_error("JSON input is not yet supported."); + case ConformanceRequest::kJsonPayload: { + string proto_binary; + Status status = JsonToBinaryString(type_resolver, *type_url, + request.json_payload(), &proto_binary); + if (!status.ok()) { + response->set_parse_error(string("Parse error: ") + + status.error_message().as_string()); + return; + } + + GOOGLE_CHECK(test_message.ParseFromString(proto_binary)); break; + } case ConformanceRequest::PAYLOAD_NOT_SET: GOOGLE_LOG(FATAL) << "Request didn't have payload."; break; } - switch (request.requested_output()) { - case ConformanceRequest::UNSPECIFIED: + switch (request.requested_output_format()) { + case conformance::UNSPECIFIED: GOOGLE_LOG(FATAL) << "Unspecified output format"; break; - case ConformanceRequest::PROTOBUF: - test_message.SerializeToString(response->mutable_protobuf_payload()); + case conformance::PROTOBUF: + GOOGLE_CHECK( + test_message.SerializeToString(response->mutable_protobuf_payload())); break; - case ConformanceRequest::JSON: - response->set_runtime_error("JSON output is not yet supported."); + case conformance::JSON: { + string proto_binary; + GOOGLE_CHECK(test_message.SerializeToString(&proto_binary)); + Status status = BinaryToJsonString(type_resolver, *type_url, proto_binary, + response->mutable_json_payload()); + GOOGLE_CHECK(status.ok()); break; + } } } @@ -146,6 +181,9 @@ bool DoTestIo() { } int main() { + type_resolver = NewTypeResolverForDescriptorPool( + kTypeUrlPrefix, DescriptorPool::generated_pool()); + type_url = new string(GetTypeUrl(TestAllTypes::descriptor())); while (1) { if (!DoTestIo()) { fprintf(stderr, "conformance-cpp: received EOF from test runner " diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc index 7a7fc6f5..0ee201f3 100644 --- a/conformance/conformance_test.cc +++ b/conformance/conformance_test.cc @@ -35,18 +35,34 @@ #include "conformance_test.h" #include #include +#include +#include +#include +#include #include using conformance::ConformanceRequest; using conformance::ConformanceResponse; using conformance::TestAllTypes; +using conformance::WireFormat; using google::protobuf::Descriptor; using google::protobuf::FieldDescriptor; using google::protobuf::internal::WireFormatLite; +using google::protobuf::TextFormat; +using google::protobuf::util::JsonToBinaryString; +using google::protobuf::util::MessageDifferencer; +using google::protobuf::util::NewTypeResolverForDescriptorPool; +using google::protobuf::util::Status; using std::string; namespace { +static const char kTypeUrlPrefix[] = "type.googleapis.com"; + +static string GetTypeUrl(const Descriptor* message) { + return string(kTypeUrlPrefix) + "/" + message->full_name(); +} + /* Routines for building arbitrary protos *************************************/ // We would use CodedOutputStream except that we want more freedom to build @@ -162,9 +178,13 @@ void ConformanceTestSuite::ReportSuccess(const string& test_name) { } void ConformanceTestSuite::ReportFailure(const string& test_name, + const ConformanceRequest& request, + const ConformanceResponse& response, const char* fmt, ...) { if (expected_to_fail_.erase(test_name) == 1) { - StringAppendF(&output_, "FAILED AS EXPECTED, test=%s: ", test_name.c_str()); + expected_failures_++; + if (!verbose_) + return; } else { StringAppendF(&output_, "ERROR, test=%s: ", test_name.c_str()); unexpected_failing_tests_.insert(test_name); @@ -173,7 +193,20 @@ void ConformanceTestSuite::ReportFailure(const string& test_name, va_start(args, fmt); StringAppendV(&output_, fmt, args); va_end(args); - failures_++; + StringAppendF(&output_, " request=%s, response=%s\n", + request.ShortDebugString().c_str(), + response.ShortDebugString().c_str()); +} + +void ConformanceTestSuite::ReportSkip(const string& test_name, + const ConformanceRequest& request, + const ConformanceResponse& response) { + if (verbose_) { + StringAppendF(&output_, "SKIPPED, test=%s request=%s, response=%s\n", + test_name.c_str(), request.ShortDebugString().c_str(), + response.ShortDebugString().c_str()); + } + skipped_.insert(test_name); } void ConformanceTestSuite::RunTest(const string& test_name, @@ -202,26 +235,117 @@ void ConformanceTestSuite::RunTest(const string& test_name, } } +void ConformanceTestSuite::RunValidInputTest( + const string& test_name, const string& input, WireFormat input_format, + const string& equivalent_text_format, WireFormat requested_output) { + TestAllTypes reference_message; + GOOGLE_CHECK( + TextFormat::ParseFromString(equivalent_text_format, &reference_message)); + + ConformanceRequest request; + ConformanceResponse response; + + switch (input_format) { + case conformance::PROTOBUF: + request.set_protobuf_payload(input); + break; + + case conformance::JSON: + request.set_json_payload(input); + break; + + case conformance::UNSPECIFIED: + GOOGLE_LOG(FATAL) << "Unspecified input format"; + + } + + request.set_requested_output_format(requested_output); + + RunTest(test_name, request, &response); + + TestAllTypes test_message; + + switch (response.result_case()) { + case ConformanceResponse::kParseError: + case ConformanceResponse::kRuntimeError: + ReportFailure(test_name, request, response, + "Failed to parse valid JSON input."); + return; + + case ConformanceResponse::kSkipped: + ReportSkip(test_name, request, response); + return; + + case ConformanceResponse::kJsonPayload: { + if (requested_output != conformance::JSON) { + ReportFailure( + test_name, request, response, + "Test was asked for protobuf output but provided JSON instead."); + return; + } + string binary_protobuf; + Status status = + JsonToBinaryString(type_resolver_.get(), type_url_, + response.json_payload(), &binary_protobuf); + if (!status.ok()) { + ReportFailure(test_name, request, response, + "JSON output we received from test was unparseable."); + return; + } + + GOOGLE_CHECK(test_message.ParseFromString(binary_protobuf)); + break; + } + + case ConformanceResponse::kProtobufPayload: { + if (requested_output != conformance::PROTOBUF) { + ReportFailure( + test_name, request, response, + "Test was asked for JSON output but provided protobuf instead."); + return; + } + + if (!test_message.ParseFromString(response.protobuf_payload())) { + ReportFailure(test_name, request, response, + "Protobuf output we received from test was unparseable."); + return; + } + + break; + } + } + + MessageDifferencer differencer; + string differences; + differencer.ReportDifferencesToString(&differences); + + if (differencer.Equals(reference_message, test_message)) { + ReportSuccess(test_name); + } else { + ReportFailure(test_name, request, response, + "Output was not equivalent to reference message: %s.", + differences.c_str()); + } +} + // Expect that this precise protobuf will cause a parse error. void ConformanceTestSuite::ExpectParseFailureForProto( const string& proto, const string& test_name) { ConformanceRequest request; ConformanceResponse response; request.set_protobuf_payload(proto); + string effective_test_name = "ProtobufInput." + test_name; // We don't expect output, but if the program erroneously accepts the protobuf // we let it send its response as this. We must not leave it unspecified. - request.set_requested_output(ConformanceRequest::PROTOBUF); + request.set_requested_output_format(conformance::PROTOBUF); - RunTest(test_name, request, &response); + RunTest(effective_test_name, request, &response); if (response.result_case() == ConformanceResponse::kParseError) { - ReportSuccess(test_name); + ReportSuccess(effective_test_name); } else { - ReportFailure(test_name, - "Should have failed to parse, but didn't. Request: %s, " - "response: %s\n", - request.ShortDebugString().c_str(), - response.ShortDebugString().c_str()); + ReportFailure(effective_test_name, request, response, + "Should have failed to parse, but didn't."); } } @@ -235,6 +359,16 @@ void ConformanceTestSuite::ExpectHardParseFailureForProto( return ExpectParseFailureForProto(proto, test_name); } +void ConformanceTestSuite::RunValidJsonTest( + const string& test_name, const string& input_json, + const string& equivalent_text_format) { + RunValidInputTest("JsonInput." + test_name + ".JsonOutput", input_json, + conformance::JSON, equivalent_text_format, + conformance::PROTOBUF); + RunValidInputTest("JsonInput." + test_name + ".ProtobufOutput", input_json, conformance::JSON, + equivalent_text_format, conformance::JSON); +} + void ConformanceTestSuite::TestPrematureEOFForType(FieldDescriptor::Type type) { // Incomplete values for each wire type. static const string incompletes[6] = { @@ -333,11 +467,12 @@ bool ConformanceTestSuite::CheckSetEmpty(const set& set_to_check, return true; } else { StringAppendF(&output_, "\n"); - StringAppendF(&output_, "ERROR: %s:\n", msg); + StringAppendF(&output_, "%s:\n", msg); for (set::const_iterator iter = set_to_check.begin(); iter != set_to_check.end(); ++iter) { - StringAppendF(&output_, "%s\n", iter->c_str()); + StringAppendF(&output_, " %s\n", iter->c_str()); } + StringAppendF(&output_, "\n"); return false; } } @@ -345,23 +480,25 @@ bool ConformanceTestSuite::CheckSetEmpty(const set& set_to_check, bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, std::string* output) { runner_ = runner; - output_.clear(); successes_ = 0; - failures_ = 0; + expected_failures_ = 0; + skipped_.clear(); test_names_.clear(); unexpected_failing_tests_.clear(); unexpected_succeeding_tests_.clear(); + type_resolver_.reset(NewTypeResolverForDescriptorPool( + kTypeUrlPrefix, DescriptorPool::generated_pool())); + type_url_ = GetTypeUrl(TestAllTypes::descriptor()); + + output_ = "\nCONFORMANCE TEST BEGIN ====================================\n\n"; for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) { if (i == FieldDescriptor::TYPE_GROUP) continue; TestPrematureEOFForType(static_cast(i)); } - StringAppendF(&output_, "\n"); - StringAppendF(&output_, - "CONFORMANCE SUITE FINISHED: completed %d tests, %d successes, " - "%d failures.\n", - successes_ + failures_, successes_, failures_); + RunValidJsonTest("HelloWorld", "{\"optionalString\":\"Hello, World!\"}", + "optional_string: 'Hello, World!'"); bool ok = CheckSetEmpty(expected_to_fail_, @@ -377,6 +514,17 @@ bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, "These tests succeeded, even though they were listed in " "the failure list. Remove them from the failure list"); + CheckSetEmpty(skipped_, + "These tests were skipped (probably because support for some " + "features is not implemented)"); + + StringAppendF(&output_, + "CONFORMANCE SUITE %s: %d successes, %d skipped, " + "%d expected failures, %d unexpected failures.\n", + ok ? "PASSED" : "FAILED", successes_, skipped_.size(), + expected_failures_, unexpected_failing_tests_.size()); + StringAppendF(&output_, "\n"); + output->assign(output_); return ok; diff --git a/conformance/conformance_test.h b/conformance/conformance_test.h index 764a8d33..cadda828 100644 --- a/conformance/conformance_test.h +++ b/conformance/conformance_test.h @@ -39,6 +39,8 @@ #define CONFORMANCE_CONFORMANCE_TEST_H #include +#include +#include #include namespace conformance { @@ -98,10 +100,22 @@ class ConformanceTestSuite { private: void ReportSuccess(const std::string& test_name); - void ReportFailure(const std::string& test_name, const char* fmt, ...); + void ReportFailure(const string& test_name, + const conformance::ConformanceRequest& request, + const conformance::ConformanceResponse& response, + const char* fmt, ...); + void ReportSkip(const string& test_name, + const conformance::ConformanceRequest& request, + const conformance::ConformanceResponse& response); void RunTest(const std::string& test_name, const conformance::ConformanceRequest& request, conformance::ConformanceResponse* response); + void RunValidInputTest(const string& test_name, const string& input, + conformance::WireFormat input_format, + const string& equivalent_text_format, + conformance::WireFormat requested_output); + void RunValidJsonTest(const string& test_name, const string& input_json, + const string& equivalent_text_format); void ExpectParseFailureForProto(const std::string& proto, const std::string& test_name); void ExpectHardParseFailureForProto(const std::string& proto, @@ -110,7 +124,7 @@ class ConformanceTestSuite { bool CheckSetEmpty(const set& set_to_check, const char* msg); ConformanceTestRunner* runner_; int successes_; - int failures_; + int expected_failures_; bool verbose_; std::string output_; @@ -127,6 +141,13 @@ class ConformanceTestSuite { // The set of tests that succeeded, but weren't expected to. std::set unexpected_succeeding_tests_; + + // The set of tests that the testee opted out of; + std::set skipped_; + + google::protobuf::internal::scoped_ptr + type_resolver_; + std::string type_url_; }; } // namespace protobuf diff --git a/conformance/failure_list_cpp.txt b/conformance/failure_list_cpp.txt index d792eddd..53d90c99 100644 --- a/conformance/failure_list_cpp.txt +++ b/conformance/failure_list_cpp.txt @@ -7,15 +7,15 @@ # TODO(haberman): insert links to corresponding bugs tracking the issue. # Should we use GitHub issues or the Google-internal bug tracker? -PrematureEofBeforeKnownRepeatedValue.MESSAGE -PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE -PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE -PrematureEofInPackedField.BOOL -PrematureEofInPackedField.ENUM -PrematureEofInPackedField.INT32 -PrematureEofInPackedField.INT64 -PrematureEofInPackedField.SINT32 -PrematureEofInPackedField.SINT64 -PrematureEofInPackedField.UINT32 -PrematureEofInPackedField.UINT64 -PrematureEofInsideKnownRepeatedValue.MESSAGE +ProtobufInput.PrematureEofBeforeKnownRepeatedValue.MESSAGE +ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE +ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE +ProtobufInput.PrematureEofInPackedField.BOOL +ProtobufInput.PrematureEofInPackedField.ENUM +ProtobufInput.PrematureEofInPackedField.INT32 +ProtobufInput.PrematureEofInPackedField.INT64 +ProtobufInput.PrematureEofInPackedField.SINT32 +ProtobufInput.PrematureEofInPackedField.SINT64 +ProtobufInput.PrematureEofInPackedField.UINT32 +ProtobufInput.PrematureEofInPackedField.UINT64 +ProtobufInput.PrematureEofInsideKnownRepeatedValue.MESSAGE -- cgit v1.2.3 From 181c7f26360429b236ab833c746d10d97811931f Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Wed, 15 Jul 2015 11:05:10 -0700 Subject: Added Ruby to conformance tests. This involved fixing a few important bugs in the Ruby implementation -- mostly cases of mixing upb field types and descriptor types (upb field types do not distinguish between int/sint/fixed/sfixed like descriptor types do). Also added protobuf-specific exceptions so parse errors can be caught specifically. Change-Id: Ib49d3db976900b2c6f3455c8b88af52cfb86e036 --- conformance/Makefile.am | 7 +- conformance/conformance_ruby.rb | 111 +++++ conformance/conformance_test.h | 2 + conformance/conformance_test_runner.cc | 8 +- conformance/failure_list_ruby.txt | 17 + ruby/ext/google/protobuf_c/defs.c | 68 ++- ruby/ext/google/protobuf_c/encode_decode.c | 6 +- ruby/ext/google/protobuf_c/protobuf.c | 6 + ruby/ext/google/protobuf_c/protobuf.h | 3 + ruby/ext/google/protobuf_c/upb.c | 483 +++++++++------------ ruby/ext/google/protobuf_c/upb.h | 460 +++++++++----------- ruby/lib/google/protobuf.rb | 9 + ruby/travis-test.sh | 4 +- .../protobuf/compiler/ruby/ruby_generated_code.rb | 6 +- .../protobuf/compiler/ruby/ruby_generator.cc | 34 +- travis.sh | 29 +- 16 files changed, 678 insertions(+), 575 deletions(-) create mode 100755 conformance/conformance_ruby.rb create mode 100644 conformance/failure_list_ruby.txt (limited to 'conformance') diff --git a/conformance/Makefile.am b/conformance/Makefile.am index cccbac9e..97251715 100644 --- a/conformance/Makefile.am +++ b/conformance/Makefile.am @@ -22,7 +22,7 @@ conformance_cpp_CPPFLAGS = -I$(top_srcdir)/src if USE_EXTERNAL_PROTOC protoc_middleman: $(protoc_inputs) - $(PROTOC) -I$(srcdir) --cpp_out=. --java_out=. $^ + $(PROTOC) -I$(srcdir) --cpp_out=. --java_out=. --ruby_out=. $^ touch protoc_middleman else @@ -31,7 +31,7 @@ else # relative to srcdir, which may not be the same as the current directory when # building out-of-tree. protoc_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(protoc_inputs) - oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. --cpp_out=$$oldpwd --java_out=$$oldpwd $(protoc_inputs) ) + oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd $(protoc_inputs) ) touch protoc_middleman endif @@ -61,3 +61,6 @@ test_cpp: protoc_middleman conformance-test-runner conformance-cpp test_java: protoc_middleman conformance-test-runner conformance-java ./conformance-test-runner ./conformance-java + +test_ruby: protoc_middleman conformance-test-runner + RUBYLIB=../ruby/lib:. ./conformance-test-runner --failure_list failure_list_ruby.txt ./conformance_ruby.rb diff --git a/conformance/conformance_ruby.rb b/conformance/conformance_ruby.rb new file mode 100755 index 00000000..e7bd4ed5 --- /dev/null +++ b/conformance/conformance_ruby.rb @@ -0,0 +1,111 @@ +#!/usr/bin/env ruby +# +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +require 'conformance' + +test_count = 0; +verbose = false; + +def do_test(request) + test_message = Conformance::TestAllTypes.new + response = Conformance::ConformanceResponse.new + + begin + case request.payload + when :protobuf_payload + begin + test_message = Conformance::TestAllTypes.decode(request.protobuf_payload) + rescue Google::Protobuf::ParseError => err + response.parse_error = err.message.encode("utf-8") + return response + end + + when :json_payload + test_message = Conformance::TestAllTypes.decode_json(request.json_payload) + + when nil + raise "Request didn't have payload."; + end + + case request.requested_output_format + when :UNSPECIFIED + raise "Unspecified output format" + + when :PROTOBUF + response.protobuf_payload = Conformance::TestAllTypes.encode(test_message) + + when :JSON + response.json_payload = Conformance::TestAllTypes.encode_json(test_message) + end + rescue Exception => err + response.runtime_error = err.message.encode("utf-8") + err.backtrace.join("\n") + end + + return response +end + +def do_test_io + length_bytes = STDIN.read(4) + return false if length_bytes.nil? + + length = length_bytes.unpack("V").first + serialized_request = STDIN.read(length) + if serialized_request.nil? or serialized_request.length != length + raise "I/O error" + end + + request = Conformance::ConformanceRequest.decode(serialized_request) + + response = do_test(request) + + serialized_response = Conformance::ConformanceResponse.encode(response) + STDOUT.write([serialized_response.length].pack("V")) + STDOUT.write(serialized_response) + STDOUT.flush + + #if verbose + # fprintf(stderr, "conformance-cpp: request=%s, response=%s\n", + # request.ShortDebugString().c_str(), + # response.ShortDebugString().c_str()); + + #test_count++; + + return true; +end + +while true + if not do_test_io() + STDERR.puts("conformance-cpp: received EOF from test runner " + + "after #{test_count} tests, exiting") + exit 0 + end +end diff --git a/conformance/conformance_test.h b/conformance/conformance_test.h index cadda828..9e6cdaee 100644 --- a/conformance/conformance_test.h +++ b/conformance/conformance_test.h @@ -85,6 +85,8 @@ class ConformanceTestSuite { public: ConformanceTestSuite() : verbose_(false) {} + void SetVerbose(bool verbose) { verbose_ = verbose; } + // Sets the list of tests that are expected to fail when RunSuite() is called. // RunSuite() will fail unless the set of failing tests is exactly the same // as this list. diff --git a/conformance/conformance_test_runner.cc b/conformance/conformance_test_runner.cc index b56e19cf..780e1c44 100644 --- a/conformance/conformance_test_runner.cc +++ b/conformance/conformance_test_runner.cc @@ -219,12 +219,16 @@ void ParseFailureList(const char *filename, vector* failure_list) { int main(int argc, char *argv[]) { int arg = 1; char *program; - vector failure_list; + google::protobuf::ConformanceTestSuite suite; for (int arg = 1; arg < argc; ++arg) { if (strcmp(argv[arg], "--failure_list") == 0) { if (++arg == argc) UsageError(); + vector failure_list; ParseFailureList(argv[arg], &failure_list); + suite.SetFailureList(failure_list); + } else if (strcmp(argv[arg], "--verbose") == 0) { + suite.SetVerbose(true); } else if (argv[arg][0] == '-') { fprintf(stderr, "Unknown option: %s\n", argv[arg]); UsageError(); @@ -238,8 +242,6 @@ int main(int argc, char *argv[]) { } ForkPipeRunner runner(program); - google::protobuf::ConformanceTestSuite suite; - suite.SetFailureList(failure_list); std::string output; bool ok = suite.RunSuite(&runner, &output); diff --git a/conformance/failure_list_ruby.txt b/conformance/failure_list_ruby.txt new file mode 100644 index 00000000..35d1ff91 --- /dev/null +++ b/conformance/failure_list_ruby.txt @@ -0,0 +1,17 @@ +JsonInput.HelloWorld.JsonOutput +JsonInput.HelloWorld.ProtobufOutput +ProtobufInput.PrematureEofBeforeUnknownValue.DOUBLE +ProtobufInput.PrematureEofBeforeUnknownValue.FIXED32 +ProtobufInput.PrematureEofBeforeUnknownValue.FIXED64 +ProtobufInput.PrematureEofBeforeUnknownValue.FLOAT +ProtobufInput.PrematureEofBeforeUnknownValue.SFIXED32 +ProtobufInput.PrematureEofBeforeUnknownValue.SFIXED64 +ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.BYTES +ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.MESSAGE +ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.STRING +ProtobufInput.PrematureEofInsideUnknownValue.DOUBLE +ProtobufInput.PrematureEofInsideUnknownValue.FIXED32 +ProtobufInput.PrematureEofInsideUnknownValue.FIXED64 +ProtobufInput.PrematureEofInsideUnknownValue.FLOAT +ProtobufInput.PrematureEofInsideUnknownValue.SFIXED32 +ProtobufInput.PrematureEofInsideUnknownValue.SFIXED64 diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index 8f9f33e2..0b2f977f 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -548,7 +548,7 @@ upb_fieldtype_t ruby_to_fieldtype(VALUE type) { #define CONVERT(upb, ruby) \ if (SYM2ID(type) == rb_intern( # ruby )) { \ - return UPB_TYPE_ ## upb; \ + return UPB_TYPE_ ## upb; \ } CONVERT(FLOAT, float); @@ -589,6 +589,68 @@ VALUE fieldtype_to_ruby(upb_fieldtype_t type) { return Qnil; } +upb_descriptortype_t ruby_to_descriptortype(VALUE type) { + if (TYPE(type) != T_SYMBOL) { + rb_raise(rb_eArgError, "Expected symbol for field type."); + } + +#define CONVERT(upb, ruby) \ + if (SYM2ID(type) == rb_intern( # ruby )) { \ + return UPB_DESCRIPTOR_TYPE_ ## upb; \ + } + + CONVERT(FLOAT, float); + CONVERT(DOUBLE, double); + CONVERT(BOOL, bool); + CONVERT(STRING, string); + CONVERT(BYTES, bytes); + CONVERT(MESSAGE, message); + CONVERT(GROUP, group); + CONVERT(ENUM, enum); + CONVERT(INT32, int32); + CONVERT(INT64, int64); + CONVERT(UINT32, uint32); + CONVERT(UINT64, uint64); + CONVERT(SINT32, sint32); + CONVERT(SINT64, sint64); + CONVERT(FIXED32, fixed32); + CONVERT(FIXED64, fixed64); + CONVERT(SFIXED32, sfixed32); + CONVERT(SFIXED64, sfixed64); + +#undef CONVERT + + rb_raise(rb_eArgError, "Unknown field type."); + return 0; +} + +VALUE descriptortype_to_ruby(upb_descriptortype_t type) { + switch (type) { +#define CONVERT(upb, ruby) \ + case UPB_DESCRIPTOR_TYPE_ ## upb : return ID2SYM(rb_intern( # ruby )); + CONVERT(FLOAT, float); + CONVERT(DOUBLE, double); + CONVERT(BOOL, bool); + CONVERT(STRING, string); + CONVERT(BYTES, bytes); + CONVERT(MESSAGE, message); + CONVERT(GROUP, group); + CONVERT(ENUM, enum); + CONVERT(INT32, int32); + CONVERT(INT64, int64); + CONVERT(UINT32, uint32); + CONVERT(UINT64, uint64); + CONVERT(SINT32, sint32); + CONVERT(SINT64, sint64); + CONVERT(FIXED32, fixed32); + CONVERT(FIXED64, fixed64); + CONVERT(SFIXED32, sfixed32); + CONVERT(SFIXED64, sfixed64); +#undef CONVERT + } + return Qnil; +} + /* * call-seq: * FieldDescriptor.type => type @@ -604,7 +666,7 @@ VALUE FieldDescriptor_type(VALUE _self) { if (!upb_fielddef_typeisset(self->fielddef)) { return Qnil; } - return fieldtype_to_ruby(upb_fielddef_type(self->fielddef)); + return descriptortype_to_ruby(upb_fielddef_descriptortype(self->fielddef)); } /* @@ -617,7 +679,7 @@ VALUE FieldDescriptor_type(VALUE _self) { VALUE FieldDescriptor_type_set(VALUE _self, VALUE type) { DEFINE_SELF(FieldDescriptor, self, _self); upb_fielddef* mut_def = check_field_notfrozen(self->fielddef); - upb_fielddef_settype(mut_def, ruby_to_fieldtype(type)); + upb_fielddef_setdescriptortype(mut_def, ruby_to_descriptortype(type)); return Qnil; } diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index f789f6d4..e488f05b 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -656,8 +656,10 @@ static bool env_error_func(void* ud, const upb_status* status) { // Free the env -- rb_raise will longjmp up the stack past the encode/decode // function so it would not otherwise have been freed. stackenv_uninit(se); - rb_raise(rb_eRuntimeError, se->ruby_error_template, - upb_status_errmsg(status)); + + // TODO(haberman): have a way to verify that this is actually a parse error, + // instead of just throwing "parse error" unconditionally. + rb_raise(cParseError, se->ruby_error_template, upb_status_errmsg(status)); // Never reached: rb_raise() always longjmp()s up the stack, past all of our // code, back to Ruby. return false; diff --git a/ruby/ext/google/protobuf_c/protobuf.c b/ruby/ext/google/protobuf_c/protobuf.c index d0625a10..ca0f7b7c 100644 --- a/ruby/ext/google/protobuf_c/protobuf.c +++ b/ruby/ext/google/protobuf_c/protobuf.c @@ -39,6 +39,9 @@ // Ruby integers) to MessageDef/EnumDef instances (as Ruby values). VALUE upb_def_to_ruby_obj_map; +VALUE cError; +VALUE cParseError; + void add_def_obj(const void* def, VALUE value) { rb_hash_aset(upb_def_to_ruby_obj_map, ULL2NUM((intptr_t)def), value); } @@ -96,6 +99,9 @@ void Init_protobuf_c() { RepeatedField_register(protobuf); Map_register(protobuf); + cError = rb_const_get(protobuf, rb_intern("Error")); + cParseError = rb_const_get(protobuf, rb_intern("ParseError")); + rb_define_singleton_method(protobuf, "deep_copy", Google_Protobuf_deep_copy, 1); diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index f8667486..8750c93d 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -161,6 +161,9 @@ extern VALUE cOneofBuilderContext; extern VALUE cEnumBuilderContext; extern VALUE cBuilder; +extern VALUE cError; +extern VALUE cParseError; + // We forward-declare all of the Ruby method implementations here because we // sometimes call the methods directly across .c files, rather than going // through Ruby's method dispatching (e.g. during message parse). It's cleaner diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index f99c7a70..8ef8e31d 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -1,11 +1,5 @@ // Amalgamated source file #include "upb.h" -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2008-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ #include @@ -1701,12 +1695,6 @@ upb_fielddef *upb_oneof_iter_field(const upb_oneof_iter *iter) { void upb_oneof_iter_setdone(upb_oneof_iter *iter) { upb_inttable_iter_setdone(iter); } -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ #include @@ -1980,14 +1968,9 @@ upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a) { return seeded_alloc; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * TODO(haberman): it's unclear whether a lot of the consistency checks should - * assert() or return false. - */ +** TODO(haberman): it's unclear whether a lot of the consistency checks should +** assert() or return false. +*/ #include @@ -2668,24 +2651,21 @@ bool upb_byteshandler_setendstr(upb_byteshandler *h, return true; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Our key invariants are: - * 1. reference cycles never span groups - * 2. for ref2(to, from), we increment to's count iff group(from) != group(to) - * - * The previous two are how we avoid leaking cycles. Other important - * invariants are: - * 3. for mutable objects "from" and "to", if there exists a ref2(to, from) - * this implies group(from) == group(to). (In practice, what we implement - * is even stronger; "from" and "to" will share a group if there has *ever* - * been a ref2(to, from), but all that is necessary for correctness is the - * weaker one). - * 4. mutable and immutable objects are never in the same group. - */ +** upb::RefCounted Implementation +** +** Our key invariants are: +** 1. reference cycles never span groups +** 2. for ref2(to, from), we increment to's count iff group(from) != group(to) +** +** The previous two are how we avoid leaking cycles. Other important +** invariants are: +** 3. for mutable objects "from" and "to", if there exists a ref2(to, from) +** this implies group(from) == group(to). (In practice, what we implement +** is even stronger; "from" and "to" will share a group if there has *ever* +** been a ref2(to, from), but all that is necessary for correctness is the +** weaker one). +** 4. mutable and immutable objects are never in the same group. +*/ #include @@ -3514,12 +3494,6 @@ bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s, } return freeze(roots, n, s, maxdepth); } -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2013 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ #include @@ -3605,12 +3579,6 @@ const upb_shim_data *upb_shim_getdata(const upb_handlers *h, upb_selector_t s, return (const upb_shim_data*)upb_handlers_gethandlerdata(h, s); } -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2008-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ #include @@ -4041,13 +4009,10 @@ const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter) { return upb_value_getptr(upb_strtable_iter_value(&iter->iter)); } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Implementation is heavily inspired by Lua's ltable.c. - */ +** upb_table Implementation +** +** Implementation is heavily inspired by Lua's ltable.c. +*/ #include @@ -4931,12 +4896,6 @@ uint32_t MurmurHash2(const void * key, size_t len, uint32_t seed) { #undef MIX #endif /* UPB_UNALIGNED_READS_OK */ -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ #include #include @@ -5860,17 +5819,12 @@ static upb_inttable reftables[212] = { #endif /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2008-2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * XXX: The routines in this file that consume a string do not currently - * support having the string span buffers. In the future, as upb_sink and - * its buffering/sharing functionality evolve there should be an easy and - * idiomatic way of correctly handling this case. For now, we accept this - * limitation since we currently only parse descriptors from single strings. - */ +** XXX: The routines in this file that consume a string do not currently +** support having the string span buffers. In the future, as upb_sink and +** its buffering/sharing functionality evolve there should be an easy and +** idiomatic way of correctly handling this case. For now, we accept this +** limitation since we currently only parse descriptors from single strings. +*/ #include @@ -6518,21 +6472,18 @@ const upb_handlers *upb_descreader_newhandlers(const void *owner) { return h; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2013 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Code to compile a upb::Handlers into bytecode for decoding a protobuf - * according to that specific schema and destination handlers. - * - * Compiling to bytecode is always the first step. If we are using the - * interpreted decoder we leave it as bytecode and interpret that. If we are - * using a JIT decoder we use a code generator to turn the bytecode into native - * code, LLVM IR, etc. - * - * Bytecode definition is in decoder.int.h. - */ +** protobuf decoder bytecode compiler +** +** Code to compile a upb::Handlers into bytecode for decoding a protobuf +** according to that specific schema and destination handlers. +** +** Compiling to bytecode is always the first step. If we are using the +** interpreted decoder we leave it as bytecode and interpret that. If we are +** using a JIT decoder we use a code generator to turn the bytecode into native +** code, LLVM IR, etc. +** +** Bytecode definition is in decoder.int.h. +*/ #include @@ -7502,24 +7453,19 @@ void upb_pbdecodermethodopts_setlazy(upb_pbdecodermethodopts *opts, bool lazy) { opts->lazy = lazy; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2008-2013 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * This file implements a VM for the interpreted (bytecode) decoder. - * - * Bytecode must previously have been generated using the bytecode compiler in - * compile_decoder.c. This decoder then walks through the bytecode op-by-op to - * parse the input. - * - * Decoding is fully resumable; we just keep a pointer to the current bytecode - * instruction and resume from there. A fair amount of the logic here is to - * handle the fact that values can span buffer seams and we have to be able to - * be capable of suspending/resuming from any byte in the stream. This - * sometimes requires keeping a few trailing bytes from the last buffer around - * in the "residual" buffer. - */ +** upb::Decoder (Bytecode Decoder VM) +** +** Bytecode must previously have been generated using the bytecode compiler in +** compile_decoder.c. This decoder then walks through the bytecode op-by-op to +** parse the input. +** +** Decoding is fully resumable; we just keep a pointer to the current bytecode +** instruction and resume from there. A fair amount of the logic here is to +** handle the fact that values can span buffer seams and we have to be able to +** be capable of suspending/resuming from any byte in the stream. This +** sometimes requires keeping a few trailing bytes from the last buffer around +** in the "residual" buffer. +*/ #include #include @@ -8529,63 +8475,60 @@ bool upb_pbdecoder_setmaxnesting(upb_pbdecoder *d, size_t max) { return true; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Since we are implementing pure handlers (ie. without any out-of-band access - * to pre-computed lengths), we have to buffer all submessages before we can - * emit even their first byte. - * - * Not knowing the size of submessages also means we can't write a perfect - * zero-copy implementation, even with buffering. Lengths are stored as - * varints, which means that we don't know how many bytes to reserve for the - * length until we know what the length is. - * - * This leaves us with three main choices: - * - * 1. buffer all submessage data in a temporary buffer, then copy it exactly - * once into the output buffer. - * - * 2. attempt to buffer data directly into the output buffer, estimating how - * many bytes each length will take. When our guesses are wrong, use - * memmove() to grow or shrink the allotted space. - * - * 3. buffer directly into the output buffer, allocating a max length - * ahead-of-time for each submessage length. If we overallocated, we waste - * space, but no memcpy() or memmove() is required. This approach requires - * defining a maximum size for submessages and rejecting submessages that - * exceed that size. - * - * (2) and (3) have the potential to have better performance, but they are more - * complicated and subtle to implement: - * - * (3) requires making an arbitrary choice of the maximum message size; it - * wastes space when submessages are shorter than this and fails - * completely when they are longer. This makes it more finicky and - * requires configuration based on the input. It also makes it impossible - * to perfectly match the output of reference encoders that always use the - * optimal amount of space for each length. - * - * (2) requires guessing the the size upfront, and if multiple lengths are - * guessed wrong the minimum required number of memmove() operations may - * be complicated to compute correctly. Implemented properly, it may have - * a useful amortized or average cost, but more investigation is required - * to determine this and what the optimal algorithm is to achieve it. - * - * (1) makes you always pay for exactly one copy, but its implementation is - * the simplest and its performance is predictable. - * - * So for now, we implement (1) only. If we wish to optimize later, we should - * be able to do it without affecting users. - * - * The strategy is to buffer the segments of data that do *not* depend on - * unknown lengths in one buffer, and keep a separate buffer of segment pointers - * and lengths. When the top-level submessage ends, we can go beginning to end, - * alternating the writing of lengths with memcpy() of the rest of the data. - * At the top level though, no buffering is required. - */ +** upb::Encoder +** +** Since we are implementing pure handlers (ie. without any out-of-band access +** to pre-computed lengths), we have to buffer all submessages before we can +** emit even their first byte. +** +** Not knowing the size of submessages also means we can't write a perfect +** zero-copy implementation, even with buffering. Lengths are stored as +** varints, which means that we don't know how many bytes to reserve for the +** length until we know what the length is. +** +** This leaves us with three main choices: +** +** 1. buffer all submessage data in a temporary buffer, then copy it exactly +** once into the output buffer. +** +** 2. attempt to buffer data directly into the output buffer, estimating how +** many bytes each length will take. When our guesses are wrong, use +** memmove() to grow or shrink the allotted space. +** +** 3. buffer directly into the output buffer, allocating a max length +** ahead-of-time for each submessage length. If we overallocated, we waste +** space, but no memcpy() or memmove() is required. This approach requires +** defining a maximum size for submessages and rejecting submessages that +** exceed that size. +** +** (2) and (3) have the potential to have better performance, but they are more +** complicated and subtle to implement: +** +** (3) requires making an arbitrary choice of the maximum message size; it +** wastes space when submessages are shorter than this and fails +** completely when they are longer. This makes it more finicky and +** requires configuration based on the input. It also makes it impossible +** to perfectly match the output of reference encoders that always use the +** optimal amount of space for each length. +** +** (2) requires guessing the the size upfront, and if multiple lengths are +** guessed wrong the minimum required number of memmove() operations may +** be complicated to compute correctly. Implemented properly, it may have +** a useful amortized or average cost, but more investigation is required +** to determine this and what the optimal algorithm is to achieve it. +** +** (1) makes you always pay for exactly one copy, but its implementation is +** the simplest and its performance is predictable. +** +** So for now, we implement (1) only. If we wish to optimize later, we should +** be able to do it without affecting users. +** +** The strategy is to buffer the segments of data that do *not* depend on +** unknown lengths in one buffer, and keep a separate buffer of segment pointers +** and lengths. When the top-level submessage ends, we can go beginning to end, +** alternating the writing of lengths with memcpy() of the rest of the data. +** At the top level though, no buffering is required. +*/ #include @@ -9095,12 +9038,6 @@ upb_pb_encoder *upb_pb_encoder_create(upb_env *env, const upb_handlers *h, } upb_sink *upb_pb_encoder_input(upb_pb_encoder *e) { return &e->input_; } -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2010-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ #include @@ -9189,10 +9126,7 @@ bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname, return success; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman + * upb::pb::TextPrinter * * OPT: This is not optimized at all. It uses printf() which parses the format * string every time, and it allocates memory for every put. @@ -9529,12 +9463,6 @@ upb_sink *upb_textprinter_input(upb_textprinter *p) { return &p->input_; } void upb_textprinter_setsingleline(upb_textprinter *p, bool single_line) { p->single_line_ = single_line; } -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ /* Index is descriptor type. */ @@ -9662,28 +9590,25 @@ upb_decoderet upb_vdecode_max8_wright(upb_decoderet r) { #line 1 "upb/json/parser.rl" /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A parser that uses the Ragel State Machine Compiler to generate - * the finite automata. - * - * Ragel only natively handles regular languages, but we can manually - * program it a bit to handle context-free languages like JSON, by using - * the "fcall" and "fret" constructs. - * - * This parser can handle the basics, but needs several things to be fleshed - * out: - * - * - handling of unicode escape sequences (including high surrogate pairs). - * - properly check and report errors for unknown fields, stack overflow, - * improper array nesting (or lack of nesting). - * - handling of base64 sequences with padding characters. - * - handling of push-back (non-success returns from sink functions). - * - handling of keys/escape-sequences/etc that span input buffers. - */ +** upb::json::Parser (upb_json_parser) +** +** A parser that uses the Ragel State Machine Compiler to generate +** the finite automata. +** +** Ragel only natively handles regular languages, but we can manually +** program it a bit to handle context-free languages like JSON, by using +** the "fcall" and "fret" constructs. +** +** This parser can handle the basics, but needs several things to be fleshed +** out: +** +** - handling of unicode escape sequences (including high surrogate pairs). +** - properly check and report errors for unknown fields, stack overflow, +** improper array nesting (or lack of nesting). +** - handling of base64 sequences with padding characters. +** - handling of push-back (non-success returns from sink functions). +** - handling of keys/escape-sequences/etc that span input buffers. +*/ #include #include @@ -9731,7 +9656,7 @@ struct upb_json_parser { upb_jsonparser_frame *top; upb_jsonparser_frame *limit; - upb_status *status; + upb_status status; /* Ragel's internal parsing stack for the parsing state machine. */ int current_state; @@ -9778,7 +9703,8 @@ static upb_selector_t parser_getsel(upb_json_parser *p) { static bool check_stack(upb_json_parser *p) { if ((p->top + 1) == p->limit) { - upb_status_seterrmsg(p->status, "Nesting too deep"); + upb_status_seterrmsg(&p->status, "Nesting too deep"); + upb_env_reporterror(p->env, &p->status); return false; } @@ -9860,9 +9786,10 @@ static bool base64_push(upb_json_parser *p, upb_selector_t sel, const char *ptr, char output[3]; if (limit - ptr < 4) { - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "Base64 input for bytes field not a multiple of 4: %s", upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); return false; } @@ -9886,9 +9813,10 @@ static bool base64_push(upb_json_parser *p, upb_selector_t sel, const char *ptr, otherchar: if (nonbase64(ptr[0]) || nonbase64(ptr[1]) || nonbase64(ptr[2]) || nonbase64(ptr[3]) ) { - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "Non-base64 characters in bytes field: %s", upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); return false; } if (ptr[2] == '=') { uint32_t val; @@ -9926,10 +9854,11 @@ otherchar: } badpadding: - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "Incorrect base64 padding for field: %s (%.*s)", upb_fielddef_name(p->top->f), 4, ptr); + upb_env_reporterror(p->env, &p->status); return false; } @@ -9976,7 +9905,8 @@ static bool accumulate_realloc(upb_json_parser *p, size_t need) { mem = upb_env_realloc(p->env, p->accumulate_buf, old_size, new_size); if (!mem) { - upb_status_seterrmsg(p->status, "Out of memory allocating buffer."); + upb_status_seterrmsg(&p->status, "Out of memory allocating buffer."); + upb_env_reporterror(p->env, &p->status); return false; } @@ -9999,7 +9929,8 @@ static bool accumulate_append(upb_json_parser *p, const char *buf, size_t len, } if (!checked_add(p->accumulated_len, len, &need)) { - upb_status_seterrmsg(p->status, "Integer overflow."); + upb_status_seterrmsg(&p->status, "Integer overflow."); + upb_env_reporterror(p->env, &p->status); return false; } @@ -10077,7 +10008,8 @@ static bool multipart_text(upb_json_parser *p, const char *buf, size_t len, switch (p->multipart_state) { case MULTIPART_INACTIVE: upb_status_seterrmsg( - p->status, "Internal error: unexpected state MULTIPART_INACTIVE"); + &p->status, "Internal error: unexpected state MULTIPART_INACTIVE"); + upb_env_reporterror(p->env, &p->status); return false; case MULTIPART_ACCUMULATE: @@ -10336,7 +10268,8 @@ static bool parse_number(upb_json_parser *p) { return true; err: - upb_status_seterrf(p->status, "error parsing number: %s", buf); + upb_status_seterrf(&p->status, "error parsing number: %s", buf); + upb_env_reporterror(p->env, &p->status); multipart_end(p); return false; } @@ -10345,9 +10278,10 @@ static bool parser_putbool(upb_json_parser *p, bool val) { bool ok; if (upb_fielddef_type(p->top->f) != UPB_TYPE_BOOL) { - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "Boolean value specified for non-bool field: %s", upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); return false; } @@ -10398,9 +10332,10 @@ static bool start_stringval(upb_json_parser *p) { multipart_startaccum(p); return true; } else { - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "String specified for non-string/non-enum field: %s", upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); return false; } } @@ -10438,7 +10373,8 @@ static bool end_stringval(upb_json_parser *p) { upb_selector_t sel = parser_getsel(p); upb_sink_putint32(&p->top->sink, sel, int_val); } else { - upb_status_seterrf(p->status, "Enum value unknown: '%.*s'", len, buf); + upb_status_seterrf(&p->status, "Enum value unknown: '%.*s'", len, buf); + upb_env_reporterror(p->env, &p->status); } break; @@ -10446,7 +10382,8 @@ static bool end_stringval(upb_json_parser *p) { default: assert(false); - upb_status_seterrmsg(p->status, "Internal error in JSON decoder"); + upb_status_seterrmsg(&p->status, "Internal error in JSON decoder"); + upb_env_reporterror(p->env, &p->status); ok = false; break; } @@ -10476,7 +10413,8 @@ static bool parse_mapentry_key(upb_json_parser *p) { p->top->f = upb_msgdef_itof(p->top->m, UPB_MAPENTRY_KEY); if (p->top->f == NULL) { - upb_status_seterrmsg(p->status, "mapentry message has no key"); + upb_status_seterrmsg(&p->status, "mapentry message has no key"); + upb_env_reporterror(p->env, &p->status); return false; } switch (upb_fielddef_type(p->top->f)) { @@ -10499,8 +10437,9 @@ static bool parse_mapentry_key(upb_json_parser *p) { return false; } } else { - upb_status_seterrmsg(p->status, + upb_status_seterrmsg(&p->status, "Map bool key not 'true' or 'false'"); + upb_env_reporterror(p->env, &p->status); return false; } multipart_end(p); @@ -10518,7 +10457,8 @@ static bool parse_mapentry_key(upb_json_parser *p) { break; } default: - upb_status_seterrmsg(p->status, "Invalid field type for map key"); + upb_status_seterrmsg(&p->status, "Invalid field type for map key"); + upb_env_reporterror(p->env, &p->status); return false; } @@ -10573,7 +10513,8 @@ static bool handle_mapentry(upb_json_parser *p) { p->top->is_mapentry = true; /* set up to pop frame after value is parsed. */ p->top->mapfield = mapfield; if (p->top->f == NULL) { - upb_status_seterrmsg(p->status, "mapentry message has no value"); + upb_status_seterrmsg(&p->status, "mapentry message has no value"); + upb_env_reporterror(p->env, &p->status); return false; } @@ -10593,7 +10534,8 @@ static bool end_membername(upb_json_parser *p) { if (!f) { /* TODO(haberman): Ignore unknown fields if requested/configured to do * so. */ - upb_status_seterrf(p->status, "No such field: %.*s\n", (int)len, buf); + upb_status_seterrf(&p->status, "No such field: %.*s\n", (int)len, buf); + upb_env_reporterror(p->env, &p->status); return false; } @@ -10669,9 +10611,10 @@ static bool start_subobject(upb_json_parser *p) { return true; } else { - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "Object specified for non-message/group field: %s", upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); return false; } } @@ -10697,9 +10640,10 @@ static bool start_array(upb_json_parser *p) { assert(p->top->f); if (!upb_fielddef_isseq(p->top->f)) { - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "Array specified for non-repeated field: %s", upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); return false; } @@ -10736,7 +10680,11 @@ static void start_object(upb_json_parser *p) { static void end_object(upb_json_parser *p) { if (!p->top->is_map) { upb_status status; + upb_status_clear(&status); upb_sink_endmsg(&p->top->sink, &status); + if (!upb_ok(&status)) { + upb_env_reporterror(p->env, &status); + } } } @@ -10762,11 +10710,11 @@ static void end_object(upb_json_parser *p) { * final state once, when the closing '"' is seen. */ -#line 1198 "upb/json/parser.rl" +#line 1218 "upb/json/parser.rl" -#line 1110 "upb/json/parser.c" +#line 1130 "upb/json/parser.c" static const char _json_actions[] = { 0, 1, 0, 1, 2, 1, 3, 1, 5, 1, 6, 1, 7, 1, 8, 1, @@ -10915,7 +10863,7 @@ static const int json_en_value_machine = 27; static const int json_en_main = 1; -#line 1201 "upb/json/parser.rl" +#line 1221 "upb/json/parser.rl" size_t parse(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { @@ -10937,7 +10885,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, capture_resume(parser, buf); -#line 1281 "upb/json/parser.c" +#line 1301 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -11012,118 +10960,118 @@ _match: switch ( *_acts++ ) { case 0: -#line 1113 "upb/json/parser.rl" +#line 1133 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 1: -#line 1114 "upb/json/parser.rl" +#line 1134 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 10; goto _again;} } break; case 2: -#line 1118 "upb/json/parser.rl" +#line 1138 "upb/json/parser.rl" { start_text(parser, p); } break; case 3: -#line 1119 "upb/json/parser.rl" +#line 1139 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 4: -#line 1125 "upb/json/parser.rl" +#line 1145 "upb/json/parser.rl" { start_hex(parser); } break; case 5: -#line 1126 "upb/json/parser.rl" +#line 1146 "upb/json/parser.rl" { hexdigit(parser, p); } break; case 6: -#line 1127 "upb/json/parser.rl" +#line 1147 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hex(parser)); } break; case 7: -#line 1133 "upb/json/parser.rl" +#line 1153 "upb/json/parser.rl" { CHECK_RETURN_TOP(escape(parser, p)); } break; case 8: -#line 1139 "upb/json/parser.rl" +#line 1159 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 9: -#line 1142 "upb/json/parser.rl" +#line 1162 "upb/json/parser.rl" { {stack[top++] = cs; cs = 19; goto _again;} } break; case 10: -#line 1144 "upb/json/parser.rl" +#line 1164 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 27; goto _again;} } break; case 11: -#line 1149 "upb/json/parser.rl" +#line 1169 "upb/json/parser.rl" { start_member(parser); } break; case 12: -#line 1150 "upb/json/parser.rl" +#line 1170 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_membername(parser)); } break; case 13: -#line 1153 "upb/json/parser.rl" +#line 1173 "upb/json/parser.rl" { end_member(parser); } break; case 14: -#line 1159 "upb/json/parser.rl" +#line 1179 "upb/json/parser.rl" { start_object(parser); } break; case 15: -#line 1162 "upb/json/parser.rl" +#line 1182 "upb/json/parser.rl" { end_object(parser); } break; case 16: -#line 1168 "upb/json/parser.rl" +#line 1188 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_array(parser)); } break; case 17: -#line 1172 "upb/json/parser.rl" +#line 1192 "upb/json/parser.rl" { end_array(parser); } break; case 18: -#line 1177 "upb/json/parser.rl" +#line 1197 "upb/json/parser.rl" { start_number(parser, p); } break; case 19: -#line 1178 "upb/json/parser.rl" +#line 1198 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 20: -#line 1180 "upb/json/parser.rl" +#line 1200 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 21: -#line 1181 "upb/json/parser.rl" +#line 1201 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 22: -#line 1183 "upb/json/parser.rl" +#line 1203 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, true)); } break; case 23: -#line 1185 "upb/json/parser.rl" +#line 1205 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, false)); } break; case 24: -#line 1187 "upb/json/parser.rl" +#line 1207 "upb/json/parser.rl" { /* null value */ } break; case 25: -#line 1189 "upb/json/parser.rl" +#line 1209 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_subobject(parser)); } break; case 26: -#line 1190 "upb/json/parser.rl" +#line 1210 "upb/json/parser.rl" { end_subobject(parser); } break; case 27: -#line 1195 "upb/json/parser.rl" +#line 1215 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 1467 "upb/json/parser.c" +#line 1487 "upb/json/parser.c" } } @@ -11136,10 +11084,11 @@ _again: _out: {} } -#line 1222 "upb/json/parser.rl" +#line 1242 "upb/json/parser.rl" if (p != pe) { - upb_status_seterrf(parser->status, "Parse error at %s\n", p); + upb_status_seterrf(&parser->status, "Parse error at %s\n", p); + upb_env_reporterror(parser->env, &parser->status); } else { capture_suspend(parser, &p); } @@ -11176,19 +11125,20 @@ static void json_parser_reset(upb_json_parser *p) { /* Emit Ragel initialization of the parser. */ -#line 1520 "upb/json/parser.c" +#line 1541 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 1261 "upb/json/parser.rl" +#line 1282 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; accumulate_clear(p); p->multipart_state = MULTIPART_INACTIVE; p->capture = NULL; p->accumulated = NULL; + upb_status_clear(&p->status); } @@ -11214,8 +11164,8 @@ upb_json_parser *upb_json_parser_create(upb_env *env, upb_sink *output) { upb_sink_reset(&p->top->sink, output->handlers, output->closure); p->top->m = upb_handlers_msgdef(output->handlers); - /* If this fails, uncomment and increase the value in parser.h. - * fprintf(stderr, "%zd\n", upb_env_bytesallocated(env) - size_before); */ + /* If this fails, uncomment and increase the value in parser.h. */ + /* fprintf(stderr, "%zd\n", upb_env_bytesallocated(env) - size_before); */ assert(upb_env_bytesallocated(env) - size_before <= UPB_JSON_PARSER_SIZE); return p; } @@ -11224,14 +11174,9 @@ upb_bytessink *upb_json_parser_input(upb_json_parser *p) { return &p->input_; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * This currently uses snprintf() to format primitives, and could be optimized - * further. - */ +** This currently uses snprintf() to format primitives, and could be optimized +** further. +*/ #include diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h index b4dcd558..b31f5c08 100644 --- a/ruby/ext/google/protobuf_c/upb.h +++ b/ruby/ext/google/protobuf_c/upb.h @@ -1,70 +1,62 @@ // Amalgamated source file /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Defs are upb's internal representation of the constructs that can appear - * in a .proto file: - * - * - upb_msgdef: describes a "message" construct. - * - upb_fielddef: describes a message field. - * - upb_enumdef: describes an enum. - * (TODO: definitions of services). - * - * Like upb_refcounted objects, defs are mutable only until frozen, and are - * only thread-safe once frozen. - * - * This is a mixed C/C++ interface that offers a full API to both languages. - * See the top-level README for more information. - */ +** Defs are upb's internal representation of the constructs that can appear +** in a .proto file: +** +** - upb::MessageDef (upb_msgdef): describes a "message" construct. +** - upb::FieldDef (upb_fielddef): describes a message field. +** - upb::EnumDef (upb_enumdef): describes an enum. +** - upb::OneofDef (upb_oneofdef): describes a oneof. +** - upb::Def (upb_def): base class of all the others. +** +** TODO: definitions of services. +** +** Like upb_refcounted objects, defs are mutable only until frozen, and are +** only thread-safe once frozen. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ #ifndef UPB_DEF_H_ #define UPB_DEF_H_ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A refcounting scheme that supports circular refs. It accomplishes this by - * partitioning the set of objects into groups such that no cycle spans groups; - * we can then reference-count the group as a whole and ignore refs within the - * group. When objects are mutable, these groups are computed very - * conservatively; we group any objects that have ever had a link between them. - * When objects are frozen, we compute strongly-connected components which - * allows us to be precise and only group objects that are actually cyclic. - * - * This is a mixed C/C++ interface that offers a full API to both languages. - * See the top-level README for more information. - */ +** upb::RefCounted (upb_refcounted) +** +** A refcounting scheme that supports circular refs. It accomplishes this by +** partitioning the set of objects into groups such that no cycle spans groups; +** we can then reference-count the group as a whole and ignore refs within the +** group. When objects are mutable, these groups are computed very +** conservatively; we group any objects that have ever had a link between them. +** When objects are frozen, we compute strongly-connected components which +** allows us to be precise and only group objects that are actually cyclic. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ #ifndef UPB_REFCOUNTED_H_ #define UPB_REFCOUNTED_H_ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * This header is INTERNAL-ONLY! Its interfaces are not public or stable! - * This file defines very fast int->upb_value (inttable) and string->upb_value - * (strtable) hash tables. - * - * The table uses chained scatter with Brent's variation (inspired by the Lua - * implementation of hash tables). The hash function for strings is Austin - * Appleby's "MurmurHash." - * - * The inttable uses uintptr_t as its key, which guarantees it can be used to - * store pointers or integers of at least 32 bits (upb isn't really useful on - * systems where sizeof(void*) < 4). - * - * The table must be homogenous (all values of the same type). In debug - * mode, we check this on insert and lookup. - */ +** upb_table +** +** This header is INTERNAL-ONLY! Its interfaces are not public or stable! +** This file defines very fast int->upb_value (inttable) and string->upb_value +** (strtable) hash tables. +** +** The table uses chained scatter with Brent's variation (inspired by the Lua +** implementation of hash tables). The hash function for strings is Austin +** Appleby's "MurmurHash." +** +** The inttable uses uintptr_t as its key, which guarantees it can be used to +** store pointers or integers of at least 32 bits (upb isn't really useful on +** systems where sizeof(void*) < 4). +** +** The table must be homogenous (all values of the same type). In debug +** mode, we check this on insert and lookup. +*/ #ifndef UPB_TABLE_H_ #define UPB_TABLE_H_ @@ -73,16 +65,11 @@ #include #include /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * This file contains shared definitions that are widely used across upb. - * - * This is a mixed C/C++ interface that offers a full API to both languages. - * See the top-level README for more information. - */ +** This file contains shared definitions that are widely used across upb. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ #ifndef UPB_H_ #define UPB_H_ @@ -3006,25 +2993,20 @@ inline bool OneofDef::const_iterator::operator!=( #endif /* UPB_DEF_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2015 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * This file contains definitions of structs that should be considered private - * and NOT stable across versions of upb. - * - * The only reason they are declared here and not in .c files is to allow upb - * and the application (if desired) to embed statically-initialized instances - * of structures like defs. - * - * If you include this file, all guarantees of ABI compatibility go out the - * window! Any code that includes this file needs to recompile against the - * exact same version of upb that they are linking against. - * - * You also need to recompile if you change the value of the UPB_DEBUG_REFS - * flag. - */ +** This file contains definitions of structs that should be considered private +** and NOT stable across versions of upb. +** +** The only reason they are declared here and not in .c files is to allow upb +** and the application (if desired) to embed statically-initialized instances +** of structures like defs. +** +** If you include this file, all guarantees of ABI compatibility go out the +** window! Any code that includes this file needs to recompile against the +** exact same version of upb that they are linking against. +** +** You also need to recompile if you change the value of the UPB_DEBUG_REFS +** flag. +*/ #ifndef UPB_STATICINIT_H_ @@ -3181,25 +3163,22 @@ struct upb_symtab { #endif /* UPB_STATICINIT_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2010-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A upb_handlers is like a virtual table for a upb_msgdef. Each field of the - * message can have associated functions that will be called when we are - * parsing or visiting a stream of data. This is similar to how handlers work - * in SAX (the Simple API for XML). - * - * The handlers have no idea where the data is coming from, so a single set of - * handlers could be used with two completely different data sources (for - * example, a parser and a visitor over in-memory objects). This decoupling is - * the most important feature of upb, because it allows parsers and serializers - * to be highly reusable. - * - * This is a mixed C/C++ interface that offers a full API to both languages. - * See the top-level README for more information. - */ +** upb::Handlers (upb_handlers) +** +** A upb_handlers is like a virtual table for a upb_msgdef. Each field of the +** message can have associated functions that will be called when we are +** parsing or visiting a stream of data. This is similar to how handlers work +** in SAX (the Simple API for XML). +** +** The handlers have no idea where the data is coming from, so a single set of +** handlers could be used with two completely different data sources (for +** example, a parser and a visitor over in-memory objects). This decoupling is +** the most important feature of upb, because it allows parsers and serializers +** to be highly reusable. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ #ifndef UPB_HANDLERS_H #define UPB_HANDLERS_H @@ -3980,14 +3959,9 @@ uint32_t upb_handlers_selectorcount(const upb_fielddef *f); UPB_END_EXTERN_C /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Inline definitions for handlers.h, which are particularly long and a bit - * tricky. - */ +** Inline definitions for handlers.h, which are particularly long and a bit +** tricky. +*/ #ifndef UPB_HANDLERS_INL_H_ #define UPB_HANDLERS_INL_H_ @@ -5128,21 +5102,18 @@ inline BytesHandler::~BytesHandler() {} #endif /* UPB_HANDLERS_H */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A upb::Environment provides a means for injecting malloc and an - * error-reporting callback into encoders/decoders. This allows them to be - * independent of nearly all assumptions about their actual environment. - * - * It is also a container for allocating the encoders/decoders themselves that - * insulates clients from knowing their actual size. This provides ABI - * compatibility even if the size of the objects change. And this allows the - * structure definitions to be in the .c files instead of the .h files, making - * the .h files smaller and more readable. - */ +** upb::Environment (upb_env) +** +** A upb::Environment provides a means for injecting malloc and an +** error-reporting callback into encoders/decoders. This allows them to be +** independent of nearly all assumptions about their actual environment. +** +** It is also a container for allocating the encoders/decoders themselves that +** insulates clients from knowing their actual size. This provides ABI +** compatibility even if the size of the objects change. And this allows the +** structure definitions to be in the .c files instead of the .h files, making +** the .h files smaller and more readable. +*/ #ifndef UPB_ENV_H_ @@ -5392,23 +5363,21 @@ inline upb_alloc_func *SeededAllocator::GetAllocationFunction() { #endif /* UPB_ENV_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2010-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A upb_sink is an object that binds a upb_handlers object to some runtime - * state. It is the object that can actually receive data via the upb_handlers - * interface. - * - * Unlike upb_def and upb_handlers, upb_sink is never frozen, immutable, or - * thread-safe. You can create as many of them as you want, but each one may - * only be used in a single thread at a time. - * - * If we compare with class-based OOP, a you can think of a upb_def as an - * abstract base class, a upb_handlers as a concrete derived class, and a - * upb_sink as an object (class instance). - */ +** upb::Sink (upb_sink) +** upb::BytesSink (upb_bytessink) +** +** A upb_sink is an object that binds a upb_handlers object to some runtime +** state. It is the object that can actually receive data via the upb_handlers +** interface. +** +** Unlike upb_def and upb_handlers, upb_sink is never frozen, immutable, or +** thread-safe. You can create as many of them as you want, but each one may +** only be used in a single thread at a time. +** +** If we compare with class-based OOP, a you can think of a upb_def as an +** abstract base class, a upb_handlers as a concrete derived class, and a +** upb_sink as an object (class instance). +*/ #ifndef UPB_SINK_H #define UPB_SINK_H @@ -5921,21 +5890,16 @@ inline bool BufferSource::PutBuffer(const char *buf, size_t len, #endif /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2013 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * For handlers that do very tiny, very simple operations, the function call - * overhead of calling a handler can be significant. This file allows the - * user to define handlers that do something very simple like store the value - * to memory and/or set a hasbit. JIT compilers can then special-case these - * handlers and emit specialized code for them instead of actually calling the - * handler. - * - * The functionality is very simple/limited right now but may expand to be able - * to call another function. - */ +** For handlers that do very tiny, very simple operations, the function call +** overhead of calling a handler can be significant. This file allows the +** user to define handlers that do something very simple like store the value +** to memory and/or set a hasbit. JIT compilers can then special-case these +** handlers and emit specialized code for them instead of actually calling the +** handler. +** +** The functionality is very simple/limited right now but may expand to be able +** to call another function. +*/ #ifndef UPB_SHIM_H #define UPB_SHIM_H @@ -5994,19 +5958,16 @@ inline const Shim::Data* Shim::GetData(const Handlers* h, Handlers::Selector s, #endif /* UPB_SHIM_H */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A symtab (symbol table) stores a name->def map of upb_defs. Clients could - * always create such tables themselves, but upb_symtab has logic for resolving - * symbolic references, and in particular, for keeping a whole set of consistent - * defs when replacing some subset of those defs. This logic is nontrivial. - * - * This is a mixed C/C++ interface that offers a full API to both languages. - * See the top-level README for more information. - */ +** upb::SymbolTable (upb_symtab) +** +** A symtab (symbol table) stores a name->def map of upb_defs. Clients could +** always create such tables themselves, but upb_symtab has logic for resolving +** symbolic references, and in particular, for keeping a whole set of consistent +** defs when replacing some subset of those defs. This logic is nontrivial. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ #ifndef UPB_SYMTAB_H_ #define UPB_SYMTAB_H_ @@ -6182,14 +6143,10 @@ inline bool SymbolTable::Add( #endif /* UPB_SYMTAB_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * upb::descriptor::Reader provides a way of building upb::Defs from - * data in descriptor.proto format. - */ +** upb::descriptor::Reader (upb_descreader) +** +** Provides a way of building upb::Defs from data in descriptor.proto format. +*/ #ifndef UPB_DESCRIPTOR_H #define UPB_DESCRIPTOR_H @@ -7067,34 +7024,26 @@ inline upb::reffed_ptr name_part() { RETURN_REFFED(upb::Fie #endif /* GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Internal-only definitions for the decoder. - */ +** Internal-only definitions for the decoder. +*/ #ifndef UPB_DECODER_INT_H_ #define UPB_DECODER_INT_H_ #include /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * upb::pb::Decoder implements a high performance, streaming, resumable decoder - * for the binary protobuf format. - * - * This interface works the same regardless of what decoder backend is being - * used. A client of this class does not need to know whether decoding is using - * a JITted decoder (DynASM, LLVM, etc) or an interpreted decoder. By default, - * it will always use the fastest available decoder. However, you can call - * set_allow_jit(false) to disable any JIT decoder that might be available. - * This is primarily useful for testing purposes. - */ +** upb::pb::Decoder +** +** A high performance, streaming, resumable decoder for the binary protobuf +** format. +** +** This interface works the same regardless of what decoder backend is being +** used. A client of this class does not need to know whether decoding is using +** a JITted decoder (DynASM, LLVM, etc) or an interpreted decoder. By default, +** it will always use the fastest available decoder. However, you can call +** set_allow_jit(false) to disable any JIT decoder that might be available. +** This is primarily useful for testing purposes. +*/ #ifndef UPB_DECODER_H_ #define UPB_DECODER_H_ @@ -7702,14 +7651,9 @@ UPB_INLINE void upb_pbdecoder_unpackdispatch(uint64_t dispatch, uint64_t *ofs, #endif /* UPB_DECODER_INT_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A number of routines for varint manipulation (we keep them all around to - * have multiple approaches available for benchmarking). - */ +** A number of routines for varint manipulation (we keep them all around to +** have multiple approaches available for benchmarking). +*/ #ifndef UPB_VARINT_DECODER_H_ #define UPB_VARINT_DECODER_H_ @@ -7873,18 +7817,15 @@ UPB_INLINE uint64_t upb_vencode32(uint32_t val) { #endif /* UPB_VARINT_DECODER_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2010 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Implements a set of upb_handlers that write protobuf data to the binary wire - * format. - * - * This encoder implementation does not have any access to any out-of-band or - * precomputed lengths for submessages, so it must buffer submessages internally - * before it can emit the first byte. - */ +** upb::pb::Encoder (upb_pb_encoder) +** +** Implements a set of upb_handlers that write protobuf data to the binary wire +** format. +** +** This encoder implementation does not have any access to any out-of-band or +** precomputed lengths for submessages, so it must buffer submessages internally +** before it can emit the first byte. +*/ #ifndef UPB_ENCODER_H_ #define UPB_ENCODER_H_ @@ -7966,29 +7907,24 @@ inline reffed_ptr Encoder::NewHandlers( #endif /* UPB_ENCODER_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * upb's core components like upb_decoder and upb_msg are carefully designed to - * avoid depending on each other for maximum orthogonality. In other words, - * you can use a upb_decoder to decode into *any* kind of structure; upb_msg is - * just one such structure. A upb_msg can be serialized/deserialized into any - * format, protobuf binary format is just one such format. - * - * However, for convenience we provide functions here for doing common - * operations like deserializing protobuf binary format into a upb_msg. The - * compromise is that this file drags in almost all of upb as a dependency, - * which could be undesirable if you're trying to use a trimmed-down build of - * upb. - * - * While these routines are convenient, they do not reuse any encoding/decoding - * state. For example, if a decoder is JIT-based, it will be re-JITted every - * time these functions are called. For this reason, if you are parsing lots - * of data and efficiency is an issue, these may not be the best functions to - * use (though they are useful for prototyping, before optimizing). - */ +** upb's core components like upb_decoder and upb_msg are carefully designed to +** avoid depending on each other for maximum orthogonality. In other words, +** you can use a upb_decoder to decode into *any* kind of structure; upb_msg is +** just one such structure. A upb_msg can be serialized/deserialized into any +** format, protobuf binary format is just one such format. +** +** However, for convenience we provide functions here for doing common +** operations like deserializing protobuf binary format into a upb_msg. The +** compromise is that this file drags in almost all of upb as a dependency, +** which could be undesirable if you're trying to use a trimmed-down build of +** upb. +** +** While these routines are convenient, they do not reuse any encoding/decoding +** state. For example, if a decoder is JIT-based, it will be re-JITted every +** time these functions are called. For this reason, if you are parsing lots +** of data and efficiency is an issue, these may not be the best functions to +** use (though they are useful for prototyping, before optimizing). +*/ #ifndef UPB_GLUE_H #define UPB_GLUE_H @@ -8047,11 +7983,10 @@ bool LoadDescriptorIntoSymtab(SymbolTable* s, const T& desc, Status* status) { #endif /* UPB_GLUE_H */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ +** upb::pb::TextPrinter (upb_textprinter) +** +** Handlers for writing to protobuf text format. +*/ #ifndef UPB_TEXT_H_ #define UPB_TEXT_H_ @@ -8127,14 +8062,11 @@ inline reffed_ptr TextPrinter::NewHandlers( #endif /* UPB_TEXT_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * upb::json::Parser can parse JSON according to a specific schema. - * Support for parsing arbitrary JSON (schema-less) will be added later. - */ +** upb::json::Parser (upb_json_parser) +** +** Parses JSON according to a specific schema. +** Support for parsing arbitrary JSON (schema-less) will be added later. +*/ #ifndef UPB_JSON_PARSER_H_ #define UPB_JSON_PARSER_H_ @@ -8156,7 +8088,7 @@ UPB_DECLARE_TYPE(upb::json::Parser, upb_json_parser) * constructed. This hint may be an overestimate for some build configurations. * But if the parser library is upgraded without recompiling the application, * it may be an underestimate. */ -#define UPB_JSON_PARSER_SIZE 3568 +#define UPB_JSON_PARSER_SIZE 3704 #ifdef __cplusplus @@ -8199,14 +8131,10 @@ inline BytesSink* Parser::input() { #endif /* UPB_JSON_PARSER_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * upb::json::Printer allows you to create handlers that emit JSON - * according to a specific protobuf schema. - */ +** upb::json::Printer +** +** Handlers that emit JSON according to a specific protobuf schema. +*/ #ifndef UPB_JSON_TYPED_PRINTER_H_ #define UPB_JSON_TYPED_PRINTER_H_ diff --git a/ruby/lib/google/protobuf.rb b/ruby/lib/google/protobuf.rb index 99b17929..74ea770d 100644 --- a/ruby/lib/google/protobuf.rb +++ b/ruby/lib/google/protobuf.rb @@ -31,6 +31,15 @@ # require mixins before we hook them into the java & c code require 'google/protobuf/message_exts' +# We define these before requiring the platform-specific modules. +# That way the module init can grab references to these. +module Google + module Protobuf + class Error < RuntimeError; end + class ParseError < Error; end + end +end + if RUBY_PLATFORM == "java" require 'json' require 'google/protobuf_java' diff --git a/ruby/travis-test.sh b/ruby/travis-test.sh index a240dd65..4a2536a5 100755 --- a/ruby/travis-test.sh +++ b/ruby/travis-test.sh @@ -9,7 +9,9 @@ test_version() { "rvm install $version && rvm use $version && \ which ruby && \ gem install bundler && bundle && \ - rake test" + rake test && \ + cd ../conformance && \ + make test_ruby" } test_version $1 diff --git a/src/google/protobuf/compiler/ruby/ruby_generated_code.rb b/src/google/protobuf/compiler/ruby/ruby_generated_code.rb index 100d6fa7..49b23fbe 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generated_code.rb +++ b/src/google/protobuf/compiler/ruby/ruby_generated_code.rb @@ -13,7 +13,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do optional :optional_double, :double, 6 optional :optional_float, :float, 7 optional :optional_string, :string, 8 - optional :optional_bytes, :string, 9 + optional :optional_bytes, :bytes, 9 optional :optional_enum, :enum, 10, "A.B.C.TestEnum" optional :optional_msg, :message, 11, "A.B.C.TestMessage" repeated :repeated_int32, :int32, 21 @@ -24,7 +24,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do repeated :repeated_double, :double, 26 repeated :repeated_float, :float, 27 repeated :repeated_string, :string, 28 - repeated :repeated_bytes, :string, 29 + repeated :repeated_bytes, :bytes, 29 repeated :repeated_enum, :enum, 30, "A.B.C.TestEnum" repeated :repeated_msg, :message, 31, "A.B.C.TestMessage" map :map_int32_string, :int32, :string, 61 @@ -47,7 +47,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do optional :oneof_double, :double, 46 optional :oneof_float, :float, 47 optional :oneof_string, :string, 48 - optional :oneof_bytes, :string, 49 + optional :oneof_bytes, :bytes, 49 optional :oneof_enum, :enum, 50, "A.B.C.TestEnum" optional :oneof_msg, :message, 51, "A.B.C.TestMessage" end diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.cc b/src/google/protobuf/compiler/ruby/ruby_generator.cc index a9b6837e..9692f1bf 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generator.cc +++ b/src/google/protobuf/compiler/ruby/ruby_generator.cc @@ -47,7 +47,7 @@ namespace compiler { namespace ruby { // Forward decls. -std::string IntToString(uint32 value); +std::string IntToString(int32 value); std::string StripDotProto(const std::string& proto_file); std::string LabelForField(google::protobuf::FieldDescriptor* field); std::string TypeName(google::protobuf::FieldDescriptor* field); @@ -64,7 +64,7 @@ void GenerateEnumAssignment( const google::protobuf::EnumDescriptor* en, google::protobuf::io::Printer* printer); -std::string IntToString(uint32 value) { +std::string IntToString(int32 value) { std::ostringstream os; os << value; return os.str(); @@ -85,17 +85,25 @@ std::string LabelForField(const google::protobuf::FieldDescriptor* field) { } std::string TypeName(const google::protobuf::FieldDescriptor* field) { - switch (field->cpp_type()) { - case FieldDescriptor::CPPTYPE_INT32: return "int32"; - case FieldDescriptor::CPPTYPE_INT64: return "int64"; - case FieldDescriptor::CPPTYPE_UINT32: return "uint32"; - case FieldDescriptor::CPPTYPE_UINT64: return "uint64"; - case FieldDescriptor::CPPTYPE_DOUBLE: return "double"; - case FieldDescriptor::CPPTYPE_FLOAT: return "float"; - case FieldDescriptor::CPPTYPE_BOOL: return "bool"; - case FieldDescriptor::CPPTYPE_ENUM: return "enum"; - case FieldDescriptor::CPPTYPE_STRING: return "string"; - case FieldDescriptor::CPPTYPE_MESSAGE: return "message"; + switch (field->type()) { + case FieldDescriptor::TYPE_INT32: return "int32"; + case FieldDescriptor::TYPE_INT64: return "int64"; + case FieldDescriptor::TYPE_UINT32: return "uint32"; + case FieldDescriptor::TYPE_UINT64: return "uint64"; + case FieldDescriptor::TYPE_SINT32: return "sint32"; + case FieldDescriptor::TYPE_SINT64: return "sint64"; + case FieldDescriptor::TYPE_FIXED32: return "fixed32"; + case FieldDescriptor::TYPE_FIXED64: return "fixed64"; + case FieldDescriptor::TYPE_SFIXED32: return "sfixed32"; + case FieldDescriptor::TYPE_SFIXED64: return "sfixed64"; + case FieldDescriptor::TYPE_DOUBLE: return "double"; + case FieldDescriptor::TYPE_FLOAT: return "float"; + case FieldDescriptor::TYPE_BOOL: return "bool"; + case FieldDescriptor::TYPE_ENUM: return "enum"; + case FieldDescriptor::TYPE_STRING: return "string"; + case FieldDescriptor::TYPE_BYTES: return "bytes"; + case FieldDescriptor::TYPE_MESSAGE: return "message"; + case FieldDescriptor::TYPE_GROUP: return "group"; default: assert(false); return ""; } } diff --git a/travis.sh b/travis.sh index 4aa67344..9514ec2b 100755 --- a/travis.sh +++ b/travis.sh @@ -8,10 +8,16 @@ # .travis.yml uses matrix.exclude to block the cases where app-get can't be # use to install things. -build_cpp() { +# For when some other test needs the C++ main build, including protoc and +# libprotobuf. +internal_build_cpp() { ./autogen.sh ./configure make -j2 +} + +build_cpp() { + internal_build_cpp make check -j2 cd conformance && make test_cpp && cd .. } @@ -62,18 +68,14 @@ use_java() { build_java() { # Java build needs `protoc`. - ./autogen.sh - ./configure - make -j2 + internal_build_cpp cd java && mvn test && cd .. cd conformance && make test_java && cd .. } build_javanano() { # Java build needs `protoc`. - ./autogen.sh - ./configure - make -j2 + internal_build_cpp cd javanano && mvn test && cd .. } @@ -104,9 +106,7 @@ build_javanano_oracle7() { } build_python() { - ./autogen.sh - ./configure - make -j2 + internal_build_cpp cd python python setup.py build python setup.py test @@ -116,9 +116,7 @@ build_python() { } build_python_cpp() { - ./autogen.sh - ./configure - make -j2 + internal_build_cpp export LD_LIBRARY_PATH=../src/.libs # for Linux export DYLD_LIBRARY_PATH=../src/.libs # for OS X cd python @@ -130,18 +128,23 @@ build_python_cpp() { } build_ruby19() { + internal_build_cpp # For conformance tests. cd ruby && bash travis-test.sh ruby-1.9 && cd .. } build_ruby20() { + internal_build_cpp # For conformance tests. cd ruby && bash travis-test.sh ruby-2.0 && cd .. } build_ruby21() { + internal_build_cpp # For conformance tests. cd ruby && bash travis-test.sh ruby-2.1 && cd .. } build_ruby22() { + internal_build_cpp # For conformance tests. cd ruby && bash travis-test.sh ruby-2.2 && cd .. } build_jruby() { + internal_build_cpp # For conformance tests. cd ruby && bash travis-test.sh jruby && cd .. } -- cgit v1.2.3 From c2c43a4917d7da263e360a4096d6603da8535bdf Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Fri, 17 Jul 2015 16:29:10 -0700 Subject: Fixed lint errors and responded to CR comments. Change-Id: If7b1cc0f03f609a7f43ddafc8509b44207c60910 --- conformance/conformance_ruby.rb | 55 ++++++++++++++++++++++------------------- ruby/lib/google/protobuf.rb | 2 +- 2 files changed, 30 insertions(+), 27 deletions(-) (limited to 'conformance') diff --git a/conformance/conformance_ruby.rb b/conformance/conformance_ruby.rb index e7bd4ed5..cd065673 100755 --- a/conformance/conformance_ruby.rb +++ b/conformance/conformance_ruby.rb @@ -32,8 +32,8 @@ require 'conformance' -test_count = 0; -verbose = false; +$test_count = 0 +$verbose = false def do_test(request) test_message = Conformance::TestAllTypes.new @@ -43,9 +43,10 @@ def do_test(request) case request.payload when :protobuf_payload begin - test_message = Conformance::TestAllTypes.decode(request.protobuf_payload) + test_message = + Conformance::TestAllTypes.decode(request.protobuf_payload) rescue Google::Protobuf::ParseError => err - response.parse_error = err.message.encode("utf-8") + response.parse_error = err.message.encode('utf-8') return response end @@ -53,34 +54,36 @@ def do_test(request) test_message = Conformance::TestAllTypes.decode_json(request.json_payload) when nil - raise "Request didn't have payload."; + fail "Request didn't have payload" end case request.requested_output_format when :UNSPECIFIED - raise "Unspecified output format" + fail 'Unspecified output format' when :PROTOBUF - response.protobuf_payload = Conformance::TestAllTypes.encode(test_message) + response.protobuf_payload = test_message.to_proto when :JSON - response.json_payload = Conformance::TestAllTypes.encode_json(test_message) + response.json_payload = test_message.to_json end - rescue Exception => err - response.runtime_error = err.message.encode("utf-8") + err.backtrace.join("\n") + rescue StandardError => err + response.runtime_error = err.message.encode('utf-8') end - return response + response end +# Returns true if the test ran successfully, false on legitimate EOF. +# If EOF is encountered in an unexpected place, raises IOError. def do_test_io length_bytes = STDIN.read(4) return false if length_bytes.nil? - length = length_bytes.unpack("V").first + length = length_bytes.unpack('V').first serialized_request = STDIN.read(length) - if serialized_request.nil? or serialized_request.length != length - raise "I/O error" + if serialized_request.nil? || serialized_request.length != length + fail IOError end request = Conformance::ConformanceRequest.decode(serialized_request) @@ -88,24 +91,24 @@ def do_test_io response = do_test(request) serialized_response = Conformance::ConformanceResponse.encode(response) - STDOUT.write([serialized_response.length].pack("V")) + STDOUT.write([serialized_response.length].pack('V')) STDOUT.write(serialized_response) STDOUT.flush - #if verbose - # fprintf(stderr, "conformance-cpp: request=%s, response=%s\n", - # request.ShortDebugString().c_str(), - # response.ShortDebugString().c_str()); + if $verbose + STDERR.puts("conformance-cpp: request={request.to_json}, " \ + "response={response.to_json}\n") + end - #test_count++; + $test_count += 1 - return true; + true end -while true - if not do_test_io() - STDERR.puts("conformance-cpp: received EOF from test runner " + - "after #{test_count} tests, exiting") - exit 0 +loop do + unless do_test_io + STDERR.puts('conformance-cpp: received EOF from test runner ' \ + "after #{$test_count} tests, exiting") + break end end diff --git a/ruby/lib/google/protobuf.rb b/ruby/lib/google/protobuf.rb index 74ea770d..f0eb6268 100644 --- a/ruby/lib/google/protobuf.rb +++ b/ruby/lib/google/protobuf.rb @@ -35,7 +35,7 @@ require 'google/protobuf/message_exts' # That way the module init can grab references to these. module Google module Protobuf - class Error < RuntimeError; end + class Error < StandardError; end class ParseError < Error; end end end -- cgit v1.2.3 From b6defa7c115a19d3671b2a6c5ebab2a471d273ea Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Tue, 4 Aug 2015 10:01:40 +0100 Subject: Added C# conformance tests. The tests are run from Travis in the same way as on other platforms. Currently some expected failures - but only expected in that they're what we got to start with. Will try to fix them in other pull requests. --- conformance/Makefile.am | 14 +++++++++++++- conformance/failure_list_csharp.txt | 22 ++++++++++++++++++++++ travis.sh | 6 ++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 conformance/failure_list_csharp.txt (limited to 'conformance') diff --git a/conformance/Makefile.am b/conformance/Makefile.am index 97251715..c74eb1c8 100644 --- a/conformance/Makefile.am +++ b/conformance/Makefile.am @@ -40,7 +40,7 @@ $(protoc_outputs): protoc_middleman BUILT_SOURCES = $(protoc_outputs) -CLEANFILES = $(protoc_outputs) protoc_middleman javac_middleman conformance-java +CLEANFILES = $(protoc_outputs) protoc_middleman javac_middleman conformance-java conformance-csharp MAINTAINERCLEANFILES = \ Makefile.in @@ -55,6 +55,15 @@ conformance-java: javac_middleman @echo 'java -classpath .:../java/target/classes ConformanceJava "$$@"' >> conformance-java @chmod +x conformance-java +# Currently the conformance code is alongside the rest of the C# +# source, as it's easier to maintain there. We assume we've already +# built that, so we just need a script to run it. +conformance-csharp: + @echo "Writing shortcut script conformance-csharp..." + @echo '#! /bin/sh' > conformance-csharp + @echo 'mono ../csharp/src/Google.Protobuf.Conformance/bin/Release/Google.Protobuf.Conformance.exe "$$@"' >> conformance-csharp + @chmod +x conformance-csharp + # Targets for actually running tests. test_cpp: protoc_middleman conformance-test-runner conformance-cpp ./conformance-test-runner --failure_list failure_list_cpp.txt ./conformance-cpp @@ -62,5 +71,8 @@ test_cpp: protoc_middleman conformance-test-runner conformance-cpp test_java: protoc_middleman conformance-test-runner conformance-java ./conformance-test-runner ./conformance-java +test_csharp: protoc_middleman conformance-test-runner conformance-csharp + ./conformance-test-runner --failure_list failure_list_csharp.txt ./conformance-csharp + test_ruby: protoc_middleman conformance-test-runner RUBYLIB=../ruby/lib:. ./conformance-test-runner --failure_list failure_list_ruby.txt ./conformance_ruby.rb diff --git a/conformance/failure_list_csharp.txt b/conformance/failure_list_csharp.txt new file mode 100644 index 00000000..34846589 --- /dev/null +++ b/conformance/failure_list_csharp.txt @@ -0,0 +1,22 @@ +ProtobufInput.PrematureEofBeforeUnknownValue.BOOL +ProtobufInput.PrematureEofBeforeUnknownValue.BYTES +ProtobufInput.PrematureEofBeforeUnknownValue.DOUBLE +ProtobufInput.PrematureEofBeforeUnknownValue.ENUM +ProtobufInput.PrematureEofBeforeUnknownValue.FIXED32 +ProtobufInput.PrematureEofBeforeUnknownValue.FIXED64 +ProtobufInput.PrematureEofBeforeUnknownValue.FLOAT +ProtobufInput.PrematureEofBeforeUnknownValue.INT32 +ProtobufInput.PrematureEofBeforeUnknownValue.INT64 +ProtobufInput.PrematureEofBeforeUnknownValue.MESSAGE +ProtobufInput.PrematureEofBeforeUnknownValue.SFIXED32 +ProtobufInput.PrematureEofBeforeUnknownValue.SFIXED64 +ProtobufInput.PrematureEofBeforeUnknownValue.SINT32 +ProtobufInput.PrematureEofBeforeUnknownValue.SINT64 +ProtobufInput.PrematureEofBeforeUnknownValue.STRING +ProtobufInput.PrematureEofBeforeUnknownValue.UINT32 +ProtobufInput.PrematureEofBeforeUnknownValue.UINT64 +ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE +ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE +ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.BYTES +ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.MESSAGE +ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.STRING diff --git a/travis.sh b/travis.sh index 9514ec2b..07004571 100755 --- a/travis.sh +++ b/travis.sh @@ -29,6 +29,11 @@ build_cpp_distcheck() { } build_csharp() { + # Just for the conformance tests. We don't currently + # need to really build protoc, but it's simplest to keep with the + # conventions of the other builds. + internal_build_cpp + # Install latest version of Mono sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list @@ -39,6 +44,7 @@ build_csharp() { (cd csharp/src; mono ../../nuget.exe restore) csharp/buildall.sh + cd conformance && make test_csharp && cd .. } use_java() { -- cgit v1.2.3 From 15bf55e2251df4ad798a4f88b8514899c26e3dc3 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Tue, 4 Aug 2015 12:38:53 +0100 Subject: Validate that after reading a message, we've consumed as many bytes as we expected to. We should now have no conformance failures. --- conformance/failure_list_csharp.txt | 22 ---------------- .../Google.Protobuf.Test/GeneratedMessageTest.cs | 26 +++++++++++++------ csharp/src/Google.Protobuf/CodedInputStream.cs | 29 ++++++++++++++++++++++ 3 files changed, 47 insertions(+), 30 deletions(-) (limited to 'conformance') diff --git a/conformance/failure_list_csharp.txt b/conformance/failure_list_csharp.txt index 34846589..e69de29b 100644 --- a/conformance/failure_list_csharp.txt +++ b/conformance/failure_list_csharp.txt @@ -1,22 +0,0 @@ -ProtobufInput.PrematureEofBeforeUnknownValue.BOOL -ProtobufInput.PrematureEofBeforeUnknownValue.BYTES -ProtobufInput.PrematureEofBeforeUnknownValue.DOUBLE -ProtobufInput.PrematureEofBeforeUnknownValue.ENUM -ProtobufInput.PrematureEofBeforeUnknownValue.FIXED32 -ProtobufInput.PrematureEofBeforeUnknownValue.FIXED64 -ProtobufInput.PrematureEofBeforeUnknownValue.FLOAT -ProtobufInput.PrematureEofBeforeUnknownValue.INT32 -ProtobufInput.PrematureEofBeforeUnknownValue.INT64 -ProtobufInput.PrematureEofBeforeUnknownValue.MESSAGE -ProtobufInput.PrematureEofBeforeUnknownValue.SFIXED32 -ProtobufInput.PrematureEofBeforeUnknownValue.SFIXED64 -ProtobufInput.PrematureEofBeforeUnknownValue.SINT32 -ProtobufInput.PrematureEofBeforeUnknownValue.SINT64 -ProtobufInput.PrematureEofBeforeUnknownValue.STRING -ProtobufInput.PrematureEofBeforeUnknownValue.UINT32 -ProtobufInput.PrematureEofBeforeUnknownValue.UINT64 -ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE -ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE -ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.BYTES -ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.MESSAGE -ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.STRING diff --git a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs index 1ef3d753..6fdd1066 100644 --- a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs +++ b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs @@ -259,7 +259,7 @@ namespace Google.Protobuf output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited); var nestedMessage = new ForeignMessage { C = 20 }; // Size of the entry (tag, size written by WriteMessage, data written by WriteMessage) - output.WriteRawVarint32((uint)(nestedMessage.CalculateSize() + 3)); + output.WriteLength(2 + nestedMessage.CalculateSize()); output.WriteTag(2, WireFormat.WireType.LengthDelimited); output.WriteMessage(nestedMessage); output.Flush(); @@ -283,7 +283,7 @@ namespace Google.Protobuf // Each field can be represented in a single byte, with a single byte tag. // Total message size: 6 bytes. - output.WriteRawVarint32(6); + output.WriteLength(6); output.WriteTag(1, WireFormat.WireType.Varint); output.WriteInt32(key); output.WriteTag(2, WireFormat.WireType.Varint); @@ -309,7 +309,7 @@ namespace Google.Protobuf // Each field can be represented in a single byte, with a single byte tag. // Total message size: 4 bytes. - output.WriteRawVarint32(4); + output.WriteLength(4); output.WriteTag(2, WireFormat.WireType.Varint); output.WriteInt32(value); output.WriteTag(1, WireFormat.WireType.Varint); @@ -335,7 +335,7 @@ namespace Google.Protobuf var key1 = 10; var value1 = 20; output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited); - output.WriteRawVarint32(4); + output.WriteLength(4); output.WriteTag(1, WireFormat.WireType.Varint); output.WriteInt32(key1); output.WriteTag(2, WireFormat.WireType.Varint); @@ -345,7 +345,7 @@ namespace Google.Protobuf var key2 = "a"; var value2 = "b"; output.WriteTag(TestMap.MapStringStringFieldNumber, WireFormat.WireType.LengthDelimited); - output.WriteRawVarint32(6); // 3 bytes per entry: tag, size, character + output.WriteLength(6); // 3 bytes per entry: tag, size, character output.WriteTag(1, WireFormat.WireType.LengthDelimited); output.WriteString(key2); output.WriteTag(2, WireFormat.WireType.LengthDelimited); @@ -355,7 +355,7 @@ namespace Google.Protobuf var key3 = 15; var value3 = 25; output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited); - output.WriteRawVarint32(4); + output.WriteLength(4); output.WriteTag(1, WireFormat.WireType.Varint); output.WriteInt32(key3); output.WriteTag(2, WireFormat.WireType.Varint); @@ -383,7 +383,7 @@ namespace Google.Protobuf // First entry output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited); - output.WriteRawVarint32(4); + output.WriteLength(4); output.WriteTag(1, WireFormat.WireType.Varint); output.WriteInt32(key); output.WriteTag(2, WireFormat.WireType.Varint); @@ -391,7 +391,7 @@ namespace Google.Protobuf // Second entry - same key, different value output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited); - output.WriteRawVarint32(4); + output.WriteLength(4); output.WriteTag(1, WireFormat.WireType.Varint); output.WriteInt32(key); output.WriteTag(2, WireFormat.WireType.Varint); @@ -619,5 +619,15 @@ namespace Google.Protobuf var empty = Empty.Parser.ParseFrom(data); Assert.AreEqual(new Empty(), empty); } + + // This was originally seen as a conformance test failure. + [Test] + public void TruncatedMessageFieldThrows() + { + // 130, 3 is the message tag + // 1 is the data length - but there's no data. + var data = new byte[] { 130, 3, 1 }; + Assert.Throws(() => TestAllTypes.Parser.ParseFrom(data)); + } } } diff --git a/csharp/src/Google.Protobuf/CodedInputStream.cs b/csharp/src/Google.Protobuf/CodedInputStream.cs index fee31e3b..5da03b5c 100644 --- a/csharp/src/Google.Protobuf/CodedInputStream.cs +++ b/csharp/src/Google.Protobuf/CodedInputStream.cs @@ -54,13 +54,37 @@ namespace Google.Protobuf /// public sealed class CodedInputStream { + /// + /// Buffer of data read from the stream or provided at construction time. + /// private readonly byte[] buffer; + + /// + /// The number of valid bytes in the buffer. + /// private int bufferSize; + private int bufferSizeAfterLimit = 0; + /// + /// The position within the current buffer (i.e. the next byte to read) + /// private int bufferPos = 0; + + /// + /// The stream to read further input from, or null if the byte array buffer was provided + /// directly on construction, with no further data available. + /// private readonly Stream input; + + /// + /// The last tag we read. 0 indicates we've read to the end of the stream + /// (or haven't read anything yet). + /// private uint lastTag = 0; + /// + /// The next tag, used to store the value read by PeekTag. + /// private uint nextTag = 0; private bool hasNextTag = false; @@ -456,6 +480,11 @@ namespace Google.Protobuf ++recursionDepth; builder.MergeFrom(this); CheckLastTagWas(0); + // Check that we've read exactly as much data as expected. + if (!ReachedLimit) + { + throw InvalidProtocolBufferException.TruncatedMessage(); + } --recursionDepth; PopLimit(oldLimit); } -- cgit v1.2.3