diff options
author | jieluo@google.com <jieluo@google.com@630680e5-0e50-0410-840e-4b1c322b438d> | 2014-07-18 00:47:59 +0000 |
---|---|---|
committer | jieluo@google.com <jieluo@google.com@630680e5-0e50-0410-840e-4b1c322b438d> | 2014-07-18 00:47:59 +0000 |
commit | 4de8f55113007fdc8e34107950e605fc0209d465 (patch) | |
tree | 92b7da8757a7740d9e1f2d3ead233542947d8c8c /src/google/protobuf/compiler | |
parent | c5553a3d18f80132b9079c5504bc0aa1f7f950a0 (diff) | |
download | protobuf-4de8f55113007fdc8e34107950e605fc0209d465.tar.gz protobuf-4de8f55113007fdc8e34107950e605fc0209d465.tar.bz2 protobuf-4de8f55113007fdc8e34107950e605fc0209d465.zip |
down integrate to svn
Diffstat (limited to 'src/google/protobuf/compiler')
78 files changed, 9061 insertions, 1998 deletions
diff --git a/src/google/protobuf/compiler/code_generator.cc b/src/google/protobuf/compiler/code_generator.cc index 455c239a..bc4800a6 100644 --- a/src/google/protobuf/compiler/code_generator.cc +++ b/src/google/protobuf/compiler/code_generator.cc @@ -44,6 +44,11 @@ namespace compiler { CodeGenerator::~CodeGenerator() {} GeneratorContext::~GeneratorContext() {} +io::ZeroCopyOutputStream* +GeneratorContext::OpenForAppend(const string& filename) { + return NULL; +} + io::ZeroCopyOutputStream* GeneratorContext::OpenForInsert( const string& filename, const string& insertion_point) { GOOGLE_LOG(FATAL) << "This GeneratorContext does not support insertion."; @@ -58,8 +63,7 @@ void GeneratorContext::ListParsedFiles( // Parses a set of comma-delimited name/value pairs. void ParseGeneratorParameter(const string& text, vector<pair<string, string> >* output) { - vector<string> parts; - SplitStringUsing(text, ",", &parts); + vector<string> parts = Split(text, ",", true); for (int i = 0; i < parts.size(); i++) { string::size_type equals_pos = parts[i].find_first_of('='); diff --git a/src/google/protobuf/compiler/code_generator.h b/src/google/protobuf/compiler/code_generator.h index 252f68d1..541f784c 100644 --- a/src/google/protobuf/compiler/code_generator.h +++ b/src/google/protobuf/compiler/code_generator.h @@ -104,6 +104,9 @@ class LIBPROTOC_EXPORT GeneratorContext { // contain "." or ".." components. virtual io::ZeroCopyOutputStream* Open(const string& filename) = 0; + // Similar to Open() but the output will be appended to the file if exists + virtual io::ZeroCopyOutputStream* OpenForAppend(const string& filename); + // Creates a ZeroCopyOutputStream which will insert code into the given file // at the given insertion point. See plugin.proto (plugin.pb.h) for more // information on insertion points. The default implementation diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index b9293c97..358d6462 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -49,8 +49,10 @@ #include <ctype.h> #include <google/protobuf/stubs/hash.h> +#include <memory> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/stringprintf.h> #include <google/protobuf/compiler/importer.h> #include <google/protobuf/compiler/code_generator.h> #include <google/protobuf/compiler/plugin.pb.h> @@ -64,7 +66,7 @@ #include <google/protobuf/io/printer.h> #include <google/protobuf/stubs/strutil.h> #include <google/protobuf/stubs/substitute.h> -#include <google/protobuf/stubs/map-util.h> +#include <google/protobuf/stubs/map_util.h> #include <google/protobuf/stubs/stl_util.h> @@ -160,8 +162,7 @@ bool VerifyDirectoryExists(const string& path) { // directories listed in |filename|. bool TryCreateParentDirectory(const string& prefix, const string& filename) { // Recursively create parent directories to the output file. - vector<string> parts; - SplitStringUsing(filename, "/", &parts); + vector<string> parts = Split(filename, "/", true); string path_so_far = prefix; for (int i = 0; i < parts.size() - 1; i++) { path_so_far += parts[i]; @@ -252,6 +253,7 @@ class CommandLineInterface::GeneratorContextImpl : public GeneratorContext { // implements GeneratorContext -------------------------------------- io::ZeroCopyOutputStream* Open(const string& filename); + io::ZeroCopyOutputStream* OpenForAppend(const string& filename); io::ZeroCopyOutputStream* OpenForInsert( const string& filename, const string& insertion_point); void ListParsedFiles(vector<const FileDescriptor*>* output) { @@ -271,7 +273,8 @@ class CommandLineInterface::GeneratorContextImpl : public GeneratorContext { class CommandLineInterface::MemoryOutputStream : public io::ZeroCopyOutputStream { public: - MemoryOutputStream(GeneratorContextImpl* directory, const string& filename); + MemoryOutputStream(GeneratorContextImpl* directory, const string& filename, + bool append_mode); MemoryOutputStream(GeneratorContextImpl* directory, const string& filename, const string& insertion_point); virtual ~MemoryOutputStream(); @@ -290,6 +293,9 @@ class CommandLineInterface::MemoryOutputStream // The string we're building. string data_; + // Whether we should append the output stream to the existing file. + bool append_mode_; + // StringOutputStream writing to data_. scoped_ptr<io::StringOutputStream> inner_; }; @@ -434,7 +440,13 @@ void CommandLineInterface::GeneratorContextImpl::AddJarManifest() { io::ZeroCopyOutputStream* CommandLineInterface::GeneratorContextImpl::Open( const string& filename) { - return new MemoryOutputStream(this, filename); + return new MemoryOutputStream(this, filename, false); +} + +io::ZeroCopyOutputStream* +CommandLineInterface::GeneratorContextImpl::OpenForAppend( + const string& filename) { + return new MemoryOutputStream(this, filename, true); } io::ZeroCopyOutputStream* @@ -446,9 +458,10 @@ CommandLineInterface::GeneratorContextImpl::OpenForInsert( // ------------------------------------------------------------------- CommandLineInterface::MemoryOutputStream::MemoryOutputStream( - GeneratorContextImpl* directory, const string& filename) + GeneratorContextImpl* directory, const string& filename, bool append_mode) : directory_(directory), filename_(filename), + append_mode_(append_mode), inner_(new io::StringOutputStream(&data_)) { } @@ -471,8 +484,12 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() { if (insertion_point_.empty()) { // This was just a regular Open(). if (*map_slot != NULL) { - cerr << filename_ << ": Tried to write the same file twice." << endl; - directory_->had_error_ = true; + if (append_mode_) { + (*map_slot)->append(data_); + } else { + cerr << filename_ << ": Tried to write the same file twice." << endl; + directory_->had_error_ = true; + } return; } @@ -565,6 +582,7 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() { CommandLineInterface::CommandLineInterface() : mode_(MODE_COMPILE), + print_mode_(PRINT_NONE), error_format_(ERROR_FORMAT_GCC), imports_in_descriptor_set_(false), source_info_in_descriptor_set_(false), @@ -632,7 +650,9 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) { // Parse each file. for (int i = 0; i < input_files_.size(); i++) { // Import the file. + importer.AddUnusedImportTrackFile(input_files_[i]); const FileDescriptor* parsed_file = importer.Import(input_files_[i]); + importer.ClearUnusedImportTrackFiles(); if (parsed_file == NULL) return 1; parsed_files.push_back(parsed_file); @@ -721,6 +741,25 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) { } } + if (mode_ == MODE_PRINT) { + switch (print_mode_) { + case PRINT_FREE_FIELDS: + for (int i = 0; i < parsed_files.size(); ++i) { + const FileDescriptor* fd = parsed_files[i]; + for (int j = 0; j < fd->message_type_count(); ++j) { + PrintFreeFieldNumbers(fd->message_type(j)); + } + } + break; + case PRINT_NONE: + GOOGLE_LOG(ERROR) << "If the code reaches here, it usually means a bug of " + "flag parsing in the CommonadLineInterface."; + return 1; + + // Do not add a default case. + } + } + return 0; } @@ -735,6 +774,7 @@ void CommandLineInterface::Clear() { descriptor_set_name_.clear(); mode_ = MODE_COMPILE; + print_mode_ = PRINT_NONE; imports_in_descriptor_set_ = false; source_info_in_descriptor_set_ = false; disallow_services_ = false; @@ -889,7 +929,8 @@ bool CommandLineInterface::ParseArgument(const char* arg, *name == "--include_imports" || *name == "--include_source_info" || *name == "--version" || - *name == "--decode_raw") { + *name == "--decode_raw" || + *name == "--print_free_field_numbers") { // HACK: These are the only flags that don't take a value. // They probably should not be hard-coded like this but for now it's // not worth doing better. @@ -919,8 +960,8 @@ CommandLineInterface::InterpretArgument(const string& name, // Java's -classpath (and some other languages) delimits path components // with colons. Let's accept that syntax too just to make things more // intuitive. - vector<string> parts; - SplitStringUsing(value, kPathSeparator, &parts); + vector<string> parts = Split( + value, kPathSeparator, true); for (int i = 0; i < parts.size(); i++) { string virtual_path; @@ -1061,6 +1102,19 @@ CommandLineInterface::InterpretArgument(const string& name, plugins_[plugin_name] = path; + } else if (name == "--print_free_field_numbers") { + if (mode_ != MODE_COMPILE) { + cerr << "Cannot use " << name << " and use --encode, --decode or print " + << "other info at the same time." << endl; + return PARSE_ARGUMENT_FAIL; + } + if (!output_directives_.empty() || !descriptor_set_name_.empty()) { + cerr << "Cannot use " << name + << " and generate code or descriptors at the same time." << endl; + return PARSE_ARGUMENT_FAIL; + } + mode_ = MODE_PRINT; + print_mode_ = PRINT_FREE_FIELDS; } else { // Some other flag. Look it up in the generators list. const GeneratorInfo* generator_info = @@ -1082,8 +1136,8 @@ CommandLineInterface::InterpretArgument(const string& name, } else { // It's an output flag. Add it to the output directives. if (mode_ != MODE_COMPILE) { - cerr << "Cannot use --encode or --decode and generate code at the " - "same time." << endl; + cerr << "Cannot use --encode, --decode or print .proto info and " + "generate code at the same time." << endl; return PARSE_ARGUMENT_FAIL; } @@ -1151,7 +1205,12 @@ void CommandLineInterface::PrintHelpText() { " well as surrounding comments.\n" " --error_format=FORMAT Set the format in which to print errors.\n" " FORMAT may be 'gcc' (the default) or 'msvs'\n" -" (Microsoft Visual Studio format)." << endl; +" (Microsoft Visual Studio format).\n" +" --print_free_field_numbers Print the free field numbers of the messages\n" +" 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." << endl; if (!plugin_prefix_.empty()) { cerr << " --plugin=EXECUTABLE Specifies a plugin executable to use.\n" @@ -1431,6 +1490,113 @@ void CommandLineInterface::GetTransitiveDependencies( } } +namespace { + +// Utility function for PrintFreeFieldNumbers. +// Stores occupied ranges into the ranges parameter, and next level of sub +// message types into the nested_messages parameter. The FieldRange is left +// inclusive, right exclusive. i.e. [a, b). +// +// Nested Messages: +// Note that it only stores the nested message type, iff the nested type is +// either a direct child of the given descriptor, or the nested type is a +// decendent of the given descriptor and all the nodes between the +// nested type and the given descriptor are group types. e.g. +// +// message Foo { +// message Bar { +// message NestedBar {} +// } +// group Baz = 1 { +// group NestedBazGroup = 2 { +// message Quz { +// message NestedQuz {} +// } +// } +// message NestedBaz {} +// } +// } +// +// In this case, Bar, Quz and NestedBaz will be added into the nested types. +// Since free field numbers of group types will not be printed, this makes sure +// the nested message types in groups will not be dropped. The nested_messages +// parameter will contain the direct children (when groups are ignored in the +// tree) of the given descriptor for the caller to traverse. The declaration +// order of the nested messages is also preserved. +typedef pair<int, int> FieldRange; +void GatherOccupiedFieldRanges(const Descriptor* descriptor, + set<FieldRange>* ranges, + vector<const Descriptor*>* nested_messages) { + set<const Descriptor*> groups; + for (int i = 0; i < descriptor->field_count(); ++i) { + const FieldDescriptor* fd = descriptor->field(i); + ranges->insert(FieldRange(fd->number(), fd->number() + 1)); + if (fd->type() == FieldDescriptor::TYPE_GROUP) { + groups.insert(fd->message_type()); + } + } + for (int i = 0; i < descriptor->extension_range_count(); ++i) { + ranges->insert(FieldRange(descriptor->extension_range(i)->start, + descriptor->extension_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) { + const Descriptor* nested_desc = descriptor->nested_type(i); + if (groups.find(nested_desc) != groups.end()) { + GatherOccupiedFieldRanges(nested_desc, ranges, nested_messages); + } else { + nested_messages->push_back(nested_desc); + } + } +} + +// Utility function for PrintFreeFieldNumbers. +// Actually prints the formatted free field numbers for given message name and +// occupied ranges. +void FormatFreeFieldNumbers(const string& name, + const set<FieldRange>& ranges) { + string output; + StringAppendF(&output, "%-35s free:", name.c_str()); + int next_free_number = 1; + for (set<FieldRange>::iterator i = ranges.begin(); + i != ranges.end(); ++i) { + // This happens when groups re-use parent field numbers, in which + // case we skip the FieldRange entirely. + if (next_free_number >= i->second) continue; + + if (next_free_number < i->first) { + if (next_free_number + 1 == i->first) { + // Singleton + StringAppendF(&output, " %d", next_free_number); + } else { + // Range + StringAppendF(&output, " %d-%d", next_free_number, i->first - 1); + } + } + next_free_number = i->second; + } + if (next_free_number <= FieldDescriptor::kMaxNumber) { + StringAppendF(&output, " %d-INF", next_free_number); + } + cout << output << endl; +} + +} // namespace + +void CommandLineInterface::PrintFreeFieldNumbers( + const Descriptor* descriptor) { + set<FieldRange> ranges; + vector<const Descriptor*> nested_messages; + GatherOccupiedFieldRanges(descriptor, &ranges, &nested_messages); + + for (int i = 0; i < nested_messages.size(); ++i) { + PrintFreeFieldNumbers(nested_messages[i]); + } + FormatFreeFieldNumbers(descriptor->full_name(), ranges); +} + + } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/command_line_interface.h b/src/google/protobuf/compiler/command_line_interface.h index 86ea9bde..3b734150 100644 --- a/src/google/protobuf/compiler/command_line_interface.h +++ b/src/google/protobuf/compiler/command_line_interface.h @@ -48,8 +48,9 @@ namespace google { namespace protobuf { -class FileDescriptor; // descriptor.h +class Descriptor; // descriptor.h class DescriptorPool; // descriptor.h +class FileDescriptor; // descriptor.h class FileDescriptorProto; // descriptor.pb.h template<typename T> class RepeatedPtrField; // repeated_field.h @@ -259,6 +260,22 @@ class LIBPROTOC_EXPORT CommandLineInterface { set<const FileDescriptor*>* already_seen, RepeatedPtrField<FileDescriptorProto>* output); + // Implements the --print_free_field_numbers. This function prints free field + // numbers into stdout for the message and it's nested message types in + // post-order, i.e. nested types first. Printed range are left-right + // inclusive, i.e. [a, b]. + // + // Groups: + // For historical reasons, groups are considered to share the same + // field number space with the parent message, thus it will not print free + // field numbers for groups. The field numbers used in the groups are + // excluded in the free field numbers of the parent message. + // + // Extension Ranges: + // Extension ranges are considered ocuppied field numbers and they will not be + // listed as free numbers in the output. + void PrintFreeFieldNumbers(const Descriptor* descriptor); + // ----------------------------------------------------------------- // The name of the executable as invoked (i.e. argv[0]). @@ -295,11 +312,19 @@ class LIBPROTOC_EXPORT CommandLineInterface { enum Mode { MODE_COMPILE, // Normal mode: parse .proto files and compile them. MODE_ENCODE, // --encode: read text from stdin, write binary to stdout. - MODE_DECODE // --decode: read binary from stdin, write text to stdout. + MODE_DECODE, // --decode: read binary from stdin, write text to stdout. + MODE_PRINT, // Print mode: print info of the given .proto files and exit. }; Mode mode_; + enum PrintMode { + PRINT_NONE, // Not in MODE_PRINT + PRINT_FREE_FIELDS, // --print_free_fields + }; + + PrintMode print_mode_; + enum ErrorFormat { ERROR_FORMAT_GCC, // GCC error output format (default). ERROR_FORMAT_MSVS // Visual Studio output (--error_format=msvs). diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index 16559923..9e244da3 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -40,6 +40,7 @@ #else #include <unistd.h> #endif +#include <memory> #include <vector> #include <google/protobuf/descriptor.pb.h> @@ -126,6 +127,9 @@ class CommandLineInterfaceTest : public testing::Test { void ExpectErrorSubstringWithZeroReturnCode( const string& expected_substring); + // Checks that the captured stdout is the same as the expected_text. + void ExpectCapturedStdout(const string& expected_text); + // Returns true if ExpectErrorSubstring(expected_substring) would pass, but // does not fail otherwise. bool HasAlternateErrorSubstring(const string& expected_substring); @@ -182,6 +186,9 @@ class CommandLineInterfaceTest : public testing::Test { // The captured stderr output. string error_text_; + // The captured stdout. + string captured_stdout_; + // Pointers which need to be deleted later. vector<CodeGenerator*> mock_generators_to_delete_; @@ -224,7 +231,7 @@ void CommandLineInterfaceTest::SetUp() { } // Create the temp directory. - GOOGLE_CHECK(File::CreateDir(temp_directory_.c_str(), DEFAULT_FILE_MODE)); + GOOGLE_CHECK_OK(File::CreateDir(temp_directory_, 0777)); // Register generators. CodeGenerator* generator = new MockCodeGenerator("test_generator"); @@ -255,8 +262,7 @@ void CommandLineInterfaceTest::TearDown() { } void CommandLineInterfaceTest::Run(const string& command) { - vector<string> args; - SplitStringUsing(command, " ", &args); + vector<string> args = Split(command, " ", true); if (!disallow_plugins_) { cli_.AllowPlugins("prefix-"); @@ -295,18 +301,20 @@ void CommandLineInterfaceTest::Run(const string& command) { } } - scoped_array<const char*> argv(new const char*[args.size()]); + scoped_array<const char*> argv(new const char* [args.size()]); for (int i = 0; i < args.size(); i++) { args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true); argv[i] = args[i].c_str(); } + CaptureTestStdout(); CaptureTestStderr(); return_code_ = cli_.Run(args.size(), argv.get()); error_text_ = GetCapturedTestStderr(); + captured_stdout_ = GetCapturedTestStdout(); } // ------------------------------------------------------------------- @@ -318,16 +326,20 @@ void CommandLineInterfaceTest::CreateTempFile( string::size_type slash_pos = name.find_last_of('/'); if (slash_pos != string::npos) { string dir = name.substr(0, slash_pos); - File::RecursivelyCreateDir(temp_directory_ + "/" + dir, 0777); + if (!File::Exists(temp_directory_ + "/" + dir)) { + GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + dir, + 0777)); + } } // Write file. string full_name = temp_directory_ + "/" + name; - File::WriteStringToFileOrDie(contents, full_name); + GOOGLE_CHECK_OK(File::SetContents(full_name, contents, true)); } void CommandLineInterfaceTest::CreateTempDir(const string& name) { - File::RecursivelyCreateDir(temp_directory_ + "/" + name, 0777); + GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + name, + 0777)); } // ------------------------------------------------------------------- @@ -414,14 +426,18 @@ void CommandLineInterfaceTest::ReadDescriptorSet( const string& filename, FileDescriptorSet* descriptor_set) { string path = temp_directory_ + "/" + filename; string file_contents; - if (!File::ReadFileToString(path, &file_contents)) { - FAIL() << "File not found: " << path; - } + GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true)); + if (!descriptor_set->ParseFromString(file_contents)) { FAIL() << "Could not parse file contents: " << path; } } +void CommandLineInterfaceTest::ExpectCapturedStdout( + const string& expected_text) { + EXPECT_EQ(expected_text, captured_stdout_); +} + // =================================================================== TEST_F(CommandLineInterfaceTest, BasicOutput) { @@ -813,7 +829,7 @@ TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) { FileDescriptorSet descriptor_set; ReadDescriptorSet("descriptor_set", &descriptor_set); if (HasFatalFailure()) return; - ASSERT_EQ(1, descriptor_set.file_size()); + EXPECT_EQ(1, descriptor_set.file_size()); EXPECT_EQ("bar.proto", descriptor_set.file(0).name()); // Descriptor set should not have source code info. EXPECT_FALSE(descriptor_set.file(0).has_source_code_info()); @@ -838,7 +854,7 @@ TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) { FileDescriptorSet descriptor_set; ReadDescriptorSet("descriptor_set", &descriptor_set); if (HasFatalFailure()) return; - ASSERT_EQ(1, descriptor_set.file_size()); + EXPECT_EQ(1, descriptor_set.file_size()); EXPECT_EQ("bar.proto", descriptor_set.file(0).name()); // Source code info included. EXPECT_TRUE(descriptor_set.file(0).has_source_code_info()); @@ -863,7 +879,7 @@ TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) { FileDescriptorSet descriptor_set; ReadDescriptorSet("descriptor_set", &descriptor_set); if (HasFatalFailure()) return; - ASSERT_EQ(2, descriptor_set.file_size()); + EXPECT_EQ(2, descriptor_set.file_size()); if (descriptor_set.file(0).name() == "bar.proto") { std::swap(descriptor_set.mutable_file()->mutable_data()[0], descriptor_set.mutable_file()->mutable_data()[1]); @@ -894,7 +910,7 @@ TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) { FileDescriptorSet descriptor_set; ReadDescriptorSet("descriptor_set", &descriptor_set); if (HasFatalFailure()) return; - ASSERT_EQ(2, descriptor_set.file_size()); + EXPECT_EQ(2, descriptor_set.file_size()); if (descriptor_set.file(0).name() == "bar.proto") { std::swap(descriptor_set.mutable_file()->mutable_data()[0], descriptor_set.mutable_file()->mutable_data()[1]); @@ -1393,6 +1409,70 @@ TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) { ExpectErrorText("Missing value for flag: --test_out\n"); } +TEST_F(CommandLineInterfaceTest, PrintFreeFieldNumbers) { + CreateTempFile( + "foo.proto", + "syntax = \"proto2\";\n" + "package foo;\n" + "message Foo {\n" + " optional int32 a = 2;\n" + " optional string b = 4;\n" + " optional string c = 5;\n" + " optional int64 d = 8;\n" + " optional double e = 10;\n" + "}\n"); + CreateTempFile( + "bar.proto", + "syntax = \"proto2\";\n" + "message Bar {\n" + " optional int32 a = 2;\n" + " extensions 4 to 5;\n" + " optional int64 d = 8;\n" + " extensions 10;\n" + "}\n"); + CreateTempFile( + "baz.proto", + "syntax = \"proto2\";\n" + "message Baz {\n" + " optional int32 a = 2;\n" + " optional int64 d = 8;\n" + " extensions 15 to max;\n" // unordered. + " extensions 13;\n" + " extensions 10 to 12;\n" + " extensions 5;\n" + " extensions 4;\n" + "}\n"); + CreateTempFile( + "quz.proto", + "syntax = \"proto2\";\n" + "message Quz {\n" + " message Foo {}\n" // nested message + " optional int32 a = 2;\n" + " optional group C = 4 {\n" + " optional int32 d = 5;\n" + " }\n" + " extensions 8 to 10;\n" + " optional group E = 11 {\n" + " optional int32 f = 9;\n" // explicitly reuse extension range 8-10 + " optional group G = 15 {\n" // nested group + " message Foo {}\n" // nested message inside nested group + " }\n" + " }\n" + "}\n"); + + Run("protocol_compiler --print_free_field_numbers --proto_path=$tmpdir " + "foo.proto bar.proto baz.proto quz.proto"); + + ExpectNoErrors(); + ExpectCapturedStdout( + "foo.Foo free: 1 3 6-7 9 11-INF\n" + "Bar free: 1 3 6-7 9 11-INF\n" + "Baz free: 1 3 6-7 9 14\n" + "Quz.Foo free: 1-INF\n" + "Quz.E.G.Foo free: 1-INF\n" + "Quz free: 1 3 6-7 12-14 16-INF\n"); +} + // =================================================================== // Test for --encode and --decode. Note that it would be easier to do this @@ -1412,7 +1492,7 @@ class EncodeDecodeTest : public testing::Test { void RedirectStdinFromText(const string& input) { string filename = TestTempDir() + "/test_stdin"; - File::WriteStringToFileOrDie(input, filename); + GOOGLE_CHECK_OK(File::SetContents(filename, input, true)); GOOGLE_CHECK(RedirectStdinFromFile(filename)); } @@ -1446,7 +1526,7 @@ class EncodeDecodeTest : public testing::Test { SplitStringUsing(command, " ", &args); args.push_back("--proto_path=" + TestSourceDir()); - scoped_array<const char*> argv(new const char*[args.size()]); + scoped_array<const char*> argv(new const char* [args.size()]); for (int i = 0; i < args.size(); i++) { argv[i] = args[i].c_str(); } @@ -1467,7 +1547,7 @@ class EncodeDecodeTest : public testing::Test { void ExpectStdoutMatchesBinaryFile(const string& filename) { string expected_output; - ASSERT_TRUE(File::ReadFileToString(filename, &expected_output)); + GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true)); // Don't use EXPECT_EQ because we don't want to print raw binary data to // stdout on failure. @@ -1476,7 +1556,7 @@ class EncodeDecodeTest : public testing::Test { void ExpectStdoutMatchesTextFile(const string& filename) { string expected_output; - ASSERT_TRUE(File::ReadFileToString(filename, &expected_output)); + GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true)); ExpectStdoutMatchesText(expected_output); } @@ -1496,22 +1576,23 @@ class EncodeDecodeTest : public testing::Test { }; TEST_F(EncodeDecodeTest, Encode) { - RedirectStdinFromFile(TestSourceDir() + - "/google/protobuf/testdata/text_format_unittest_data.txt"); + RedirectStdinFromFile(TestSourceDir() + "/google/protobuf/" + "testdata/text_format_unittest_data_oneof_implemented.txt"); EXPECT_TRUE(Run("google/protobuf/unittest.proto " "--encode=protobuf_unittest.TestAllTypes")); ExpectStdoutMatchesBinaryFile(TestSourceDir() + - "/google/protobuf/testdata/golden_message"); + "/google/protobuf/testdata/golden_message_oneof_implemented"); ExpectStderrMatchesText(""); } TEST_F(EncodeDecodeTest, Decode) { RedirectStdinFromFile(TestSourceDir() + - "/google/protobuf/testdata/golden_message"); + "/google/protobuf/testdata/golden_message_oneof_implemented"); EXPECT_TRUE(Run("google/protobuf/unittest.proto " "--decode=protobuf_unittest.TestAllTypes")); ExpectStdoutMatchesTextFile(TestSourceDir() + - "/google/protobuf/testdata/text_format_unittest_data.txt"); + "/google/protobuf/" + "testdata/text_format_unittest_data_oneof_implemented.txt"); ExpectStderrMatchesText(""); } diff --git a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc index b7c1766b..48da534c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc @@ -48,7 +48,7 @@ #include <google/protobuf/compiler/importer.h> #include <google/protobuf/descriptor.h> #include <google/protobuf/io/zero_copy_stream_impl.h> -#include <google/protobuf/stubs/map-util.h> +#include <google/protobuf/stubs/map_util.h> #include <google/protobuf/stubs/stl_util.h> #include <google/protobuf/stubs/strutil.h> #include <google/protobuf/stubs/substitute.h> @@ -93,9 +93,9 @@ class MockGeneratorContext : public GeneratorContext { << "Generator failed to generate file: " << virtual_filename; string actual_contents; - File::ReadFileToStringOrDie( - TestSourceDir() + "/" + physical_filename, - &actual_contents); + GOOGLE_CHECK_OK( + File::GetContents(TestSourceDir() + "/" + physical_filename, + &actual_contents, true)); EXPECT_TRUE(actual_contents == *expected_contents) << physical_filename << " needs to be regenerated. Please run " "generate_descriptor_proto.sh and add this file " diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc index 67c12d7a..0c4796f6 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc @@ -45,11 +45,27 @@ namespace protobuf { namespace compiler { namespace cpp { +namespace { +// The GOOGLE_ARRAYSIZE constant is the max enum value plus 1. If the max enum value +// is kint32max, GOOGLE_ARRAYSIZE will overflow. In such cases we should omit the +// generation of the GOOGLE_ARRAYSIZE constant. +bool ShouldGenerateArraySize(const EnumDescriptor* descriptor) { + int32 max_value = descriptor->value(0)->number(); + for (int i = 0; i < descriptor->value_count(); i++) { + if (descriptor->value(i)->number() > max_value) { + max_value = descriptor->value(i)->number(); + } + } + return max_value != kint32max; +} +} // namespace + EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, const Options& options) : descriptor_(descriptor), classname_(ClassName(descriptor, false)), - options_(options) { + options_(options), + generate_array_size_(ShouldGenerateArraySize(descriptor)) { } EnumGenerator::~EnumGenerator() {} @@ -67,7 +83,10 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) { for (int i = 0; i < descriptor_->value_count(); i++) { vars["name"] = descriptor_->value(i)->name(); - vars["number"] = SimpleItoa(descriptor_->value(i)->number()); + // In C++, an value of -2147483648 gets interpreted as the negative of + // 2147483648, and since 2147483648 can't fit in an integer, this produces a + // compiler warning. This works around that issue. + vars["number"] = Int32ToString(descriptor_->value(i)->number()); vars["prefix"] = (descriptor_->containing_type() == NULL) ? "" : classname_ + "_"; @@ -97,9 +116,13 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) { printer->Print(vars, "$dllexport$bool $classname$_IsValid(int value);\n" "const $classname$ $prefix$$short_name$_MIN = $prefix$$min_name$;\n" - "const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n" - "const int $prefix$$short_name$_ARRAYSIZE = $prefix$$short_name$_MAX + 1;\n" - "\n"); + "const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n"); + + if (generate_array_size_) { + printer->Print(vars, + "const int $prefix$$short_name$_ARRAYSIZE = " + "$prefix$$short_name$_MAX + 1;\n\n"); + } if (HasDescriptorMethods(descriptor_->file())) { printer->Print(vars, @@ -123,6 +146,7 @@ void EnumGenerator:: GenerateGetEnumDescriptorSpecializations(io::Printer* printer) { if (HasDescriptorMethods(descriptor_->file())) { printer->Print( + "template <> struct is_proto_enum< $classname$> : ::google::protobuf::internal::true_type {};\n" "template <>\n" "inline const EnumDescriptor* GetEnumDescriptor< $classname$>() {\n" " return $classname$_descriptor();\n" @@ -150,9 +174,12 @@ void EnumGenerator::GenerateSymbolImports(io::Printer* printer) { "static const $nested_name$ $nested_name$_MIN =\n" " $classname$_$nested_name$_MIN;\n" "static const $nested_name$ $nested_name$_MAX =\n" - " $classname$_$nested_name$_MAX;\n" - "static const int $nested_name$_ARRAYSIZE =\n" - " $classname$_$nested_name$_ARRAYSIZE;\n"); + " $classname$_$nested_name$_MAX;\n"); + if (generate_array_size_) { + printer->Print(vars, + "static const int $nested_name$_ARRAYSIZE =\n" + " $classname$_$nested_name$_ARRAYSIZE;\n"); + } if (HasDescriptorMethods(descriptor_->file())) { printer->Print(vars, @@ -218,7 +245,7 @@ void EnumGenerator::GenerateMethods(io::Printer* printer) { iter != numbers.end(); ++iter) { printer->Print( " case $number$:\n", - "number", SimpleItoa(*iter)); + "number", Int32ToString(*iter)); } printer->Print(vars, @@ -245,8 +272,11 @@ void EnumGenerator::GenerateMethods(io::Printer* printer) { } printer->Print(vars, "const $classname$ $parent$::$nested_name$_MIN;\n" - "const $classname$ $parent$::$nested_name$_MAX;\n" - "const int $parent$::$nested_name$_ARRAYSIZE;\n"); + "const $classname$ $parent$::$nested_name$_MAX;\n"); + if (generate_array_size_) { + printer->Print(vars, + "const int $parent$::$nested_name$_ARRAYSIZE;\n"); + } printer->Print("#endif // _MSC_VER\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.h b/src/google/protobuf/compiler/cpp/cpp_enum.h index 2e85a0bd..98cbdf69 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.h +++ b/src/google/protobuf/compiler/cpp/cpp_enum.h @@ -89,6 +89,8 @@ class EnumGenerator { const EnumDescriptor* descriptor_; string classname_; Options options_; + // whether to generate the *_ARRAYSIZE constant. + bool generate_array_size_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator); }; diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc index 6e1620d4..2723c04a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -51,7 +51,8 @@ void SetEnumVariables(const FieldDescriptor* descriptor, SetCommonFieldVariables(descriptor, variables, options); const EnumValueDescriptor* default_value = descriptor->default_value_enum(); (*variables)["type"] = ClassName(descriptor->enum_type(), true); - (*variables)["default"] = SimpleItoa(default_value->number()); + (*variables)["default"] = Int32ToString(default_value->number()); + (*variables)["full_name"] = descriptor->full_name(); } } // namespace @@ -83,12 +84,14 @@ void EnumFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { printer->Print(variables_, "inline $type$ $classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" " return static_cast< $type$ >($name$_);\n" "}\n" "inline void $classname$::set_$name$($type$ value) {\n" " assert($type$_IsValid(value));\n" " set_has_$name$();\n" " $name$_ = value;\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" "}\n"); } @@ -121,10 +124,15 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { " input, &value)));\n" "if ($type$_IsValid(value)) {\n" " set_$name$(static_cast< $type$ >(value));\n"); - if (HasUnknownFields(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file())) { printer->Print(variables_, "} else {\n" " mutable_unknown_fields()->AddVarint($number$, value);\n"); + } else { + printer->Print( + "} else {\n" + " unknown_fields_stream.WriteVarint32(tag);\n" + " unknown_fields_stream.WriteVarint32(value);\n"); } printer->Print(variables_, "}\n"); @@ -153,6 +161,52 @@ GenerateByteSize(io::Printer* printer) const { // =================================================================== +EnumOneofFieldGenerator:: +EnumOneofFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) + : EnumFieldGenerator(descriptor, options) { + SetCommonOneofFieldVariables(descriptor, &variables_); +} + +EnumOneofFieldGenerator::~EnumOneofFieldGenerator() {} + +void EnumOneofFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline $type$ $classname$::$name$() const {\n" + " if (has_$name$()) {\n" + " return static_cast< $type$ >($oneof_prefix$$name$_);\n" + " }\n" + " return static_cast< $type$ >($default$);\n" + "}\n" + "inline void $classname$::set_$name$($type$ value) {\n" + " assert($type$_IsValid(value));\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " }\n" + " $oneof_prefix$$name$_ = value;\n" + "}\n"); +} + +void EnumOneofFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$oneof_prefix$$name$_ = $default$;\n"); +} + +void EnumOneofFieldGenerator:: +GenerateSwappingCode(io::Printer* printer) const { + // Don't print any swapping code. Swapping the union will swap this field. +} + +void EnumOneofFieldGenerator:: +GenerateConstructorCode(io::Printer* printer) const { + printer->Print(variables_, + " $classname$_default_oneof_instance_->$name$_ = $default$;\n"); +} + +// =================================================================== + RepeatedEnumFieldGenerator:: RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, const Options& options) @@ -166,7 +220,8 @@ void RepeatedEnumFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { printer->Print(variables_, "::google::protobuf::RepeatedField<int> $name$_;\n"); - if (descriptor_->options().packed() && HasGeneratedMethods(descriptor_->file())) { + if (descriptor_->options().packed() + && HasGeneratedMethods(descriptor_->file())) { printer->Print(variables_, "mutable int _$name$_cached_byte_size_;\n"); } @@ -187,23 +242,28 @@ void RepeatedEnumFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { printer->Print(variables_, "inline $type$ $classname$::$name$(int index) const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" " return static_cast< $type$ >($name$_.Get(index));\n" "}\n" "inline void $classname$::set_$name$(int index, $type$ value) {\n" " assert($type$_IsValid(value));\n" " $name$_.Set(index, value);\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" "}\n" "inline void $classname$::add_$name$($type$ value) {\n" " assert($type$_IsValid(value));\n" " $name$_.Add(value);\n" + " // @@protoc_insertion_point(field_add:$full_name$)\n" "}\n"); printer->Print(variables_, "inline const ::google::protobuf::RepeatedField<int>&\n" "$classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_list:$full_name$)\n" " return $name$_;\n" "}\n" "inline ::google::protobuf::RepeatedField<int>*\n" "$classname$::mutable_$name$() {\n" + " // @@protoc_insertion_point(field_mutable_list:$full_name$)\n" " return &$name$_;\n" "}\n"); } @@ -238,10 +298,15 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { " input, &value)));\n" "if ($type$_IsValid(value)) {\n" " add_$name$(static_cast< $type$ >(value));\n"); - if (HasUnknownFields(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file())) { printer->Print(variables_, "} else {\n" " mutable_unknown_fields()->AddVarint($number$, value);\n"); + } else { + printer->Print( + "} else {\n" + " unknown_fields_stream.WriteVarint32(tag);\n" + " unknown_fields_stream.WriteVarint32(value);\n"); } printer->Print("}\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.h b/src/google/protobuf/compiler/cpp/cpp_enum_field.h index 65770083..1da1623a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.h @@ -63,13 +63,30 @@ class EnumFieldGenerator : public FieldGenerator { void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; void GenerateByteSize(io::Printer* printer) const; - private: + protected: const FieldDescriptor* descriptor_; map<string, string> variables_; + private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator); }; +class EnumOneofFieldGenerator : public EnumFieldGenerator { + public: + explicit EnumOneofFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); + ~EnumOneofFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateSwappingCode(io::Printer* printer) const; + void GenerateConstructorCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumOneofFieldGenerator); +}; + class RepeatedEnumFieldGenerator : public FieldGenerator { public: explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, diff --git a/src/google/protobuf/compiler/cpp/cpp_field.cc b/src/google/protobuf/compiler/cpp/cpp_field.cc index 0786176b..beea8bac 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_field.cc @@ -33,6 +33,8 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include <google/protobuf/compiler/cpp/cpp_field.h> +#include <memory> + #include <google/protobuf/compiler/cpp/cpp_helpers.h> #include <google/protobuf/compiler/cpp/cpp_primitive_field.h> #include <google/protobuf/compiler/cpp/cpp_string_field.h> @@ -68,6 +70,12 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor, (*variables)["cppget"] = "Get"; } +void SetCommonOneofFieldVariables(const FieldDescriptor* descriptor, + map<string, string>* variables) { + (*variables)["oneof_prefix"] = descriptor->containing_oneof()->name() + "_."; + (*variables)["oneof_name"] = descriptor->containing_oneof()->name(); +} + FieldGenerator::~FieldGenerator() {} void FieldGenerator:: @@ -84,8 +92,9 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor, const Options& options) - : descriptor_(descriptor), - field_generators_(new scoped_ptr<FieldGenerator>[descriptor->field_count()]) { + : descriptor_(descriptor), + field_generators_( + new scoped_ptr<FieldGenerator>[descriptor->field_count()]) { // Construct all the FieldGenerators. for (int i = 0; i < descriptor->field_count(); i++) { field_generators_[i].reset(MakeGenerator(descriptor->field(i), options)); @@ -109,6 +118,21 @@ FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field, default: return new RepeatedPrimitiveFieldGenerator(field, options); } + } else if (field->containing_oneof()) { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_MESSAGE: + return new MessageOneofFieldGenerator(field, options); + case FieldDescriptor::CPPTYPE_STRING: + switch (field->options().ctype()) { + default: // StringOneofFieldGenerator handles unknown ctypes. + case FieldOptions::STRING: + return new StringOneofFieldGenerator(field, options); + } + case FieldDescriptor::CPPTYPE_ENUM: + return new EnumOneofFieldGenerator(field, options); + default: + return new PrimitiveOneofFieldGenerator(field, options); + } } else { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_MESSAGE: diff --git a/src/google/protobuf/compiler/cpp/cpp_field.h b/src/google/protobuf/compiler/cpp/cpp_field.h index f7d99b15..e4340672 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_field.h @@ -36,9 +36,9 @@ #define GOOGLE_PROTOBUF_COMPILER_CPP_FIELD_H__ #include <map> +#include <memory> #include <string> -#include <google/protobuf/stubs/common.h> #include <google/protobuf/descriptor.h> #include <google/protobuf/compiler/cpp/cpp_options.h> @@ -61,6 +61,9 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor, map<string, string>* variables, const Options& options); +void SetCommonOneofFieldVariables(const FieldDescriptor* descriptor, + map<string, string>* variables); + class FieldGenerator { public: FieldGenerator() {} @@ -71,6 +74,11 @@ class FieldGenerator { // class. virtual void GeneratePrivateMembers(io::Printer* printer) const = 0; + // Generate static default variable for this field. These are placed inside + // the message class. Most field types don't need this, so the default + // implementation is empty. + virtual void GenerateStaticMembers(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; diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index 3d869a37..f7f2cdda 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -33,6 +33,9 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include <google/protobuf/compiler/cpp/cpp_file.h> +#include <memory> +#include <set> + #include <google/protobuf/compiler/cpp/cpp_enum.h> #include <google/protobuf/compiler/cpp/cpp_service.h> #include <google/protobuf/compiler/cpp/cpp_extension.h> @@ -50,18 +53,17 @@ namespace cpp { // =================================================================== -FileGenerator::FileGenerator(const FileDescriptor* file, - const Options& options) - : file_(file), - message_generators_( - new scoped_ptr<MessageGenerator>[file->message_type_count()]), - enum_generators_( - new scoped_ptr<EnumGenerator>[file->enum_type_count()]), - service_generators_( - new scoped_ptr<ServiceGenerator>[file->service_count()]), - extension_generators_( - new scoped_ptr<ExtensionGenerator>[file->extension_count()]), - options_(options) { +FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options) + : file_(file), + message_generators_( + new scoped_ptr<MessageGenerator>[file->message_type_count()]), + enum_generators_( + new scoped_ptr<EnumGenerator>[file->enum_type_count()]), + service_generators_( + new scoped_ptr<ServiceGenerator>[file->service_count()]), + extension_generators_( + new scoped_ptr<ExtensionGenerator>[file->extension_count()]), + options_(options) { for (int i = 0; i < file->message_type_count(); i++) { message_generators_[i].reset( @@ -153,19 +155,28 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { "#include <google/protobuf/service.h>\n"); } - if (HasUnknownFields(file_) && file_->message_type_count() > 0) { + if (UseUnknownFieldSet(file_) && file_->message_type_count() > 0) { printer->Print( "#include <google/protobuf/unknown_field_set.h>\n"); } + set<string> 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\"\n", - "dependency", StripProto(file_->dependency(i)->name())); + "#include \"$dependency$.pb.h\"$iwyu$\n", + "dependency", StripProto(name), + "iwyu", (public_import) ? " // IWYU pragma: export" : ""); } - printer->Print( "// @@protoc_insertion_point(includes)\n"); @@ -248,6 +259,7 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { printer->Print(kThickSeparator); printer->Print("\n"); + // Generate class inline methods. for (int i = 0; i < file_->message_type_count(); i++) { if (i > 0) { @@ -317,6 +329,12 @@ void FileGenerator::GenerateSource(io::Printer* printer) { "filename", file_->name(), "basename", StripProto(file_->name())); + // Unknown fields implementation in lite mode uses StringOutputStream + if (!UseUnknownFieldSet(file_) && file_->message_type_count() > 0) { + printer->Print( + "#include <google/protobuf/io/zero_copy_stream_impl_lite.h>\n"); + } + if (HasDescriptorMethods(file_)) { printer->Print( "#include <google/protobuf/descriptor.h>\n" @@ -542,17 +560,12 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { for (int i = 0; i < file_->dependency_count(); i++) { const FileDescriptor* dependency = file_->dependency(i); // Print the namespace prefix for the dependency. - vector<string> dependency_package_parts; - SplitStringUsing(dependency->package(), ".", &dependency_package_parts); - printer->Print("::"); - for (int j = 0; j < dependency_package_parts.size(); j++) { - printer->Print("$name$::", - "name", dependency_package_parts[j]); - } + string add_desc_name = QualifiedFileLevelSymbol( + dependency->package(), GlobalAddDescriptorsName(dependency->name())); // Call its AddDescriptors function. printer->Print( "$name$();\n", - "name", GlobalAddDescriptorsName(dependency->name())); + "name", add_desc_name); } if (HasDescriptorMethods(file_)) { diff --git a/src/google/protobuf/compiler/cpp/cpp_file.h b/src/google/protobuf/compiler/cpp/cpp_file.h index 2deefaa8..07970971 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.h +++ b/src/google/protobuf/compiler/cpp/cpp_file.h @@ -35,6 +35,7 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__ #define GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__ +#include <memory> #include <string> #include <vector> #include <google/protobuf/stubs/common.h> @@ -85,7 +86,6 @@ class FileGenerator { // E.g. if the package is foo.bar, package_parts_ is {"foo", "bar"}. vector<string> package_parts_; - const Options options_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator); diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.cc b/src/google/protobuf/compiler/cpp/cpp_generator.cc index 1813510b..a43e993b 100644 --- a/src/google/protobuf/compiler/cpp/cpp_generator.cc +++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc @@ -35,6 +35,7 @@ #include <google/protobuf/compiler/cpp/cpp_generator.h> #include <vector> +#include <memory> #include <utility> #include <google/protobuf/compiler/cpp/cpp_file.h> @@ -102,7 +103,7 @@ bool CppGenerator::Generate(const FileDescriptor* file, // Generate header. { scoped_ptr<io::ZeroCopyOutputStream> output( - generator_context->Open(basename + ".h")); + generator_context->Open(basename + ".h")); io::Printer printer(output.get(), '$'); file_generator.GenerateHeader(&printer); } @@ -110,7 +111,7 @@ bool CppGenerator::Generate(const FileDescriptor* file, // Generate cc file. { scoped_ptr<io::ZeroCopyOutputStream> output( - generator_context->Open(basename + ".cc")); + generator_context->Open(basename + ".cc")); io::Printer printer(output.get(), '$'); file_generator.GenerateSource(&printer); } diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc index 28911ab0..2c94b3a9 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc @@ -82,6 +82,22 @@ hash_set<string> MakeKeywordsMap() { hash_set<string> kKeywords = MakeKeywordsMap(); +// Returns whether the provided descriptor has an extension. This includes its +// nested types. +bool HasExtension(const Descriptor* descriptor) { + if (descriptor->extension_count() > 0) { + return true; + } + for (int i = 0; i < descriptor->nested_type_count(); ++i) { + if (HasExtension(descriptor->nested_type(i))) { + return true; + } + } + return false; +} + +} // namespace + string UnderscoresToCamelCase(const string& input, bool cap_next_letter) { string result; // Note: I distrust ctype.h due to locales. @@ -107,22 +123,6 @@ string UnderscoresToCamelCase(const string& input, bool cap_next_letter) { return result; } -// Returns whether the provided descriptor has an extension. This includes its -// nested types. -bool HasExtension(const Descriptor* descriptor) { - if (descriptor->extension_count() > 0) { - return true; - } - for (int i = 0; i < descriptor->nested_type_count(); ++i) { - if (HasExtension(descriptor->nested_type(i))) { - return true; - } - } - return false; -} - -} // namespace - const char kThickSeparator[] = "// ===================================================================\n"; const char kThinSeparator[] = @@ -256,27 +256,35 @@ const char* DeclaredTypeMethodName(FieldDescriptor::Type type) { return ""; } +string Int32ToString(int number) { + // gcc rejects the decimal form of kint32min. + if (number == kint32min) { + GOOGLE_COMPILE_ASSERT(kint32min == (~0x7fffffff), kint32min_value_error); + return "(~0x7fffffff)"; + } else { + return SimpleItoa(number); + } +} + +string Int64ToString(int64 number) { + // gcc rejects the decimal form of kint64min + if (number == kint64min) { + // Make sure we are in a 2's complement system. + GOOGLE_COMPILE_ASSERT(kint64min == GOOGLE_LONGLONG(-0x8000000000000000), + kint64min_value_error); + return "GOOGLE_LONGLONG(-0x8000000000000000)"; + } + return "GOOGLE_LONGLONG(" + SimpleItoa(number) + ")"; +} + string DefaultValue(const FieldDescriptor* field) { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_INT32: - // gcc rejects the decimal form of kint32min and kint64min. - if (field->default_value_int32() == kint32min) { - // Make sure we are in a 2's complement system. - GOOGLE_COMPILE_ASSERT(kint32min == -0x80000000, kint32min_value_error); - return "-0x80000000"; - } - return SimpleItoa(field->default_value_int32()); + return Int32ToString(field->default_value_int32()); case FieldDescriptor::CPPTYPE_UINT32: return SimpleItoa(field->default_value_uint32()) + "u"; case FieldDescriptor::CPPTYPE_INT64: - // See the comments for CPPTYPE_INT32. - if (field->default_value_int64() == kint64min) { - // Make sure we are in a 2's complement system. - GOOGLE_COMPILE_ASSERT(kint64min == GOOGLE_LONGLONG(-0x8000000000000000), - kint64min_value_error); - return "GOOGLE_LONGLONG(-0x8000000000000000)"; - } - return "GOOGLE_LONGLONG(" + SimpleItoa(field->default_value_int64()) + ")"; + return Int64ToString(field->default_value_int64()); case FieldDescriptor::CPPTYPE_UINT64: return "GOOGLE_ULONGLONG(" + SimpleItoa(field->default_value_uint64())+ ")"; case FieldDescriptor::CPPTYPE_DOUBLE: { @@ -319,7 +327,7 @@ string DefaultValue(const FieldDescriptor* field) { return strings::Substitute( "static_cast< $0 >($1)", ClassName(field->enum_type(), true), - field->default_value_enum()->number()); + Int32ToString(field->default_value_enum()->number())); case FieldDescriptor::CPPTYPE_STRING: return "\"" + EscapeTrigraphs( CEscape(field->default_value_string())) + @@ -366,11 +374,39 @@ string GlobalShutdownFileName(const string& filename) { return "protobuf_ShutdownFile_" + FilenameIdentifier(filename); } +// Return the qualified C++ name for a file level symbol. +string QualifiedFileLevelSymbol(const string& package, const string& name) { + if (package.empty()) { + return StrCat("::", name); + } + return StrCat("::", DotsToColons(package), "::", name); +} + // Escape C++ trigraphs by escaping question marks to \? string EscapeTrigraphs(const string& to_escape) { return StringReplace(to_escape, "?", "\\?", true); } +// Escaped function name to eliminate naming conflict. +string SafeFunctionName(const Descriptor* descriptor, + const FieldDescriptor* field, + const string& prefix) { + // Do not use FieldName() since it will escape keywords. + string name = field->name(); + LowerString(&name); + string function_name = prefix + name; + if (descriptor->FindFieldByName(function_name)) { + // Single underscore will also make it conflicting with the private data + // member. We use double underscore to escape function names. + function_name.append("__"); + } else if (kKeywords.count(name) > 0) { + // If the field name is a keyword, we append the underscore back to keep it + // consistent with other function names. + function_name.append("_"); + } + return function_name; +} + bool StaticInitializersForced(const FileDescriptor* file) { if (HasDescriptorMethods(file) || file->extension_count() > 0) { return true; @@ -432,6 +468,26 @@ bool HasEnumDefinitions(const FileDescriptor* file) { return false; } +bool IsStringOrMessage(const FieldDescriptor* field) { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + case FieldDescriptor::CPPTYPE_INT64: + case FieldDescriptor::CPPTYPE_UINT32: + case FieldDescriptor::CPPTYPE_UINT64: + case FieldDescriptor::CPPTYPE_DOUBLE: + case FieldDescriptor::CPPTYPE_FLOAT: + case FieldDescriptor::CPPTYPE_BOOL: + case FieldDescriptor::CPPTYPE_ENUM: + return false; + case FieldDescriptor::CPPTYPE_STRING: + case FieldDescriptor::CPPTYPE_MESSAGE: + return true; + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return false; +} + } // 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 526e19cc..27444d5a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.h +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h @@ -103,6 +103,12 @@ const char* PrimitiveTypeName(FieldDescriptor::CppType type); // methods of WireFormat. For example, TYPE_INT32 becomes "Int32". const char* DeclaredTypeMethodName(FieldDescriptor::Type type); +// Return the code that evaluates to the number when compiled. +string Int32ToString(int number); + +// Return the code that evaluates to the number when compiled. +string Int64ToString(int64 number); + // Get code that evaluates to the field's default value. string DefaultValue(const FieldDescriptor* field); @@ -115,14 +121,23 @@ string GlobalAddDescriptorsName(const string& filename); // Return the name of the AssignDescriptors() function for a given file. string GlobalAssignDescriptorsName(const string& filename); +// Return the qualified C++ name for a file level symbol. +string QualifiedFileLevelSymbol(const string& package, const string& name); + // Return the name of the ShutdownFile() function for a given file. string GlobalShutdownFileName(const string& filename); // Escape C++ trigraphs by escaping question marks to \? string EscapeTrigraphs(const string& to_escape); -// Do message classes in this file keep track of unknown fields? -inline bool HasUnknownFields(const FileDescriptor* file) { +// Escaped function name to eliminate naming conflict. +string SafeFunctionName(const Descriptor* descriptor, + const FieldDescriptor* field, + const string& prefix); + +// Do message classes in this file use UnknownFieldSet? +// Otherwise, messages will store unknown fields in a string +inline bool UseUnknownFieldSet(const FileDescriptor* file) { return file->options().optimize_for() != FileOptions::LITE_RUNTIME; } @@ -178,6 +193,11 @@ void PrintHandlingOptionalStaticInitializers( const char* without_static_init); +// Returns true if the field's CPPTYPE is string or message. +bool IsStringOrMessage(const FieldDescriptor* field); + +string UnderscoresToCamelCase(const string& input, bool cap_next_letter); + } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index 1ea4f13d..962ff535 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -35,6 +35,8 @@ #include <algorithm> #include <google/protobuf/stubs/hash.h> #include <map> +#include <memory> +#include <set> #include <utility> #include <vector> #include <google/protobuf/compiler/cpp/cpp_message.h> @@ -74,15 +76,6 @@ struct FieldOrderingByNumber { } }; -const char* kWireTypeNames[] = { - "VARINT", - "FIXED64", - "LENGTH_DELIMITED", - "START_GROUP", - "END_GROUP", - "FIXED32", -}; - // Sort the fields of the given Descriptor by number into a new[]'d array // and return it. const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) { @@ -255,8 +248,9 @@ void OptimizePadding(vector<const FieldDescriptor*>* fields) { aligned_to_4.push_back(field_group); } // Sort by preferred location to keep fields as close to their original - // location as possible. - sort(aligned_to_4.begin(), aligned_to_4.end()); + // location as possible. Using stable_sort ensures that the output is + // consistent across runs. + stable_sort(aligned_to_4.begin(), aligned_to_4.end()); // Now group fields aligned to 4 bytes (or the 4-field groups created above) // into pairs, and treat those like a single field aligned to 8 bytes. @@ -271,9 +265,8 @@ void OptimizePadding(vector<const FieldDescriptor*>* fields) { } aligned_to_8.push_back(field_group); } - // Sort by preferred location to keep fields as close to their original - // location as possible. - sort(aligned_to_8.begin(), aligned_to_8.end()); + // Sort by preferred location. + stable_sort(aligned_to_8.begin(), aligned_to_8.end()); // Now pull out all the FieldDescriptors in order. fields->clear(); @@ -284,22 +277,26 @@ void OptimizePadding(vector<const FieldDescriptor*>* fields) { } } +string MessageTypeProtoName(const FieldDescriptor* field) { + return field->message_type()->full_name(); +} + } // =================================================================== MessageGenerator::MessageGenerator(const Descriptor* descriptor, const Options& options) - : descriptor_(descriptor), - classname_(ClassName(descriptor, false)), - options_(options), - field_generators_(descriptor, options), - nested_generators_(new scoped_ptr<MessageGenerator>[ - descriptor->nested_type_count()]), - enum_generators_(new scoped_ptr<EnumGenerator>[ - descriptor->enum_type_count()]), - extension_generators_(new scoped_ptr<ExtensionGenerator>[ - descriptor->extension_count()]) { + : descriptor_(descriptor), + classname_(ClassName(descriptor, false)), + options_(options), + field_generators_(descriptor, options), + nested_generators_(new scoped_ptr< + MessageGenerator>[descriptor->nested_type_count()]), + enum_generators_( + new scoped_ptr<EnumGenerator>[descriptor->enum_type_count()]), + extension_generators_(new scoped_ptr< + ExtensionGenerator>[descriptor->extension_count()]) { for (int i = 0; i < descriptor->nested_type_count(); i++) { nested_generators_[i].reset( @@ -383,6 +380,14 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { "GOOGLE_PROTOBUF_EXTENSION_ACCESSORS($classname$)\n", "classname", classname_); } + + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "inline $camel_oneof_name$Case $oneof_name$_case() const;\n", + "camel_oneof_name", + UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true), + "oneof_name", descriptor_->oneof_decl(i)->name()); + } } void MessageGenerator:: @@ -403,6 +408,18 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) { "inline int $classname$::$name$_size() const {\n" " return $name$_.size();\n" "}\n"); + } else if (field->containing_oneof()) { + // Singular field in a oneof + 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" + "inline void $classname$::set_has_$name$() {\n" + " _oneof_case_[$oneof_index$] = k$field_name$;\n" + "}\n"); } else { // Singular field. char buffer[kFastToBufferSize]; @@ -426,14 +443,27 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) { "inline void $classname$::clear_$name$() {\n"); printer->Indent(); - field_generators_.get(field).GenerateClearingCode(printer); - printer->Outdent(); - if (!field->is_repeated()) { + if (field->containing_oneof()) { + // Clear this field only if it is the active field in this oneof, + // otherwise ignore printer->Print(vars, - " clear_has_$name$();\n"); + "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 (!field->is_repeated()) { + printer->Print(vars, + "clear_has_$name$();\n"); + } } + printer->Outdent(); printer->Print("}\n"); // Generate type-specific accessors. @@ -441,6 +471,49 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) { printer->Print("\n"); } + + // Generate has_$name$() and clear_has_$name$() functions for oneofs + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + map<string, string> 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_; + printer->Print( + vars, + "inline bool $classname$::has_$oneof_name$() {\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"); + } +} + +// Helper for the code that emits the Clear() method. +static bool CanClearByZeroing(const FieldDescriptor* field) { + if (field->is_repeated() || field->is_extension()) return false; + switch (field->cpp_type()) { + case internal::WireFormatLite::CPPTYPE_ENUM: + return field->default_value_enum()->number() == 0; + case internal::WireFormatLite::CPPTYPE_INT32: + return field->default_value_int32() == 0; + case internal::WireFormatLite::CPPTYPE_INT64: + return field->default_value_int64() == 0; + case internal::WireFormatLite::CPPTYPE_UINT32: + return field->default_value_uint32() == 0; + case internal::WireFormatLite::CPPTYPE_UINT64: + return field->default_value_uint64() == 0; + case internal::WireFormatLite::CPPTYPE_FLOAT: + return field->default_value_float() == 0; + case internal::WireFormatLite::CPPTYPE_DOUBLE: + return field->default_value_double() == 0; + case internal::WireFormatLite::CPPTYPE_BOOL: + return field->default_value_bool() == false; + default: + return false; + } } void MessageGenerator:: @@ -455,6 +528,7 @@ GenerateClassDefinition(io::Printer* printer) { map<string, string> vars; vars["classname"] = classname_; vars["field_count"] = SimpleItoa(descriptor_->field_count()); + vars["oneof_decl_count"] = SimpleItoa(descriptor_->oneof_decl_count()); if (options_.dllexport_decl.empty()) { vars["dllexport"] = ""; } else { @@ -479,7 +553,7 @@ GenerateClassDefinition(io::Printer* printer) { "}\n" "\n"); - if (HasUnknownFields(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file())) { printer->Print( "inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {\n" " return _unknown_fields_;\n" @@ -489,6 +563,16 @@ GenerateClassDefinition(io::Printer* printer) { " return &_unknown_fields_;\n" "}\n" "\n"); + } else { + printer->Print( + "inline const ::std::string& unknown_fields() const {\n" + " return _unknown_fields_;\n" + "}\n" + "\n" + "inline ::std::string* mutable_unknown_fields() {\n" + " return &_unknown_fields_;\n" + "}\n" + "\n"); } // Only generate this member if it's not disabled. @@ -502,6 +586,33 @@ GenerateClassDefinition(io::Printer* printer) { "static const $classname$& default_instance();\n" "\n"); + // Generate enum values for every field in oneofs. One list is generated for + // each oneof with an additional *_NOT_SET value. + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "enum $camel_oneof_name$Case {\n", + "camel_oneof_name", + UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true)); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + printer->Print( + "k$field_name$ = $field_number$,\n", + "field_name", + UnderscoresToCamelCase( + descriptor_->oneof_decl(i)->field(j)->name(), true), + "field_number", + SimpleItoa(descriptor_->oneof_decl(i)->field(j)->number())); + } + printer->Print( + "$cap_oneof_name$_NOT_SET = 0,\n", + "cap_oneof_name", + ToUpper(descriptor_->oneof_decl(i)->name())); + printer->Outdent(); + printer->Print( + "};\n" + "\n"); + } + if (!StaticInitializersForced(descriptor_->file())) { printer->Print(vars, "#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER\n" @@ -545,20 +656,50 @@ GenerateClassDefinition(io::Printer* printer) { " ::google::protobuf::io::CodedInputStream* input);\n" "void SerializeWithCachedSizes(\n" " ::google::protobuf::io::CodedOutputStream* output) const;\n"); + // DiscardUnknownFields() is implemented in message.cc using reflections. We + // need to implement this function in generated code for messages. + if (!UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "void DiscardUnknownFields();\n"); + } if (HasFastArraySerialization(descriptor_->file())) { printer->Print( "::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;\n"); } } - printer->Print(vars, + // Check all FieldDescriptors including those in oneofs to estimate + // whether ::std::string is likely to be used, and depending on that + // estimate, set uses_string_ to true or false. That contols + // whether to force initialization of empty_string_ in SharedCtor(). + // It's often advantageous to do so to keep "is empty_string_ + // inited?" code from appearing all over the place. + vector<const FieldDescriptor*> descriptors; + for (int i = 0; i < descriptor_->field_count(); i++) { + descriptors.push_back(descriptor_->field(i)); + } + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + descriptors.push_back(descriptor_->oneof_decl(i)->field(j)); + } + } + uses_string_ = false; + for (int i = 0; i < descriptors.size(); i++) { + const FieldDescriptor* field = descriptors[i]; + if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { + switch (field->options().ctype()) { + default: uses_string_ = true; break; + } + } + } + + printer->Print( "int GetCachedSize() const { return _cached_size_; }\n" "private:\n" "void SharedCtor();\n" "void SharedDtor();\n" "void SetCachedSize(int size) const;\n" - "public:\n" - "\n"); + "public:\n"); if (HasDescriptorMethods(descriptor_->file())) { printer->Print( @@ -621,13 +762,44 @@ GenerateClassDefinition(io::Printer* printer) { printer->Print( "inline void set_has_$name$();\n", "name", FieldName(descriptor_->field(i))); - printer->Print( - "inline void clear_has_$name$();\n", - "name", FieldName(descriptor_->field(i))); + if (!descriptor_->field(i)->containing_oneof()) { + printer->Print( + "inline void clear_has_$name$();\n", + "name", FieldName(descriptor_->field(i))); + } } } printer->Print("\n"); + // Generate oneof function declarations + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "inline bool has_$oneof_name$();\n" + "void clear_$oneof_name$();\n" + "inline void clear_has_$oneof_name$();\n\n", + "oneof_name", descriptor_->oneof_decl(i)->name()); + } + + // Prepare decls for _cached_size_ and _has_bits_. Their position in the + // output will be determined later. + + bool need_to_emit_cached_size = true; + // TODO(kenton): Make _cached_size_ an atomic<int> when C++ supports it. + const string cached_size_decl = "mutable int _cached_size_;\n"; + + // TODO(jieluo) - Optimize _has_bits_ for repeated and oneof fields. + size_t sizeof_has_bits = (descriptor_->field_count() + 31) / 32 * 4; + if (descriptor_->field_count() == 0) { + // Zero-size arrays aren't technically allowed, and MSVC in particular + // doesn't like them. We still need to declare these arrays to make + // other code compile. Since this is an uncommon case, we'll just declare + // them with size 1 and waste some space. Oh well. + sizeof_has_bits = 4; + } + const string has_bits_decl = sizeof_has_bits == 0 ? "" : + "::google::protobuf::uint32 _has_bits_[" + SimpleItoa(sizeof_has_bits / 4) + "];\n"; + + // To minimize padding, data members are divided into three sections: // (1) members assumed to align to 8 bytes // (2) members corresponding to message fields, re-ordered to optimize @@ -642,42 +814,92 @@ GenerateClassDefinition(io::Printer* printer) { "\n"); } - if (HasUnknownFields(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file())) { printer->Print( "::google::protobuf::UnknownFieldSet _unknown_fields_;\n" "\n"); + } else { + printer->Print( + "::std::string _unknown_fields_;\n" + "\n"); + } + + // _has_bits_ is frequently accessed, so to reduce code size and improve + // speed, it should be close to the start of the object. But, try not to + // waste space:_has_bits_ by itself always makes sense if its size is a + // multiple of 8, but, otherwise, maybe _has_bits_ and cached_size_ together + // will work well. + printer->Print(has_bits_decl.c_str()); + if ((sizeof_has_bits % 8) != 0) { + printer->Print(cached_size_decl.c_str()); + need_to_emit_cached_size = false; } // Field members: + // List fields which doesn't belong to any oneof vector<const FieldDescriptor*> fields; + hash_map<string, int> fieldname_to_chunk; for (int i = 0; i < descriptor_->field_count(); i++) { - fields.push_back(descriptor_->field(i)); + if (!descriptor_->field(i)->containing_oneof()) { + const FieldDescriptor* field = descriptor_->field(i); + fields.push_back(field); + fieldname_to_chunk[FieldName(field)] = i / 8; + } } OptimizePadding(&fields); + // Emit some private and static members + runs_of_fields_ = vector< vector<string> >(1); for (int i = 0; i < fields.size(); ++i) { - field_generators_.get(fields[i]).GeneratePrivateMembers(printer); + const FieldDescriptor* field = fields[i]; + const FieldGenerator& generator = field_generators_.get(field); + generator.GenerateStaticMembers(printer); + generator.GeneratePrivateMembers(printer); + if (CanClearByZeroing(field)) { + const string& fieldname = FieldName(field); + if (!runs_of_fields_.back().empty() && + (fieldname_to_chunk[runs_of_fields_.back().back()] != + fieldname_to_chunk[fieldname])) { + runs_of_fields_.push_back(vector<string>()); + } + runs_of_fields_.back().push_back(fieldname); + } else if (!runs_of_fields_.back().empty()) { + runs_of_fields_.push_back(vector<string>()); + } + } + + // For each oneof generate a union + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "union $camel_oneof_name$Union {\n", + "camel_oneof_name", + UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true)); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + field_generators_.get(descriptor_->oneof_decl(i)-> + field(j)).GeneratePrivateMembers(printer); + } + printer->Outdent(); + printer->Print( + "} $oneof_name$_;\n", + "oneof_name", descriptor_->oneof_decl(i)->name()); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + field_generators_.get(descriptor_->oneof_decl(i)-> + field(j)).GenerateStaticMembers(printer); + } } // Members assumed to align to 4 bytes: - // TODO(kenton): Make _cached_size_ an atomic<int> when C++ supports it. - printer->Print( - "\n" - "mutable int _cached_size_;\n"); + if (need_to_emit_cached_size) { + printer->Print(cached_size_decl.c_str()); + need_to_emit_cached_size = false; + } - // Generate _has_bits_. - if (descriptor_->field_count() > 0) { + // Generate _oneof_case_. + if (descriptor_->oneof_decl_count() > 0) { printer->Print(vars, - "::google::protobuf::uint32 _has_bits_[($field_count$ + 31) / 32];\n" - "\n"); - } else { - // Zero-size arrays aren't technically allowed, and MSVC in particular - // doesn't like them. We still need to declare these arrays to make - // other code compile. Since this is an uncommon case, we'll just declare - // them with size 1 and waste some space. Oh well. - printer->Print( - "::google::protobuf::uint32 _has_bits_[1];\n" + "::google::protobuf::uint32 _oneof_case_[$oneof_decl_count$];\n" "\n"); } @@ -710,6 +932,7 @@ GenerateClassDefinition(io::Printer* printer) { printer->Outdent(); printer->Print(vars, "};"); + GOOGLE_DCHECK(!need_to_emit_cached_size); } void MessageGenerator:: @@ -721,6 +944,23 @@ GenerateInlineMethods(io::Printer* printer) { } GenerateFieldAccessorDefinitions(printer); + + // Generate oneof_case() functions. + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + map<string, string> vars; + vars["class_name"] = classname_; + vars["camel_oneof_name"] = UnderscoresToCamelCase( + descriptor_->oneof_decl(i)->name(), true); + vars["oneof_name"] = descriptor_->oneof_decl(i)->name(); + vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); + printer->Print( + vars, + "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" + "}\n"); + } } void MessageGenerator:: @@ -731,6 +971,25 @@ GenerateDescriptorDeclarations(io::Printer* printer) { " $name$_reflection_ = NULL;\n", "name", classname_); + // Generate oneof default instance for reflection usage. + if (descriptor_->oneof_decl_count() > 0) { + printer->Print("struct $name$OneofInstance {\n", + "name", classname_); + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + printer->Print(" "); + if (IsStringOrMessage(field)) { + printer->Print("const "); + } + field_generators_.get(field).GeneratePrivateMembers(printer); + } + } + + printer->Print("}* $name$_default_oneof_instance_ = NULL;\n", + "name", classname_); + } + for (int i = 0; i < descriptor_->nested_type_count(); i++) { nested_generators_[i]->GenerateDescriptorDeclarations(printer); } @@ -783,6 +1042,14 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { printer->Print(vars, " -1,\n"); } + + if (descriptor_->oneof_decl_count() > 0) { + printer->Print(vars, + " $classname$_default_oneof_instance_,\n" + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" + "$classname$, _oneof_case_[0]),\n"); + } + printer->Print( " ::google::protobuf::DescriptorPool::generated_pool(),\n"); printer->Print(vars, @@ -830,6 +1097,13 @@ GenerateDefaultInstanceAllocator(io::Printer* printer) { "$classname$::default_instance_ = new $classname$();\n", "classname", classname_); + if ((descriptor_->oneof_decl_count() > 0) && + HasDescriptorMethods(descriptor_->file())) { + printer->Print( + "$classname$_default_oneof_instance_ = new $classname$OneofInstance;\n", + "classname", classname_); + } + // Handle nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { nested_generators_[i]->GenerateDefaultInstanceAllocator(printer); @@ -861,6 +1135,11 @@ GenerateShutdownCode(io::Printer* printer) { "classname", classname_); if (HasDescriptorMethods(descriptor_->file())) { + if (descriptor_->oneof_decl_count() > 0) { + printer->Print( + "delete $classname$_default_oneof_instance_;\n", + "classname", classname_); + } printer->Print( "delete $classname$_reflection_;\n", "classname", classname_); @@ -918,6 +1197,11 @@ GenerateClassMethods(io::Printer* printer) { GenerateStructors(printer); printer->Print("\n"); + if (descriptor_->oneof_decl_count() > 0) { + GenerateOneofClear(printer); + printer->Print("\n"); + } + if (HasGeneratedMethods(descriptor_->file())) { GenerateClear(printer); printer->Print("\n"); @@ -977,15 +1261,33 @@ GenerateOffsets(io::Printer* printer) { printer->Print( "static const int $classname$_offsets_[$field_count$] = {\n", "classname", classname_, - "field_count", SimpleItoa(max(1, descriptor_->field_count()))); + "field_count", SimpleItoa(max( + 1, descriptor_->field_count() + descriptor_->oneof_decl_count()))); printer->Indent(); for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); + if (field->containing_oneof()) { + printer->Print( + "PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(" + "$classname$_default_oneof_instance_, $name$_),\n", + "classname", classname_, + "name", FieldName(field)); + } else { + printer->Print( + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, " + "$name$_),\n", + "classname", classname_, + "name", FieldName(field)); + } + } + + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + const OneofDescriptor* oneof = descriptor_->oneof_decl(i); printer->Print( "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, $name$_),\n", "classname", classname_, - "name", FieldName(field)); + "name", oneof->name()); } printer->Outdent(); @@ -999,17 +1301,26 @@ GenerateSharedConstructorCode(io::Printer* printer) { "classname", classname_); printer->Indent(); - printer->Print( - "_cached_size_ = 0;\n"); + printer->Print(StrCat( + uses_string_ ? "::google::protobuf::internal::GetEmptyString();\n" : "", + "_cached_size_ = 0;\n").c_str()); for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(descriptor_->field(i)) - .GenerateConstructorCode(printer); + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get(descriptor_->field(i)) + .GenerateConstructorCode(printer); + } } printer->Print( "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "clear_has_$oneof_name$();\n", + "oneof_name", descriptor_->oneof_decl(i)->name()); + } + printer->Outdent(); printer->Print("}\n\n"); } @@ -1020,10 +1331,21 @@ GenerateSharedDestructorCode(io::Printer* printer) { "void $classname$::SharedDtor() {\n", "classname", classname_); printer->Indent(); - // Write the destructors for each field. + // Write the destructors for each field except oneof members. for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(descriptor_->field(i)) - .GenerateDestructorCode(printer); + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get(descriptor_->field(i)) + .GenerateDestructorCode(printer); + } + } + + // Generate code to destruct oneofs. Clearing should do the work. + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "if (has_$oneof_name$()) {\n" + " clear_$oneof_name$();\n" + "}\n", + "oneof_name", descriptor_->oneof_decl(i)->name()); } PrintHandlingOptionalStaticInitializers( @@ -1042,8 +1364,12 @@ GenerateSharedDestructorCode(io::Printer* printer) { if (!field->is_repeated() && field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - printer->Print(" delete $name$_;\n", - "name", FieldName(field)); + // Skip oneof members + if (!field->containing_oneof()) { + printer->Print( + " delete $name$_;\n", + "name", FieldName(field)); + } } } @@ -1063,9 +1389,11 @@ GenerateStructors(io::Printer* printer) { "$classname$::$classname$()\n" " : $superclass$() {\n" " SharedCtor();\n" + " // @@protoc_insertion_point(constructor:$full_name$)\n" "}\n", "classname", classname_, - "superclass", superclass); + "superclass", superclass, + "full_name", descriptor_->full_name()); printer->Print( "\n" @@ -1082,7 +1410,14 @@ GenerateStructors(io::Printer* printer) { const FieldDescriptor* field = descriptor_->field(i); if (!field->is_repeated() && - field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && + (field->containing_oneof() == NULL || + HasDescriptorMethods(descriptor_->file()))) { + string name; + if (field->containing_oneof()) { + name = classname_ + "_default_oneof_instance_->"; + } + name += FieldName(field); PrintHandlingOptionalStaticInitializers( descriptor_->file(), printer, // With static initializers. @@ -1091,8 +1426,12 @@ GenerateStructors(io::Printer* printer) { " $name$_ = const_cast< $type$*>(\n" " $type$::internal_default_instance());\n", // Vars. - "name", FieldName(field), + "name", name, "type", FieldMessageTypeName(field)); + } else if (field->containing_oneof() && + HasDescriptorMethods(descriptor_->file())) { + field_generators_.get(descriptor_->field(i)) + .GenerateConstructorCode(printer); } } printer->Print( @@ -1105,10 +1444,12 @@ GenerateStructors(io::Printer* printer) { " : $superclass$() {\n" " SharedCtor();\n" " MergeFrom(from);\n" + " // @@protoc_insertion_point(copy_constructor:$full_name$)\n" "}\n" "\n", "classname", classname_, - "superclass", superclass); + "superclass", superclass, + "full_name", descriptor_->full_name()); // Generate the shared constructor code. GenerateSharedConstructorCode(printer); @@ -1116,10 +1457,12 @@ GenerateStructors(io::Printer* printer) { // Generate the destructor. printer->Print( "$classname$::~$classname$() {\n" + " // @@protoc_insertion_point(destructor:$full_name$)\n" " SharedDtor();\n" "}\n" "\n", - "classname", classname_); + "classname", classname_, + "full_name", descriptor_->full_name()); // Generate the shared destructor code. GenerateSharedDestructorCode(printer); @@ -1166,13 +1509,25 @@ GenerateStructors(io::Printer* printer) { "}\n" "\n" "$classname$* $classname$::default_instance_ = NULL;\n" - "\n" + "\n", + "classname", classname_); + + printer->Print( "$classname$* $classname$::New() const {\n" " return new $classname$;\n" "}\n", - "classname", classname_, - "adddescriptorsname", - GlobalAddDescriptorsName(descriptor_->file()->name())); + "classname", classname_); + +} + +// Return the number of bits set in n, a non-negative integer. +static int popcnt(uint32 n) { + int result = 0; + while (n != 0) { + result += (n & 1); + n = n / 2; + } + return result; } void MessageGenerator:: @@ -1181,64 +1536,133 @@ GenerateClear(io::Printer* printer) { "classname", classname_); printer->Indent(); - int last_index = -1; - + // Step 1: Extensions if (descriptor_->extension_range_count() > 0) { printer->Print("_extensions_.Clear();\n"); } + // Step 2: Everything but extensions, repeateds, unions. + // These are handled in chunks of 8. The first chunk is + // the non-extensions-non-repeateds-non-unions in + // descriptor_->field(0), descriptor_->field(1), ... descriptor_->field(7), + // and the second chunk is the same for + // descriptor_->field(8), descriptor_->field(9), ... descriptor_->field(15), + // etc. + set<int> step2_indices; + hash_map<string, int> fieldname_to_chunk; + hash_map<int, string> memsets_for_chunk; + hash_map<int, int> memset_field_count_for_chunk; + hash_set<string> handled; // fields that appear anywhere in memsets_for_chunk + hash_map<int, uint32> fields_mask_for_chunk; for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); + if (!field->is_repeated() && !field->containing_oneof()) { + step2_indices.insert(i); + int chunk = i / 8; + fieldname_to_chunk[FieldName(field)] = chunk; + fields_mask_for_chunk[chunk] |= static_cast<uint32>(1) << (i % 32); + } + } - if (!field->is_repeated()) { - // We can use the fact that _has_bits_ is a giant bitfield to our - // advantage: We can check up to 32 bits at a time for equality to - // zero, and skip the whole range if so. This can improve the speed - // of Clear() for messages which contain a very large number of - // optional fields of which only a few are used at a time. Here, - // we've chosen to check 8 bits at a time rather than 32. - if (i / 8 != last_index / 8 || last_index < 0) { - if (last_index >= 0) { - printer->Outdent(); - printer->Print("}\n"); - } - printer->Print( - "if (_has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n", - "index", SimpleItoa(field->index())); - printer->Indent(); + // Step 2a: Greedily seek runs of fields that can be cleared by memset-to-0. + // The generated code uses two macros to help it clear runs of fields: + // OFFSET_OF_FIELD_ computes the offset (in bytes) of a field in the Message. + // ZR_ zeroes a non-empty range of fields via memset. + const char* macros = + "#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \\\n" + " &reinterpret_cast<$classname$*>(16)->f) - \\\n" + " reinterpret_cast<char*>(16))\n\n" + "#define ZR_(first, last) do { \\\n" + " size_t f = OFFSET_OF_FIELD_(first); \\\n" + " size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \\\n" + " ::memset(&first, 0, n); \\\n" + " } while (0)\n\n"; + for (int i = 0; i < runs_of_fields_.size(); i++) { + const vector<string>& run = runs_of_fields_[i]; + if (run.size() < 2) continue; + const string& first_field_name = run[0]; + const string& last_field_name = run.back(); + int chunk = fieldname_to_chunk[run[0]]; + memsets_for_chunk[chunk].append( + "ZR_(" + first_field_name + "_, " + last_field_name + "_);\n"); + for (int j = 0; j < run.size(); j++) { + GOOGLE_DCHECK_EQ(chunk, fieldname_to_chunk[run[j]]); + handled.insert(run[j]); + } + memset_field_count_for_chunk[chunk] += run.size(); + } + const bool macros_are_needed = handled.size() > 0; + if (macros_are_needed) { + printer->Outdent(); + printer->Print(macros, + "classname", classname_); + printer->Indent(); + } + // Step 2b: Finish step 2, ignoring fields handled in step 2a. + int last_index = -1; + bool chunk_block_in_progress = false; + for (int i = 0; i < descriptor_->field_count(); i++) { + if (step2_indices.count(i) == 0) continue; + const FieldDescriptor* field = descriptor_->field(i); + const string fieldname = FieldName(field); + if (i / 8 != last_index / 8 || last_index < 0) { + // End previous chunk, if there was one. + if (chunk_block_in_progress) { + printer->Outdent(); + printer->Print("}\n"); + chunk_block_in_progress = false; } - last_index = i; - - // It's faster to just overwrite primitive types, but we should - // only clear strings and messages if they were set. - // TODO(kenton): Let the CppFieldGenerator decide this somehow. - bool should_check_bit = - field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE || - field->cpp_type() == FieldDescriptor::CPPTYPE_STRING; - - if (should_check_bit) { + // Start chunk. + const string& memsets = memsets_for_chunk[i / 8]; + uint32 mask = fields_mask_for_chunk[i / 8]; + int count = popcnt(mask); + if (count == 1 || + (count <= 4 && count == memset_field_count_for_chunk[i / 8])) { + // No "if" here because the chunk is trivial. + } else { printer->Print( - "if (has_$name$()) {\n", - "name", FieldName(field)); + "if (_has_bits_[$index$ / 32] & $mask$) {\n", + "index", SimpleItoa(i / 8 * 8), + "mask", SimpleItoa(mask)); printer->Indent(); + chunk_block_in_progress = true; } + printer->Print(memsets.c_str()); + } + last_index = i; + if (handled.count(fieldname) > 0) continue; + + // It's faster to just overwrite primitive types, but we should + // only clear strings and messages if they were set. + // TODO(kenton): Let the CppFieldGenerator decide this somehow. + bool should_check_bit = + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE || + field->cpp_type() == FieldDescriptor::CPPTYPE_STRING; + + if (should_check_bit) { + printer->Print("if (has_$name$()) {\n", "name", fieldname); + printer->Indent(); + } - field_generators_.get(field).GenerateClearingCode(printer); + field_generators_.get(field).GenerateClearingCode(printer); - if (should_check_bit) { - printer->Outdent(); - printer->Print("}\n"); - } + if (should_check_bit) { + printer->Outdent(); + printer->Print("}\n"); } } - if (last_index >= 0) { + if (chunk_block_in_progress) { printer->Outdent(); printer->Print("}\n"); } + if (macros_are_needed) { + printer->Outdent(); + printer->Print("\n#undef OFFSET_OF_FIELD_\n#undef ZR_\n\n"); + printer->Indent(); + } - // Repeated fields don't use _has_bits_ so we clear them in a separate - // pass. + // Step 3: Repeated fields don't use _has_bits_; emit code to clear them here. for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); @@ -1247,12 +1671,23 @@ GenerateClear(io::Printer* printer) { } } + // Step 4: Unions. + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "clear_$oneof_name$();\n", + "oneof_name", descriptor_->oneof_decl(i)->name()); + } + + // Step 5: Everything else. printer->Print( "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); - if (HasUnknownFields(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file())) { printer->Print( "mutable_unknown_fields()->Clear();\n"); + } else { + printer->Print( + "mutable_unknown_fields()->clear();\n"); } printer->Outdent(); @@ -1260,6 +1695,58 @@ GenerateClear(io::Printer* printer) { } 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()); + printer->Indent(); + printer->Print( + "switch($oneofname$_case()) {\n", + "oneofname", descriptor_->oneof_decl(i)->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 k$field_name$: {\n", + "field_name", UnderscoresToCamelCase(field->name(), true)); + printer->Indent(); + // We clear only allocated objects in oneofs + if (!IsStringOrMessage(field)) { + printer->Print( + "// No need to clear\n"); + } else { + field_generators_.get(field).GenerateClearingCode(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(descriptor_->oneof_decl(i)->name())); + printer->Outdent(); + printer->Print( + "}\n" + "_oneof_case_[$oneof_index$] = $cap_oneof_name$_NOT_SET;\n", + "oneof_index", SimpleItoa(i), + "cap_oneof_name", + ToUpper(descriptor_->oneof_decl(i)->name())); + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + } +} + +void MessageGenerator:: GenerateSwap(io::Printer* printer) { // Generate the Swap member function. printer->Print("void $classname$::Swap($classname$* other) {\n", @@ -1274,13 +1761,23 @@ GenerateSwap(io::Printer* printer) { field_generators_.get(field).GenerateSwappingCode(printer); } + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "std::swap($oneof_name$_, other->$oneof_name$_);\n" + "std::swap(_oneof_case_[$i$], other->_oneof_case_[$i$]);\n", + "oneof_name", descriptor_->oneof_decl(i)->name(), + "i", SimpleItoa(i)); + } + for (int i = 0; i < (descriptor_->field_count() + 31) / 32; ++i) { printer->Print("std::swap(_has_bits_[$i$], other->_has_bits_[$i$]);\n", "i", SimpleItoa(i)); } - if (HasUnknownFields(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file())) { printer->Print("_unknown_fields_.Swap(&other->_unknown_fields_);\n"); + } else { + printer->Print("_unknown_fields_.swap(other->_unknown_fields_);\n"); } printer->Print("std::swap(_cached_size_, other->_cached_size_);\n"); if (descriptor_->extension_range_count() > 0) { @@ -1352,13 +1849,43 @@ GenerateMergeFrom(io::Printer* printer) { } } + // Merge oneof fields. Oneof field requires oneof case check. + for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) { + printer->Print( + "switch (from.$oneofname$_case()) {\n", + "oneofname", descriptor_->oneof_decl(i)->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 k$field_name$: {\n", + "field_name", UnderscoresToCamelCase(field->name(), true)); + 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(descriptor_->oneof_decl(i)->name())); + printer->Outdent(); + printer->Print( + "}\n"); + } + // Merge Optional and Required fields (after a _has_bit check). int last_index = -1; for (int i = 0; i < descriptor_->field_count(); ++i) { const FieldDescriptor* field = descriptor_->field(i); - if (!field->is_repeated()) { + if (!field->is_repeated() && !field->containing_oneof()) { // See above in GenerateClear for an explanation of this. if (i / 8 != last_index / 8 || last_index < 0) { if (last_index >= 0) { @@ -1394,9 +1921,12 @@ GenerateMergeFrom(io::Printer* printer) { printer->Print("_extensions_.MergeFrom(from._extensions_);\n"); } - if (HasUnknownFields(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file())) { printer->Print( "mutable_unknown_fields()->MergeFrom(from.unknown_fields());\n"); + } else { + printer->Print( + "mutable_unknown_fields()->append(from.unknown_fields());\n"); } printer->Outdent(); @@ -1465,14 +1995,39 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Print( "bool $classname$::MergePartialFromCodedStream(\n" " ::google::protobuf::io::CodedInputStream* input) {\n" - "#define DO_(EXPRESSION) if (!(EXPRESSION)) return false\n" - " ::google::protobuf::uint32 tag;\n" - " while ((tag = input->ReadTag()) != 0) {\n", + "#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure\n" + " ::google::protobuf::uint32 tag;\n", "classname", classname_); + if (!UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + " ::google::protobuf::io::StringOutputStream unknown_fields_string(\n" + " mutable_unknown_fields());\n" + " ::google::protobuf::io::CodedOutputStream unknown_fields_stream(\n" + " &unknown_fields_string);\n"); + } + + printer->Print( + " // @@protoc_insertion_point(parse_start:$full_name$)\n", + "full_name", descriptor_->full_name()); + printer->Indent(); + printer->Print("for (;;) {\n"); printer->Indent(); + scoped_array<const FieldDescriptor*> ordered_fields( + SortFieldsByNumber(descriptor_)); + uint32 maxtag = descriptor_->field_count() == 0 ? 0 : + WireFormat::MakeTag(ordered_fields[descriptor_->field_count() - 1]); + const int kCutoff0 = 127; // fits in 1-byte varint + const int kCutoff1 = (127 << 7) + 127; // fits in 2-byte varint + printer->Print("::std::pair< ::google::protobuf::uint32, bool> p = " + "input->ReadTagWithCutoff($max$);\n" + "tag = p.first;\n" + "if (!p.second) goto handle_unusual;\n", + "max", SimpleItoa(maxtag <= kCutoff0 ? kCutoff0 : + (maxtag <= kCutoff1 ? kCutoff1 : + maxtag))); if (descriptor_->field_count() > 0) { // We don't even want to print the switch() if we have no fields because // MSVC dislikes switch() statements that contain only a default value. @@ -1482,14 +2037,11 @@ GenerateMergeFromCodedStream(io::Printer* printer) { // of each case. However, this is actually a bit slower in practice as it // creates a jump table that is 8x larger and sparser, and meanwhile the // if()s are highly predictable. - printer->Print( - "switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {\n"); + printer->Print("switch (::google::protobuf::internal::WireFormatLite::" + "GetTagFieldNumber(tag)) {\n"); printer->Indent(); - scoped_array<const FieldDescriptor*> ordered_fields( - SortFieldsByNumber(descriptor_)); - for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = ordered_fields[i]; @@ -1502,10 +2054,8 @@ GenerateMergeFromCodedStream(io::Printer* printer) { const FieldGenerator& field_generator = field_generators_.get(field); // Emit code to parse the common, expected case. - printer->Print( - "if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==\n" - " ::google::protobuf::internal::WireFormatLite::WIRETYPE_$wiretype$) {\n", - "wiretype", kWireTypeNames[WireFormat::WireTypeForField(field)]); + printer->Print("if (tag == $commontag$) {\n", + "commontag", SimpleItoa(WireFormat::MakeTag(field))); if (i > 0 || (field->is_repeated() && !field->options().packed())) { printer->Print( @@ -1523,20 +2073,22 @@ GenerateMergeFromCodedStream(io::Printer* printer) { // Emit code to parse unexpectedly packed or unpacked values. if (field->is_packable() && field->options().packed()) { - printer->Print( - "} else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)\n" - " == ::google::protobuf::internal::WireFormatLite::\n" - " WIRETYPE_$wiretype$) {\n", - "wiretype", - kWireTypeNames[WireFormat::WireTypeForFieldType(field->type())]); + internal::WireFormatLite::WireType wiretype = + WireFormat::WireTypeForFieldType(field->type()); + printer->Print("} else if (tag == $uncommontag$) {\n", + "uncommontag", SimpleItoa( + internal::WireFormatLite::MakeTag( + field->number(), wiretype))); printer->Indent(); field_generator.GenerateMergeFromCodedStream(printer); printer->Outdent(); } else if (field->is_packable() && !field->options().packed()) { - printer->Print( - "} else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)\n" - " == ::google::protobuf::internal::WireFormatLite::\n" - " WIRETYPE_LENGTH_DELIMITED) {\n"); + internal::WireFormatLite::WireType wiretype = + internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED; + printer->Print("} else if (tag == $uncommontag$) {\n", + "uncommontag", SimpleItoa( + internal::WireFormatLite::MakeTag( + field->number(), wiretype))); printer->Indent(); field_generator.GenerateMergeFromCodedStreamWithPacking(printer); printer->Outdent(); @@ -1544,7 +2096,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Print( "} else {\n" - " goto handle_uninterpreted;\n" + " goto handle_unusual;\n" "}\n"); // switch() is slow since it can't be predicted well. Insert some if()s @@ -1568,7 +2120,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) { // Expect EOF. // TODO(kenton): Expect group end-tag? printer->Print( - "if (input->ExpectAtEnd()) return true;\n"); + "if (input->ExpectAtEnd()) goto success;\n"); } printer->Print( @@ -1578,17 +2130,19 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Print("}\n\n"); } - printer->Print( - "default: {\n" - "handle_uninterpreted:\n"); + printer->Print("default: {\n"); printer->Indent(); } - // Is this an end-group tag? If so, this must be the end of the message. + printer->Outdent(); + printer->Print("handle_unusual:\n"); + printer->Indent(); + // If tag is 0 or an end-group tag then this must be the end of the message. printer->Print( - "if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==\n" + "if (tag == 0 ||\n" + " ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==\n" " ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {\n" - " return true;\n" + " goto success;\n" "}\n"); // Handle extension ranges. @@ -1617,7 +2171,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) { } } printer->Print(") {\n"); - if (HasUnknownFields(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file())) { PrintHandlingOptionalStaticInitializers( descriptor_->file(), printer, // With static initializers. @@ -1630,9 +2184,11 @@ GenerateMergeFromCodedStream(io::Printer* printer) { PrintHandlingOptionalStaticInitializers( descriptor_->file(), printer, // With static initializers. - " DO_(_extensions_.ParseField(tag, input, default_instance_));\n", + " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" + " &unknown_fields_stream));\n", // Without. - " DO_(_extensions_.ParseField(tag, input, &default_instance()));\n"); + " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n" + " &unknown_fields_stream));\n"); } printer->Print( " continue;\n" @@ -1640,13 +2196,14 @@ GenerateMergeFromCodedStream(io::Printer* printer) { } // We really don't recognize this tag. Skip it. - if (HasUnknownFields(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file())) { printer->Print( "DO_(::google::protobuf::internal::WireFormat::SkipField(\n" " input, tag, mutable_unknown_fields()));\n"); } else { printer->Print( - "DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));\n"); + "DO_(::google::protobuf::internal::WireFormatLite::SkipField(\n" + " input, tag, &unknown_fields_stream));\n"); } if (descriptor_->field_count() > 0) { @@ -1660,10 +2217,15 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Outdent(); printer->Outdent(); printer->Print( - " }\n" // while + " }\n" // for (;;) + "success:\n" + " // @@protoc_insertion_point(parse_success:$full_name$)\n" " return true;\n" + "failure:\n" + " // @@protoc_insertion_point(parse_failure:$full_name$)\n" + " return false;\n" "#undef DO_\n" - "}\n"); + "}\n", "full_name", descriptor_->full_name()); } void MessageGenerator::GenerateSerializeOneField( @@ -1719,11 +2281,10 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) { " ::google::protobuf::io::CodedOutputStream* output) const {\n" " _extensions_.SerializeMessageSetWithCachedSizes(output);\n", "classname", classname_); - if (HasUnknownFields(descriptor_->file())) { - printer->Print( - " ::google::protobuf::internal::WireFormat::SerializeUnknownMessageSetItems(\n" - " unknown_fields(), output);\n"); - } + GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file())); + printer->Print( + " ::google::protobuf::internal::WireFormat::SerializeUnknownMessageSetItems(\n" + " unknown_fields(), output);\n"); printer->Print( "}\n"); return; @@ -1735,8 +2296,16 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) { "classname", classname_); printer->Indent(); + printer->Print( + "// @@protoc_insertion_point(serialize_start:$full_name$)\n", + "full_name", descriptor_->full_name()); + GenerateSerializeWithCachedSizesBody(printer, false); + printer->Print( + "// @@protoc_insertion_point(serialize_end:$full_name$)\n", + "full_name", descriptor_->full_name()); + printer->Outdent(); printer->Print( "}\n"); @@ -1752,12 +2321,11 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) { " target =\n" " _extensions_.SerializeMessageSetWithCachedSizesToArray(target);\n", "classname", classname_); - if (HasUnknownFields(descriptor_->file())) { - printer->Print( - " target = ::google::protobuf::internal::WireFormat::\n" - " SerializeUnknownMessageSetItemsToArray(\n" - " unknown_fields(), target);\n"); - } + GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file())); + printer->Print( + " target = ::google::protobuf::internal::WireFormat::\n" + " SerializeUnknownMessageSetItemsToArray(\n" + " unknown_fields(), target);\n"); printer->Print( " return target;\n" "}\n"); @@ -1770,8 +2338,16 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) { "classname", classname_); printer->Indent(); + printer->Print( + "// @@protoc_insertion_point(serialize_to_array_start:$full_name$)\n", + "full_name", descriptor_->full_name()); + GenerateSerializeWithCachedSizesBody(printer, true); + printer->Print( + "// @@protoc_insertion_point(serialize_to_array_end:$full_name$)\n", + "full_name", descriptor_->full_name()); + printer->Outdent(); printer->Print( " return target;\n" @@ -1781,7 +2357,7 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) { void MessageGenerator:: GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { scoped_array<const FieldDescriptor*> ordered_fields( - SortFieldsByNumber(descriptor_)); + SortFieldsByNumber(descriptor_)); vector<const Descriptor::ExtensionRange*> sorted_extensions; for (int i = 0; i < descriptor_->extension_range_count(); ++i) { @@ -1810,7 +2386,7 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { } } - if (HasUnknownFields(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file())) { printer->Print("if (!unknown_fields().empty()) {\n"); printer->Indent(); if (to_array) { @@ -1827,6 +2403,10 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { printer->Print( "}\n"); + } else { + printer->Print( + "output->WriteRaw(unknown_fields().data(),\n" + " unknown_fields().size());\n"); } } @@ -1838,11 +2418,10 @@ GenerateByteSize(io::Printer* printer) { "int $classname$::ByteSize() const {\n" " int total_size = _extensions_.MessageSetByteSize();\n", "classname", classname_); - if (HasUnknownFields(descriptor_->file())) { - printer->Print( - " total_size += ::google::protobuf::internal::WireFormat::\n" - " ComputeUnknownMessageSetItemsSize(unknown_fields());\n"); - } + GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file())); + printer->Print( + " total_size += ::google::protobuf::internal::WireFormat::\n" + " ComputeUnknownMessageSetItemsSize(unknown_fields());\n"); printer->Print( " GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();\n" " _cached_size_ = total_size;\n" @@ -1865,7 +2444,7 @@ GenerateByteSize(io::Printer* printer) { for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); - if (!field->is_repeated()) { + if (!field->is_repeated() && !field->containing_oneof()) { // See above in GenerateClear for an explanation of this. // TODO(kenton): Share code? Unclear how to do so without // over-engineering. @@ -1915,13 +2494,45 @@ GenerateByteSize(io::Printer* printer) { } } + // Fields inside a oneof don't use _has_bits_ so we count them in a separate + // pass. + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "switch ($oneofname$_case()) {\n", + "oneofname", descriptor_->oneof_decl(i)->name()); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + PrintFieldComment(printer, field); + printer->Print( + "case k$field_name$: {\n", + "field_name", UnderscoresToCamelCase(field->name(), true)); + printer->Indent(); + field_generators_.get(field).GenerateByteSize(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(descriptor_->oneof_decl(i)->name())); + printer->Outdent(); + printer->Print( + "}\n"); + } + if (descriptor_->extension_range_count() > 0) { printer->Print( "total_size += _extensions_.ByteSize();\n" "\n"); } - if (HasUnknownFields(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file())) { printer->Print("if (!unknown_fields().empty()) {\n"); printer->Indent(); printer->Print( @@ -1930,6 +2541,10 @@ GenerateByteSize(io::Printer* printer) { " unknown_fields());\n"); printer->Outdent(); printer->Print("}\n"); + } else { + printer->Print( + "total_size += unknown_fields().size();\n" + "\n"); } // We update _cached_size_ even though this is a const method. In theory, @@ -1987,16 +2602,26 @@ GenerateIsInitialized(io::Printer* printer) { HasRequiredFields(field->message_type())) { if (field->is_repeated()) { printer->Print( - "for (int i = 0; i < $name$_size(); i++) {\n" - " if (!this->$name$(i).IsInitialized()) return false;\n" - "}\n", + "if (!::google::protobuf::internal::AllAreInitialized(this->$name$()))" + " return false;\n", "name", FieldName(field)); } else { - printer->Print( - "if (has_$name$()) {\n" - " if (!this->$name$().IsInitialized()) return false;\n" - "}\n", - "name", FieldName(field)); + if (field->options().weak()) { + // For weak fields, use the data member (google::protobuf::Message*) instead + // of the getter to avoid a link dependency on the weak message type + // which is only forward declared. + printer->Print( + "if (has_$name$()) {\n" + " if (!this->$name$_->IsInitialized()) return false;\n" + "}\n", + "name", FieldName(field)); + } else { + printer->Print( + "if (has_$name$()) {\n" + " if (!this->$name$().IsInitialized()) return false;\n" + "}\n", + "name", FieldName(field)); + } } } } diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h index a7e43d9c..3b4085dc 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.h +++ b/src/google/protobuf/compiler/cpp/cpp_message.h @@ -35,8 +35,9 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__ #define GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__ +#include <memory> #include <string> -#include <google/protobuf/stubs/common.h> +#include <vector> #include <google/protobuf/compiler/cpp/cpp_field.h> #include <google/protobuf/compiler/cpp/cpp_options.h> @@ -132,6 +133,7 @@ class MessageGenerator { // Generate standard Message methods. void GenerateClear(io::Printer* printer); + void GenerateOneofClear(io::Printer* printer); void GenerateMergeFromCodedStream(io::Printer* printer); void GenerateSerializeWithCachedSizes(io::Printer* printer); void GenerateSerializeWithCachedSizesToArray(io::Printer* printer); @@ -156,9 +158,11 @@ class MessageGenerator { string classname_; Options options_; FieldGeneratorMap field_generators_; + vector< vector<string> > runs_of_fields_; // that might be trivially cleared scoped_array<scoped_ptr<MessageGenerator> > nested_generators_; scoped_array<scoped_ptr<EnumGenerator> > enum_generators_; scoped_array<scoped_ptr<ExtensionGenerator> > extension_generators_; + bool uses_string_; 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 447f975f..08a4f257 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -53,6 +53,12 @@ void SetMessageVariables(const FieldDescriptor* descriptor, (HasFastArraySerialization(descriptor->message_type()->file()) ? "MaybeToArray" : ""); + // NOTE: Escaped here to unblock proto1->proto2 migration. + // TODO(liujisi): Extend this to apply for other conflicting methods. + (*variables)["release_name"] = + SafeFunctionName(descriptor->containing_type(), + descriptor, "release_"); + (*variables)["full_name"] = descriptor->full_name(); } } // namespace @@ -78,14 +84,15 @@ GenerateAccessorDeclarations(io::Printer* printer) const { printer->Print(variables_, "inline const $type$& $name$() const$deprecation$;\n" "inline $type$* mutable_$name$()$deprecation$;\n" - "inline $type$* release_$name$()$deprecation$;\n" + "inline $type$* $release_name$()$deprecation$;\n" "inline void set_allocated_$name$($type$* $name$)$deprecation$;\n"); } void MessageFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { printer->Print(variables_, - "inline const $type$& $classname$::$name$() const {\n"); + "inline const $type$& $classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n"); PrintHandlingOptionalStaticInitializers( variables_, descriptor_->file(), printer, @@ -99,9 +106,10 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { "inline $type$* $classname$::mutable_$name$() {\n" " set_has_$name$();\n" " if ($name$_ == NULL) $name$_ = new $type$;\n" + " // @@protoc_insertion_point(field_mutable:$full_name$)\n" " return $name$_;\n" "}\n" - "inline $type$* $classname$::release_$name$() {\n" + "inline $type$* $classname$::$release_name$() {\n" " clear_has_$name$();\n" " $type$* temp = $name$_;\n" " $name$_ = NULL;\n" @@ -115,6 +123,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " } else {\n" " clear_has_$name$();\n" " }\n" + " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" "}\n"); } @@ -178,6 +187,69 @@ GenerateByteSize(io::Printer* printer) const { // =================================================================== +MessageOneofFieldGenerator:: +MessageOneofFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) + : MessageFieldGenerator(descriptor, options) { + SetCommonOneofFieldVariables(descriptor, &variables_); +} + +MessageOneofFieldGenerator::~MessageOneofFieldGenerator() {} + +void MessageOneofFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const $type$& $classname$::$name$() const {\n" + " return has_$name$() ? *$oneof_prefix$$name$_\n" + " : $type$::default_instance();\n" + "}\n" + "inline $type$* $classname$::mutable_$name$() {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_ = new $type$;\n" + " }\n" + " return $oneof_prefix$$name$_;\n" + "}\n" + "inline $type$* $classname$::$release_name$() {\n" + " if (has_$name$()) {\n" + " clear_has_$oneof_name$();\n" + " $type$* temp = $oneof_prefix$$name$_;\n" + " $oneof_prefix$$name$_ = NULL;\n" + " return temp;\n" + " } else {\n" + " return NULL;\n" + " }\n" + "}\n" + "inline void $classname$::set_allocated_$name$($type$* $name$) {\n" + " clear_$oneof_name$();\n" + " if ($name$) {\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_ = $name$;\n" + " }\n" + "}\n"); +} + +void MessageOneofFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + // if it is the active field, it cannot be NULL. + printer->Print(variables_, + "delete $oneof_prefix$$name$_;\n"); +} + +void MessageOneofFieldGenerator:: +GenerateSwappingCode(io::Printer* printer) const { + // Don't print any swapping code. Swapping the union will swap this field. +} + +void MessageOneofFieldGenerator:: +GenerateConstructorCode(io::Printer* printer) const { + // Don't print any constructor code. The field is in a union. We allocate + // space only when this field is used. +} + +// =================================================================== + RepeatedMessageFieldGenerator:: RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, const Options& options) @@ -210,21 +282,26 @@ void RepeatedMessageFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { printer->Print(variables_, "inline const $type$& $classname$::$name$(int index) const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" " return $name$_.$cppget$(index);\n" "}\n" "inline $type$* $classname$::mutable_$name$(int index) {\n" + " // @@protoc_insertion_point(field_mutable:$full_name$)\n" " return $name$_.Mutable(index);\n" "}\n" "inline $type$* $classname$::add_$name$() {\n" + " // @@protoc_insertion_point(field_add:$full_name$)\n" " return $name$_.Add();\n" "}\n"); printer->Print(variables_, "inline const ::google::protobuf::RepeatedPtrField< $type$ >&\n" "$classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_list:$full_name$)\n" " return $name$_;\n" "}\n" "inline ::google::protobuf::RepeatedPtrField< $type$ >*\n" "$classname$::mutable_$name$() {\n" + " // @@protoc_insertion_point(field_mutable_list:$full_name$)\n" " return &$name$_;\n" "}\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.h b/src/google/protobuf/compiler/cpp/cpp_message_field.h index a5ed68a5..4c01795c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.h @@ -63,13 +63,30 @@ class MessageFieldGenerator : public FieldGenerator { void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; void GenerateByteSize(io::Printer* printer) const; - private: + protected: const FieldDescriptor* descriptor_; map<string, string> variables_; + private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator); }; +class MessageOneofFieldGenerator : public MessageFieldGenerator { + public: + explicit MessageOneofFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); + ~MessageOneofFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateSwappingCode(io::Printer* printer) const; + void GenerateConstructorCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageOneofFieldGenerator); +}; + class RepeatedMessageFieldGenerator : public FieldGenerator { public: explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, diff --git a/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc index 5c4aa4fb..697b95f0 100644 --- a/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc @@ -34,6 +34,8 @@ // It seemed like parameterizing it would add more complexity than it is // worth. +#include <memory> + #include <google/protobuf/compiler/cpp/cpp_generator.h> #include <google/protobuf/compiler/command_line_interface.h> #include <google/protobuf/io/zero_copy_stream.h> @@ -73,7 +75,7 @@ class TestGenerator : public CodeGenerator { void TryInsert(const string& filename, const string& insertion_point, GeneratorContext* context) const { scoped_ptr<io::ZeroCopyOutputStream> output( - context->OpenForInsert(filename, insertion_point)); + context->OpenForInsert(filename, insertion_point)); io::Printer printer(output.get(), '$'); printer.Print("// inserted $name$\n", "name", insertion_point); } @@ -83,13 +85,13 @@ class TestGenerator : public CodeGenerator { // not verify that they are correctly-placed; that would require actually // compiling the output which is a bit more than I care to do for this test. TEST(CppPluginTest, PluginTest) { - File::WriteStringToFileOrDie( - "syntax = \"proto2\";\n" - "package foo;\n" - "message Bar {\n" - " message Baz {}\n" - "}\n", - TestTempDir() + "/test.proto"); + GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/test.proto", + "syntax = \"proto2\";\n" + "package foo;\n" + "message Bar {\n" + " message Baz {}\n" + "}\n", + true)); google::protobuf::compiler::CommandLineInterface cli; cli.SetInputsAreProtoPathRelative(true); diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc index 1c35fefa..cb72fb1d 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc @@ -93,6 +93,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, (*variables)["wire_format_field_type"] = "::google::protobuf::internal::WireFormatLite::" + FieldDescriptorProto_Type_Name( static_cast<FieldDescriptorProto_Type>(descriptor->type())); + (*variables)["full_name"] = descriptor->full_name(); } } // namespace @@ -124,11 +125,13 @@ void PrimitiveFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { printer->Print(variables_, "inline $type$ $classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" " return $name$_;\n" "}\n" "inline void $classname$::set_$name$($type$ value) {\n" " set_has_$name$();\n" " $name$_ = value;\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" "}\n"); } @@ -191,6 +194,62 @@ GenerateByteSize(io::Printer* printer) const { // =================================================================== +PrimitiveOneofFieldGenerator:: +PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) + : PrimitiveFieldGenerator(descriptor, options) { + SetCommonOneofFieldVariables(descriptor, &variables_); +} + +PrimitiveOneofFieldGenerator::~PrimitiveOneofFieldGenerator() {} + +void PrimitiveOneofFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline $type$ $classname$::$name$() const {\n" + " if (has_$name$()) {\n" + " return $oneof_prefix$$name$_;\n" + " }\n" + " return $default$;\n" + "}\n" + "inline void $classname$::set_$name$($type$ value) {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " }\n" + " $oneof_prefix$$name$_ = value;\n" + "}\n"); +} + +void PrimitiveOneofFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$oneof_prefix$$name$_ = $default$;\n"); +} + +void PrimitiveOneofFieldGenerator:: +GenerateSwappingCode(io::Printer* printer) const { + // Don't print any swapping code. Swapping the union will swap this field. +} + +void PrimitiveOneofFieldGenerator:: +GenerateConstructorCode(io::Printer* printer) const { + printer->Print( + variables_, + " $classname$_default_oneof_instance_->$name$_ = $default$;\n"); +} + +void PrimitiveOneofFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "clear_$oneof_name$();\n" + "DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<\n" + " $type$, $wire_format_field_type$>(\n" + " input, &$oneof_prefix$$name$_)));\n" + "set_has_$name$();\n"); +} + +// =================================================================== + RepeatedPrimitiveFieldGenerator:: RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, const Options& options) @@ -235,21 +294,26 @@ void RepeatedPrimitiveFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { printer->Print(variables_, "inline $type$ $classname$::$name$(int index) const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" " return $name$_.Get(index);\n" "}\n" "inline void $classname$::set_$name$(int index, $type$ value) {\n" " $name$_.Set(index, value);\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" "}\n" "inline void $classname$::add_$name$($type$ value) {\n" " $name$_.Add(value);\n" + " // @@protoc_insertion_point(field_add:$full_name$)\n" "}\n"); printer->Print(variables_, "inline const ::google::protobuf::RepeatedField< $type$ >&\n" "$classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_list:$full_name$)\n" " return $name$_;\n" "}\n" "inline ::google::protobuf::RepeatedField< $type$ >*\n" "$classname$::mutable_$name$() {\n" + " // @@protoc_insertion_point(field_mutable_list:$full_name$)\n" " return &$name$_;\n" "}\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.h b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h index 48249c40..1f66c9df 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h @@ -63,13 +63,31 @@ class PrimitiveFieldGenerator : public FieldGenerator { void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; void GenerateByteSize(io::Printer* printer) const; - private: + protected: const FieldDescriptor* descriptor_; map<string, string> variables_; + private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator); }; +class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator { + public: + explicit PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); + ~PrimitiveOneofFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateSwappingCode(io::Printer* printer) const; + void GenerateConstructorCode(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveOneofFieldGenerator); +}; + class RepeatedPrimitiveFieldGenerator : public FieldGenerator { public: explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, diff --git a/src/google/protobuf/compiler/cpp/cpp_service.h b/src/google/protobuf/compiler/cpp/cpp_service.h index 820f9f5f..493f314a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_service.h +++ b/src/google/protobuf/compiler/cpp/cpp_service.h @@ -37,7 +37,6 @@ #include <map> #include <string> -#include <google/protobuf/stubs/common.h> #include <google/protobuf/compiler/cpp/cpp_options.h> #include <google/protobuf/descriptor.h> diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc index 0b58b981..d41b5bcd 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -53,10 +53,16 @@ void SetStringVariables(const FieldDescriptor* descriptor, (*variables)["default_length"] = SimpleItoa(descriptor->default_value_string().length()); (*variables)["default_variable"] = descriptor->default_value_string().empty() - ? "&::google::protobuf::internal::GetEmptyString()" + ? "&::google::protobuf::internal::GetEmptyStringAlreadyInited()" : "_default_" + FieldName(descriptor) + "_"; (*variables)["pointer_type"] = descriptor->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char"; + // NOTE: Escaped here to unblock proto1->proto2 migration. + // TODO(liujisi): Extend this to apply for other conflicting methods. + (*variables)["release_name"] = + SafeFunctionName(descriptor->containing_type(), + descriptor, "release_"); + (*variables)["full_name"] = descriptor->full_name(); } } // namespace @@ -75,6 +81,10 @@ StringFieldGenerator::~StringFieldGenerator() {} void StringFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { printer->Print(variables_, "::std::string* $name$_;\n"); +} + +void StringFieldGenerator:: +GenerateStaticMembers(io::Printer* printer) const { if (!descriptor_->default_value_string().empty()) { printer->Print(variables_, "static ::std::string* $default_variable$;\n"); } @@ -113,7 +123,7 @@ GenerateAccessorDeclarations(io::Printer* printer) const { "inline void set_$name$(const $pointer_type$* value, size_t size)" "$deprecation$;\n" "inline ::std::string* mutable_$name$()$deprecation$;\n" - "inline ::std::string* release_$name$()$deprecation$;\n" + "inline ::std::string* $release_name$()$deprecation$;\n" "inline void set_allocated_$name$(::std::string* $name$)$deprecation$;\n"); @@ -128,6 +138,7 @@ void StringFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { printer->Print(variables_, "inline const ::std::string& $classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" " return *$name$_;\n" "}\n" "inline void $classname$::set_$name$(const ::std::string& value) {\n" @@ -136,6 +147,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " $name$_ = new ::std::string;\n" " }\n" " $name$_->assign(value);\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" "}\n" "inline void $classname$::set_$name$(const char* value) {\n" " set_has_$name$();\n" @@ -143,6 +155,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " $name$_ = new ::std::string;\n" " }\n" " $name$_->assign(value);\n" + " // @@protoc_insertion_point(field_set_char:$full_name$)\n" "}\n" "inline " "void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n" @@ -151,6 +164,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " $name$_ = new ::std::string;\n" " }\n" " $name$_->assign(reinterpret_cast<const char*>(value), size);\n" + " // @@protoc_insertion_point(field_set_pointer:$full_name$)\n" "}\n" "inline ::std::string* $classname$::mutable_$name$() {\n" " set_has_$name$();\n" @@ -164,9 +178,10 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { } printer->Print(variables_, " }\n" + " // @@protoc_insertion_point(field_mutable:$full_name$)\n" " return $name$_;\n" "}\n" - "inline ::std::string* $classname$::release_$name$() {\n" + "inline ::std::string* $classname$::$release_name$() {\n" " clear_has_$name$();\n" " if ($name$_ == $default_variable$) {\n" " return NULL;\n" @@ -187,6 +202,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " clear_has_$name$();\n" " $name$_ = const_cast< ::std::string*>($default_variable$);\n" " }\n" + " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" "}\n"); } @@ -263,9 +279,10 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { if (HasUtf8Verification(descriptor_->file()) && descriptor_->type() == FieldDescriptor::TYPE_STRING) { printer->Print(variables_, - "::google::protobuf::internal::WireFormat::VerifyUTF8String(\n" + "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" " this->$name$().data(), this->$name$().length(),\n" - " ::google::protobuf::internal::WireFormat::PARSE);\n"); + " ::google::protobuf::internal::WireFormat::PARSE,\n" + " \"$name$\");\n"); } } @@ -274,12 +291,13 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { if (HasUtf8Verification(descriptor_->file()) && descriptor_->type() == FieldDescriptor::TYPE_STRING) { printer->Print(variables_, - "::google::protobuf::internal::WireFormat::VerifyUTF8String(\n" + "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" " this->$name$().data(), this->$name$().length(),\n" - " ::google::protobuf::internal::WireFormat::SERIALIZE);\n"); + " ::google::protobuf::internal::WireFormat::SERIALIZE,\n" + " \"$name$\");\n"); } printer->Print(variables_, - "::google::protobuf::internal::WireFormatLite::Write$declared_type$(\n" + "::google::protobuf::internal::WireFormatLite::Write$declared_type$MaybeAliased(\n" " $number$, this->$name$(), output);\n"); } @@ -288,9 +306,10 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { if (HasUtf8Verification(descriptor_->file()) && descriptor_->type() == FieldDescriptor::TYPE_STRING) { printer->Print(variables_, - "::google::protobuf::internal::WireFormat::VerifyUTF8String(\n" + "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" " this->$name$().data(), this->$name$().length(),\n" - " ::google::protobuf::internal::WireFormat::SERIALIZE);\n"); + " ::google::protobuf::internal::WireFormat::SERIALIZE,\n" + " \"$name$\");\n"); } printer->Print(variables_, "target =\n" @@ -308,6 +327,125 @@ GenerateByteSize(io::Printer* printer) const { // =================================================================== +StringOneofFieldGenerator:: +StringOneofFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) + : StringFieldGenerator(descriptor, options) { + SetCommonOneofFieldVariables(descriptor, &variables_); +} + +StringOneofFieldGenerator::~StringOneofFieldGenerator() {} + +void StringOneofFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::std::string& $classname$::$name$() const {\n" + " if (has_$name$()) {\n" + " return *$oneof_prefix$$name$_;\n" + " }\n"); + if (descriptor_->default_value_string().empty()) { + printer->Print(variables_, + " return ::google::protobuf::internal::GetEmptyStringAlreadyInited();\n"); + } else { + printer->Print(variables_, + " return *$default_variable$;\n"); + } + printer->Print(variables_, + "}\n" + "inline void $classname$::set_$name$(const ::std::string& value) {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_ = new ::std::string;\n" + " }\n" + " $oneof_prefix$$name$_->assign(value);\n" + "}\n" + "inline void $classname$::set_$name$(const char* value) {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_ = new ::std::string;\n" + " }\n" + " $oneof_prefix$$name$_->assign(value);\n" + "}\n" + "inline " + "void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_ = new ::std::string;\n" + " }\n" + " $oneof_prefix$$name$_->assign(\n" + " reinterpret_cast<const char*>(value), size);\n" + "}\n" + "inline ::std::string* $classname$::mutable_$name$() {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n"); + if (descriptor_->default_value_string().empty()) { + printer->Print(variables_, + " $oneof_prefix$$name$_ = new ::std::string;\n"); + } else { + printer->Print(variables_, + " $oneof_prefix$$name$_ = new ::std::string(*$default_variable$);\n"); + } + printer->Print(variables_, + " }\n" + " return $oneof_prefix$$name$_;\n" + "}\n" + "inline ::std::string* $classname$::$release_name$() {\n" + " if (has_$name$()) {\n" + " clear_has_$oneof_name$();\n" + " ::std::string* temp = $oneof_prefix$$name$_;\n" + " $oneof_prefix$$name$_ = NULL;\n" + " return temp;\n" + " } else {\n" + " return NULL;\n" + " }\n" + "}\n" + "inline void $classname$::set_allocated_$name$(::std::string* $name$) {\n" + " clear_$oneof_name$();\n" + " if ($name$) {\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_ = $name$;\n" + " }\n" + "}\n"); +} + +void StringOneofFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, + "delete $oneof_prefix$$name$_;\n"); +} + +void StringOneofFieldGenerator:: +GenerateSwappingCode(io::Printer* printer) const { + // Don't print any swapping code. Swapping the union will swap this field. +} + +void StringOneofFieldGenerator:: +GenerateConstructorCode(io::Printer* printer) const { + if (!descriptor_->default_value_string().empty()) { + printer->Print(variables_, + " $classname$_default_oneof_instance_->$name$_ = " + "$classname$::$default_variable$;\n"); + } else { + printer->Print(variables_, + " $classname$_default_oneof_instance_->$name$_ = " + "$default_variable$;\n"); + } +} + +void StringOneofFieldGenerator:: +GenerateDestructorCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has_$name$()) {\n" + " delete $oneof_prefix$$name$_;\n" + "}\n"); +} + +// =================================================================== + RepeatedStringFieldGenerator:: RepeatedStringFieldGenerator(const FieldDescriptor* descriptor, const Options& options) @@ -365,43 +503,53 @@ void RepeatedStringFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { printer->Print(variables_, "inline const ::std::string& $classname$::$name$(int index) const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" " return $name$_.$cppget$(index);\n" "}\n" "inline ::std::string* $classname$::mutable_$name$(int index) {\n" + " // @@protoc_insertion_point(field_mutable:$full_name$)\n" " return $name$_.Mutable(index);\n" "}\n" "inline void $classname$::set_$name$(int index, const ::std::string& value) {\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" " $name$_.Mutable(index)->assign(value);\n" "}\n" "inline void $classname$::set_$name$(int index, const char* value) {\n" " $name$_.Mutable(index)->assign(value);\n" + " // @@protoc_insertion_point(field_set_char:$full_name$)\n" "}\n" "inline void " "$classname$::set_$name$" "(int index, const $pointer_type$* value, size_t size) {\n" " $name$_.Mutable(index)->assign(\n" " reinterpret_cast<const char*>(value), size);\n" + " // @@protoc_insertion_point(field_set_pointer:$full_name$)\n" "}\n" "inline ::std::string* $classname$::add_$name$() {\n" " return $name$_.Add();\n" "}\n" "inline void $classname$::add_$name$(const ::std::string& value) {\n" " $name$_.Add()->assign(value);\n" + " // @@protoc_insertion_point(field_add:$full_name$)\n" "}\n" "inline void $classname$::add_$name$(const char* value) {\n" " $name$_.Add()->assign(value);\n" + " // @@protoc_insertion_point(field_add_char:$full_name$)\n" "}\n" "inline void " "$classname$::add_$name$(const $pointer_type$* value, size_t size) {\n" " $name$_.Add()->assign(reinterpret_cast<const char*>(value), size);\n" + " // @@protoc_insertion_point(field_add_pointer:$full_name$)\n" "}\n"); printer->Print(variables_, "inline const ::google::protobuf::RepeatedPtrField< ::std::string>&\n" "$classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_list:$full_name$)\n" " return $name$_;\n" "}\n" "inline ::google::protobuf::RepeatedPtrField< ::std::string>*\n" "$classname$::mutable_$name$() {\n" + " // @@protoc_insertion_point(field_mutable_list:$full_name$)\n" " return &$name$_;\n" "}\n"); } @@ -434,10 +582,11 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { if (HasUtf8Verification(descriptor_->file()) && descriptor_->type() == FieldDescriptor::TYPE_STRING) { printer->Print(variables_, - "::google::protobuf::internal::WireFormat::VerifyUTF8String(\n" + "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" " this->$name$(this->$name$_size() - 1).data(),\n" " this->$name$(this->$name$_size() - 1).length(),\n" - " ::google::protobuf::internal::WireFormat::PARSE);\n"); + " ::google::protobuf::internal::WireFormat::PARSE,\n" + " \"$name$\");\n"); } } @@ -448,9 +597,10 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { if (HasUtf8Verification(descriptor_->file()) && descriptor_->type() == FieldDescriptor::TYPE_STRING) { printer->Print(variables_, - "::google::protobuf::internal::WireFormat::VerifyUTF8String(\n" + "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" " this->$name$(i).data(), this->$name$(i).length(),\n" - " ::google::protobuf::internal::WireFormat::SERIALIZE);\n"); + " ::google::protobuf::internal::WireFormat::SERIALIZE,\n" + " \"$name$\");\n"); } printer->Print(variables_, " ::google::protobuf::internal::WireFormatLite::Write$declared_type$(\n" @@ -465,9 +615,10 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { if (HasUtf8Verification(descriptor_->file()) && descriptor_->type() == FieldDescriptor::TYPE_STRING) { printer->Print(variables_, - " ::google::protobuf::internal::WireFormat::VerifyUTF8String(\n" + " ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" " this->$name$(i).data(), this->$name$(i).length(),\n" - " ::google::protobuf::internal::WireFormat::SERIALIZE);\n"); + " ::google::protobuf::internal::WireFormat::SERIALIZE,\n" + " \"$name$\");\n"); } printer->Print(variables_, " target = ::google::protobuf::internal::WireFormatLite::\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.h b/src/google/protobuf/compiler/cpp/cpp_string_field.h index 3264134a..65f605c0 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.h @@ -52,6 +52,7 @@ class StringFieldGenerator : public FieldGenerator { // implements FieldGenerator --------------------------------------- void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateStaticMembers(io::Printer* printer) const; void GenerateAccessorDeclarations(io::Printer* printer) const; void GenerateInlineAccessorDefinitions(io::Printer* printer) const; void GenerateNonInlineAccessorDefinitions(io::Printer* printer) const; @@ -67,13 +68,31 @@ class StringFieldGenerator : public FieldGenerator { void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; void GenerateByteSize(io::Printer* printer) const; - private: + protected: const FieldDescriptor* descriptor_; map<string, string> variables_; + private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringFieldGenerator); }; +class StringOneofFieldGenerator : public StringFieldGenerator { + public: + explicit StringOneofFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); + ~StringOneofFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateSwappingCode(io::Printer* printer) const; + void GenerateConstructorCode(io::Printer* printer) const; + void GenerateDestructorCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringOneofFieldGenerator); +}; + class RepeatedStringFieldGenerator : public FieldGenerator { public: explicit RepeatedStringFieldGenerator(const FieldDescriptor* descriptor, 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 e14a818c..8b9ff5ae 100644 --- a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto +++ b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto @@ -98,6 +98,7 @@ message TestConflictingSymbolNames { // Some keywords. optional uint32 int = 30; optional uint32 friend = 31; + optional uint32 class = 37; // The generator used to #define a macro called "DO" inside the .cc file. message DO {} @@ -107,6 +108,14 @@ message TestConflictingSymbolNames { optional int32 field_type = 33; optional bool is_packed = 34; + // test conflicting release_$name$. "length" and "do" field in this message + // must remain string or message fields to make the test valid. + optional string release_length = 35; + // A more extreme case, the field name "do" here is a keyword, which will be + // escaped to "do_" already. Test there is no conflict even with escaped field + // names. + optional DO release_do = 36; + extensions 1000 to max; } diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc index 1eae29b5..4ef1da1e 100644 --- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -46,6 +46,7 @@ #include <google/protobuf/compiler/cpp/cpp_unittest.h> +#include <memory> #include <vector> #include <google/protobuf/unittest.pb.h> @@ -53,6 +54,7 @@ #include <google/protobuf/unittest_embed_optimize_for.pb.h> #include <google/protobuf/unittest_no_generic_services.pb.h> #include <google/protobuf/test_util.h> +#include <google/protobuf/compiler/cpp/cpp_helpers.h> #include <google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.h> #include <google/protobuf/compiler/importer.h> #include <google/protobuf/io/coded_stream.h> @@ -148,6 +150,19 @@ TEST(GeneratedMessageTest, Defaults) { &message.optional_import_message()); } +TEST(GeneratedMessageTest, Int32StringConversion) { + EXPECT_EQ("971", Int32ToString(971)); + EXPECT_EQ("(~0x7fffffff)", Int32ToString(kint32min)); + EXPECT_EQ("2147483647", Int32ToString(kint32max)); +} + +TEST(GeneratedMessageTest, Int64StringConversion) { + EXPECT_EQ("GOOGLE_LONGLONG(971)", Int64ToString(971)); + EXPECT_EQ("GOOGLE_LONGLONG(-2147483648)", Int64ToString(kint32min)); + EXPECT_EQ("GOOGLE_LONGLONG(-0x8000000000000000)", Int64ToString(kint64min)); + EXPECT_EQ("GOOGLE_LONGLONG(9223372036854775807)", Int64ToString(kint64max)); +} + TEST(GeneratedMessageTest, FloatingPointDefaults) { const unittest::TestExtremeDefaultValues& extreme_default = unittest::TestExtremeDefaultValues::default_instance(); @@ -233,11 +248,10 @@ TEST(GeneratedMessageTest, ReleaseString) { message.set_default_string("blah"); EXPECT_TRUE(message.has_default_string()); - string* str = message.release_default_string(); + scoped_ptr<string> str(message.release_default_string()); EXPECT_FALSE(message.has_default_string()); ASSERT_TRUE(str != NULL); EXPECT_EQ("blah", *str); - delete str; EXPECT_EQ(NULL, message.release_default_string()); EXPECT_FALSE(message.has_default_string()); @@ -253,12 +267,11 @@ TEST(GeneratedMessageTest, ReleaseMessage) { EXPECT_FALSE(message.has_optional_nested_message()); message.mutable_optional_nested_message()->set_bb(1); - unittest::TestAllTypes::NestedMessage* nest = - message.release_optional_nested_message(); + scoped_ptr<unittest::TestAllTypes::NestedMessage> nest( + message.release_optional_nested_message()); EXPECT_FALSE(message.has_optional_nested_message()); ASSERT_TRUE(nest != NULL); EXPECT_EQ(1, nest->bb()); - delete nest; EXPECT_EQ(NULL, message.release_optional_nested_message()); EXPECT_FALSE(message.has_optional_nested_message()); @@ -381,6 +394,7 @@ TEST(GeneratedMessageTest, StringCharStarLength) { EXPECT_EQ("wx", message.repeated_string(0)); } + TEST(GeneratedMessageTest, CopyFrom) { unittest::TestAllTypes message1, message2; @@ -393,6 +407,7 @@ TEST(GeneratedMessageTest, CopyFrom) { TestUtil::ExpectAllFieldsSet(message2); } + TEST(GeneratedMessageTest, SwapWithEmpty) { unittest::TestAllTypes message1, message2; TestUtil::SetAllFields(&message1); @@ -763,6 +778,9 @@ TEST(GeneratedMessageTest, TestConflictingSymbolNames) { message.set_friend_(5); EXPECT_EQ(5, message.friend_()); + message.set_class_(6); + EXPECT_EQ(6, message.class_()); + // Instantiate extension template functions to test conflicting template // parameter names. typedef protobuf_unittest::TestConflictingSymbolNamesExtension ExtensionMessage; @@ -840,6 +858,40 @@ TEST(GeneratedMessageTest, TestSpaceUsed) { message1.SpaceUsed()); } +TEST(GeneratedMessageTest, TestOneofSpaceUsed) { + unittest::TestOneof2 message1; + EXPECT_LE(sizeof(unittest::TestOneof2), message1.SpaceUsed()); + + const int empty_message_size = message1.SpaceUsed(); + // Setting primitive types shouldn't affect the space used. + message1.set_foo_int(123); + message1.set_bar_int(12345); + EXPECT_EQ(empty_message_size, message1.SpaceUsed()); + + // Setting a string in oneof to a small value should only increase SpaceUsed() + // by the size of a string object. + message1.set_foo_string("abc"); + EXPECT_LE(empty_message_size + sizeof(string), message1.SpaceUsed()); + + // Setting a string in oneof to a value larger than the string object itself + // should increase SpaceUsed(), because it cannot store the value internally. + message1.set_foo_string(string(sizeof(string) + 1, 'x')); + int min_expected_increase = message1.foo_string().capacity() + + sizeof(string); + EXPECT_LE(empty_message_size + min_expected_increase, + message1.SpaceUsed()); + + // Setting a message in oneof should delete the other fields and increase the + // size by the size of the nested message type. NestedMessage is simple enough + // that it is equal to sizeof(NestedMessage) + message1.mutable_foo_message(); + ASSERT_EQ(sizeof(unittest::TestOneof2::NestedMessage), + message1.foo_message().SpaceUsed()); + EXPECT_EQ(empty_message_size + + sizeof(unittest::TestOneof2::NestedMessage), + message1.SpaceUsed()); +} + #endif // !PROTOBUF_TEST_NO_DESCRIPTORS @@ -887,6 +939,9 @@ TEST(GeneratedEnumTest, EnumValuesAsSwitchCases) { case unittest::TestAllTypes::BAZ: i = 3; break; + case unittest::TestAllTypes::NEG: + i = -1; + break; // no default case: We want to make sure the compiler recognizes that // all cases are covered. (GCC warns if you do not cover all cases of // an enum in a switch.) @@ -915,7 +970,7 @@ TEST(GeneratedEnumTest, IsValidValue) { } TEST(GeneratedEnumTest, MinAndMax) { - EXPECT_EQ(unittest::TestAllTypes::FOO, + EXPECT_EQ(unittest::TestAllTypes::NEG, unittest::TestAllTypes::NestedEnum_MIN); EXPECT_EQ(unittest::TestAllTypes::BAZ, unittest::TestAllTypes::NestedEnum_MAX); @@ -989,6 +1044,20 @@ TEST(GeneratedEnumTest, GetEnumDescriptor) { GetEnumDescriptor<unittest::TestSparseEnum>()); } +enum NonProtoEnum { + kFoo = 1, +}; + +TEST(GeneratedEnumTest, IsProtoEnumTypeTrait) { + EXPECT_TRUE(is_proto_enum<unittest::TestAllTypes::NestedEnum>::value); + EXPECT_TRUE(is_proto_enum<unittest::ForeignEnum>::value); + EXPECT_TRUE(is_proto_enum<unittest::TestEnumWithDupValue>::value); + EXPECT_TRUE(is_proto_enum<unittest::TestSparseEnum>::value); + + EXPECT_FALSE(is_proto_enum<int>::value); + EXPECT_FALSE(is_proto_enum<NonProtoEnum>::value); +} + #endif // PROTOBUF_TEST_NO_DESCRIPTORS // =================================================================== @@ -1288,6 +1357,657 @@ TEST_F(GeneratedServiceTest, NotImplemented) { EXPECT_TRUE(controller.called_); } +// =================================================================== + +class OneofTest : public testing::Test { + protected: + virtual void SetUp() { + } + + void ExpectEnumCasesWork(const unittest::TestOneof2 &message) { + switch (message.foo_case()) { + case unittest::TestOneof2::kFooInt: + EXPECT_TRUE(message.has_foo_int()); + break; + case unittest::TestOneof2::kFooString: + EXPECT_TRUE(message.has_foo_string()); + break; + case unittest::TestOneof2::kFooBytes: + EXPECT_TRUE(message.has_foo_bytes()); + break; + case unittest::TestOneof2::kFooEnum: + EXPECT_TRUE(message.has_foo_enum()); + break; + case unittest::TestOneof2::kFooMessage: + EXPECT_TRUE(message.has_foo_message()); + break; + case unittest::TestOneof2::kFoogroup: + EXPECT_TRUE(message.has_foogroup()); + break; + case unittest::TestOneof2::FOO_NOT_SET: + break; + } + } +}; + +TEST_F(OneofTest, SettingOneFieldClearsOthers) { + unittest::TestOneof2 message; + + message.set_foo_int(123); + EXPECT_TRUE(message.has_foo_int()); + TestUtil::ExpectAtMostOneFieldSetInOneof(message); + + message.set_foo_string("foo"); + EXPECT_TRUE(message.has_foo_string()); + TestUtil::ExpectAtMostOneFieldSetInOneof(message); + + + message.set_foo_bytes("qux"); + EXPECT_TRUE(message.has_foo_bytes()); + TestUtil::ExpectAtMostOneFieldSetInOneof(message); + + message.set_foo_enum(unittest::TestOneof2::FOO); + EXPECT_TRUE(message.has_foo_enum()); + TestUtil::ExpectAtMostOneFieldSetInOneof(message); + + message.mutable_foo_message()->set_qux_int(234); + EXPECT_TRUE(message.has_foo_message()); + TestUtil::ExpectAtMostOneFieldSetInOneof(message); + + message.mutable_foogroup()->set_a(345); + EXPECT_TRUE(message.has_foogroup()); + TestUtil::ExpectAtMostOneFieldSetInOneof(message); + + + // we repeat this because we didn't test if this properly clears other fields + // at the beginning. + message.set_foo_int(123); + EXPECT_TRUE(message.has_foo_int()); + TestUtil::ExpectAtMostOneFieldSetInOneof(message); +} + +TEST_F(OneofTest, EnumCases) { + unittest::TestOneof2 message; + + message.set_foo_int(123); + ExpectEnumCasesWork(message); + message.set_foo_string("foo"); + ExpectEnumCasesWork(message); + message.set_foo_bytes("qux"); + ExpectEnumCasesWork(message); + message.set_foo_enum(unittest::TestOneof2::FOO); + ExpectEnumCasesWork(message); + message.mutable_foo_message()->set_qux_int(234); + ExpectEnumCasesWork(message); + message.mutable_foogroup()->set_a(345); + ExpectEnumCasesWork(message); +} + +TEST_F(OneofTest, PrimitiveType) { + unittest::TestOneof2 message; + // Unset field returns default value + EXPECT_EQ(message.foo_int(), 0); + + message.set_foo_int(123); + EXPECT_TRUE(message.has_foo_int()); + EXPECT_EQ(message.foo_int(), 123); + message.clear_foo_int(); + EXPECT_FALSE(message.has_foo_int()); +} + +TEST_F(OneofTest, EnumType) { + unittest::TestOneof2 message; + // Unset field returns default value + EXPECT_EQ(message.foo_enum(), 1); + + message.set_foo_enum(unittest::TestOneof2::FOO); + EXPECT_TRUE(message.has_foo_enum()); + EXPECT_EQ(message.foo_enum(), unittest::TestOneof2::FOO); + message.clear_foo_enum(); + EXPECT_FALSE(message.has_foo_enum()); +} + +TEST_F(OneofTest, SetString) { + // Check that setting a string field in various ways works + unittest::TestOneof2 message; + + // Unset field returns default value + EXPECT_EQ(message.foo_string(), ""); + + message.set_foo_string("foo"); + EXPECT_TRUE(message.has_foo_string()); + EXPECT_EQ(message.foo_string(), "foo"); + message.clear_foo_string(); + EXPECT_FALSE(message.has_foo_string()); + + message.set_foo_string(string("bar")); + EXPECT_TRUE(message.has_foo_string()); + EXPECT_EQ(message.foo_string(), "bar"); + message.clear_foo_string(); + EXPECT_FALSE(message.has_foo_string()); + + + message.set_foo_string("qux", 3); + EXPECT_TRUE(message.has_foo_string()); + EXPECT_EQ(message.foo_string(), "qux"); + message.clear_foo_string(); + EXPECT_FALSE(message.has_foo_string()); + + message.mutable_foo_string()->assign("quux"); + EXPECT_TRUE(message.has_foo_string()); + EXPECT_EQ(message.foo_string(), "quux"); + message.clear_foo_string(); + EXPECT_FALSE(message.has_foo_string()); + + message.set_foo_string("corge"); + EXPECT_TRUE(message.has_foo_string()); + EXPECT_EQ(message.foo_string(), "corge"); + message.clear_foo_string(); + EXPECT_FALSE(message.has_foo_string()); +} + +TEST_F(OneofTest, ReleaseString) { + // Check that release_foo() starts out NULL, and gives us a value + // that we can delete after it's been set. + unittest::TestOneof2 message; + + EXPECT_EQ(NULL, message.release_foo_string()); + EXPECT_FALSE(message.has_foo_string()); + + message.set_foo_string("blah"); + EXPECT_TRUE(message.has_foo_string()); + scoped_ptr<string> str(message.release_foo_string()); + EXPECT_FALSE(message.has_foo_string()); + ASSERT_TRUE(str != NULL); + EXPECT_EQ("blah", *str); + + EXPECT_EQ(NULL, message.release_foo_string()); + EXPECT_FALSE(message.has_foo_string()); +} + +TEST_F(OneofTest, SetAllocatedString) { + // Check that set_allocated_foo() works for strings. + unittest::TestOneof2 message; + + EXPECT_FALSE(message.has_foo_string()); + const string kHello("hello"); + message.set_foo_string(kHello); + EXPECT_TRUE(message.has_foo_string()); + + message.set_allocated_foo_string(NULL); + EXPECT_FALSE(message.has_foo_string()); + EXPECT_EQ("", message.foo_string()); + + message.set_allocated_foo_string(new string(kHello)); + EXPECT_TRUE(message.has_foo_string()); + EXPECT_EQ(kHello, message.foo_string()); +} + + +TEST_F(OneofTest, SetMessage) { + // Check that setting a message field works + unittest::TestOneof2 message; + + // Unset field returns default instance + EXPECT_EQ(&message.foo_message(), + &unittest::TestOneof2_NestedMessage::default_instance()); + EXPECT_EQ(message.foo_message().qux_int(), 0); + + message.mutable_foo_message()->set_qux_int(234); + EXPECT_TRUE(message.has_foo_message()); + EXPECT_EQ(message.foo_message().qux_int(), 234); + message.clear_foo_message(); + EXPECT_FALSE(message.has_foo_message()); +} + +TEST_F(OneofTest, ReleaseMessage) { + // Check that release_foo() starts out NULL, and gives us a value + // that we can delete after it's been set. + unittest::TestOneof2 message; + + EXPECT_EQ(NULL, message.release_foo_message()); + EXPECT_FALSE(message.has_foo_message()); + + message.mutable_foo_message()->set_qux_int(1); + EXPECT_TRUE(message.has_foo_message()); + scoped_ptr<unittest::TestOneof2_NestedMessage> mes( + message.release_foo_message()); + EXPECT_FALSE(message.has_foo_message()); + ASSERT_TRUE(mes != NULL); + EXPECT_EQ(1, mes->qux_int()); + + EXPECT_EQ(NULL, message.release_foo_message()); + EXPECT_FALSE(message.has_foo_message()); +} + +TEST_F(OneofTest, SetAllocatedMessage) { + // Check that set_allocated_foo() works for messages. + unittest::TestOneof2 message; + + EXPECT_FALSE(message.has_foo_message()); + + message.mutable_foo_message()->set_qux_int(1); + EXPECT_TRUE(message.has_foo_message()); + + message.set_allocated_foo_message(NULL); + EXPECT_FALSE(message.has_foo_message()); + EXPECT_EQ(&message.foo_message(), + &unittest::TestOneof2_NestedMessage::default_instance()); + + message.mutable_foo_message()->set_qux_int(1); + unittest::TestOneof2_NestedMessage* mes = message.release_foo_message(); + ASSERT_TRUE(mes != NULL); + EXPECT_FALSE(message.has_foo_message()); + + message.set_allocated_foo_message(mes); + EXPECT_TRUE(message.has_foo_message()); + EXPECT_EQ(1, message.foo_message().qux_int()); +} + + +TEST_F(OneofTest, Clear) { + unittest::TestOneof2 message; + + message.set_foo_int(1); + EXPECT_TRUE(message.has_foo_int()); + message.clear_foo_int(); + EXPECT_FALSE(message.has_foo_int()); +} + +TEST_F(OneofTest, Defaults) { + unittest::TestOneof2 message; + + EXPECT_FALSE(message.has_foo_int()); + EXPECT_EQ(message.foo_int(), 0); + + EXPECT_FALSE(message.has_foo_string()); + EXPECT_EQ(message.foo_string(), ""); + + + EXPECT_FALSE(message.has_foo_bytes()); + EXPECT_EQ(message.foo_bytes(), ""); + + EXPECT_FALSE(message.has_foo_enum()); + EXPECT_EQ(message.foo_enum(), 1); + + EXPECT_FALSE(message.has_foo_message()); + EXPECT_EQ(message.foo_message().qux_int(), 0); + + EXPECT_FALSE(message.has_foogroup()); + EXPECT_EQ(message.foogroup().a(), 0); + + + EXPECT_FALSE(message.has_bar_int()); + EXPECT_EQ(message.bar_int(), 5); + + EXPECT_FALSE(message.has_bar_string()); + EXPECT_EQ(message.bar_string(), "STRING"); + + + EXPECT_FALSE(message.has_bar_bytes()); + EXPECT_EQ(message.bar_bytes(), "BYTES"); + + EXPECT_FALSE(message.has_bar_enum()); + EXPECT_EQ(message.bar_enum(), 2); +} + +TEST_F(OneofTest, SwapWithEmpty) { + unittest::TestOneof2 message1, message2; + message1.set_foo_string("FOO"); + EXPECT_TRUE(message1.has_foo_string()); + message1.Swap(&message2); + EXPECT_FALSE(message1.has_foo_string()); + EXPECT_TRUE(message2.has_foo_string()); + EXPECT_EQ(message2.foo_string(), "FOO"); +} + +TEST_F(OneofTest, SwapWithSelf) { + unittest::TestOneof2 message; + message.set_foo_string("FOO"); + EXPECT_TRUE(message.has_foo_string()); + message.Swap(&message); + EXPECT_TRUE(message.has_foo_string()); + EXPECT_EQ(message.foo_string(), "FOO"); +} + +TEST_F(OneofTest, SwapBothHasFields) { + unittest::TestOneof2 message1, message2; + + message1.set_foo_string("FOO"); + EXPECT_TRUE(message1.has_foo_string()); + message2.mutable_foo_message()->set_qux_int(1); + EXPECT_TRUE(message2.has_foo_message()); + + message1.Swap(&message2); + EXPECT_FALSE(message1.has_foo_string()); + EXPECT_FALSE(message2.has_foo_message()); + EXPECT_TRUE(message1.has_foo_message()); + EXPECT_EQ(message1.foo_message().qux_int(), 1); + EXPECT_TRUE(message2.has_foo_string()); + EXPECT_EQ(message2.foo_string(), "FOO"); +} + +TEST_F(OneofTest, CopyContructor) { + unittest::TestOneof2 message1; + message1.set_foo_bytes("FOO"); + + unittest::TestOneof2 message2(message1); + EXPECT_TRUE(message2.has_foo_bytes()); + EXPECT_EQ(message2.foo_bytes(), "FOO"); +} + +TEST_F(OneofTest, CopyFrom) { + unittest::TestOneof2 message1, message2; + message1.set_foo_enum(unittest::TestOneof2::BAR); + EXPECT_TRUE(message1.has_foo_enum()); + + message2.CopyFrom(message1); + EXPECT_TRUE(message2.has_foo_enum()); + EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::BAR); + + // Copying from self should be a no-op. + message2.CopyFrom(message2); + EXPECT_TRUE(message2.has_foo_enum()); + EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::BAR); +} + +TEST_F(OneofTest, CopyAssignmentOperator) { + unittest::TestOneof2 message1; + message1.mutable_foo_message()->set_qux_int(123); + EXPECT_TRUE(message1.has_foo_message()); + + unittest::TestOneof2 message2; + message2 = message1; + EXPECT_EQ(message2.foo_message().qux_int(), 123); + + // Make sure that self-assignment does something sane. + message2 = message2; + EXPECT_EQ(message2.foo_message().qux_int(), 123); +} + +TEST_F(OneofTest, UpcastCopyFrom) { + // Test the CopyFrom method that takes in the generic const Message& + // parameter. + unittest::TestOneof2 message1, message2; + message1.mutable_foogroup()->set_a(123); + EXPECT_TRUE(message1.has_foogroup()); + + const Message* source = implicit_cast<const Message*>(&message1); + message2.CopyFrom(*source); + + EXPECT_TRUE(message2.has_foogroup()); + EXPECT_EQ(message2.foogroup().a(), 123); +} + +// Test the generated SerializeWithCachedSizesToArray(), +// This indirectly tests MergePartialFromCodedStream() +// We have to test each field type separately because we cannot set them at the +// same time +TEST_F(OneofTest, SerializationToArray) { + // Primitive type + { + unittest::TestOneof2 message1, message2; + string data; + message1.set_foo_int(123); + int size = message1.ByteSize(); + data.resize(size); + uint8* start = reinterpret_cast<uint8*>(string_as_array(&data)); + uint8* end = message1.SerializeWithCachedSizesToArray(start); + EXPECT_EQ(size, end - start); + EXPECT_TRUE(message2.ParseFromString(data)); + EXPECT_EQ(message2.foo_int(), 123); + } + + // String + { + unittest::TestOneof2 message1, message2; + string data; + message1.set_foo_string("foo"); + int size = message1.ByteSize(); + data.resize(size); + uint8* start = reinterpret_cast<uint8*>(string_as_array(&data)); + uint8* end = message1.SerializeWithCachedSizesToArray(start); + EXPECT_EQ(size, end - start); + EXPECT_TRUE(message2.ParseFromString(data)); + EXPECT_EQ(message2.foo_string(), "foo"); + } + + + // Bytes + { + unittest::TestOneof2 message1, message2; + string data; + message1.set_foo_bytes("qux"); + int size = message1.ByteSize(); + data.resize(size); + uint8* start = reinterpret_cast<uint8*>(string_as_array(&data)); + uint8* end = message1.SerializeWithCachedSizesToArray(start); + EXPECT_EQ(size, end - start); + EXPECT_TRUE(message2.ParseFromString(data)); + EXPECT_EQ(message2.foo_bytes(), "qux"); + } + + // Enum + { + unittest::TestOneof2 message1, message2; + string data; + message1.set_foo_enum(unittest::TestOneof2::FOO); + int size = message1.ByteSize(); + data.resize(size); + uint8* start = reinterpret_cast<uint8*>(string_as_array(&data)); + uint8* end = message1.SerializeWithCachedSizesToArray(start); + EXPECT_EQ(size, end - start); + EXPECT_TRUE(message2.ParseFromString(data)); + EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::FOO); + } + + // Message + { + unittest::TestOneof2 message1, message2; + string data; + message1.mutable_foo_message()->set_qux_int(234); + int size = message1.ByteSize(); + data.resize(size); + uint8* start = reinterpret_cast<uint8*>(string_as_array(&data)); + uint8* end = message1.SerializeWithCachedSizesToArray(start); + EXPECT_EQ(size, end - start); + EXPECT_TRUE(message2.ParseFromString(data)); + EXPECT_EQ(message2.foo_message().qux_int(), 234); + } + + // Group + { + unittest::TestOneof2 message1, message2; + string data; + message1.mutable_foogroup()->set_a(345); + int size = message1.ByteSize(); + data.resize(size); + uint8* start = reinterpret_cast<uint8*>(string_as_array(&data)); + uint8* end = message1.SerializeWithCachedSizesToArray(start); + EXPECT_EQ(size, end - start); + EXPECT_TRUE(message2.ParseFromString(data)); + EXPECT_EQ(message2.foogroup().a(), 345); + } + +} + +// Test the generated SerializeWithCachedSizes() by forcing the buffer to write +// one byte at a time. +// This indirectly tests MergePartialFromCodedStream() +// We have to test each field type separately because we cannot set them at the +// same time +TEST_F(OneofTest, SerializationToStream) { + // Primitive type + { + unittest::TestOneof2 message1, message2; + string data; + message1.set_foo_int(123); + int size = message1.ByteSize(); + data.resize(size); + + { + // Allow the output stream to buffer only one byte at a time. + io::ArrayOutputStream array_stream(string_as_array(&data), size, 1); + 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)); + EXPECT_EQ(message2.foo_int(), 123); + } + + // String + { + unittest::TestOneof2 message1, message2; + string data; + message1.set_foo_string("foo"); + int size = message1.ByteSize(); + data.resize(size); + + { + // Allow the output stream to buffer only one byte at a time. + io::ArrayOutputStream array_stream(string_as_array(&data), size, 1); + 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)); + EXPECT_EQ(message2.foo_string(), "foo"); + } + + + // Bytes + { + unittest::TestOneof2 message1, message2; + string data; + message1.set_foo_bytes("qux"); + int size = message1.ByteSize(); + data.resize(size); + + { + // Allow the output stream to buffer only one byte at a time. + io::ArrayOutputStream array_stream(string_as_array(&data), size, 1); + 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)); + EXPECT_EQ(message2.foo_bytes(), "qux"); + } + + // Enum + { + unittest::TestOneof2 message1, message2; + string data; + message1.set_foo_enum(unittest::TestOneof2::FOO); + int size = message1.ByteSize(); + data.resize(size); + + { + // Allow the output stream to buffer only one byte at a time. + io::ArrayOutputStream array_stream(string_as_array(&data), size, 1); + 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)); + EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::FOO); + } + + // Message + { + unittest::TestOneof2 message1, message2; + string data; + message1.mutable_foo_message()->set_qux_int(234); + int size = message1.ByteSize(); + data.resize(size); + + { + // Allow the output stream to buffer only one byte at a time. + io::ArrayOutputStream array_stream(string_as_array(&data), size, 1); + 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)); + EXPECT_EQ(message2.foo_message().qux_int(), 234); + } + + // Group + { + unittest::TestOneof2 message1, message2; + string data; + message1.mutable_foogroup()->set_a(345); + int size = message1.ByteSize(); + data.resize(size); + + { + // Allow the output stream to buffer only one byte at a time. + io::ArrayOutputStream array_stream(string_as_array(&data), size, 1); + 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)); + EXPECT_EQ(message2.foogroup().a(), 345); + } + +} + +TEST_F(OneofTest, MergeFrom) { + unittest::TestOneof2 message1, message2; + + message1.set_foo_int(123); + message2.MergeFrom(message1); + TestUtil::ExpectAtMostOneFieldSetInOneof(message2); + EXPECT_TRUE(message2.has_foo_int()); + EXPECT_EQ(message2.foo_int(), 123); + + message1.set_foo_string("foo"); + message2.MergeFrom(message1); + TestUtil::ExpectAtMostOneFieldSetInOneof(message2); + EXPECT_TRUE(message2.has_foo_string()); + EXPECT_EQ(message2.foo_string(), "foo"); + + + message1.set_foo_bytes("qux"); + message2.MergeFrom(message1); + TestUtil::ExpectAtMostOneFieldSetInOneof(message2); + EXPECT_TRUE(message2.has_foo_bytes()); + EXPECT_EQ(message2.foo_bytes(), "qux"); + + message1.set_foo_enum(unittest::TestOneof2::FOO); + message2.MergeFrom(message1); + TestUtil::ExpectAtMostOneFieldSetInOneof(message2); + EXPECT_TRUE(message2.has_foo_enum()); + EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::FOO); + + message1.mutable_foo_message()->set_qux_int(234); + message2.MergeFrom(message1); + TestUtil::ExpectAtMostOneFieldSetInOneof(message2); + EXPECT_TRUE(message2.has_foo_message()); + EXPECT_EQ(message2.foo_message().qux_int(), 234); + + message1.mutable_foogroup()->set_a(345); + message2.MergeFrom(message1); + TestUtil::ExpectAtMostOneFieldSetInOneof(message2); + EXPECT_TRUE(message2.has_foogroup()); + EXPECT_EQ(message2.foogroup().a(), 345); + +} + } // namespace cpp_unittest } // namespace cpp } // namespace compiler diff --git a/src/google/protobuf/compiler/importer.cc b/src/google/protobuf/compiler/importer.cc index 422f759f..701ac483 100644 --- a/src/google/protobuf/compiler/importer.cc +++ b/src/google/protobuf/compiler/importer.cc @@ -43,6 +43,7 @@ #include <errno.h> #include <algorithm> +#include <memory> #include <google/protobuf/compiler/importer.h> @@ -124,7 +125,8 @@ bool SourceTreeDescriptorDatabase::FindFileByName( scoped_ptr<io::ZeroCopyInputStream> input(source_tree_->Open(filename)); if (input == NULL) { if (error_collector_ != NULL) { - error_collector_->AddError(filename, -1, 0, "File not found."); + error_collector_->AddError(filename, -1, 0, + source_tree_->GetLastErrorMessage()); } return false; } @@ -186,6 +188,7 @@ Importer::Importer(SourceTree* source_tree, MultiFileErrorCollector* error_collector) : database_(source_tree), pool_(&database_, database_.GetValidationErrorCollector()) { + pool_.EnforceWeakDependencies(true); database_.RecordErrorsTo(error_collector); } @@ -195,10 +198,22 @@ const FileDescriptor* Importer::Import(const string& filename) { return pool_.FindFileByName(filename); } +void Importer::AddUnusedImportTrackFile(const string& file_name) { + pool_.AddUnusedImportTrackFile(file_name); +} + +void Importer::ClearUnusedImportTrackFiles() { + pool_.ClearUnusedImportTrackFiles(); +} + // =================================================================== SourceTree::~SourceTree() {} +string SourceTree::GetLastErrorMessage() { + return "File not found."; +} + DiskSourceTree::DiskSourceTree() {} DiskSourceTree::~DiskSourceTree() {} @@ -239,9 +254,9 @@ static string CanonicalizePath(string path) { } #endif - vector<string> parts; vector<string> canonical_parts; - SplitStringUsing(path, "/", &parts); // Note: Removes empty parts. + vector<string> parts = Split( + path, "/", true); // Note: Removes empty parts. for (int i = 0; i < parts.size(); i++) { if (parts[i] == ".") { // Ignore. @@ -249,7 +264,7 @@ static string CanonicalizePath(string path) { canonical_parts.push_back(parts[i]); } } - string result = JoinStrings(canonical_parts, "/"); + string result = Join(canonical_parts, "/"); if (!path.empty() && path[0] == '/') { // Restore leading slash. result = '/' + result; @@ -395,8 +410,8 @@ DiskSourceTree::DiskFileToVirtualFile( bool DiskSourceTree::VirtualFileToDiskFile(const string& virtual_file, string* disk_file) { - scoped_ptr<io::ZeroCopyInputStream> stream(OpenVirtualFile(virtual_file, - disk_file)); + scoped_ptr<io::ZeroCopyInputStream> stream( + OpenVirtualFile(virtual_file, disk_file)); return stream != NULL; } @@ -404,6 +419,10 @@ io::ZeroCopyInputStream* DiskSourceTree::Open(const string& filename) { return OpenVirtualFile(filename, NULL); } +string DiskSourceTree::GetLastErrorMessage() { + return last_error_message_; +} + io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile( const string& virtual_file, string* disk_file) { @@ -412,6 +431,8 @@ io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile( // We do not allow importing of paths containing things like ".." or // consecutive slashes since the compiler expects files to be uniquely // identified by file name. + last_error_message_ = "Backslashes, consecutive slashes, \".\", or \"..\" " + "are not allowed in the virtual path"; return NULL; } @@ -429,13 +450,13 @@ io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile( if (errno == EACCES) { // The file exists but is not readable. - // TODO(kenton): Find a way to report this more nicely. - GOOGLE_LOG(WARNING) << "Read access is denied for file: " << temp_disk_file; + last_error_message_ = "Read access is denied for file: " + + temp_disk_file; return NULL; } } } - + last_error_message_ = "File not found."; return NULL; } diff --git a/src/google/protobuf/compiler/importer.h b/src/google/protobuf/compiler/importer.h index 7a62fa0e..0171d2fa 100644 --- a/src/google/protobuf/compiler/importer.h +++ b/src/google/protobuf/compiler/importer.h @@ -166,6 +166,9 @@ class LIBPROTOBUF_EXPORT Importer { return &pool_; } + void AddUnusedImportTrackFile(const string& file_name); + void ClearUnusedImportTrackFiles(); + private: SourceTreeDescriptorDatabase database_; DescriptorPool pool_; @@ -204,6 +207,13 @@ class LIBPROTOBUF_EXPORT SourceTree { // contain "." or ".." components. virtual io::ZeroCopyInputStream* Open(const string& filename) = 0; + // If Open() returns NULL, calling this method immediately will return an + // description of the error. + // Subclasses should implement this method and return a meaningful value for + // better error reporting. + // TODO(xiaofeng): change this to a pure virtual function. + virtual string GetLastErrorMessage(); + private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SourceTree); }; @@ -273,7 +283,9 @@ class LIBPROTOBUF_EXPORT DiskSourceTree : public SourceTree { bool VirtualFileToDiskFile(const string& virtual_file, string* disk_file); // implements SourceTree ------------------------------------------- - io::ZeroCopyInputStream* Open(const string& filename); + virtual io::ZeroCopyInputStream* Open(const string& filename); + + virtual string GetLastErrorMessage(); private: struct Mapping { @@ -285,6 +297,7 @@ class LIBPROTOBUF_EXPORT DiskSourceTree : public SourceTree { : virtual_path(virtual_path_param), disk_path(disk_path_param) {} }; vector<Mapping> mappings_; + string last_error_message_; // Like Open(), but returns the on-disk path in disk_file if disk_file is // non-NULL and the file could be successfully opened. diff --git a/src/google/protobuf/compiler/importer_unittest.cc b/src/google/protobuf/compiler/importer_unittest.cc index 56fad56e..b54d9b73 100644 --- a/src/google/protobuf/compiler/importer_unittest.cc +++ b/src/google/protobuf/compiler/importer_unittest.cc @@ -33,12 +33,13 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include <google/protobuf/stubs/hash.h> +#include <memory> #include <google/protobuf/compiler/importer.h> #include <google/protobuf/descriptor.h> #include <google/protobuf/io/zero_copy_stream_impl.h> -#include <google/protobuf/stubs/map-util.h> +#include <google/protobuf/stubs/map_util.h> #include <google/protobuf/stubs/common.h> #include <google/protobuf/testing/file.h> #include <google/protobuf/stubs/strutil.h> @@ -92,6 +93,10 @@ class MockSourceTree : public SourceTree { } } + string GetLastErrorMessage() { + return "File not found."; + } + private: hash_map<string, const char*> files_; }; @@ -324,6 +329,7 @@ TEST_F(ImporterTest, MapFieldKeyNotScalar) { EXPECT_SUBSTRING("must name a scalar or string", error()); } + // =================================================================== class DiskSourceTreeTest : public testing::Test { @@ -336,7 +342,7 @@ class DiskSourceTreeTest : public testing::Test { if (File::Exists(dirnames_[i])) { File::DeleteRecursively(dirnames_[i], NULL, NULL); } - GOOGLE_CHECK(File::CreateDir(dirnames_[i].c_str(), DEFAULT_FILE_MODE)); + GOOGLE_CHECK_OK(File::CreateDir(dirnames_[i], 0777)); } } @@ -347,11 +353,11 @@ class DiskSourceTreeTest : public testing::Test { } void AddFile(const string& filename, const char* contents) { - File::WriteStringToFileOrDie(contents, filename); + GOOGLE_CHECK_OK(File::SetContents(filename, contents, true)); } void AddSubdir(const string& dirname) { - GOOGLE_CHECK(File::CreateDir(dirname.c_str(), DEFAULT_FILE_MODE)); + GOOGLE_CHECK_OK(File::CreateDir(dirname, 0777)); } void ExpectFileContents(const string& filename, @@ -371,9 +377,11 @@ class DiskSourceTreeTest : public testing::Test { EXPECT_EQ(expected_contents, file_contents); } - void ExpectFileNotFound(const string& filename) { + void ExpectCannotOpenFile(const string& filename, + const string& error_message) { scoped_ptr<io::ZeroCopyInputStream> input(source_tree_.Open(filename)); EXPECT_TRUE(input == NULL); + EXPECT_EQ(error_message, source_tree_.GetLastErrorMessage()); } DiskSourceTree source_tree_; @@ -389,7 +397,7 @@ TEST_F(DiskSourceTreeTest, MapRoot) { source_tree_.MapPath("", dirnames_[0]); ExpectFileContents("foo", "Hello World!"); - ExpectFileNotFound("bar"); + ExpectCannotOpenFile("bar", "File not found."); } TEST_F(DiskSourceTreeTest, MapDirectory) { @@ -400,15 +408,21 @@ TEST_F(DiskSourceTreeTest, MapDirectory) { source_tree_.MapPath("baz", dirnames_[0]); ExpectFileContents("baz/foo", "Hello World!"); - ExpectFileNotFound("baz/bar"); - ExpectFileNotFound("foo"); - ExpectFileNotFound("bar"); + ExpectCannotOpenFile("baz/bar", "File not found."); + ExpectCannotOpenFile("foo", "File not found."); + ExpectCannotOpenFile("bar", "File not found."); // Non-canonical file names should not work. - ExpectFileNotFound("baz//foo"); - ExpectFileNotFound("baz/../baz/foo"); - ExpectFileNotFound("baz/./foo"); - ExpectFileNotFound("baz/foo/"); + ExpectCannotOpenFile("baz//foo", + "Backslashes, consecutive slashes, \".\", or \"..\" are " + "not allowed in the virtual path"); + ExpectCannotOpenFile("baz/../baz/foo", + "Backslashes, consecutive slashes, \".\", or \"..\" are " + "not allowed in the virtual path"); + ExpectCannotOpenFile("baz/./foo", + "Backslashes, consecutive slashes, \".\", or \"..\" are " + "not allowed in the virtual path"); + ExpectCannotOpenFile("baz/foo/", "File not found."); } TEST_F(DiskSourceTreeTest, NoParent) { @@ -420,8 +434,12 @@ TEST_F(DiskSourceTreeTest, NoParent) { source_tree_.MapPath("", dirnames_[0] + "/bar"); ExpectFileContents("baz", "Blah."); - ExpectFileNotFound("../foo"); - ExpectFileNotFound("../bar/baz"); + ExpectCannotOpenFile("../foo", + "Backslashes, consecutive slashes, \".\", or \"..\" are " + "not allowed in the virtual path"); + ExpectCannotOpenFile("../bar/baz", + "Backslashes, consecutive slashes, \".\", or \"..\" are " + "not allowed in the virtual path"); } TEST_F(DiskSourceTreeTest, MapFile) { @@ -431,7 +449,7 @@ TEST_F(DiskSourceTreeTest, MapFile) { source_tree_.MapPath("foo", dirnames_[0] + "/foo"); ExpectFileContents("foo", "Hello World!"); - ExpectFileNotFound("bar"); + ExpectCannotOpenFile("bar", "File not found."); } TEST_F(DiskSourceTreeTest, SearchMultipleDirectories) { @@ -445,7 +463,7 @@ TEST_F(DiskSourceTreeTest, SearchMultipleDirectories) { ExpectFileContents("foo", "Hello World!"); ExpectFileContents("bar", "Goodbye World!"); - ExpectFileNotFound("baz"); + ExpectCannotOpenFile("baz", "File not found."); } TEST_F(DiskSourceTreeTest, OrderingTrumpsSpecificity) { @@ -453,8 +471,7 @@ TEST_F(DiskSourceTreeTest, OrderingTrumpsSpecificity) { // directory is more-specific than a former one. // Create the "bar" directory so we can put a file in it. - ASSERT_TRUE(File::CreateDir((dirnames_[0] + "/bar").c_str(), - DEFAULT_FILE_MODE)); + GOOGLE_CHECK_OK(File::CreateDir(dirnames_[0] + "/bar", 0777)); // Add files and map paths. AddFile(dirnames_[0] + "/bar/foo", "Hello World!"); diff --git a/src/google/protobuf/compiler/java/java_context.cc b/src/google/protobuf/compiler/java/java_context.cc new file mode 100644 index 00000000..67ac0ef3 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_context.cc @@ -0,0 +1,195 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <google/protobuf/compiler/java/java_context.h> + +#include <google/protobuf/compiler/java/java_field.h> +#include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/stubs/map_util.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +Context::Context(const FileDescriptor* file) + : name_resolver_(new ClassNameResolver) { + InitializeFieldGeneratorInfo(file); +} + +Context::~Context() { +} + +ClassNameResolver* Context::GetNameResolver() { + return name_resolver_.get(); +} + +namespace { +// Whether two fields have conflicting accessors (assuming name1 and name2 +// are different). name1 and name2 are field1 and field2's camel-case name +// respectively. +bool IsConflicting(const FieldDescriptor* field1, const string& name1, + const FieldDescriptor* field2, const string& name2, + string* info) { + if (field1->is_repeated()) { + if (field2->is_repeated()) { + // Both fields are repeated. + return false; + } else { + // field1 is repeated, and field2 is not. + if (name1 + "Count" == name2) { + *info = "both repeated field \"" + field1->name() + "\" and singular " + + "field \"" + field2->name() + "\" generates the method \"" + + "get" + name1 + "Count()\""; + return true; + } + if (name1 + "List" == name2) { + *info = "both repeated field \"" + field1->name() + "\" and singular " + + "field \"" + field2->name() + "\" generates the method \"" + + "get" + name1 + "List()\""; + return true; + } + // Well, there are obviously many more conflicting cases, but it probably + // doesn't worth the effort to exhaust all of them because they rarely + // happen and as we are continuing adding new methods/changing existing + // methods the number of different conflicting cases will keep growing. + // We can just add more cases here when they are found in the real world. + return false; + } + } else { + if (field2->is_repeated()) { + return IsConflicting(field2, name2, field1, name1, info); + } else { + // None of the two fields are repeated. + return false; + } + } +} +} // namespace + +void Context::InitializeFieldGeneratorInfo(const FileDescriptor* file) { + for (int i = 0; i < file->message_type_count(); ++i) { + InitializeFieldGeneratorInfoForMessage(file->message_type(i)); + } +} + +void Context::InitializeFieldGeneratorInfoForMessage( + const Descriptor* message) { + for (int i = 0; i < message->nested_type_count(); ++i) { + InitializeFieldGeneratorInfoForMessage(message->nested_type(i)); + } + vector<const FieldDescriptor*> fields; + for (int i = 0; i < message->field_count(); ++i) { + fields.push_back(message->field(i)); + } + InitializeFieldGeneratorInfoForFields(fields); + + for (int i = 0; i < message->oneof_decl_count(); ++i) { + const OneofDescriptor* oneof = message->oneof_decl(i); + OneofGeneratorInfo info; + info.name = UnderscoresToCamelCase(oneof->name(), false); + info.capitalized_name = UnderscoresToCamelCase(oneof->name(), true); + oneof_generator_info_map_[oneof] = info; + } +} + +void Context::InitializeFieldGeneratorInfoForFields( + const vector<const FieldDescriptor*>& fields) { + // Find out all fields that conflict with some other field in the same + // message. + vector<bool> is_conflict(fields.size()); + vector<string> conflict_reason(fields.size()); + for (int i = 0; i < fields.size(); ++i) { + const FieldDescriptor* field = fields[i]; + const string& name = UnderscoresToCapitalizedCamelCase(field); + for (int j = i + 1; j < fields.size(); ++j) { + const FieldDescriptor* other = fields[j]; + const string& other_name = UnderscoresToCapitalizedCamelCase(other); + if (name == other_name) { + is_conflict[i] = is_conflict[j] = true; + conflict_reason[i] = conflict_reason[j] = + "capitalized name of field \"" + field->name() + + "\" conflicts with field \"" + other->name() + "\""; + } else if (IsConflicting(field, name, other, other_name, + &conflict_reason[j])) { + is_conflict[i] = is_conflict[j] = true; + conflict_reason[i] = conflict_reason[j]; + } + } + if (is_conflict[i]) { + GOOGLE_LOG(WARNING) << "field \"" << field->full_name() << "\" is conflicting " + << "with another field: " << conflict_reason[i]; + } + } + for (int i = 0; i < fields.size(); ++i) { + const FieldDescriptor* field = fields[i]; + FieldGeneratorInfo info; + info.name = UnderscoresToCamelCase(field); + info.capitalized_name = UnderscoresToCapitalizedCamelCase(field); + // For fields conflicting with some other fields, we append the field + // number to their field names in generated code to avoid conflicts. + if (is_conflict[i]) { + info.name += SimpleItoa(field->number()); + info.capitalized_name += SimpleItoa(field->number()); + info.disambiguated_reason = conflict_reason[i]; + } + field_generator_info_map_[field] = info; + } +} + +const FieldGeneratorInfo* Context::GetFieldGeneratorInfo( + const FieldDescriptor* field) const { + const FieldGeneratorInfo* result = + FindOrNull(field_generator_info_map_, field); + if (result == NULL) { + GOOGLE_LOG(FATAL) << "Can not find FieldGeneratorInfo for field: " + << field->full_name(); + } + return result; +} + +const OneofGeneratorInfo* Context::GetOneofGeneratorInfo( + const OneofDescriptor* oneof) const { + const OneofGeneratorInfo* result = + FindOrNull(oneof_generator_info_map_, oneof); + if (result == NULL) { + GOOGLE_LOG(FATAL) << "Can not find OneofGeneratorInfo for oneof: " + << oneof->name(); + } + return result; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_context.h b/src/google/protobuf/compiler/java/java_context.h new file mode 100644 index 00000000..c622b319 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_context.h @@ -0,0 +1,95 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_CONTEXT_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_CONTEXT_H__ + +#include <map> +#include <memory> +#include <vector> + +#include <google/protobuf/stubs/common.h> + +namespace google { +namespace protobuf { + class FileDescriptor; + class FieldDescriptor; + class OneofDescriptor; + class Descriptor; + namespace compiler { + namespace java { + class ClassNameResolver; // name_resolver.h + } + } +} // namespace protobuf + +namespace protobuf { +namespace compiler { +namespace java { + +struct FieldGeneratorInfo; +struct OneofGeneratorInfo; +// A context object holds the information that is shared among all code +// generators. +class Context { + public: + explicit Context(const FileDescriptor* file); + ~Context(); + + // Get the name resolver associated with this context. The resolver + // can be used to map descriptors to Java class names. + ClassNameResolver* GetNameResolver(); + + // Get the FieldGeneratorInfo for a given field. + const FieldGeneratorInfo* GetFieldGeneratorInfo( + const FieldDescriptor* field) const; + + // Get the OneofGeneratorInfo for a given oneof. + const OneofGeneratorInfo* GetOneofGeneratorInfo( + const OneofDescriptor* oneof) const; + + private: + void InitializeFieldGeneratorInfo(const FileDescriptor* file); + void InitializeFieldGeneratorInfoForMessage(const Descriptor* message); + void InitializeFieldGeneratorInfoForFields( + const vector<const FieldDescriptor*>& fields); + + scoped_ptr<ClassNameResolver> name_resolver_; + map<const FieldDescriptor*, FieldGeneratorInfo> field_generator_info_map_; + map<const OneofDescriptor*, OneofGeneratorInfo> oneof_generator_info_map_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Context); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_CONTEXT_H__ diff --git a/src/google/protobuf/compiler/java/java_doc_comment.cc b/src/google/protobuf/compiler/java/java_doc_comment.cc index 60b4f2ac..23127b7f 100644 --- a/src/google/protobuf/compiler/java/java_doc_comment.cc +++ b/src/google/protobuf/compiler/java/java_doc_comment.cc @@ -70,12 +70,10 @@ string EscapeJavadoc(const string& input) { } break; case '@': - // "{@" starts Javadoc markup. - if (prev == '{') { - result.append("@"); - } else { - result.push_back(c); - } + // '@' starts javadoc tags including the @deprecated tag, which will + // cause a compile-time error if inserted before a declaration that + // does not have a corresponding @Deprecated annotation. + result.append("@"); break; case '<': // Avoid interpretation as HTML. @@ -117,8 +115,7 @@ static void WriteDocCommentBodyForLocation( // HTML-escape them so that they don't accidentally close the doc comment. comments = EscapeJavadoc(comments); - vector<string> lines; - SplitStringAllowEmpty(comments, "\n", &lines); + vector<string> lines = Split(comments, "\n"); while (!lines.empty() && lines.back().empty()) { lines.pop_back(); } diff --git a/src/google/protobuf/compiler/java/java_doc_comment_unittest.cc b/src/google/protobuf/compiler/java/java_doc_comment_unittest.cc index 28b6d8b4..41ea9f42 100644 --- a/src/google/protobuf/compiler/java/java_doc_comment_unittest.cc +++ b/src/google/protobuf/compiler/java/java_doc_comment_unittest.cc @@ -46,6 +46,7 @@ TEST(JavaDocCommentTest, Escaping) { EXPECT_EQ("{@foo}", EscapeJavadoc("{@foo}")); EXPECT_EQ("<i>&</i>", EscapeJavadoc("<i>&</i>")); EXPECT_EQ("foo\u1234bar", EscapeJavadoc("foo\\u1234bar")); + EXPECT_EQ("@deprecated", EscapeJavadoc("@deprecated")); } // TODO(kenton): It's hard to write a robust test of the doc comments -- we diff --git a/src/google/protobuf/compiler/java/java_enum.cc b/src/google/protobuf/compiler/java/java_enum.cc index cfed815f..6bebe213 100644 --- a/src/google/protobuf/compiler/java/java_enum.cc +++ b/src/google/protobuf/compiler/java/java_enum.cc @@ -35,9 +35,11 @@ #include <map> #include <string> +#include <google/protobuf/compiler/java/java_context.h> #include <google/protobuf/compiler/java/java_enum.h> #include <google/protobuf/compiler/java/java_doc_comment.h> #include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/descriptor.pb.h> #include <google/protobuf/stubs/strutil.h> @@ -47,8 +49,22 @@ namespace protobuf { namespace compiler { namespace java { -EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor) - : descriptor_(descriptor) { +namespace { +bool EnumHasCustomOptions(const EnumDescriptor* descriptor) { + if (descriptor->options().unknown_fields().field_count() > 0) return true; + for (int i = 0; i < descriptor->value_count(); ++i) { + const EnumValueDescriptor* value = descriptor->value(i); + if (value->options().unknown_fields().field_count() > 0) return true; + } + return false; +} +} // namespace + +EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, + bool immutable_api, + Context* context) + : descriptor_(descriptor), immutable_api_(immutable_api), + name_resolver_(context->GetNameResolver()) { for (int i = 0; i < descriptor_->value_count(); i++) { const EnumValueDescriptor* value = descriptor_->value(i); const EnumValueDescriptor* canonical_value = @@ -88,6 +104,9 @@ void EnumGenerator::Generate(io::Printer* printer) { vars["index"] = SimpleItoa(canonical_values_[i]->index()); vars["number"] = SimpleItoa(canonical_values_[i]->number()); WriteEnumValueDocComment(printer, canonical_values_[i]); + if (canonical_values_[i]->options().deprecated()) { + printer->Print("@java.lang.Deprecated\n"); + } printer->Print(vars, "$name$($index$, $number$),\n"); } @@ -178,15 +197,58 @@ void EnumGenerator::Generate(io::Printer* printer) { // at module init time because it wouldn't work with descriptor.proto, but // we can cache the value the first time getDescriptor() is called. if (descriptor_->containing_type() == NULL) { - printer->Print( - " return $file$.getDescriptor().getEnumTypes().get($index$);\n", - "file", ClassName(descriptor_->file()), - "index", SimpleItoa(descriptor_->index())); + if (!MultipleJavaFiles(descriptor_->file(), immutable_api_)) { + printer->Print( + " return $file$.getDescriptor().getEnumTypes().get($index$);\n", + "file", name_resolver_->GetClassName(descriptor_->file(), + immutable_api_), + "index", SimpleItoa(descriptor_->index())); + } else { + printer->Indent(); + if (EnumHasCustomOptions(descriptor_)) { + // We need to load the immutable classes in order to parse custom + // options. However, since file level enums (no outer class) are + // shared by immutable code and mutable code, the immutable classes + // may not exist. So we try to use Java reflection to retrieve the + // descriptor from immutable classes. + printer->Print( + "try {\n" + " java.lang.Class immutableFileClass =\n" + " java.lang.Class.forName(\"$immutable_file_class_name$\");\n" + " @java.lang.SuppressWarnings(\"unchecked\")\n" + " java.lang.reflect.Method m =\n" + " immutableFileClass.getMethod(\"getDescriptor\");\n" + " com.google.protobuf.Descriptors.FileDescriptor file =\n" + " (com.google.protobuf.Descriptors.FileDescriptor)\n" + " m.invoke(immutableFileClass);\n" + " return file.getEnumTypes().get($index$);\n" + "} catch (Exception e) {\n" + // Immutable classes cannot be found. Proceed as if custom options + // don't exist. + "}\n", + "immutable_file_class_name", + name_resolver_->GetImmutableClassName(descriptor_->file()), + "index", SimpleItoa(descriptor_->index())); + } + printer->Print( + "return $immutable_package$.$descriptor_class$.descriptor\n" + " .getEnumTypes().get($index$);\n", + "immutable_package", FileJavaPackage(descriptor_->file(), true), + "descriptor_class", + name_resolver_->GetDescriptorClassName(descriptor_->file()), + "index", SimpleItoa(descriptor_->index())); + printer->Outdent(); + } } else { printer->Print( - " return $parent$.getDescriptor().getEnumTypes().get($index$);\n", - "parent", ClassName(descriptor_->containing_type()), - "index", SimpleItoa(descriptor_->index())); + " return $parent$.$descriptor$.getEnumTypes().get($index$);\n", + "parent", name_resolver_->GetClassName(descriptor_->containing_type(), + immutable_api_), + "descriptor", descriptor_->containing_type()->options() + .no_standard_descriptor_accessor() + ? "getDefaultInstance().getDescriptorForType()" + : "getDescriptor()", + "index", SimpleItoa(descriptor_->index())); } printer->Print( diff --git a/src/google/protobuf/compiler/java/java_enum.h b/src/google/protobuf/compiler/java/java_enum.h index 9a9e5742..af0cd15d 100644 --- a/src/google/protobuf/compiler/java/java_enum.h +++ b/src/google/protobuf/compiler/java/java_enum.h @@ -41,6 +41,12 @@ namespace google { namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } namespace io { class Printer; // printer.h } @@ -52,7 +58,9 @@ namespace java { class EnumGenerator { public: - explicit EnumGenerator(const EnumDescriptor* descriptor); + explicit EnumGenerator(const EnumDescriptor* descriptor, + bool immutable_api, + Context* context); ~EnumGenerator(); void Generate(io::Printer* printer); @@ -73,6 +81,11 @@ class EnumGenerator { }; vector<Alias> aliases_; + bool immutable_api_; + + Context* context_; + ClassNameResolver* name_resolver_; + bool CanUseEnumValues(); GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator); diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc index ec0b067e..cab9bd0e 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.cc +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -35,10 +35,12 @@ #include <map> #include <string> -#include <google/protobuf/compiler/java/java_enum_field.h> -#include <google/protobuf/compiler/java/java_doc_comment.h> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/java/java_context.h> +#include <google/protobuf/compiler/java/java_doc_comment.h> +#include <google/protobuf/compiler/java/java_enum_field.h> #include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/wire_format.h> #include <google/protobuf/stubs/strutil.h> @@ -50,20 +52,19 @@ namespace java { namespace { -// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of -// repeat code between this and the other field types. void SetEnumVariables(const FieldDescriptor* descriptor, int messageBitIndex, int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, map<string, string>* variables) { - (*variables)["name"] = - UnderscoresToCamelCase(descriptor); - (*variables)["capitalized_name"] = - UnderscoresToCapitalizedCamelCase(descriptor); - (*variables)["constant_name"] = FieldConstantName(descriptor); - (*variables)["number"] = SimpleItoa(descriptor->number()); - (*variables)["type"] = ClassName(descriptor->enum_type()); - (*variables)["default"] = DefaultValue(descriptor); + 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)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); (*variables)["tag_size"] = SimpleItoa( internal::WireFormat::TagSize(descriptor->number(), GetType(descriptor))); @@ -74,14 +75,28 @@ void SetEnumVariables(const FieldDescriptor* descriptor, (*variables)["on_changed"] = HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; - // For singular messages and builders, one bit is used for the hasField bit. - (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); - (*variables)["set_has_field_bit_message"] = GenerateSetBit(messageBitIndex); + 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); + (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); + + // Note that these have a trailing ";". + (*variables)["set_has_field_bit_message"] = + GenerateSetBit(messageBitIndex) + ";"; + (*variables)["set_has_field_bit_builder"] = + GenerateSetBit(builderBitIndex) + ";"; + (*variables)["clear_has_field_bit_builder"] = + GenerateClearBit(builderBitIndex) + ";"; + + (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex); + } else { + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["set_has_field_bit_builder"] = ""; + (*variables)["clear_has_field_bit_builder"] = ""; - (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); - (*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex); - (*variables)["clear_has_field_bit_builder"] = - GenerateClearBit(builderBitIndex); + (*variables)["is_field_present_message"] = + (*variables)["name"] + "_ != " + (*variables)["default"]; + } // For repated builders, one bit is used for whether the array is immutable. (*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex); @@ -105,44 +120,53 @@ void SetEnumVariables(const FieldDescriptor* descriptor, // =================================================================== -EnumFieldGenerator:: -EnumFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, - int builderBitIndex) +ImmutableEnumFieldGenerator:: +ImmutableEnumFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) : descriptor_(descriptor), messageBitIndex_(messageBitIndex), - builderBitIndex_(builderBitIndex) { - SetEnumVariables(descriptor, messageBitIndex, builderBitIndex, &variables_); + builderBitIndex_(builderBitIndex), + name_resolver_(context->GetNameResolver()) { + SetEnumVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); } -EnumFieldGenerator::~EnumFieldGenerator() {} +ImmutableEnumFieldGenerator::~ImmutableEnumFieldGenerator() {} -int EnumFieldGenerator::GetNumBitsForMessage() const { +int ImmutableEnumFieldGenerator::GetNumBitsForMessage() const { return 1; } -int EnumFieldGenerator::GetNumBitsForBuilder() const { +int ImmutableEnumFieldGenerator::GetNumBitsForBuilder() const { return 1; } -void EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$boolean has$capitalized_name$();\n"); + 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 EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private $type$ $name$_;\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public boolean has$capitalized_name$() {\n" - " return $get_has_field_bit_message$;\n" - "}\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" @@ -150,15 +174,17 @@ GenerateMembers(io::Printer* printer) const { "}\n"); } -void EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, "private $type$ $name$_ = $default$;\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public boolean has$capitalized_name$() {\n" - " return $get_has_field_bit_builder$;\n" - "}\n"); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_builder$;\n" + "}\n"); + } WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" @@ -170,7 +196,7 @@ GenerateBuilderMembers(io::Printer* printer) const { " if (value == null) {\n" " throw new NullPointerException();\n" " }\n" - " $set_has_field_bit_builder$;\n" + " $set_has_field_bit_builder$\n" " $name$_ = value;\n" " $on_changed$\n" " return this;\n" @@ -178,129 +204,274 @@ GenerateBuilderMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public Builder clear$capitalized_name$() {\n" - " $clear_has_field_bit_builder$;\n" + " $clear_has_field_bit_builder$\n" " $name$_ = $default$;\n" " $on_changed$\n" " return this;\n" "}\n"); } -void EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateFieldBuilderInitializationCode(io::Printer* printer) const { // noop for enums } -void EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = $default$;\n"); } -void EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateBuilderClearCode(io::Printer* printer) const { printer->Print(variables_, - "$name$_ = $default$;\n" - "$clear_has_field_bit_builder$;\n"); + "$name$_ = $default$;\n" + "$clear_has_field_bit_builder$\n"); } -void EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateMergingCode(io::Printer* printer) const { - printer->Print(variables_, - "if (other.has$capitalized_name$()) {\n" - " set$capitalized_name$(other.get$capitalized_name$());\n" - "}\n"); + 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 EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + printer->Print(variables_, + "if ($get_has_field_bit_from_local$) {\n" + " $set_has_field_bit_to_local$;\n" + "}\n"); + } printer->Print(variables_, - "if ($get_has_field_bit_from_local$) {\n" - " $set_has_field_bit_to_local$;\n" - "}\n" "result.$name$_ = $name$_;\n"); } -void EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n"); - if (HasUnknownFields(descriptor_->containing_type())) { + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n"); + if (UseUnknownFieldSet(descriptor_->containing_type())) { printer->Print(variables_, - "if (value == null) {\n" - " unknownFields.mergeVarintField($number$, rawValue);\n" - "} else {\n"); + " unknownFields.mergeVarintField($number$, rawValue);\n"); } else { printer->Print(variables_, - "if (value != null) {\n"); + " unknownFieldsCodedOutput.writeRawVarint32(tag);\n" + " unknownFieldsCodedOutput.writeRawVarint32(rawValue);\n"); } printer->Print(variables_, - " $set_has_field_bit_message$;\n" + "} else {\n" + " $set_has_field_bit_message$\n" " $name$_ = value;\n" "}\n"); } -void EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateParsingDoneCode(io::Printer* printer) const { // noop for enums } -void EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, - "if ($get_has_field_bit_message$) {\n" + "if ($is_field_present_message$) {\n" " output.writeEnum($number$, $name$_.getNumber());\n" "}\n"); } -void EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, - "if ($get_has_field_bit_message$) {\n" + "if ($is_field_present_message$) {\n" " size += com.google.protobuf.CodedOutputStream\n" " .computeEnumSize($number$, $name$_.getNumber());\n" "}\n"); } -void EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateEqualsCode(io::Printer* printer) const { printer->Print(variables_, "result = result &&\n" " (get$capitalized_name$() == other.get$capitalized_name$());\n"); } -void EnumFieldGenerator:: +void ImmutableEnumFieldGenerator:: GenerateHashCode(io::Printer* printer) const { printer->Print(variables_, "hash = (37 * hash) + $constant_name$;\n" - "hash = (53 * hash) + hashEnum(get$capitalized_name$());\n"); + "hash = (53 * hash) + com.google.protobuf.Internal.hashEnum(\n" + " get$capitalized_name$());\n"); +} + +string ImmutableEnumFieldGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->enum_type()); +} + +// =================================================================== + +ImmutableEnumOneofFieldGenerator:: +ImmutableEnumOneofFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableEnumFieldGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); +} + +ImmutableEnumOneofFieldGenerator:: +~ImmutableEnumOneofFieldGenerator() {} + +void ImmutableEnumOneofFieldGenerator:: +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 $default$;\n" + "}\n"); } -string EnumFieldGenerator::GetBoxedType() const { - return ClassName(descriptor_->enum_type()); +void ImmutableEnumOneofFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + 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 $default$;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + " $on_changed$\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " $on_changed$\n" + " }\n" + " return this;\n" + "}\n"); +} + +void ImmutableEnumOneofFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " result.$oneof_name$_ = $oneof_name$_;\n" + "}\n"); +} + +void ImmutableEnumOneofFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "set$capitalized_name$(other.get$capitalized_name$());\n"); +} + +void ImmutableEnumOneofFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n"); + if (UseUnknownFieldSet(descriptor_->containing_type())) { + printer->Print(variables_, + " unknownFields.mergeVarintField($number$, rawValue);\n"); + } else { + printer->Print(variables_, + " unknownFieldsCodedOutput.writeRawVarint32(tag);\n" + " unknownFieldsCodedOutput.writeRawVarint32(rawValue);\n"); + } + printer->Print(variables_, + "} else {\n" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + "}\n"); +} + +void ImmutableEnumOneofFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " output.writeEnum($number$, (($type$) $oneof_name$_).getNumber());\n" + "}\n"); +} + +void ImmutableEnumOneofFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeEnumSize($number$, (($type$) $oneof_name$_).getNumber());\n" + "}\n"); } // =================================================================== -RepeatedEnumFieldGenerator:: -RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, - int builderBitIndex) +RepeatedImmutableEnumFieldGenerator:: +RepeatedImmutableEnumFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) : descriptor_(descriptor), messageBitIndex_(messageBitIndex), - builderBitIndex_(builderBitIndex) { - SetEnumVariables(descriptor, messageBitIndex, builderBitIndex, &variables_); + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetEnumVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); } -RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {} +RepeatedImmutableEnumFieldGenerator::~RepeatedImmutableEnumFieldGenerator() {} -int RepeatedEnumFieldGenerator::GetNumBitsForMessage() const { +int RepeatedImmutableEnumFieldGenerator::GetNumBitsForMessage() const { return 0; } -int RepeatedEnumFieldGenerator::GetNumBitsForBuilder() const { +int RepeatedImmutableEnumFieldGenerator::GetNumBitsForBuilder() const { return 1; } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, @@ -313,10 +484,11 @@ GenerateInterfaceMembers(io::Printer* printer) const { "$deprecation$$type$ get$capitalized_name$(int index);\n"); } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private java.util.List<$type$> $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" @@ -340,7 +512,7 @@ GenerateMembers(io::Printer* printer) const { } } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, // One field is the list and the other field keeps track of whether the @@ -409,7 +581,8 @@ GenerateBuilderMembers(io::Printer* printer) const { "$deprecation$public Builder addAll$capitalized_name$(\n" " java.lang.Iterable<? extends $type$> values) {\n" " ensure$capitalized_name$IsMutable();\n" - " super.addAll(values, $name$_);\n" + " com.google.protobuf.AbstractMessageLite.Builder.addAll(\n" + " values, $name$_);\n" " $on_changed$\n" " return this;\n" "}\n"); @@ -423,24 +596,24 @@ GenerateBuilderMembers(io::Printer* printer) const { "}\n"); } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateFieldBuilderInitializationCode(io::Printer* printer) const { // noop for enums } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = java.util.Collections.emptyList();\n"); } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateBuilderClearCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = java.util.Collections.emptyList();\n" "$clear_mutable_bit_builder$;\n"); } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: 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 @@ -460,7 +633,7 @@ GenerateMergingCode(io::Printer* printer) const { "}\n"); } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { // The code below ensures that the result has an immutable list. If our // list is immutable, we can just reuse it. If not, we make it immutable. @@ -472,22 +645,23 @@ GenerateBuildingCode(io::Printer* printer) const { "result.$name$_ = $name$_;\n"); } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { // Read and store the enum printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n"); - if (HasUnknownFields(descriptor_->containing_type())) { + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n"); + if (UseUnknownFieldSet(descriptor_->containing_type())) { printer->Print(variables_, - "if (value == null) {\n" - " unknownFields.mergeVarintField($number$, rawValue);\n" - "} else {\n"); + " unknownFields.mergeVarintField($number$, rawValue);\n"); } else { printer->Print(variables_, - "if (value != null) {\n"); + " unknownFieldsCodedOutput.writeRawVarint32(tag);\n" + " unknownFieldsCodedOutput.writeRawVarint32(rawValue);\n"); } printer->Print(variables_, + " } else {\n" " if (!$get_mutable_bit_parser$) {\n" " $name$_ = new java.util.ArrayList<$type$>();\n" " $set_mutable_bit_parser$;\n" @@ -496,7 +670,7 @@ GenerateParsingCode(io::Printer* printer) const { "}\n"); } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateParsingCodeFromPacked(io::Printer* printer) const { // Wrap GenerateParsingCode's contents with a while loop. @@ -514,7 +688,7 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const { "input.popLimit(oldLimit);\n"); } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateParsingDoneCode(io::Printer* printer) const { printer->Print(variables_, "if ($get_mutable_bit_parser$) {\n" @@ -522,7 +696,7 @@ GenerateParsingDoneCode(io::Printer* printer) const { "}\n"); } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { if (descriptor_->options().packed()) { printer->Print(variables_, @@ -541,7 +715,7 @@ GenerateSerializationCode(io::Printer* printer) const { } } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, "{\n" @@ -577,24 +751,25 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print("}\n"); } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateEqualsCode(io::Printer* printer) const { printer->Print(variables_, "result = result && get$capitalized_name$List()\n" " .equals(other.get$capitalized_name$List());\n"); } -void RepeatedEnumFieldGenerator:: +void RepeatedImmutableEnumFieldGenerator:: GenerateHashCode(io::Printer* printer) const { printer->Print(variables_, "if (get$capitalized_name$Count() > 0) {\n" " hash = (37 * hash) + $constant_name$;\n" - " hash = (53 * hash) + hashEnumList(get$capitalized_name$List());\n" + " hash = (53 * hash) + com.google.protobuf.Internal.hashEnumList(\n" + " get$capitalized_name$List());\n" "}\n"); } -string RepeatedEnumFieldGenerator::GetBoxedType() const { - return ClassName(descriptor_->enum_type()); +string RepeatedImmutableEnumFieldGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->enum_type()); } } // namespace java diff --git a/src/google/protobuf/compiler/java/java_enum_field.h b/src/google/protobuf/compiler/java/java_enum_field.h index 90fae639..a3afb160 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.h +++ b/src/google/protobuf/compiler/java/java_enum_field.h @@ -41,16 +41,26 @@ namespace google { namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } +} + +namespace protobuf { namespace compiler { namespace java { -class EnumFieldGenerator : public FieldGenerator { +class ImmutableEnumFieldGenerator : public ImmutableFieldGenerator { public: - explicit EnumFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, int builderBitIndex); - ~EnumFieldGenerator(); + explicit ImmutableEnumFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableEnumFieldGenerator(); - // implements FieldGenerator --------------------------------------- + // implements ImmutableFieldGenerator --------------------------------------- int GetNumBitsForMessage() const; int GetNumBitsForBuilder() const; void GenerateInterfaceMembers(io::Printer* printer) const; @@ -70,22 +80,45 @@ class EnumFieldGenerator : public FieldGenerator { string GetBoxedType() const; - private: + protected: const FieldDescriptor* descriptor_; map<string, string> variables_; const int messageBitIndex_; const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator); + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableEnumFieldGenerator); +}; + +class ImmutableEnumOneofFieldGenerator : public ImmutableEnumFieldGenerator { + public: + ImmutableEnumOneofFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableEnumOneofFieldGenerator(); + + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableEnumOneofFieldGenerator); }; -class RepeatedEnumFieldGenerator : public FieldGenerator { +class RepeatedImmutableEnumFieldGenerator : public ImmutableFieldGenerator { public: - explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, int builderBitIndex); - ~RepeatedEnumFieldGenerator(); + explicit RepeatedImmutableEnumFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~RepeatedImmutableEnumFieldGenerator(); - // implements FieldGenerator --------------------------------------- + // implements ImmutableFieldGenerator --------------------------------------- int GetNumBitsForMessage() const; int GetNumBitsForBuilder() const; void GenerateInterfaceMembers(io::Printer* printer) const; @@ -111,8 +144,10 @@ class RepeatedEnumFieldGenerator : public FieldGenerator { map<string, string> variables_; const int messageBitIndex_; const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator); + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableEnumFieldGenerator); }; } // namespace java diff --git a/src/google/protobuf/compiler/java/java_extension.cc b/src/google/protobuf/compiler/java/java_extension.cc index 921fe658..c62dbfaf 100644 --- a/src/google/protobuf/compiler/java/java_extension.cc +++ b/src/google/protobuf/compiler/java/java_extension.cc @@ -33,74 +33,50 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include <google/protobuf/compiler/java/java_extension.h> + +#include <google/protobuf/compiler/java/java_context.h> #include <google/protobuf/compiler/java/java_doc_comment.h> #include <google/protobuf/compiler/java/java_helpers.h> -#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> #include <google/protobuf/io/printer.h> +#include <google/protobuf/stubs/strutil.h> namespace google { namespace protobuf { namespace compiler { namespace java { -namespace { - -const char* TypeName(FieldDescriptor::Type field_type) { - switch (field_type) { - case FieldDescriptor::TYPE_INT32 : return "INT32"; - case FieldDescriptor::TYPE_UINT32 : return "UINT32"; - case FieldDescriptor::TYPE_SINT32 : return "SINT32"; - case FieldDescriptor::TYPE_FIXED32 : return "FIXED32"; - case FieldDescriptor::TYPE_SFIXED32: return "SFIXED32"; - case FieldDescriptor::TYPE_INT64 : return "INT64"; - case FieldDescriptor::TYPE_UINT64 : return "UINT64"; - case FieldDescriptor::TYPE_SINT64 : return "SINT64"; - case FieldDescriptor::TYPE_FIXED64 : return "FIXED64"; - case FieldDescriptor::TYPE_SFIXED64: return "SFIXED64"; - case FieldDescriptor::TYPE_FLOAT : return "FLOAT"; - case FieldDescriptor::TYPE_DOUBLE : return "DOUBLE"; - case FieldDescriptor::TYPE_BOOL : return "BOOL"; - case FieldDescriptor::TYPE_STRING : return "STRING"; - case FieldDescriptor::TYPE_BYTES : return "BYTES"; - case FieldDescriptor::TYPE_ENUM : return "ENUM"; - case FieldDescriptor::TYPE_GROUP : return "GROUP"; - case FieldDescriptor::TYPE_MESSAGE : return "MESSAGE"; - - // No default because we want the compiler to complain if any new - // types are added. - } - - GOOGLE_LOG(FATAL) << "Can't get here."; - return NULL; -} - -} - -ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor) - : descriptor_(descriptor) { +ImmutableExtensionGenerator::ImmutableExtensionGenerator( + const FieldDescriptor* descriptor, Context* context) + : descriptor_(descriptor), context_(context), + name_resolver_(context->GetNameResolver()) { if (descriptor_->extension_scope() != NULL) { - scope_ = ClassName(descriptor_->extension_scope()); + scope_ = name_resolver_->GetImmutableClassName( + descriptor_->extension_scope()); } else { - scope_ = ClassName(descriptor_->file()); + scope_ = name_resolver_->GetImmutableClassName(descriptor_->file()); } } -ExtensionGenerator::~ExtensionGenerator() {} +ImmutableExtensionGenerator::~ImmutableExtensionGenerator() {} // Initializes the vars referenced in the generated code templates. -void InitTemplateVars(const FieldDescriptor* descriptor, - const string& scope, - map<string, string>* vars_pointer) { +void ExtensionGenerator::InitTemplateVars(const FieldDescriptor* descriptor, + const string& scope, + bool immutable, + ClassNameResolver* name_resolver, + map<string, string>* vars_pointer) { map<string, string> &vars = *vars_pointer; vars["scope"] = scope; vars["name"] = UnderscoresToCamelCase(descriptor); - vars["containing_type"] = ClassName(descriptor->containing_type()); + vars["containing_type"] = + name_resolver->GetClassName(descriptor->containing_type(), immutable); vars["number"] = SimpleItoa(descriptor->number()); vars["constant_name"] = FieldConstantName(descriptor); vars["index"] = SimpleItoa(descriptor->index()); - vars["default"] = - descriptor->is_repeated() ? "" : DefaultValue(descriptor); - vars["type_constant"] = TypeName(GetType(descriptor)); + vars["default"] = descriptor->is_repeated() ? + "" : DefaultValue(descriptor, immutable, name_resolver); + vars["type_constant"] = FieldTypeName(GetType(descriptor)); vars["packed"] = descriptor->options().packed() ? "true" : "false"; vars["enum_map"] = "null"; vars["prototype"] = "null"; @@ -109,13 +85,21 @@ void InitTemplateVars(const FieldDescriptor* descriptor, string singular_type; switch (java_type) { case JAVATYPE_MESSAGE: - singular_type = ClassName(descriptor->message_type()); + singular_type = name_resolver->GetClassName(descriptor->message_type(), + immutable); vars["prototype"] = singular_type + ".getDefaultInstance()"; break; case JAVATYPE_ENUM: - singular_type = ClassName(descriptor->enum_type()); + singular_type = name_resolver->GetClassName(descriptor->enum_type(), + immutable); vars["enum_map"] = singular_type + ".internalGetValueMap()"; break; + case JAVATYPE_STRING: + singular_type = "java.lang.String"; + break; + case JAVATYPE_BYTES: + singular_type = immutable ? "com.google.protobuf.ByteString" : "byte[]"; + break; default: singular_type = BoxedPrimitiveTypeName(java_type); break; @@ -125,9 +109,11 @@ void InitTemplateVars(const FieldDescriptor* descriptor, vars["singular_type"] = singular_type; } -void ExtensionGenerator::Generate(io::Printer* printer) { +void ImmutableExtensionGenerator::Generate(io::Printer* printer) { map<string, string> vars; - InitTemplateVars(descriptor_, scope_, &vars); + const bool kUseImmutableNames = true; + InitTemplateVars(descriptor_, scope_, kUseImmutableNames, name_resolver_, + &vars); printer->Print(vars, "public static final int $constant_name$ = $number$;\n"); @@ -174,7 +160,8 @@ void ExtensionGenerator::Generate(io::Printer* printer) { " $enum_map$,\n" " $number$,\n" " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n" - " $packed$);\n"); + " $packed$,\n" + " $singular_type$.class);\n"); } else { printer->Print( vars, @@ -188,12 +175,13 @@ void ExtensionGenerator::Generate(io::Printer* printer) { " $prototype$,\n" " $enum_map$,\n" " $number$,\n" - " com.google.protobuf.WireFormat.FieldType.$type_constant$);\n"); + " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n" + " $singular_type$.class);\n"); } } } -void ExtensionGenerator::GenerateNonNestedInitializationCode( +void ImmutableExtensionGenerator::GenerateNonNestedInitializationCode( io::Printer* printer) { if (descriptor_->extension_scope() == NULL && HasDescriptorMethods(descriptor_->file())) { @@ -205,7 +193,8 @@ void ExtensionGenerator::GenerateNonNestedInitializationCode( } } -void ExtensionGenerator::GenerateRegistrationCode(io::Printer* printer) { +void ImmutableExtensionGenerator::GenerateRegistrationCode( + io::Printer* printer) { printer->Print( "registry.add($scope$.$name$);\n", "scope", scope_, diff --git a/src/google/protobuf/compiler/java/java_extension.h b/src/google/protobuf/compiler/java/java_extension.h index 009ed9ff..69686ef2 100644 --- a/src/google/protobuf/compiler/java/java_extension.h +++ b/src/google/protobuf/compiler/java/java_extension.h @@ -35,6 +35,7 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H__ #define GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H__ +#include <map> #include <string> #include <google/protobuf/stubs/common.h> @@ -42,6 +43,12 @@ namespace google { namespace protobuf { class FieldDescriptor; // descriptor.h + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } namespace io { class Printer; // printer.h } @@ -56,17 +63,42 @@ namespace java { // since extensions are just simple identifiers with interesting types. class ExtensionGenerator { public: - explicit ExtensionGenerator(const FieldDescriptor* descriptor); - ~ExtensionGenerator(); + explicit ExtensionGenerator() {} + virtual ~ExtensionGenerator() {} + + virtual void Generate(io::Printer* printer) = 0; + virtual void GenerateNonNestedInitializationCode(io::Printer* printer) = 0; + virtual void GenerateRegistrationCode(io::Printer* printer) = 0; - void Generate(io::Printer* printer); - void GenerateNonNestedInitializationCode(io::Printer* printer); - void GenerateRegistrationCode(io::Printer* printer); + protected: + static void InitTemplateVars(const FieldDescriptor* descriptor, + const string& scope, + bool immutable, + ClassNameResolver* name_resolver, + map<string, string>* vars_pointer); private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator); +}; + +class ImmutableExtensionGenerator : public ExtensionGenerator { + public: + explicit ImmutableExtensionGenerator(const FieldDescriptor* descriptor, + Context* context); + virtual ~ImmutableExtensionGenerator(); + + virtual void Generate(io::Printer* printer); + virtual void GenerateNonNestedInitializationCode(io::Printer* printer); + virtual void GenerateRegistrationCode(io::Printer* printer); + + protected: const FieldDescriptor* descriptor_; + Context* context_; + ClassNameResolver* name_resolver_; string scope_; - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableExtensionGenerator); }; } // namespace java @@ -74,4 +106,4 @@ class ExtensionGenerator { } // namespace protobuf } // namespace google -#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__ +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H__ diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc index c7d433c8..f670477c 100644 --- a/src/google/protobuf/compiler/java/java_field.cc +++ b/src/google/protobuf/compiler/java/java_field.cc @@ -33,102 +33,178 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include <google/protobuf/compiler/java/java_field.h> -#include <google/protobuf/compiler/java/java_helpers.h> -#include <google/protobuf/compiler/java/java_primitive_field.h> + +#include <memory> + +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/java/java_context.h> #include <google/protobuf/compiler/java/java_enum_field.h> +#include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_lazy_message_field.h> #include <google/protobuf/compiler/java/java_message_field.h> +#include <google/protobuf/compiler/java/java_primitive_field.h> #include <google/protobuf/compiler/java/java_string_field.h> -#include <google/protobuf/stubs/common.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/stubs/substitute.h> namespace google { namespace protobuf { namespace compiler { namespace java { -FieldGenerator::~FieldGenerator() {} +namespace { + +ImmutableFieldGenerator* MakeImmutableGenerator( + const FieldDescriptor* field, int messageBitIndex, int builderBitIndex, + Context* context) { + if (field->is_repeated()) { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + if (IsLazy(field)) { + return new RepeatedImmutableLazyMessageFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + } else { + return new RepeatedImmutableMessageFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + } + case JAVATYPE_ENUM: + return new RepeatedImmutableEnumFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + case JAVATYPE_STRING: + return new RepeatedImmutableStringFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + default: + return new RepeatedImmutablePrimitiveFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + } + } else { + if (field->containing_oneof()) { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + if (IsLazy(field)) { + return new ImmutableLazyMessageOneofFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + } else { + return new ImmutableMessageOneofFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + } + case JAVATYPE_ENUM: + return new ImmutableEnumOneofFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + case JAVATYPE_STRING: + return new ImmutableStringOneofFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + default: + return new ImmutablePrimitiveOneofFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + } + } else { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + if (IsLazy(field)) { + return new ImmutableLazyMessageFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + } else { + return new ImmutableMessageFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + } + case JAVATYPE_ENUM: + return new ImmutableEnumFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + case JAVATYPE_STRING: + return new ImmutableStringFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + default: + return new ImmutablePrimitiveFieldGenerator( + field, messageBitIndex, builderBitIndex, context); + } + } + } +} + -void FieldGenerator::GenerateParsingCodeFromPacked(io::Printer* printer) const { +static inline void ReportUnexpectedPackedFieldsCall(io::Printer* printer) { // Reaching here indicates a bug. Cases are: - // - This FieldGenerator should support packing, but this method should be - // overridden. - // - This FieldGenerator doesn't support packing, and this method should - // never have been called. + // - This FieldGenerator should support packing, + // but this method should be overridden. + // - This FieldGenerator doesn't support packing, and this method + // should never have been called. GOOGLE_LOG(FATAL) << "GenerateParsingCodeFromPacked() " << "called on field generator that does not support packing."; } -FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor) - : descriptor_(descriptor), - field_generators_( - new scoped_ptr<FieldGenerator>[descriptor->field_count()]), - extension_generators_( - new scoped_ptr<FieldGenerator>[descriptor->extension_count()]) { +} // namespace + +ImmutableFieldGenerator::~ImmutableFieldGenerator() {} + +void ImmutableFieldGenerator:: +GenerateParsingCodeFromPacked(io::Printer* printer) const { + ReportUnexpectedPackedFieldsCall(printer); +} + +// =================================================================== + +template <> +FieldGeneratorMap<ImmutableFieldGenerator>::FieldGeneratorMap( + const Descriptor* descriptor, Context* context) + : descriptor_(descriptor), + field_generators_(new scoped_ptr< + ImmutableFieldGenerator>[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++) { - FieldGenerator* generator = MakeGenerator(descriptor->field(i), - messageBitIndex, builderBitIndex); + ImmutableFieldGenerator* generator = MakeImmutableGenerator( + descriptor->field(i), messageBitIndex, builderBitIndex, context); field_generators_[i].reset(generator); messageBitIndex += generator->GetNumBitsForMessage(); builderBitIndex += generator->GetNumBitsForBuilder(); } - for (int i = 0; i < descriptor->extension_count(); i++) { - FieldGenerator* generator = MakeGenerator(descriptor->extension(i), - messageBitIndex, builderBitIndex); - extension_generators_[i].reset(generator); - messageBitIndex += generator->GetNumBitsForMessage(); - builderBitIndex += generator->GetNumBitsForBuilder(); - } } -FieldGenerator* FieldGeneratorMap::MakeGenerator( - const FieldDescriptor* field, int messageBitIndex, int builderBitIndex) { - if (field->is_repeated()) { - switch (GetJavaType(field)) { - case JAVATYPE_MESSAGE: - return new RepeatedMessageFieldGenerator( - field, messageBitIndex, builderBitIndex); - case JAVATYPE_ENUM: - return new RepeatedEnumFieldGenerator( - field, messageBitIndex, builderBitIndex); - case JAVATYPE_STRING: - return new RepeatedStringFieldGenerator( - field, messageBitIndex, builderBitIndex); - default: - return new RepeatedPrimitiveFieldGenerator( - field, messageBitIndex, builderBitIndex); - } - } else { - switch (GetJavaType(field)) { - case JAVATYPE_MESSAGE: - return new MessageFieldGenerator( - field, messageBitIndex, builderBitIndex); - case JAVATYPE_ENUM: - return new EnumFieldGenerator( - field, messageBitIndex, builderBitIndex); - case JAVATYPE_STRING: - return new StringFieldGenerator( - field, messageBitIndex, builderBitIndex); - default: - return new PrimitiveFieldGenerator( - field, messageBitIndex, builderBitIndex); - } - } -} +template<> +FieldGeneratorMap<ImmutableFieldGenerator>::~FieldGeneratorMap() {} -FieldGeneratorMap::~FieldGeneratorMap() {} -const FieldGenerator& FieldGeneratorMap::get( - const FieldDescriptor* field) const { - GOOGLE_CHECK_EQ(field->containing_type(), descriptor_); - return *field_generators_[field->index()]; +void SetCommonFieldVariables(const FieldDescriptor* descriptor, + const FieldGeneratorInfo* info, + map<string, string>* variables) { + (*variables)["field_name"] = descriptor->name(); + (*variables)["name"] = info->name; + (*variables)["capitalized_name"] = info->capitalized_name; + (*variables)["disambiguated_reason"] = info->disambiguated_reason; + (*variables)["constant_name"] = FieldConstantName(descriptor); + (*variables)["number"] = SimpleItoa(descriptor->number()); } -const FieldGenerator& FieldGeneratorMap::get_extension(int index) const { - return *extension_generators_[index]; +void SetCommonOneofVariables(const FieldDescriptor* descriptor, + const OneofGeneratorInfo* info, + map<string, string>* variables) { + (*variables)["oneof_name"] = info->name; + (*variables)["oneof_capitalized_name"] = info->capitalized_name; + (*variables)["oneof_index"] = + SimpleItoa(descriptor->containing_oneof()->index()); + (*variables)["set_oneof_case_message"] = info->name + + "Case_ = " + SimpleItoa(descriptor->number()); + (*variables)["clear_oneof_case_message"] = info->name + + "Case_ = 0"; + (*variables)["has_oneof_case_message"] = info->name + + "Case_ == " + SimpleItoa(descriptor->number()); +} + +void PrintExtraFieldInfo(const map<string, string>& variables, + io::Printer* printer) { + const map<string, string>::const_iterator it = + variables.find("disambiguated_reason"); + if (it != variables.end() && !it->second.empty()) { + printer->Print( + variables, + "// An alternative name is used for field \"$field_name$\" because:\n" + "// $disambiguated_reason$\n"); + } } } // namespace java diff --git a/src/google/protobuf/compiler/java/java_field.h b/src/google/protobuf/compiler/java/java_field.h index 4dd0efd6..a4aa3bce 100644 --- a/src/google/protobuf/compiler/java/java_field.h +++ b/src/google/protobuf/compiler/java/java_field.h @@ -35,14 +35,23 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_FIELD_H__ #define GOOGLE_PROTOBUF_COMPILER_JAVA_FIELD_H__ +#include <map> +#include <memory> #include <string> + #include <google/protobuf/stubs/common.h> #include <google/protobuf/descriptor.h> namespace google { namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } namespace io { - class Printer; // printer.h + class Printer; // printer.h } } @@ -50,10 +59,10 @@ namespace protobuf { namespace compiler { namespace java { -class FieldGenerator { +class ImmutableFieldGenerator { public: - FieldGenerator() {} - virtual ~FieldGenerator(); + ImmutableFieldGenerator() {} + virtual ~ImmutableFieldGenerator(); virtual int GetNumBitsForMessage() const = 0; virtual int GetNumBitsForBuilder() const = 0; @@ -78,29 +87,73 @@ class FieldGenerator { virtual string GetBoxedType() const = 0; private: - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGenerator); + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableFieldGenerator); }; + // Convenience class which constructs FieldGenerators for a Descriptor. +template<typename FieldGeneratorType> class FieldGeneratorMap { public: - explicit FieldGeneratorMap(const Descriptor* descriptor); + explicit FieldGeneratorMap(const Descriptor* descriptor, + Context* context); ~FieldGeneratorMap(); - const FieldGenerator& get(const FieldDescriptor* field) const; - const FieldGenerator& get_extension(int index) const; + const FieldGeneratorType& get(const FieldDescriptor* field) const; private: const Descriptor* descriptor_; - scoped_array<scoped_ptr<FieldGenerator> > field_generators_; - scoped_array<scoped_ptr<FieldGenerator> > extension_generators_; - - static FieldGenerator* MakeGenerator(const FieldDescriptor* field, - int messageBitIndex, int builderBitIndex); + Context* context_; + ClassNameResolver* name_resolver_; + scoped_array<scoped_ptr<FieldGeneratorType> > field_generators_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap); }; +template<typename FieldGeneratorType> +inline const FieldGeneratorType& +FieldGeneratorMap<FieldGeneratorType>::get(const FieldDescriptor* field) const { + GOOGLE_CHECK_EQ(field->containing_type(), descriptor_); + return *field_generators_[field->index()]; +} + +// Instantiate template for mutable and immutable maps. +template<> +FieldGeneratorMap<ImmutableFieldGenerator>:: +FieldGeneratorMap(const Descriptor* descriptor, + Context* context); + +template<> +FieldGeneratorMap<ImmutableFieldGenerator>::~FieldGeneratorMap(); + + +// Field information used in FieldGeneartors. +struct FieldGeneratorInfo { + string name; + string capitalized_name; + string disambiguated_reason; +}; + +// Oneof information used in OneofFieldGeneartors. +struct OneofGeneratorInfo { + string name; + string capitalized_name; +}; + +// Set some common variables used in variable FieldGenerators. +void SetCommonFieldVariables(const FieldDescriptor* descriptor, + const FieldGeneratorInfo* info, + map<string, string>* variables); + +// Set some common oneof variables used in OneofFieldGenerators. +void SetCommonOneofVariables(const FieldDescriptor* descriptor, + const OneofGeneratorInfo* info, + map<string, string>* variables); + +// Print useful comments before a field's accessors. +void PrintExtraFieldInfo(const map<string, string>& variables, + io::Printer* printer); + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc index f43e5500..deec0e71 100644 --- a/src/google/protobuf/compiler/java/java_file.cc +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -33,11 +33,17 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include <google/protobuf/compiler/java/java_file.h> + +#include <memory> + +#include <google/protobuf/compiler/java/java_context.h> #include <google/protobuf/compiler/java/java_enum.h> -#include <google/protobuf/compiler/java/java_service.h> #include <google/protobuf/compiler/java/java_extension.h> +#include <google/protobuf/compiler/java/java_generator_factory.h> #include <google/protobuf/compiler/java/java_helpers.h> #include <google/protobuf/compiler/java/java_message.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> +#include <google/protobuf/compiler/java/java_service.h> #include <google/protobuf/compiler/code_generator.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/io/zero_copy_stream.h> @@ -132,10 +138,27 @@ void CollectExtensions(const FileDescriptorProto& file_proto, } // namespace -FileGenerator::FileGenerator(const FileDescriptor* file) - : file_(file), - java_package_(FileJavaPackage(file)), - classname_(FileClassName(file)) { +FileGenerator::FileGenerator(const FileDescriptor* file, bool immutable_api) + : file_(file), + java_package_(FileJavaPackage(file, immutable_api)), + message_generators_( + new scoped_ptr<MessageGenerator>[file->message_type_count()]), + extension_generators_( + new scoped_ptr<ExtensionGenerator>[file->extension_count()]), + context_(new Context(file)), + name_resolver_(context_->GetNameResolver()), + immutable_api_(immutable_api) { + classname_ = name_resolver_->GetFileClassName(file, immutable_api); + generator_factory_.reset( + new ImmutableGeneratorFactory(context_.get())); + for (int i = 0; i < file_->message_type_count(); ++i) { + message_generators_[i].reset( + generator_factory_->NewMessageGenerator(file_->message_type(i))); + } + for (int i = 0; i < file_->extension_count(); ++i) { + extension_generators_[i].reset( + generator_factory_->NewExtensionGenerator(file_->extension(i))); + } } FileGenerator::~FileGenerator() {} @@ -145,25 +168,7 @@ bool FileGenerator::Validate(string* error) { // problem that leads to Java compile errors that can be hard to understand. // It's especially bad when using the java_multiple_files, since we would // end up overwriting the outer class with one of the inner ones. - - bool found_conflict = false; - for (int i = 0; i < file_->enum_type_count() && !found_conflict; i++) { - if (file_->enum_type(i)->name() == classname_) { - found_conflict = true; - } - } - for (int i = 0; i < file_->message_type_count() && !found_conflict; i++) { - if (file_->message_type(i)->name() == classname_) { - found_conflict = true; - } - } - for (int i = 0; i < file_->service_count() && !found_conflict; i++) { - if (file_->service(i)->name() == classname_) { - found_conflict = true; - } - } - - if (found_conflict) { + if (name_resolver_->HasConflictingClassName(file_, classname_)) { error->assign(file_->name()); error->append( ": Cannot generate Java output because the file's outer class name, \""); @@ -174,7 +179,29 @@ bool FileGenerator::Validate(string* error) { "option to specify a different outer class name for the .proto file."); return false; } - + // If java_outer_classname option is not set and the default outer class name + // conflicts with a type defined in the message, we will append a suffix to + // avoid the conflict. This allows proto1 API protos to be dual-compiled into + // proto2 API without code change. When this happens we'd like to issue an + // warning to let the user know that the outer class name has been changed. + // Although we only do this automatic naming fix for immutable API, mutable + // outer class name will also be affected as it's contructed from immutable + // outer class name with an additional "Mutable" prefix. Since the naming + // change in mutable API is not caused by a naming conflict, we generate the + // warning for immutable API only. + if (immutable_api_ && !file_->options().has_java_outer_classname()) { + string default_classname = + name_resolver_->GetFileDefaultImmutableClassName(file_); + if (default_classname != classname_) { + GOOGLE_LOG(WARNING) << file_->name() << ": The default outer class name, \"" + << default_classname << "\", conflicts with a type " + << "declared in the proto file and an alternative outer " + << "class name is used: \"" << classname_ << "\". To avoid " + << "this warning, please use the java_outer_classname " + << "option to specify a different outer class name for " + << "the .proto file."; + } + } return true; } @@ -208,12 +235,11 @@ void FileGenerator::Generate(io::Printer* printer) { printer->Indent(); for (int i = 0; i < file_->extension_count(); i++) { - ExtensionGenerator(file_->extension(i)).GenerateRegistrationCode(printer); + extension_generators_[i]->GenerateRegistrationCode(printer); } for (int i = 0; i < file_->message_type_count(); i++) { - MessageGenerator(file_->message_type(i)) - .GenerateExtensionRegistrationCode(printer); + message_generators_[i]->GenerateExtensionRegistrationCode(printer); } printer->Outdent(); @@ -222,18 +248,20 @@ void FileGenerator::Generate(io::Printer* printer) { // ----------------------------------------------------------------- - if (!file_->options().java_multiple_files()) { + if (!MultipleJavaFiles(file_, immutable_api_)) { for (int i = 0; i < file_->enum_type_count(); i++) { - EnumGenerator(file_->enum_type(i)).Generate(printer); + EnumGenerator(file_->enum_type(i), immutable_api_, context_.get()) + .Generate(printer); } for (int i = 0; i < file_->message_type_count(); i++) { - MessageGenerator messageGenerator(file_->message_type(i)); - messageGenerator.GenerateInterface(printer); - messageGenerator.Generate(printer); + message_generators_[i]->GenerateInterface(printer); + message_generators_[i]->Generate(printer); } if (HasGenericServices(file_)) { for (int i = 0; i < file_->service_count(); i++) { - ServiceGenerator(file_->service(i)).Generate(printer); + scoped_ptr<ServiceGenerator> generator( + generator_factory_->NewServiceGenerator(file_->service(i))); + generator->Generate(printer); } } } @@ -241,28 +269,29 @@ void FileGenerator::Generate(io::Printer* printer) { // Extensions must be generated in the outer class since they are values, // not classes. for (int i = 0; i < file_->extension_count(); i++) { - ExtensionGenerator(file_->extension(i)).Generate(printer); + extension_generators_[i]->Generate(printer); } // Static variables. for (int i = 0; i < file_->message_type_count(); i++) { - // TODO(kenton): Reuse MessageGenerator objects? - MessageGenerator(file_->message_type(i)).GenerateStaticVariables(printer); + message_generators_[i]->GenerateStaticVariables(printer); } printer->Print("\n"); if (HasDescriptorMethods(file_)) { - GenerateEmbeddedDescriptor(printer); + if (immutable_api_) { + GenerateDescriptorInitializationCodeForImmutable(printer); + } else { + GenerateDescriptorInitializationCodeForMutable(printer); + } } else { printer->Print( "static {\n"); printer->Indent(); for (int i = 0; i < file_->message_type_count(); i++) { - // TODO(kenton): Reuse MessageGenerator objects? - MessageGenerator(file_->message_type(i)) - .GenerateStaticVariableInitializers(printer); + message_generators_[i]->GenerateStaticVariableInitializers(printer); } printer->Outdent(); @@ -278,23 +307,8 @@ void FileGenerator::Generate(io::Printer* printer) { printer->Print("}\n"); } -void FileGenerator::GenerateEmbeddedDescriptor(io::Printer* printer) { - // Embed the descriptor. We simply serialize the entire FileDescriptorProto - // and embed it as a string literal, which is parsed and built into real - // descriptors at initialization time. We unfortunately have to put it in - // a string literal, not a byte array, because apparently using a literal - // byte array causes the Java compiler to generate *instructions* to - // initialize each and every byte of the array, e.g. as if you typed: - // b[0] = 123; b[1] = 456; b[2] = 789; - // This makes huge bytecode files and can easily hit the compiler's internal - // code size limits (error "code to large"). String literals are apparently - // embedded raw, which is what we want. - FileDescriptorProto file_proto; - file_->CopyTo(&file_proto); - - string file_data; - file_proto.SerializeToString(&file_data); - +void FileGenerator::GenerateDescriptorInitializationCodeForImmutable( + io::Printer* printer) { printer->Print( "public static com.google.protobuf.Descriptors.FileDescriptor\n" " getDescriptor() {\n" @@ -302,55 +316,18 @@ void FileGenerator::GenerateEmbeddedDescriptor(io::Printer* printer) { "}\n" "private static com.google.protobuf.Descriptors.FileDescriptor\n" " descriptor;\n" - "static {\n" - " java.lang.String[] descriptorData = {\n"); + "static {\n"); printer->Indent(); - printer->Indent(); - - // Only write 40 bytes per line. - static const int kBytesPerLine = 40; - for (int i = 0; i < file_data.size(); i += kBytesPerLine) { - if (i > 0) { - // Every 400 lines, start a new string literal, in order to avoid the - // 64k length limit. - if (i % 400 == 0) { - printer->Print(",\n"); - } else { - printer->Print(" +\n"); - } - } - printer->Print("\"$data$\"", - "data", CEscape(file_data.substr(i, kBytesPerLine))); - } - - printer->Outdent(); - printer->Print("\n};\n"); - - // ----------------------------------------------------------------- - // Create the InternalDescriptorAssigner. printer->Print( - "com.google.protobuf.Descriptors.FileDescriptor." - "InternalDescriptorAssigner assigner =\n" - " new com.google.protobuf.Descriptors.FileDescriptor." - "InternalDescriptorAssigner() {\n" - " public com.google.protobuf.ExtensionRegistry assignDescriptors(\n" - " com.google.protobuf.Descriptors.FileDescriptor root) {\n" - " descriptor = root;\n"); - - printer->Indent(); - printer->Indent(); - printer->Indent(); + "descriptor = $descriptor_classname$.descriptor;\n", + "descriptor_classname", name_resolver_->GetDescriptorClassName(file_)); for (int i = 0; i < file_->message_type_count(); i++) { - // TODO(kenton): Reuse MessageGenerator objects? - MessageGenerator(file_->message_type(i)) - .GenerateStaticVariableInitializers(printer); + message_generators_[i]->GenerateStaticVariableInitializers(printer); } for (int i = 0; i < file_->extension_count(); i++) { - // TODO(kenton): Reuse ExtensionGenerator objects? - ExtensionGenerator(file_->extension(i)) - .GenerateNonNestedInitializationCode(printer); + extension_generators_[i]->GenerateNonNestedInitializationCode(printer); } // Proto compiler builds a DescriptorPool, which holds all the descriptors to @@ -368,52 +345,107 @@ void FileGenerator::GenerateEmbeddedDescriptor(io::Printer* printer) { // To find those extensions, we need to parse the data into a dynamic message // of the FileDescriptor based on the builder-pool, then we can use // reflections to find all extension fields + FileDescriptorProto file_proto; + file_->CopyTo(&file_proto); + string file_data; + file_proto.SerializeToString(&file_data); vector<const FieldDescriptor*> extensions; CollectExtensions(file_proto, *file_->pool(), &extensions, file_data); if (extensions.size() > 0) { // Must construct an ExtensionRegistry containing all existing extensions - // and return it. + // and use it to parse the descriptor data again to recognize extensions. printer->Print( "com.google.protobuf.ExtensionRegistry registry =\n" - " com.google.protobuf.ExtensionRegistry.newInstance();\n"); + " com.google.protobuf.ExtensionRegistry.newInstance();\n"); for (int i = 0; i < extensions.size(); i++) { - ExtensionGenerator(extensions[i]).GenerateRegistrationCode(printer); + scoped_ptr<ExtensionGenerator> generator( + generator_factory_->NewExtensionGenerator(extensions[i])); + generator->GenerateRegistrationCode(printer); } printer->Print( - "return registry;\n"); - } else { - printer->Print( - "return null;\n"); + "com.google.protobuf.Descriptors.FileDescriptor\n" + " .internalUpdateFileDescriptor(descriptor, registry);\n"); } - printer->Outdent(); - printer->Outdent(); - printer->Outdent(); + // Force descriptor initialization of all dependencies. + for (int i = 0; i < file_->dependency_count(); i++) { + if (ShouldIncludeDependency(file_->dependency(i), true)) { + string dependency = + name_resolver_->GetImmutableClassName(file_->dependency(i)); + printer->Print( + "$dependency$.getDescriptor();\n", + "dependency", dependency); + } + } + printer->Outdent(); printer->Print( - " }\n" - " };\n"); + "}\n"); +} - // ----------------------------------------------------------------- - // Invoke internalBuildGeneratedFileFrom() to build the file. +void FileGenerator::GenerateDescriptorInitializationCodeForMutable(io::Printer* printer) { + printer->Print( + "public static com.google.protobuf.Descriptors.FileDescriptor\n" + " getDescriptor() {\n" + " return descriptor;\n" + "}\n" + "private static com.google.protobuf.Descriptors.FileDescriptor\n" + " descriptor;\n" + "static {\n"); + printer->Indent(); printer->Print( - "com.google.protobuf.Descriptors.FileDescriptor\n" - " .internalBuildGeneratedFileFrom(descriptorData,\n" - " new com.google.protobuf.Descriptors.FileDescriptor[] {\n"); + "descriptor = $immutable_package$.$descriptor_classname$.descriptor;\n", + "immutable_package", FileJavaPackage(file_, true), + "descriptor_classname", name_resolver_->GetDescriptorClassName(file_)); + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateStaticVariableInitializers(printer); + } + for (int i = 0; i < file_->extension_count(); i++) { + extension_generators_[i]->GenerateNonNestedInitializationCode(printer); + } + + // Check if custom options exist. If any, try to load immutable classes since + // custom options are only represented with immutable messages. + FileDescriptorProto file_proto; + file_->CopyTo(&file_proto); + string file_data; + file_proto.SerializeToString(&file_data); + vector<const FieldDescriptor*> extensions; + CollectExtensions(file_proto, *file_->pool(), &extensions, file_data); + + if (extensions.size() > 0) { + // Try to load immutable messages' outer class. Its initialization code + // will take care of interpreting custom options. + printer->Print( + "try {\n" + // Note that we have to load the immutable class dynamically here as + // we want the mutable code to be independent from the immutable code + // at compile time. It is required to implement dual-compile for + // mutable and immutable API in blaze. + " java.lang.Class immutableClass = java.lang.Class.forName(\n" + " \"$immutable_classname$\");\n" + "} catch (java.lang.ClassNotFoundException e) {\n" + // The immutable class can not be found. Custom options are left + // as unknown fields. + // TODO(xiaofeng): inform the user with a warning? + "}\n", + "immutable_classname", name_resolver_->GetImmutableClassName(file_)); + } + + // Force descriptor initialization of all dependencies. for (int i = 0; i < file_->dependency_count(); i++) { - if (ShouldIncludeDependency(file_->dependency(i))) { + if (ShouldIncludeDependency(file_->dependency(i), false)) { + string dependency = name_resolver_->GetMutableClassName( + file_->dependency(i)); printer->Print( - " $dependency$.getDescriptor(),\n", - "dependency", ClassName(file_->dependency(i))); + "$dependency$.getDescriptor();\n", + "dependency", dependency); } } - printer->Print( - " }, assigner);\n"); - printer->Outdent(); printer->Print( "}\n"); @@ -426,6 +458,7 @@ static void GenerateSibling(const string& package_dir, GeneratorContext* context, vector<string>* file_list, const string& name_suffix, + GeneratorClass* generator, void (GeneratorClass::*pfn)(io::Printer* printer)) { string filename = package_dir + descriptor->name() + name_suffix + ".java"; file_list->push_back(filename); @@ -445,42 +478,53 @@ static void GenerateSibling(const string& package_dir, "package", java_package); } - GeneratorClass generator(descriptor); - (generator.*pfn)(&printer); + (generator->*pfn)(&printer); } void FileGenerator::GenerateSiblings(const string& package_dir, GeneratorContext* context, vector<string>* file_list) { - if (file_->options().java_multiple_files()) { + if (MultipleJavaFiles(file_, immutable_api_)) { for (int i = 0; i < file_->enum_type_count(); i++) { + EnumGenerator generator(file_->enum_type(i), immutable_api_, + context_.get()); GenerateSibling<EnumGenerator>(package_dir, java_package_, file_->enum_type(i), context, file_list, "", + &generator, &EnumGenerator::Generate); } for (int i = 0; i < file_->message_type_count(); i++) { - GenerateSibling<MessageGenerator>(package_dir, java_package_, - file_->message_type(i), - context, file_list, "OrBuilder", - &MessageGenerator::GenerateInterface); + if (immutable_api_) { + GenerateSibling<MessageGenerator>(package_dir, java_package_, + file_->message_type(i), + context, file_list, + "OrBuilder", + message_generators_[i].get(), + &MessageGenerator::GenerateInterface); + } GenerateSibling<MessageGenerator>(package_dir, java_package_, file_->message_type(i), context, file_list, "", + message_generators_[i].get(), &MessageGenerator::Generate); } if (HasGenericServices(file_)) { for (int i = 0; i < file_->service_count(); i++) { + scoped_ptr<ServiceGenerator> generator( + generator_factory_->NewServiceGenerator(file_->service(i))); GenerateSibling<ServiceGenerator>(package_dir, java_package_, file_->service(i), context, file_list, "", + generator.get(), &ServiceGenerator::Generate); } } } } -bool FileGenerator::ShouldIncludeDependency(const FileDescriptor* descriptor) { +bool FileGenerator::ShouldIncludeDependency( + const FileDescriptor* descriptor, bool immutable_api) { return true; } diff --git a/src/google/protobuf/compiler/java/java_file.h b/src/google/protobuf/compiler/java/java_file.h index 59911462..7f289491 100644 --- a/src/google/protobuf/compiler/java/java_file.h +++ b/src/google/protobuf/compiler/java/java_file.h @@ -35,18 +35,26 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_FILE_H__ #define GOOGLE_PROTOBUF_COMPILER_JAVA_FILE_H__ +#include <memory> #include <string> #include <vector> #include <google/protobuf/stubs/common.h> namespace google { namespace protobuf { - class FileDescriptor; // descriptor.h + class FileDescriptor; // descriptor.h namespace io { - class Printer; // printer.h + class Printer; // printer.h } namespace compiler { - class GeneratorContext; // code_generator.h + class GeneratorContext; // code_generator.h + namespace java { + class Context; // context.h + class MessageGenerator; // message.h + class GeneratorFactory; // generator_factory.h + class ExtensionGenerator; // extension.h + class ClassNameResolver; // name_resolver.h + } } } @@ -56,7 +64,7 @@ namespace java { class FileGenerator { public: - explicit FileGenerator(const FileDescriptor* file); + FileGenerator(const FileDescriptor* file, bool immutable_api = true); ~FileGenerator(); // Checks for problems that would otherwise lead to cryptic compile errors. @@ -78,17 +86,23 @@ class FileGenerator { private: - // Returns whether the dependency should be included in the output file. - // Always returns true for opensource, but used internally at Google to help - // improve compatibility with version 1 of protocol buffers. - bool ShouldIncludeDependency(const FileDescriptor* descriptor); + void GenerateDescriptorInitializationCodeForImmutable(io::Printer* printer); + void GenerateDescriptorInitializationCodeForMutable(io::Printer* printer); + + bool ShouldIncludeDependency(const FileDescriptor* descriptor, + bool immutable_api_); const FileDescriptor* file_; string java_package_; string classname_; + scoped_array<scoped_ptr<MessageGenerator> > message_generators_; + scoped_array<scoped_ptr<ExtensionGenerator> > extension_generators_; + scoped_ptr<GeneratorFactory> generator_factory_; + scoped_ptr<Context> context_; + ClassNameResolver* name_resolver_; + bool immutable_api_; - void GenerateEmbeddedDescriptor(io::Printer* printer); GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator); }; diff --git a/src/google/protobuf/compiler/java/java_generator.cc b/src/google/protobuf/compiler/java/java_generator.cc index e6c79abc..b713797b 100644 --- a/src/google/protobuf/compiler/java/java_generator.cc +++ b/src/google/protobuf/compiler/java/java_generator.cc @@ -33,8 +33,13 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include <google/protobuf/compiler/java/java_generator.h> + +#include <memory> + #include <google/protobuf/compiler/java/java_file.h> +#include <google/protobuf/compiler/java/java_generator_factory.h> #include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_shared_code_generator.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/io/zero_copy_stream.h> #include <google/protobuf/descriptor.pb.h> @@ -64,55 +69,89 @@ bool JavaGenerator::Generate(const FileDescriptor* file, vector<pair<string, string> > options; ParseGeneratorParameter(parameter, &options); + bool generate_immutable_code = false; + bool generate_mutable_code = false; + bool generate_shared_code = false; for (int i = 0; i < options.size(); i++) { if (options[i].first == "output_list_file") { output_list_file = options[i].second; + } else if (options[i].first == "immutable") { + generate_immutable_code = true; + } else if (options[i].first == "mutable") { + generate_mutable_code = true; + } else if (options[i].first == "shared") { + generate_shared_code = true; } else { *error = "Unknown generator option: " + options[i].first; return false; } } + // By default we generate immutable code and shared code for immutable API. + if (!generate_immutable_code && !generate_mutable_code && + !generate_shared_code) { + generate_immutable_code = true; + generate_shared_code = true; + } + // ----------------------------------------------------------------- - if (file->options().optimize_for() == FileOptions::LITE_RUNTIME && - file->options().java_generate_equals_and_hash()) { - *error = "The \"java_generate_equals_and_hash\" option is incompatible " - "with \"optimize_for = LITE_RUNTIME\". You must optimize for " - "SPEED or CODE_SIZE if you want to use this option."; - return false; + vector<string> all_files; + + if (generate_shared_code) { + // Generate code shared between immutable and mutable API. + SharedCodeGenerator shared_code_generator(file); + shared_code_generator.Generate(context, &all_files); } - FileGenerator file_generator(file); - if (!file_generator.Validate(error)) { - return false; + vector<FileGenerator*> file_generators; + if (generate_immutable_code) { + file_generators.push_back(new FileGenerator(file, /* immutable = */ true)); + } + if (generate_mutable_code) { + file_generators.push_back(new FileGenerator(file, /* mutable = */ false)); + } + for (int i = 0; i < file_generators.size(); ++i) { + if (!file_generators[i]->Validate(error)) { + for (int j = 0; j < file_generators.size(); ++j) { + delete file_generators[j]; + } + return false; + } } - string package_dir = JavaPackageToDir(file_generator.java_package()); + for (int i = 0; i < file_generators.size(); ++i) { + FileGenerator* file_generator = file_generators[i]; - vector<string> all_files; + string package_dir = JavaPackageToDir(file_generator->java_package()); - string java_filename = package_dir; - java_filename += file_generator.classname(); - java_filename += ".java"; - all_files.push_back(java_filename); + string java_filename = package_dir; + java_filename += file_generator->classname(); + java_filename += ".java"; + all_files.push_back(java_filename); - // Generate main java file. - scoped_ptr<io::ZeroCopyOutputStream> output( - context->Open(java_filename)); - io::Printer printer(output.get(), '$'); - file_generator.Generate(&printer); + // Generate main java file. + scoped_ptr<io::ZeroCopyOutputStream> output( + context->Open(java_filename)); + io::Printer printer(output.get(), '$'); + file_generator->Generate(&printer); - // Generate sibling files. - file_generator.GenerateSiblings(package_dir, context, &all_files); + // Generate sibling files. + file_generator->GenerateSiblings(package_dir, context, &all_files); + } + + for (int i = 0; i < file_generators.size(); ++i) { + delete file_generators[i]; + } + file_generators.clear(); // Generate output list if requested. if (!output_list_file.empty()) { // Generate output list. This is just a simple text file placed in a // deterministic location which lists the .java files being generated. scoped_ptr<io::ZeroCopyOutputStream> srclist_raw_output( - context->Open(output_list_file)); + context->Open(output_list_file)); io::Printer srclist_printer(srclist_raw_output.get(), '$'); for (int i = 0; i < all_files.size(); i++) { srclist_printer.Print("$filename$\n", "filename", all_files[i]); diff --git a/src/google/protobuf/compiler/java/java_generator_factory.cc b/src/google/protobuf/compiler/java/java_generator_factory.cc new file mode 100644 index 00000000..ce71d436 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_generator_factory.cc @@ -0,0 +1,77 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: liujisi@google.com (Pherl Liu) + +#include <google/protobuf/compiler/java/java_generator_factory.h> + +#include <google/protobuf/compiler/java/java_context.h> +#include <google/protobuf/compiler/java/java_enum_field.h> +#include <google/protobuf/compiler/java/java_extension.h> +#include <google/protobuf/compiler/java/java_field.h> +#include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_message.h> +#include <google/protobuf/compiler/java/java_service.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +GeneratorFactory::GeneratorFactory() {} +GeneratorFactory::~GeneratorFactory() {} + +// =================================================================== + +ImmutableGeneratorFactory::ImmutableGeneratorFactory( + Context* context) : context_(context) { +} +ImmutableGeneratorFactory::~ImmutableGeneratorFactory() {} + +MessageGenerator* ImmutableGeneratorFactory::NewMessageGenerator( + const Descriptor* descriptor) const { + return new ImmutableMessageGenerator(descriptor, context_); +} + +ExtensionGenerator* ImmutableGeneratorFactory::NewExtensionGenerator( + const FieldDescriptor* descriptor) const { + return new ImmutableExtensionGenerator(descriptor, context_); +} + +ServiceGenerator* ImmutableGeneratorFactory::NewServiceGenerator( + const ServiceDescriptor* descriptor) const { + return new ImmutableServiceGenerator(descriptor, context_); +} + + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_generator_factory.h b/src/google/protobuf/compiler/java/java_generator_factory.h new file mode 100644 index 00000000..abf894f6 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_generator_factory.h @@ -0,0 +1,101 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: liujisi@google.com (Pherl Liu) + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_GENERATOR_FACTORY_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_GENERATOR_FACTORY_H__ + +#include <google/protobuf/stubs/common.h> + +namespace google { +namespace protobuf { + class FieldDescriptor; // descriptor.h + class Descriptor; // descriptor.h + class ServiceDescriptor; // descriptor.h + namespace compiler { + namespace java { + class MessageGenerator; // message.h + class ExtensionGenerator; // extension.h + class ServiceGenerator; // service.h + class Context; // context.h + } + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class GeneratorFactory { + public: + GeneratorFactory(); + virtual ~GeneratorFactory(); + + virtual MessageGenerator* NewMessageGenerator( + const Descriptor* descriptor) const = 0; + + virtual ExtensionGenerator* NewExtensionGenerator( + const FieldDescriptor* descriptor) const = 0; + + virtual ServiceGenerator* NewServiceGenerator( + const ServiceDescriptor* descriptor) const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GeneratorFactory); +}; + +// Factory that creates generators for immutable-default messages. +class ImmutableGeneratorFactory : public GeneratorFactory { + public: + ImmutableGeneratorFactory(Context* context); + virtual ~ImmutableGeneratorFactory(); + + virtual MessageGenerator* NewMessageGenerator( + const Descriptor* descriptor) const; + + virtual ExtensionGenerator* NewExtensionGenerator( + const FieldDescriptor* descriptor) const; + + virtual ServiceGenerator* NewServiceGenerator( + const ServiceDescriptor* descriptor) const; + + private: + Context* context_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableGeneratorFactory); +}; + + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_GENERATOR_FACTORY_H__ diff --git a/src/google/protobuf/compiler/java/java_helpers.cc b/src/google/protobuf/compiler/java/java_helpers.cc index cf241b8a..fdd4e5e5 100644 --- a/src/google/protobuf/compiler/java/java_helpers.cc +++ b/src/google/protobuf/compiler/java/java_helpers.cc @@ -32,11 +32,15 @@ // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. +#include <algorithm> +#include <google/protobuf/stubs/hash.h> #include <limits> #include <vector> #include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> #include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/wire_format.h> #include <google/protobuf/stubs/strutil.h> #include <google/protobuf/stubs/substitute.h> @@ -45,6 +49,9 @@ namespace protobuf { namespace compiler { namespace java { +using internal::WireFormat; +using internal::WireFormatLite; + const char kThickSeparator[] = "// ===================================================================\n"; const char kThinSeparator[] = @@ -54,18 +61,47 @@ namespace { const char* kDefaultPackage = ""; -const string& FieldName(const FieldDescriptor* field) { +// Names that should be avoided as field names. +// Using them will cause the compiler to generate accessors whose names are +// colliding with methods defined in base classes. +const char* kForbiddenWordList[] = { + // message base class: + "cached_size", "serialized_size", + // java.lang.Object: + "class", +}; + +bool IsForbidden(const string& field_name) { + for (int i = 0; i < GOOGLE_ARRAYSIZE(kForbiddenWordList); ++i) { + if (field_name == kForbiddenWordList[i]) { + return true; + } + } + return false; +} + +string FieldName(const FieldDescriptor* field) { + string field_name; // Groups are hacky: The name of the field is just the lower-cased name // of the group type. In Java, though, we would like to retain the original // capitalization of the type name. if (GetType(field) == FieldDescriptor::TYPE_GROUP) { - return field->message_type()->name(); + field_name = field->message_type()->name(); } else { - return field->name(); + field_name = field->name(); + } + if (IsForbidden(field_name)) { + // Append a trailing "#" to indicate that the name should be decorated to + // avoid collision with other names. + field_name += "#"; } + return field_name; } -string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) { + +} // namespace + +string UnderscoresToCamelCase(const string& input, bool cap_next_letter) { string result; // Note: I distrust ctype.h due to locales. for (int i = 0; i < input.size(); i++) { @@ -93,21 +129,27 @@ string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) { cap_next_letter = true; } } + // Add a trailing "_" if the name should be altered. + if (input[input.size() - 1] == '#') { + result += '_'; + } return result; } -} // namespace - string UnderscoresToCamelCase(const FieldDescriptor* field) { - return UnderscoresToCamelCaseImpl(FieldName(field), false); + return UnderscoresToCamelCase(FieldName(field), false); } string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) { - return UnderscoresToCamelCaseImpl(FieldName(field), true); + return UnderscoresToCamelCase(FieldName(field), true); } string UnderscoresToCamelCase(const MethodDescriptor* method) { - return UnderscoresToCamelCaseImpl(method->name(), false); + return UnderscoresToCamelCase(method->name(), false); +} + +string UniqueFileScopeIdentifier(const Descriptor* descriptor) { + return "static_" + StringReplace(descriptor->full_name(), ".", "_", true); } string StripProto(const string& filename) { @@ -118,22 +160,12 @@ string StripProto(const string& filename) { } } -string FileClassName(const FileDescriptor* file) { - if (file->options().has_java_outer_classname()) { - return file->options().java_outer_classname(); - } else { - string basename; - string::size_type last_slash = file->name().find_last_of('/'); - if (last_slash == string::npos) { - basename = file->name(); - } else { - basename = file->name().substr(last_slash + 1); - } - return UnderscoresToCamelCaseImpl(StripProto(basename), true); - } +string FileClassName(const FileDescriptor* file, bool immutable) { + ClassNameResolver name_resolver; + return name_resolver.GetFileClassName(file, immutable); } -string FileJavaPackage(const FileDescriptor* file) { +string FileJavaPackage(const FileDescriptor* file, bool immutable) { string result; if (file->options().has_java_package()) { @@ -146,7 +178,6 @@ string FileJavaPackage(const FileDescriptor* file) { } } - return result; } @@ -157,7 +188,10 @@ string JavaPackageToDir(string package_name) { return package_dir; } -string ToJavaName(const string& full_name, const FileDescriptor* file) { +// TODO(xiaofeng): This function is only kept for it's publicly referenced. +// It should be removed after mutable API up-integration. +string ToJavaName(const string& full_name, + const FileDescriptor* file) { string result; if (file->options().java_multiple_files()) { result = FileJavaPackage(file); @@ -178,22 +212,42 @@ string ToJavaName(const string& full_name, const FileDescriptor* file) { } string ClassName(const Descriptor* descriptor) { - return ToJavaName(descriptor->full_name(), descriptor->file()); + ClassNameResolver name_resolver; + return name_resolver.GetClassName(descriptor, true); } string ClassName(const EnumDescriptor* descriptor) { - return ToJavaName(descriptor->full_name(), descriptor->file()); + ClassNameResolver name_resolver; + return name_resolver.GetClassName(descriptor, true); } string ClassName(const ServiceDescriptor* descriptor) { - return ToJavaName(descriptor->full_name(), descriptor->file()); + ClassNameResolver name_resolver; + return name_resolver.GetClassName(descriptor, true); } string ClassName(const FileDescriptor* descriptor) { - string result = FileJavaPackage(descriptor); - if (!result.empty()) result += '.'; - result += FileClassName(descriptor); - return result; + ClassNameResolver name_resolver; + return name_resolver.GetClassName(descriptor, true); +} + +string ExtraMessageInterfaces(const Descriptor* descriptor) { + string interfaces = "// @@protoc_insertion_point(message_implements:" + + descriptor->full_name() + ")"; + return interfaces; +} + + +string ExtraBuilderInterfaces(const Descriptor* descriptor) { + string interfaces = "// @@protoc_insertion_point(builder_implements:" + + descriptor->full_name() + ")"; + return interfaces; +} + +string ExtraMessageOrBuilderInterfaces(const Descriptor* descriptor) { + string interfaces = "// @@protoc_insertion_point(interface_extends:" + + descriptor->full_name() + ")"; + return interfaces; } string FieldConstantName(const FieldDescriptor *field) { @@ -272,6 +326,35 @@ const char* BoxedPrimitiveTypeName(JavaType type) { return NULL; } +const char* FieldTypeName(FieldDescriptor::Type field_type) { + switch (field_type) { + case FieldDescriptor::TYPE_INT32 : return "INT32"; + case FieldDescriptor::TYPE_UINT32 : return "UINT32"; + case FieldDescriptor::TYPE_SINT32 : return "SINT32"; + case FieldDescriptor::TYPE_FIXED32 : return "FIXED32"; + case FieldDescriptor::TYPE_SFIXED32: return "SFIXED32"; + case FieldDescriptor::TYPE_INT64 : return "INT64"; + case FieldDescriptor::TYPE_UINT64 : return "UINT64"; + case FieldDescriptor::TYPE_SINT64 : return "SINT64"; + case FieldDescriptor::TYPE_FIXED64 : return "FIXED64"; + case FieldDescriptor::TYPE_SFIXED64: return "SFIXED64"; + case FieldDescriptor::TYPE_FLOAT : return "FLOAT"; + case FieldDescriptor::TYPE_DOUBLE : return "DOUBLE"; + case FieldDescriptor::TYPE_BOOL : return "BOOL"; + case FieldDescriptor::TYPE_STRING : return "STRING"; + case FieldDescriptor::TYPE_BYTES : return "BYTES"; + case FieldDescriptor::TYPE_ENUM : return "ENUM"; + case FieldDescriptor::TYPE_GROUP : return "GROUP"; + case FieldDescriptor::TYPE_MESSAGE : return "MESSAGE"; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + bool AllAscii(const string& text) { for (int i = 0; i < text.size(); i++) { if ((text[i] & 0x80) != 0) { @@ -281,7 +364,8 @@ bool AllAscii(const string& text) { return true; } -string DefaultValue(const FieldDescriptor* field) { +string DefaultValue(const FieldDescriptor* field, bool immutable, + ClassNameResolver* name_resolver) { // Switch on CppType since we need to know which default_value_* method // of FieldDescriptor to call. switch (field->cpp_type()) { @@ -344,11 +428,12 @@ string DefaultValue(const FieldDescriptor* field) { } case FieldDescriptor::CPPTYPE_ENUM: - return ClassName(field->enum_type()) + "." + + return name_resolver->GetClassName(field->enum_type(), immutable) + "." + field->default_value_enum()->name(); case FieldDescriptor::CPPTYPE_MESSAGE: - return ClassName(field->message_type()) + ".getDefaultInstance()"; + return name_resolver->GetClassName(field->message_type(), immutable) + + ".getDefaultInstance()"; // No default because we want the compiler to complain if any new // types are added. @@ -494,6 +579,158 @@ string GenerateSetBitMutableLocal(int bitIndex) { return GenerateSetBitInternal("mutable_", bitIndex); } +bool IsReferenceType(JavaType type) { + switch (type) { + case JAVATYPE_INT : return false; + case JAVATYPE_LONG : return false; + case JAVATYPE_FLOAT : return false; + case JAVATYPE_DOUBLE : return false; + case JAVATYPE_BOOLEAN: return false; + case JAVATYPE_STRING : return true; + case JAVATYPE_BYTES : return true; + case JAVATYPE_ENUM : return true; + case JAVATYPE_MESSAGE: return true; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return false; +} + +const char* GetCapitalizedType(const FieldDescriptor* field, bool immutable) { + switch (GetType(field)) { + case FieldDescriptor::TYPE_INT32 : return "Int32"; + case FieldDescriptor::TYPE_UINT32 : return "UInt32"; + case FieldDescriptor::TYPE_SINT32 : return "SInt32"; + case FieldDescriptor::TYPE_FIXED32 : return "Fixed32"; + case FieldDescriptor::TYPE_SFIXED32: return "SFixed32"; + case FieldDescriptor::TYPE_INT64 : return "Int64"; + case FieldDescriptor::TYPE_UINT64 : return "UInt64"; + case FieldDescriptor::TYPE_SINT64 : return "SInt64"; + case FieldDescriptor::TYPE_FIXED64 : return "Fixed64"; + case FieldDescriptor::TYPE_SFIXED64: return "SFixed64"; + case FieldDescriptor::TYPE_FLOAT : return "Float"; + case FieldDescriptor::TYPE_DOUBLE : return "Double"; + case FieldDescriptor::TYPE_BOOL : return "Bool"; + case FieldDescriptor::TYPE_STRING : return "String"; + case FieldDescriptor::TYPE_BYTES : { + return "Bytes"; + } + case FieldDescriptor::TYPE_ENUM : return "Enum"; + case FieldDescriptor::TYPE_GROUP : return "Group"; + case FieldDescriptor::TYPE_MESSAGE : return "Message"; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +// For encodings with fixed sizes, returns that size in bytes. Otherwise +// returns -1. +int FixedSize(FieldDescriptor::Type type) { + switch (type) { + case FieldDescriptor::TYPE_INT32 : return -1; + case FieldDescriptor::TYPE_INT64 : return -1; + case FieldDescriptor::TYPE_UINT32 : return -1; + case FieldDescriptor::TYPE_UINT64 : return -1; + case FieldDescriptor::TYPE_SINT32 : return -1; + case FieldDescriptor::TYPE_SINT64 : return -1; + case FieldDescriptor::TYPE_FIXED32 : return WireFormatLite::kFixed32Size; + case FieldDescriptor::TYPE_FIXED64 : return WireFormatLite::kFixed64Size; + case FieldDescriptor::TYPE_SFIXED32: return WireFormatLite::kSFixed32Size; + case FieldDescriptor::TYPE_SFIXED64: return WireFormatLite::kSFixed64Size; + case FieldDescriptor::TYPE_FLOAT : return WireFormatLite::kFloatSize; + case FieldDescriptor::TYPE_DOUBLE : return WireFormatLite::kDoubleSize; + + case FieldDescriptor::TYPE_BOOL : return WireFormatLite::kBoolSize; + case FieldDescriptor::TYPE_ENUM : return -1; + + case FieldDescriptor::TYPE_STRING : return -1; + case FieldDescriptor::TYPE_BYTES : return -1; + case FieldDescriptor::TYPE_GROUP : return -1; + case FieldDescriptor::TYPE_MESSAGE : return -1; + + // No default because we want the compiler to complain if any new + // types are added. + } + GOOGLE_LOG(FATAL) << "Can't get here."; + return -1; +} + +// Sort the fields of the given Descriptor by number into a new[]'d array +// and return it. The caller should delete the returned array. +const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) { + const FieldDescriptor** fields = + new const FieldDescriptor*[descriptor->field_count()]; + for (int i = 0; i < descriptor->field_count(); i++) { + fields[i] = descriptor->field(i); + } + sort(fields, fields + descriptor->field_count(), + FieldOrderingByNumber()); + return fields; +} + +// Returns true if the message type has any required fields. If it doesn't, +// we can optimize out calls to its isInitialized() method. +// +// already_seen is used to avoid checking the same type multiple times +// (and also to protect against recursion). +bool HasRequiredFields( + const Descriptor* type, + hash_set<const Descriptor*>* already_seen) { + if (already_seen->count(type) > 0) { + // The type is already in cache. This means that either: + // a. The type has no required fields. + // b. We are in the midst of checking if the type has required fields, + // somewhere up the stack. In this case, we know that if the type + // has any required fields, they'll be found when we return to it, + // and the whole call to HasRequiredFields() will return true. + // Therefore, we don't have to check if this type has required fields + // here. + return false; + } + already_seen->insert(type); + + // If the type has extensions, an extension with message type could contain + // required fields, so we have to be conservative and assume such an + // extension exists. + if (type->extension_range_count() > 0) return true; + + for (int i = 0; i < type->field_count(); i++) { + const FieldDescriptor* field = type->field(i); + if (field->is_required()) { + return true; + } + if (GetJavaType(field) == JAVATYPE_MESSAGE) { + if (HasRequiredFields(field->message_type(), already_seen)) { + return true; + } + } + } + + return false; +} + +bool HasRequiredFields(const Descriptor* type) { + hash_set<const Descriptor*> already_seen; + return HasRequiredFields(type, &already_seen); +} + +bool HasRepeatedFields(const Descriptor* descriptor) { + for (int i = 0; i < descriptor->field_count(); ++i) { + const FieldDescriptor* field = descriptor->field(i); + if (field->is_repeated()) { + return true; + } + } + return false; +} + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h index 3937f069..6d1eae0a 100644 --- a/src/google/protobuf/compiler/java/java_helpers.h +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -49,6 +49,9 @@ namespace java { extern const char kThickSeparator[]; extern const char kThinSeparator[]; +// Converts a name to camel-case. If cap_first_letter is true, capitalize the +// first letter. +string UnderscoresToCamelCase(const string& name, bool cap_first_letter); // Converts the field's name to camel-case, e.g. "foo_bar_baz" becomes // "fooBarBaz" or "FooBarBaz", respectively. string UnderscoresToCamelCase(const FieldDescriptor* field); @@ -58,15 +61,22 @@ string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field); // of lower-casing the first letter of the name.) string UnderscoresToCamelCase(const MethodDescriptor* method); +// Get an identifier that uniquely identifies this type within the file. +// This is used to declare static variables related to this type at the +// outermost file scope. +string UniqueFileScopeIdentifier(const Descriptor* descriptor); + // Strips ".proto" or ".protodevel" from the end of a filename. string StripProto(const string& filename); -// Gets the unqualified class name for the file. Each .proto file becomes a -// single Java class, with all its contents nested in that class. -string FileClassName(const FileDescriptor* file); +// Gets the unqualified class name for the file. For each .proto file, there +// will be one Java class containing all the immutable messages and another +// Java class containing all the mutable messages. +// TODO(xiaofeng): remove the default value after updating client code. +string FileClassName(const FileDescriptor* file, bool immutable = true); // Returns the file's Java package name. -string FileJavaPackage(const FileDescriptor* file); +string FileJavaPackage(const FileDescriptor* file, bool immutable = true); // Returns output directory for the given package name. string JavaPackageToDir(string package_name); @@ -74,17 +84,46 @@ string JavaPackageToDir(string package_name); // Converts the given fully-qualified name in the proto namespace to its // fully-qualified name in the Java namespace, given that it is in the given // file. -string ToJavaName(const string& full_name, const FileDescriptor* file); - -// These return the fully-qualified class name corresponding to the given -// descriptor. +// TODO(xiaofeng): this method is deprecated and should be removed in the +// future. +string ToJavaName(const string& full_name, + const FileDescriptor* file); + +// TODO(xiaofeng): the following methods are kept for they are exposed +// publicly in //google/protobuf/compiler/java/names.h. They return +// immutable names only and should be removed after mutable API is +// integrated into google3. string ClassName(const Descriptor* descriptor); string ClassName(const EnumDescriptor* descriptor); string ClassName(const ServiceDescriptor* descriptor); string ClassName(const FileDescriptor* descriptor); -inline string ExtensionIdentifierName(const FieldDescriptor* descriptor) { - return ToJavaName(descriptor->full_name(), descriptor->file()); +// Comma-separate list of option-specified interfaces implemented by the +// Message, to follow the "implements" declaration of the Message definition. +string ExtraMessageInterfaces(const Descriptor* descriptor); +// Comma-separate list of option-specified interfaces implemented by the +// MutableMessage, to follow the "implements" declaration of the MutableMessage +// definition. +string ExtraMutableMessageInterfaces(const Descriptor* descriptor); +// Comma-separate list of option-specified interfaces implemented by the +// Builder, to follow the "implements" declaration of the Builder definition. +string ExtraBuilderInterfaces(const Descriptor* descriptor); +// Comma-separate list of option-specified interfaces extended by the +// MessageOrBuilder, to follow the "extends" declaration of the +// MessageOrBuilder definition. +string ExtraMessageOrBuilderInterfaces(const Descriptor* descriptor); + +// Get the unqualified Java class name for mutable messages. i.e. without +// package or outer classnames. +inline string ShortMutableJavaClassName(const Descriptor* descriptor) { + return descriptor->name(); +} + + +// Whether we should generate multiple java files for messages. +inline bool MultipleJavaFiles( + const FileDescriptor* descriptor, bool immutable) { + return descriptor->options().java_multiple_files(); } // Get the unqualified name that should be used for a field's field @@ -115,11 +154,23 @@ JavaType GetJavaType(const FieldDescriptor* field); // types. const char* BoxedPrimitiveTypeName(JavaType type); -string DefaultValue(const FieldDescriptor* field); +// Get the name of the java enum constant representing this type. E.g., +// "INT32" for FieldDescriptor::TYPE_INT32. The enum constant's full +// name is "com.google.protobuf.WireFormat.FieldType.INT32". +const char* FieldTypeName(const FieldDescriptor::Type field_type); + +class ClassNameResolver; +string DefaultValue(const FieldDescriptor* field, bool immutable, + ClassNameResolver* name_resolver); +inline string ImmutableDefaultValue(const FieldDescriptor* field, + ClassNameResolver* name_resolver) { + return DefaultValue(field, true, name_resolver); +} bool IsDefaultValueJavaDefault(const FieldDescriptor* field); -// Does this message class keep track of unknown fields? -inline bool HasUnknownFields(const Descriptor* descriptor) { +// Does this message class use UnknownFieldSet? +// Otherwise, unknown fields will be stored in a ByteString object +inline bool UseUnknownFieldSet(const Descriptor* descriptor) { return descriptor->file()->options().optimize_for() != FileOptions::LITE_RUNTIME; } @@ -163,6 +214,15 @@ inline bool HasGenericServices(const FileDescriptor *file) { file->options().java_generic_services(); } +inline bool IsLazy(const FieldDescriptor* descriptor) { + // Currently, the proto-lite version suports lazy field. + // TODO(niwasaki): Support lazy fields also for other proto runtimes. + if (descriptor->file()->options().optimize_for() != + FileOptions::LITE_RUNTIME) { + return false; + } + return descriptor->options().lazy(); +} // Methods for shared bitfields. @@ -212,6 +272,48 @@ string GenerateGetBitMutableLocal(int bitIndex); // Example: "mutable_bitField1_ = (mutable_bitField1_ | 0x04)" string GenerateSetBitMutableLocal(int bitIndex); +// Returns whether the JavaType is a reference type. +bool IsReferenceType(JavaType type); + +// Returns the capitalized name for calling relative functions in +// CodedInputStream +const char* GetCapitalizedType(const FieldDescriptor* field, bool immutable); + +// For encodings with fixed sizes, returns that size in bytes. Otherwise +// returns -1. +int FixedSize(FieldDescriptor::Type type); + +// Comparators used to sort fields in MessageGenerator +struct FieldOrderingByNumber { + inline bool operator()(const FieldDescriptor* a, + const FieldDescriptor* b) const { + return a->number() < b->number(); + } +}; + +struct ExtensionRangeOrdering { + bool operator()(const Descriptor::ExtensionRange* a, + const Descriptor::ExtensionRange* b) const { + return a->start < b->start; + } +}; + +// Sort the fields of the given Descriptor by number into a new[]'d array +// and return it. The caller should delete the returned array. +const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor); + +// Check a message type and its sub-message types recursively to see if any of +// them has a required field. Return true if a required field is found. +bool HasRequiredFields(const Descriptor* descriptor); + +// Whether a .proto file supports field presence test for non-message types. +inline bool SupportFieldPresence(const FileDescriptor* descriptor) { + return true; +} + +// Check whether a mesasge has repeated fields. +bool HasRepeatedFields(const Descriptor* descriptor); + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_lazy_message_field.cc b/src/google/protobuf/compiler/java/java_lazy_message_field.cc new file mode 100644 index 00000000..32d97b6f --- /dev/null +++ b/src/google/protobuf/compiler/java/java_lazy_message_field.cc @@ -0,0 +1,826 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: niwasaki@google.com (Naoki Iwasaki) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include <google/protobuf/compiler/java/java_context.h> +#include <google/protobuf/compiler/java/java_lazy_message_field.h> +#include <google/protobuf/compiler/java/java_doc_comment.h> +#include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/io/printer.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +ImmutableLazyMessageFieldGenerator:: +ImmutableLazyMessageFieldGenerator( + const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableMessageFieldGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { +} + +ImmutableLazyMessageFieldGenerator::~ImmutableLazyMessageFieldGenerator() {} + +void ImmutableLazyMessageFieldGenerator:: +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"); + if (HasNestedBuilders(descriptor_->containing_type())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + " return $name$_;\n" + "}\n"); + } +} + +void ImmutableLazyMessageFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // When using nested-builders, the code initially works just like the + // non-nested builder case. It only creates a nested builder lazily on + // demand and then forever delegates to it after creation. + + printer->Print(variables_, + "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"); + } + + // 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 $get_has_field_bit_builder$;\n" + "}\n"); + + 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_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder set$capitalized_name$($type$ value)", + + "if (value == null) {\n" + " throw new NullPointerException();\n" + "}\n" + "$name$_.setValue(value);\n" + "$on_changed$\n", + + NULL, // Lazy fields are supported only for lite-runtime. + + "$set_has_field_bit_builder$;\n" + "return this;\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder set$capitalized_name$(\n" + " $type$.Builder builderForValue)", + + "$name$_.setValue(builderForValue.build());\n" + "$on_changed$\n", + + NULL, + + "$set_has_field_bit_builder$;\n" + "return this;\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder merge$capitalized_name$($type$ value)", + + "if ($get_has_field_bit_builder$ &&\n" + " !$name$_.containsDefaultInstance()) {\n" + " $name$_.setValue(\n" + " $type$.newBuilder(\n" + " get$capitalized_name$()).mergeFrom(value).buildPartial());\n" + "} else {\n" + " $name$_.setValue(value);\n" + "}\n" + "$on_changed$\n", + + NULL, + + "$set_has_field_bit_builder$;\n" + "return this;\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder clear$capitalized_name$()", + + "$name$_.clear();\n" + "$on_changed$\n", + + NULL, + + "$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"); + } +} + + +void ImmutableLazyMessageFieldGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.clear();\n"); +} + +void ImmutableLazyMessageFieldGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.clear();\n"); + printer->Print(variables_, "$clear_has_field_bit_builder$;\n"); +} + +void ImmutableLazyMessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " $name$_.merge(other.$name$_);\n" + " $set_has_field_bit_builder$;\n" + "}\n"); +} + +void ImmutableLazyMessageFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_has_field_bit_from_local$) {\n" + " $set_has_field_bit_to_local$;\n" + "}\n"); + + printer->Print(variables_, + "result.$name$_.setByteString(\n" + " $name$_.toByteString(),\n" + " $name$_.getExtensionRegistry());\n"); +} + +void ImmutableLazyMessageFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.setByteString(input.readBytes(), extensionRegistry);\n"); + printer->Print(variables_, + "$set_has_field_bit_message$;\n"); +} + +void ImmutableLazyMessageFieldGenerator:: +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 ImmutableLazyMessageFieldGenerator:: +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"); +} + +// =================================================================== + +ImmutableLazyMessageOneofFieldGenerator:: +ImmutableLazyMessageOneofFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableLazyMessageFieldGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); + variables_["lazy_type"] = "com.google.protobuf.LazyFieldLite"; +} + +ImmutableLazyMessageOneofFieldGenerator:: +~ImmutableLazyMessageOneofFieldGenerator() {} + +void ImmutableLazyMessageOneofFieldGenerator:: +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"); +} + +void ImmutableLazyMessageOneofFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // boolean hasField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $has_oneof_case_message$;\n" + "}\n"); + + 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_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder set$capitalized_name$($type$ value)", + + "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" + "$on_changed$\n", + + NULL, // Lazy fields are supported only for lite-runtime. + + "return this;\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder set$capitalized_name$(\n" + " $type$.Builder builderForValue)", + + "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" + "$on_changed$\n", + + NULL, + + "return this;\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder merge$capitalized_name$($type$ value)", + + "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" + "$on_changed$\n", + + NULL, + + "return this;\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder clear$capitalized_name$()", + + "if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " $on_changed$\n" + "}\n", + + NULL, + + "return this;\n"); +} + +void ImmutableLazyMessageOneofFieldGenerator:: +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 ImmutableLazyMessageOneofFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n"); + printer->Indent(); + + printer->Print(variables_, + "result.$oneof_name$_ = new $lazy_type$();\n" + "(($lazy_type$) result.$oneof_name$_).setByteString(\n" + " (($lazy_type$) $oneof_name$_).toByteString(),\n" + " (($lazy_type$) $oneof_name$_).getExtensionRegistry());\n"); + printer->Outdent(); + printer->Print("}\n"); +} + +void ImmutableLazyMessageOneofFieldGenerator:: +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 ImmutableLazyMessageOneofFieldGenerator:: +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 ImmutableLazyMessageOneofFieldGenerator:: +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"); +} + +// =================================================================== + +RepeatedImmutableLazyMessageFieldGenerator:: +RepeatedImmutableLazyMessageFieldGenerator( + const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : RepeatedImmutableMessageFieldGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { +} + + +RepeatedImmutableLazyMessageFieldGenerator:: +~RepeatedImmutableLazyMessageFieldGenerator() {} + +void RepeatedImmutableLazyMessageFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private java.util.List<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" + " return list;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<? extends $type$OrBuilder>\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"); +} + +void RepeatedImmutableLazyMessageFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // When using nested-builders, the code initially works just like the + // non-nested builder case. It only creates a nested builder lazily on + // demand and then forever delegates to it after creation. + + printer->Print(variables_, + "private java.util.List<com.google.protobuf.LazyFieldLite> $name$_ =\n" + " java.util.Collections.emptyList();\n" + + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$get_mutable_bit_builder$) {\n" + " $name$_ =\n" + " new java.util.ArrayList<com.google.protobuf.LazyFieldLite>(\n" + " $name$_);\n" + " $set_mutable_bit_builder$;\n" + " }\n" + "}\n" + "\n"); + + if (HasNestedBuilders(descriptor_->containing_type())) { + printer->Print(variables_, + // If this builder is non-null, it is used and the other fields are + // ignored. + "private com.google.protobuf.RepeatedFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;\n" + "\n"); + } + + // The comments above the methods below are based on a hypothetical + // repeated field of type "Field" called "RepeatedField". + + // List<Field> getRepeatedFieldList() + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public java.util.List<$type$> get$capitalized_name$List()", + + "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" + "return java.util.Collections.unmodifiableList(list);\n", + + "return $name$Builder_.getMessageList();\n", + + NULL); + + // int getRepeatedFieldCount() + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public int get$capitalized_name$Count()", + + "return $name$_.size();\n", + "return $name$Builder_.getCount();\n", + + NULL); + + // Field getRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public $type$ get$capitalized_name$(int index)", + + "return ($type$) $name$_.get(index).getValue(\n" + " $type$.getDefaultInstance());\n", + + "return $name$Builder_.getMessage(index);\n", + + NULL); + + // Builder setRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$ value)", + "if (value == null) {\n" + " throw new NullPointerException();\n" + "}\n" + "ensure$capitalized_name$IsMutable();\n" + "$name$_.set(index, com.google.protobuf.LazyFieldLite.fromValue(value));\n" + "$on_changed$\n", + "$name$Builder_.setMessage(index, value);\n", + "return this;\n"); + + // Builder setRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$.Builder builderForValue)", + + "ensure$capitalized_name$IsMutable();\n" + "$name$_.set(index, com.google.protobuf.LazyFieldLite.fromValue(\n" + " builderForValue.build()));\n" + "$on_changed$\n", + + "$name$Builder_.setMessage(index, builderForValue.build());\n", + + "return this;\n"); + + // Builder addRepeatedField(Field value) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder add$capitalized_name$($type$ value)", + + "if (value == null) {\n" + " throw new NullPointerException();\n" + "}\n" + "ensure$capitalized_name$IsMutable();\n" + "$name$_.add(com.google.protobuf.LazyFieldLite.fromValue(value));\n" + + "$on_changed$\n", + + "$name$Builder_.addMessage(value);\n", + + "return this;\n"); + + // Builder addRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder add$capitalized_name$(\n" + " int index, $type$ value)", + + "if (value == null) {\n" + " throw new NullPointerException();\n" + "}\n" + "ensure$capitalized_name$IsMutable();\n" + "$name$_.add(index, com.google.protobuf.LazyFieldLite.fromValue(value));\n" + "$on_changed$\n", + + "$name$Builder_.addMessage(index, value);\n", + + "return this;\n"); + + // Builder addRepeatedField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder add$capitalized_name$(\n" + " $type$.Builder builderForValue)", + + "ensure$capitalized_name$IsMutable();\n" + "$name$_.add(com.google.protobuf.LazyFieldLite.fromValue(\n" + " builderForValue.build()));\n" + "$on_changed$\n", + + "$name$Builder_.addMessage(builderForValue.build());\n", + + "return this;\n"); + + // Builder addRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder add$capitalized_name$(\n" + " int index, $type$.Builder builderForValue)", + + "ensure$capitalized_name$IsMutable();\n" + "$name$_.add(index, com.google.protobuf.LazyFieldLite.fromValue(\n" + " builderForValue.build()));\n" + "$on_changed$\n", + + "$name$Builder_.addMessage(index, builderForValue.build());\n", + + "return this;\n"); + + // Builder addAllRepeatedField(Iterable<Field> values) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable<? extends $type$> values)", + + "ensure$capitalized_name$IsMutable();\n" + "for (com.google.protobuf.MessageLite v : values) {\n" + " $name$_.add(com.google.protobuf.LazyFieldLite.fromValue(v));\n" + "}\n" + "$on_changed$\n", + + "$name$Builder_.addAllMessages(values);\n", + + "return this;\n"); + + // Builder clearAllRepeatedField() + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder clear$capitalized_name$()", + + "$name$_ = java.util.Collections.emptyList();\n" + "$clear_mutable_bit_builder$;\n" + "$on_changed$\n", + + "$name$Builder_.clear();\n", + + "return this;\n"); + + // Builder removeRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder remove$capitalized_name$(int index)", + + "ensure$capitalized_name$IsMutable();\n" + "$name$_.remove(index);\n" + "$on_changed$\n", + + "$name$Builder_.remove(index);\n", + + "return this;\n"); + + if (HasNestedBuilders(descriptor_->containing_type())) { + 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 java.util.List<? extends $type$OrBuilder> \n" + " get$capitalized_name$OrBuilderList() {\n" + " if ($name$Builder_ != null) {\n" + " return $name$Builder_.getMessageOrBuilderList();\n" + " } else {\n" + " return java.util.Collections.unmodifiableList($name$_);\n" + " }\n" + "}\n"); + + 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:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!$get_mutable_bit_parser$) {\n" + " $name$_ =\n" + " new java.util.ArrayList<com.google.protobuf.LazyFieldLite>();\n" + " $set_mutable_bit_parser$;\n" + "}\n" + "$name$_.add(new com.google.protobuf.LazyFieldLite(\n" + " extensionRegistry, input.readBytes()));\n"); +} + +void RepeatedImmutableLazyMessageFieldGenerator:: +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 RepeatedImmutableLazyMessageFieldGenerator:: +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.h b/src/google/protobuf/compiler/java/java_lazy_message_field.h new file mode 100644 index 00000000..2a1f8574 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_lazy_message_field.h @@ -0,0 +1,121 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: 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_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_LAZY_MESSAGE_FIELD_H__ + +#include <google/protobuf/compiler/java/java_field.h> +#include <google/protobuf/compiler/java/java_message_field.h> + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + } + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableLazyMessageFieldGenerator + : public ImmutableMessageFieldGenerator { + public: + explicit ImmutableLazyMessageFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableLazyMessageFieldGenerator(); + + // overroads ImmutableMessageFieldGenerator --------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateBuilderClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableLazyMessageFieldGenerator); +}; + +class ImmutableLazyMessageOneofFieldGenerator + : public ImmutableLazyMessageFieldGenerator { + public: + ImmutableLazyMessageOneofFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableLazyMessageOneofFieldGenerator(); + + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableLazyMessageOneofFieldGenerator); +}; + +class RepeatedImmutableLazyMessageFieldGenerator + : public RepeatedImmutableMessageFieldGenerator { + public: + explicit RepeatedImmutableLazyMessageFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~RepeatedImmutableLazyMessageFieldGenerator(); + + // overroads RepeatedImmutableMessageFieldGenerator ------------------------- + 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(RepeatedImmutableLazyMessageFieldGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_LAZY_MESSAGE_FIELD_H__ diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc index 9322e242..1cd08f7d 100644 --- a/src/google/protobuf/compiler/java/java_message.cc +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -37,12 +37,16 @@ #include <algorithm> #include <google/protobuf/stubs/hash.h> #include <map> +#include <memory> #include <vector> +#include <google/protobuf/compiler/java/java_context.h> #include <google/protobuf/compiler/java/java_doc_comment.h> #include <google/protobuf/compiler/java/java_enum.h> #include <google/protobuf/compiler/java/java_extension.h> +#include <google/protobuf/compiler/java/java_generator_factory.h> #include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/descriptor.pb.h> @@ -59,107 +63,31 @@ using internal::WireFormat; 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(); - printer->Print("// $def$\n", - "def", def.substr(0, def.find_first_of('\n'))); +bool GenerateHasBits(const Descriptor* descriptor) { + return SupportFieldPresence(descriptor->file()) || + HasRepeatedFields(descriptor); } - -struct FieldOrderingByNumber { - inline bool operator()(const FieldDescriptor* a, - const FieldDescriptor* b) const { - return a->number() < b->number(); - } -}; - -struct ExtensionRangeOrdering { - bool operator()(const Descriptor::ExtensionRange* a, - const Descriptor::ExtensionRange* b) const { - return a->start < b->start; - } -}; - -// Sort the fields of the given Descriptor by number into a new[]'d array -// and return it. -const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) { - const FieldDescriptor** fields = - new const FieldDescriptor*[descriptor->field_count()]; - for (int i = 0; i < descriptor->field_count(); i++) { - fields[i] = descriptor->field(i); - } - sort(fields, fields + descriptor->field_count(), - FieldOrderingByNumber()); - return fields; -} - -// Get an identifier that uniquely identifies this type within the file. -// This is used to declare static variables related to this type at the -// outermost file scope. -string UniqueFileScopeIdentifier(const Descriptor* descriptor) { - return "static_" + StringReplace(descriptor->full_name(), ".", "_", true); -} - -// Returns true if the message type has any required fields. If it doesn't, -// we can optimize out calls to its isInitialized() method. -// -// already_seen is used to avoid checking the same type multiple times -// (and also to protect against recursion). -static bool HasRequiredFields( - const Descriptor* type, - hash_set<const Descriptor*>* already_seen) { - if (already_seen->count(type) > 0) { - // The type is already in cache. This means that either: - // a. The type has no required fields. - // b. We are in the midst of checking if the type has required fields, - // somewhere up the stack. In this case, we know that if the type - // has any required fields, they'll be found when we return to it, - // and the whole call to HasRequiredFields() will return true. - // Therefore, we don't have to check if this type has required fields - // here. - return false; - } - already_seen->insert(type); - - // If the type has extensions, an extension with message type could contain - // required fields, so we have to be conservative and assume such an - // extension exists. - if (type->extension_range_count() > 0) return true; - - for (int i = 0; i < type->field_count(); i++) { - const FieldDescriptor* field = type->field(i); - if (field->is_required()) { - return true; - } - if (GetJavaType(field) == JAVATYPE_MESSAGE) { - if (HasRequiredFields(field->message_type(), already_seen)) { - return true; - } - } - } - - return false; -} - -static bool HasRequiredFields(const Descriptor* type) { - hash_set<const Descriptor*> already_seen; - return HasRequiredFields(type, &already_seen); -} - } // namespace // =================================================================== MessageGenerator::MessageGenerator(const Descriptor* descriptor) - : descriptor_(descriptor), - field_generators_(descriptor) { -} + : descriptor_(descriptor) {} MessageGenerator::~MessageGenerator() {} -void MessageGenerator::GenerateStaticVariables(io::Printer* printer) { +// =================================================================== +// TODO(api): Move this class to a separate immutable_message.cc file. +ImmutableMessageGenerator::ImmutableMessageGenerator( + const Descriptor* descriptor, Context* context) + : MessageGenerator(descriptor), context_(context), + name_resolver_(context->GetNameResolver()), + field_generators_(descriptor, context_) { +} + +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 @@ -171,12 +99,12 @@ void MessageGenerator::GenerateStaticVariables(io::Printer* printer) { map<string, string> vars; vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); vars["index"] = SimpleItoa(descriptor_->index()); - vars["classname"] = ClassName(descriptor_); + vars["classname"] = name_resolver_->GetImmutableClassName(descriptor_); if (descriptor_->containing_type() != NULL) { vars["parent"] = UniqueFileScopeIdentifier( descriptor_->containing_type()); } - if (descriptor_->file()->options().java_multiple_files()) { + 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"] = ""; @@ -186,31 +114,28 @@ void MessageGenerator::GenerateStaticVariables(io::Printer* printer) { // The descriptor for this type. printer->Print(vars, - "$private$static com.google.protobuf.Descriptors.Descriptor\n" + "$private$static final com.google.protobuf.Descriptors.Descriptor\n" " internal_$identifier$_descriptor;\n"); // And the FieldAccessorTable. - printer->Print(vars, - "$private$static\n" - " com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" - " internal_$identifier$_fieldAccessorTable;\n"); + GenerateFieldAccessorTable(printer); } // Generate static members for all nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { // TODO(kenton): Reuse MessageGenerator objects? - MessageGenerator(descriptor_->nested_type(i)) + ImmutableMessageGenerator(descriptor_->nested_type(i), context_) .GenerateStaticVariables(printer); } } -void MessageGenerator::GenerateStaticVariableInitializers( +void ImmutableMessageGenerator::GenerateStaticVariableInitializers( io::Printer* printer) { if (HasDescriptorMethods(descriptor_)) { map<string, string> vars; vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); vars["index"] = SimpleItoa(descriptor_->index()); - vars["classname"] = ClassName(descriptor_); + vars["classname"] = name_resolver_->GetImmutableClassName(descriptor_); if (descriptor_->containing_type() != NULL) { vars["parent"] = UniqueFileScopeIdentifier( descriptor_->containing_type()); @@ -228,56 +153,95 @@ void MessageGenerator::GenerateStaticVariableInitializers( } // And the FieldAccessorTable. - printer->Print(vars, - "internal_$identifier$_fieldAccessorTable = new\n" - " com.google.protobuf.GeneratedMessage.FieldAccessorTable(\n" - " internal_$identifier$_descriptor,\n" - " new java.lang.String[] { "); - for (int i = 0; i < descriptor_->field_count(); i++) { - printer->Print( - "\"$field_name$\", ", - "field_name", - UnderscoresToCapitalizedCamelCase(descriptor_->field(i))); - } - printer->Print( - "});\n"); + 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? - MessageGenerator(descriptor_->nested_type(i)) + ImmutableMessageGenerator(descriptor_->nested_type(i), context_) .GenerateStaticVariableInitializers(printer); } } +void ImmutableMessageGenerator:: +GenerateFieldAccessorTable(io::Printer* printer) { + map<string, string> vars; + vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); + 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 "; + } + printer->Print(vars, + "$private$static\n" + " com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" + " internal_$identifier$_fieldAccessorTable;\n"); +} + +void ImmutableMessageGenerator:: +GenerateFieldAccessorTableInitializer(io::Printer* printer) { + printer->Print( + "internal_$identifier$_fieldAccessorTable = new\n" + " com.google.protobuf.GeneratedMessage.FieldAccessorTable(\n" + " internal_$identifier$_descriptor,\n" + " new java.lang.String[] { ", + "identifier", + UniqueFileScopeIdentifier(descriptor_)); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + printer->Print( + "\"$field_name$\", ", + "field_name", info->capitalized_name); + } + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + const OneofDescriptor* oneof = descriptor_->oneof_decl(i); + const OneofGeneratorInfo* info = context_->GetOneofGeneratorInfo(oneof); + printer->Print( + "\"$oneof_name$\", ", + "oneof_name", info->capitalized_name); + } + printer->Print("});\n"); +} + // =================================================================== -void MessageGenerator::GenerateInterface(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<$classname$> {\n", + "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), "classname", descriptor_->name()); } } else { if (HasDescriptorMethods(descriptor_)) { printer->Print( - "public interface $classname$OrBuilder\n" - " extends com.google.protobuf.MessageOrBuilder {\n", + "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\n" - " extends com.google.protobuf.MessageLiteOrBuilder {\n", + "public interface $classname$OrBuilder extends\n" + " $extra_interfaces$\n" + " com.google.protobuf.MessageLiteOrBuilder {\n", + "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), "classname", descriptor_->name()); } } @@ -285,7 +249,6 @@ void MessageGenerator::GenerateInterface(io::Printer* printer) { printer->Indent(); for (int i = 0; i < descriptor_->field_count(); i++) { printer->Print("\n"); - PrintFieldComment(printer, descriptor_->field(i)); field_generators_.get(descriptor_->field(i)) .GenerateInterfaceMembers(printer); } @@ -296,10 +259,10 @@ void MessageGenerator::GenerateInterface(io::Printer* printer) { // =================================================================== -void MessageGenerator::Generate(io::Printer* printer) { +void ImmutableMessageGenerator::Generate(io::Printer* printer) { bool is_own_file = descriptor_->containing_type() == NULL && - descriptor_->file()->options().java_multiple_files(); + MultipleJavaFiles(descriptor_->file(), /* immutable = */ true); WriteMessageDocComment(printer, descriptor_); @@ -308,41 +271,51 @@ void MessageGenerator::Generate(io::Printer* printer) { if (descriptor_->extension_range_count() > 0) { if (HasDescriptorMethods(descriptor_)) { printer->Print( - "public $static$ final class $classname$ extends\n" + "public$static$final class $classname$ extends\n" " com.google.protobuf.GeneratedMessage.ExtendableMessage<\n" - " $classname$> implements $classname$OrBuilder {\n", - "static", is_own_file ? "" : "static", - "classname", descriptor_->name()); + " $classname$> implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n", + "static", is_own_file ? " " : " static ", + "classname", descriptor_->name(), + "extra_interfaces", ExtraMessageInterfaces(descriptor_)); builder_type = strings::Substitute( "com.google.protobuf.GeneratedMessage.ExtendableBuilder<$0, ?>", - ClassName(descriptor_)); + name_resolver_->GetImmutableClassName(descriptor_)); } else { printer->Print( - "public $static$ final class $classname$ extends\n" + "public$static$final class $classname$ extends\n" " com.google.protobuf.GeneratedMessageLite.ExtendableMessage<\n" - " $classname$> implements $classname$OrBuilder {\n", - "static", is_own_file ? "" : "static", - "classname", descriptor_->name()); + " $classname$> implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n", + "static", is_own_file ? " " : " static ", + "classname", descriptor_->name(), + "extra_interfaces", ExtraMessageInterfaces(descriptor_)); builder_type = strings::Substitute( "com.google.protobuf.GeneratedMessageLite.ExtendableBuilder<$0, ?>", - ClassName(descriptor_)); + name_resolver_->GetImmutableClassName(descriptor_)); } } else { if (HasDescriptorMethods(descriptor_)) { printer->Print( - "public $static$ final class $classname$ extends\n" - " com.google.protobuf.GeneratedMessage\n" - " implements $classname$OrBuilder {\n", - "static", is_own_file ? "" : "static", - "classname", descriptor_->name()); + "public$static$final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessage implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n", + "static", is_own_file ? " " : " static ", + "classname", descriptor_->name(), + "extra_interfaces", ExtraMessageInterfaces(descriptor_)); builder_type = "com.google.protobuf.GeneratedMessage.Builder<?>"; } else { printer->Print( - "public $static$ final class $classname$ extends\n" - " com.google.protobuf.GeneratedMessageLite\n" - " implements $classname$OrBuilder {\n", - "static", is_own_file ? "" : "static", - "classname", descriptor_->name()); + "public$static$final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessageLite implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n", + "static", is_own_file ? " " : " static ", + "classname", descriptor_->name(), + "extra_interfaces", ExtraMessageInterfaces(descriptor_)); builder_type = "com.google.protobuf.GeneratedMessageLite.Builder"; } } @@ -359,8 +332,8 @@ void MessageGenerator::Generate(io::Printer* printer) { "}\n", "classname", descriptor_->name(), "buildertype", builder_type, - "set_unknown_fields", HasUnknownFields(descriptor_) - ? " this.unknownFields = builder.getUnknownFields();" : ""); + "set_unknown_fields", + " this.unknownFields = builder.getUnknownFields();"); printer->Print( // Used when constructing the default instance, which cannot be initialized // immediately because it may cyclically refer to other default instances. @@ -376,11 +349,12 @@ void MessageGenerator::Generate(io::Printer* printer) { "}\n" "\n", "classname", descriptor_->name(), - "set_default_unknown_fields", HasUnknownFields(descriptor_) + "set_default_unknown_fields", UseUnknownFieldSet(descriptor_) ? " this.unknownFields =" - " com.google.protobuf.UnknownFieldSet.getDefaultInstance(); " : ""); + " com.google.protobuf.UnknownFieldSet.getDefaultInstance(); " + : " this.unknownFields = com.google.protobuf.ByteString.EMPTY;"); - if (HasUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_)) { printer->Print( "private final com.google.protobuf.UnknownFieldSet unknownFields;\n" "" @@ -389,6 +363,9 @@ void MessageGenerator::Generate(io::Printer* printer) { " getUnknownFields() {\n" " return this.unknownFields;\n" "}\n"); + } else { + printer->Print( + "private final com.google.protobuf.ByteString unknownFields;\n"); } if (HasGeneratedMethods(descriptor_)) { @@ -400,30 +377,102 @@ void MessageGenerator::Generate(io::Printer* printer) { // Nested types for (int i = 0; i < descriptor_->enum_type_count(); i++) { - EnumGenerator(descriptor_->enum_type(i)).Generate(printer); + EnumGenerator(descriptor_->enum_type(i), true, context_) + .Generate(printer); } for (int i = 0; i < descriptor_->nested_type_count(); i++) { - MessageGenerator messageGenerator(descriptor_->nested_type(i)); + ImmutableMessageGenerator messageGenerator( + descriptor_->nested_type(i), context_); messageGenerator.GenerateInterface(printer); messageGenerator.Generate(printer); } - // Integers for bit fields. - int totalBits = 0; - for (int i = 0; i < descriptor_->field_count(); i++) { - totalBits += field_generators_.get(descriptor_->field(i)) - .GetNumBitsForMessage(); + 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)); + } } - 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<string, string> 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"); } // Fields for (int i = 0; i < descriptor_->field_count(); i++) { - PrintFieldComment(printer, descriptor_->field(i)); printer->Print("public static final int $constant_name$ = $number$;\n", "constant_name", FieldConstantName(descriptor_->field(i)), "number", SimpleItoa(descriptor_->field(i)->number())); @@ -436,9 +485,12 @@ void MessageGenerator::Generate(io::Printer* printer) { printer->Print("private void initFields() {\n"); printer->Indent(); for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(descriptor_->field(i)) - .GenerateInitializationCode(printer); + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get(descriptor_->field(i)) + .GenerateInitializationCode(printer); + } } + printer->Outdent(); printer->Print("}\n"); @@ -451,6 +503,7 @@ void MessageGenerator::Generate(io::Printer* printer) { GenerateEqualsAndHashCode(printer); } + GenerateParseFromMethods(printer); GenerateBuilder(printer); @@ -471,7 +524,8 @@ void MessageGenerator::Generate(io::Printer* printer) { // because the defaultInstance is used by the extension to lazily retrieve // the outer class's FileDescriptor. for (int i = 0; i < descriptor_->extension_count(); i++) { - ExtensionGenerator(descriptor_->extension(i)).Generate(printer); + ImmutableExtensionGenerator(descriptor_->extension(i), context_) + .Generate(printer); } printer->Outdent(); @@ -481,10 +535,10 @@ void MessageGenerator::Generate(io::Printer* printer) { // =================================================================== -void MessageGenerator:: +void ImmutableMessageGenerator:: GenerateMessageSerializationMethods(io::Printer* printer) { scoped_array<const FieldDescriptor*> sorted_fields( - SortFieldsByNumber(descriptor_)); + SortFieldsByNumber(descriptor_)); vector<const Descriptor::ExtensionRange*> sorted_extensions; for (int i = 0; i < descriptor_->extension_range_count(); ++i) { @@ -513,14 +567,14 @@ GenerateMessageSerializationMethods(io::Printer* printer) { " .ExtendableMessage<$classname$>.ExtensionWriter extensionWriter =\n" " newMessageSetExtensionWriter();\n", "lite", HasDescriptorMethods(descriptor_) ? "" : "Lite", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); } else { printer->Print( "com.google.protobuf.GeneratedMessage$lite$\n" " .ExtendableMessage<$classname$>.ExtensionWriter extensionWriter =\n" " newExtensionWriter();\n", "lite", HasDescriptorMethods(descriptor_) ? "" : "Lite", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); } } @@ -539,7 +593,7 @@ GenerateMessageSerializationMethods(io::Printer* printer) { } } - if (HasUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_)) { if (descriptor_->options().message_set_wire_format()) { printer->Print( "getUnknownFields().writeAsMessageSetTo(output);\n"); @@ -547,6 +601,9 @@ GenerateMessageSerializationMethods(io::Printer* printer) { printer->Print( "getUnknownFields().writeTo(output);\n"); } + } else { + printer->Print( + "output.writeRawBytes(unknownFields);\n"); } printer->Outdent(); @@ -575,7 +632,7 @@ GenerateMessageSerializationMethods(io::Printer* printer) { } } - if (HasUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_)) { if (descriptor_->options().message_set_wire_format()) { printer->Print( "size += getUnknownFields().getSerializedSizeAsMessageSet();\n"); @@ -583,6 +640,9 @@ GenerateMessageSerializationMethods(io::Printer* printer) { printer->Print( "size += getUnknownFields().getSerializedSize();\n"); } + } else { + printer->Print( + "size += unknownFields.size();\n"); } printer->Outdent(); @@ -602,7 +662,7 @@ GenerateMessageSerializationMethods(io::Printer* printer) { "\n"); } -void MessageGenerator:: +void ImmutableMessageGenerator:: GenerateParseFromMethods(io::Printer* printer) { // Note: These are separate from GenerateMessageSerializationMethods() // because they need to be generated even for messages that are optimized @@ -661,15 +721,15 @@ GenerateParseFromMethods(io::Printer* printer) { " return PARSER.parseFrom(input, extensionRegistry);\n" "}\n" "\n", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); } -void MessageGenerator::GenerateSerializeOneField( +void ImmutableMessageGenerator::GenerateSerializeOneField( io::Printer* printer, const FieldDescriptor* field) { field_generators_.get(field).GenerateSerializationCode(printer); } -void MessageGenerator::GenerateSerializeOneExtensionRange( +void ImmutableMessageGenerator::GenerateSerializeOneExtensionRange( io::Printer* printer, const Descriptor::ExtensionRange* range) { printer->Print( "extensionWriter.writeUntil($end$, output);\n", @@ -678,7 +738,7 @@ void MessageGenerator::GenerateSerializeOneExtensionRange( // =================================================================== -void MessageGenerator::GenerateBuilder(io::Printer* printer) { +void ImmutableMessageGenerator::GenerateBuilder(io::Printer* printer) { printer->Print( "public static Builder newBuilder() { return Builder.create(); }\n" "public Builder newBuilderForType() { return newBuilder(); }\n" @@ -687,7 +747,7 @@ void MessageGenerator::GenerateBuilder(io::Printer* printer) { "}\n" "public Builder toBuilder() { return newBuilder(this); }\n" "\n", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); if (HasNestedBuilders(descriptor_)) { printer->Print( @@ -706,29 +766,40 @@ void MessageGenerator::GenerateBuilder(io::Printer* printer) { printer->Print( "public static final class Builder extends\n" " com.google.protobuf.GeneratedMessage.ExtendableBuilder<\n" - " $classname$, Builder> implements $classname$OrBuilder {\n", - "classname", ClassName(descriptor_)); + " $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 $classname$OrBuilder {\n", - "classname", ClassName(descriptor_)); + " $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<Builder>\n" - " implements $classname$OrBuilder {\n", - "classname", ClassName(descriptor_)); + " com.google.protobuf.GeneratedMessage.Builder<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 $classname$OrBuilder {\n", - "classname", ClassName(descriptor_)); + " implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_), + "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); } } printer->Indent(); @@ -741,21 +812,54 @@ void MessageGenerator::GenerateBuilder(io::Printer* printer) { GenerateBuilderParsingMethods(printer); } - // Integers for bit fields. - int totalBits = 0; - for (int i = 0; i < descriptor_->field_count(); i++) { - totalBits += field_generators_.get(descriptor_->field(i)) - .GetNumBitsForBuilder(); + // oneof + map<string, string> 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"); } - 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)); + + 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"); - PrintFieldComment(printer, descriptor_->field(i)); field_generators_.get(descriptor_->field(i)) .GenerateBuilderMembers(printer); } @@ -769,7 +873,8 @@ void MessageGenerator::GenerateBuilder(io::Printer* printer) { printer->Print("}\n"); } -void MessageGenerator::GenerateDescriptorMethods(io::Printer* printer) { +void ImmutableMessageGenerator:: +GenerateDescriptorMethods(io::Printer* printer) { if (HasDescriptorMethods(descriptor_)) { if (!descriptor_->options().no_standard_descriptor_accessor()) { printer->Print( @@ -778,7 +883,7 @@ void MessageGenerator::GenerateDescriptorMethods(io::Printer* printer) { " return $fileclass$.internal_$identifier$_descriptor;\n" "}\n" "\n", - "fileclass", ClassName(descriptor_->file()), + "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), "identifier", UniqueFileScopeIdentifier(descriptor_)); } printer->Print( @@ -789,22 +894,23 @@ void MessageGenerator::GenerateDescriptorMethods(io::Printer* printer) { " $classname$.class, $classname$.Builder.class);\n" "}\n" "\n", - "classname", ClassName(descriptor_), - "fileclass", ClassName(descriptor_->file()), + "classname", name_resolver_->GetImmutableClassName(descriptor_), + "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), "identifier", UniqueFileScopeIdentifier(descriptor_)); } } // =================================================================== -void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { +void ImmutableMessageGenerator:: +GenerateCommonBuilderMethods(io::Printer* printer) { printer->Print( "// Construct using $classname$.newBuilder()\n" "private Builder() {\n" " maybeForceBuilderInitialization();\n" "}\n" "\n", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); if (HasDescriptorMethods(descriptor_)) { printer->Print( @@ -813,7 +919,7 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { " super(parent);\n" " maybeForceBuilderInitialization();\n" "}\n", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); } @@ -825,8 +931,10 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { printer->Indent(); printer->Indent(); for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(descriptor_->field(i)) - .GenerateFieldBuilderInitializationCode(printer); + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get(descriptor_->field(i)) + .GenerateFieldBuilderInitializationCode(printer); + } } printer->Outdent(); printer->Outdent(); @@ -847,13 +955,23 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { "\n" "public Builder clear() {\n" " super.clear();\n", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); printer->Indent(); for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(descriptor_->field(i)) - .GenerateBuilderClearCode(printer); + 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(); @@ -866,7 +984,7 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { " return create().mergeFrom(buildPartial());\n" "}\n" "\n", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); if (HasDescriptorMethods(descriptor_)) { printer->Print( "public com.google.protobuf.Descriptors.Descriptor\n" @@ -874,7 +992,7 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { " return $fileclass$.internal_$identifier$_descriptor;\n" "}\n" "\n", - "fileclass", ClassName(descriptor_->file()), + "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), "identifier", UniqueFileScopeIdentifier(descriptor_)); } printer->Print( @@ -882,7 +1000,7 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { " return $classname$.getDefaultInstance();\n" "}\n" "\n", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); // ----------------------------------------------------------------- @@ -897,30 +1015,34 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { "\n" "public $classname$ buildPartial() {\n" " $classname$ result = new $classname$(this);\n", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); printer->Indent(); - // Local vars for from and to bit fields to avoid accessing the builder and - // message over and over for these fields. Seems to provide a slight - // perforamance improvement in micro benchmark and this is also what proto1 - // code does. int totalBuilderBits = 0; int totalMessageBits = 0; for (int i = 0; i < descriptor_->field_count(); i++) { - const FieldGenerator& field = field_generators_.get(descriptor_->field(i)); + 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; - 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)); + + 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. @@ -928,10 +1050,18 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { field_generators_.get(descriptor_->field(i)).GenerateBuildingCode(printer); } - // Copy the bit field results to the generated message - for (int i = 0; i < totalMessageInts; i++) { - printer->Print("result.$bit_field_name$ = to_$bit_field_name$;\n", - "bit_field_name", GetBitFieldName(i)); + 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(); @@ -945,7 +1075,7 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { " return result;\n" "}\n" "\n", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); // ----------------------------------------------------------------- @@ -963,7 +1093,7 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { " }\n" "}\n" "\n", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); } printer->Print( @@ -971,11 +1101,48 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { // 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", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); printer->Indent(); for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(descriptor_->field(i)).GenerateMergingCode(printer); + 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(); @@ -986,9 +1153,13 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { " this.mergeExtensionFields(other);\n"); } - if (HasUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_)) { printer->Print( " this.mergeUnknownFields(other.getUnknownFields());\n"); + } else { + printer->Print( + " setUnknownFields(\n" + " getUnknownFields().concat(other.unknownFields));\n"); } printer->Print( @@ -1000,7 +1171,8 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { // =================================================================== -void MessageGenerator::GenerateBuilderParsingMethods(io::Printer* printer) { +void ImmutableMessageGenerator:: +GenerateBuilderParsingMethods(io::Printer* printer) { printer->Print( "public Builder mergeFrom(\n" " com.google.protobuf.CodedInputStream input,\n" @@ -1019,12 +1191,12 @@ void MessageGenerator::GenerateBuilderParsingMethods(io::Printer* printer) { " }\n" " return this;\n" "}\n", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); } // =================================================================== -void MessageGenerator::GenerateIsInitialized( +void ImmutableMessageGenerator::GenerateIsInitialized( io::Printer* printer, UseMemoization useMemoization) { bool memoization = useMemoization == MEMOIZE; if (memoization) { @@ -1039,9 +1211,11 @@ void MessageGenerator::GenerateIsInitialized( 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 isInitialized == 1;\n" + "if (isInitialized == 1) return true;\n" + "if (isInitialized == 0) return false;\n" "\n"); } @@ -1050,6 +1224,7 @@ void MessageGenerator::GenerateIsInitialized( // "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( @@ -1057,7 +1232,7 @@ void MessageGenerator::GenerateIsInitialized( " $memoize$\n" " return false;\n" "}\n", - "name", UnderscoresToCapitalizedCamelCase(field), + "name", info->capitalized_name, "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); } } @@ -1065,6 +1240,7 @@ void MessageGenerator::GenerateIsInitialized( // 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()) { @@ -1074,8 +1250,9 @@ void MessageGenerator::GenerateIsInitialized( " $memoize$\n" " return false;\n" "}\n", - "type", ClassName(field->message_type()), - "name", UnderscoresToCapitalizedCamelCase(field), + "type", name_resolver_->GetImmutableClassName( + field->message_type()), + "name", info->capitalized_name, "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); break; case FieldDescriptor::LABEL_OPTIONAL: @@ -1086,8 +1263,9 @@ void MessageGenerator::GenerateIsInitialized( " return false;\n" " }\n" "}\n", - "type", ClassName(field->message_type()), - "name", UnderscoresToCapitalizedCamelCase(field), + "type", name_resolver_->GetImmutableClassName( + field->message_type()), + "name", info->capitalized_name, "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); break; case FieldDescriptor::LABEL_REPEATED: @@ -1098,8 +1276,9 @@ void MessageGenerator::GenerateIsInitialized( " return false;\n" " }\n" "}\n", - "type", ClassName(field->message_type()), - "name", UnderscoresToCapitalizedCamelCase(field), + "type", name_resolver_->GetImmutableClassName( + field->message_type()), + "name", info->capitalized_name, "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); break; } @@ -1130,7 +1309,21 @@ void MessageGenerator::GenerateIsInitialized( // =================================================================== -void MessageGenerator::GenerateEqualsAndHashCode(io::Printer* printer) { +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 ImmutableMessageGenerator:: +GenerateEqualsAndHashCode(io::Printer* printer) { printer->Print( "@java.lang.Override\n" "public boolean equals(final java.lang.Object obj) {\n"); @@ -1144,20 +1337,22 @@ void MessageGenerator::GenerateEqualsAndHashCode(io::Printer* printer) { "}\n" "$classname$ other = ($classname$) obj;\n" "\n", - "classname", ClassName(descriptor_)); + "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); - if (!field->is_repeated()) { + 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", UnderscoresToCapitalizedCamelCase(field)); + "name", info->capitalized_name); printer->Indent(); } field_generators_.get(field).GenerateEqualsCode(printer); - if (!field->is_repeated()) { + if (check_has_bits) { printer->Outdent(); printer->Print( "}\n"); @@ -1181,8 +1376,6 @@ void MessageGenerator::GenerateEqualsAndHashCode(io::Printer* printer) { "\n"); printer->Print( - "private int memoizedHashCode = 0;\n"); - printer->Print( "@java.lang.Override\n" "public int hashCode() {\n"); printer->Indent(); @@ -1194,18 +1387,29 @@ void MessageGenerator::GenerateEqualsAndHashCode(io::Printer* printer) { printer->Outdent(); printer->Print( "}\n" - "int hash = 41;\n" - "hash = (19 * hash) + getDescriptorForType().hashCode();\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_)); + } + for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); - if (!field->is_repeated()) { + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); + if (check_has_bits) { printer->Print( "if (has$name$()) {\n", - "name", UnderscoresToCapitalizedCamelCase(field)); + "name", info->capitalized_name); printer->Indent(); } field_generators_.get(field).GenerateHashCode(printer); - if (!field->is_repeated()) { + if (check_has_bits) { printer->Outdent(); printer->Print("}\n"); } @@ -1216,8 +1420,15 @@ void MessageGenerator::GenerateEqualsAndHashCode(io::Printer* printer) { "hash = hashFields(hash, getExtensionFields());\n"); } } + + if (UseUnknownFieldSet(descriptor_)) { + printer->Print( + "hash = (29 * hash) + getUnknownFields().hashCode();\n"); + } else { + printer->Print( + "hash = (29 * hash) + unknownFields.hashCode();\n"); + } printer->Print( - "hash = (29 * hash) + getUnknownFields().hashCode();\n" "memoizedHashCode = hash;\n" "return hash;\n"); printer->Outdent(); @@ -1228,20 +1439,22 @@ void MessageGenerator::GenerateEqualsAndHashCode(io::Printer* printer) { // =================================================================== -void MessageGenerator::GenerateExtensionRegistrationCode(io::Printer* printer) { +void ImmutableMessageGenerator:: +GenerateExtensionRegistrationCode(io::Printer* printer) { for (int i = 0; i < descriptor_->extension_count(); i++) { - ExtensionGenerator(descriptor_->extension(i)) + ImmutableExtensionGenerator(descriptor_->extension(i), context_) .GenerateRegistrationCode(printer); } for (int i = 0; i < descriptor_->nested_type_count(); i++) { - MessageGenerator(descriptor_->nested_type(i)) + ImmutableMessageGenerator(descriptor_->nested_type(i), context_) .GenerateExtensionRegistrationCode(printer); } } // =================================================================== -void MessageGenerator::GenerateParsingConstructor(io::Printer* printer) { +void ImmutableMessageGenerator:: +GenerateParsingConstructor(io::Printer* printer) { scoped_array<const FieldDescriptor*> sorted_fields( SortFieldsByNumber(descriptor_)); @@ -1260,7 +1473,8 @@ void MessageGenerator::GenerateParsingConstructor(io::Printer* printer) { // Use builder bits to track mutable repeated fields. int totalBuilderBits = 0; for (int i = 0; i < descriptor_->field_count(); i++) { - const FieldGenerator& field = field_generators_.get(descriptor_->field(i)); + const ImmutableFieldGenerator& field = + field_generators_.get(descriptor_->field(i)); totalBuilderBits += field.GetNumBitsForBuilder(); } int totalBuilderInts = (totalBuilderBits + 31) / 32; @@ -1269,10 +1483,17 @@ void MessageGenerator::GenerateParsingConstructor(io::Printer* printer) { "bit_field_name", GetBitFieldName(i)); } - if (HasUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_)) { printer->Print( "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n" " com.google.protobuf.UnknownFieldSet.newBuilder();\n"); + } else { + printer->Print( + "com.google.protobuf.ByteString.Output unknownFieldsOutput =\n" + " com.google.protobuf.ByteString.newOutput();\n" + "com.google.protobuf.CodedOutputStream unknownFieldsCodedOutput =\n" + " com.google.protobuf.CodedOutputStream.newInstance(\n" + " unknownFieldsOutput);\n"); } printer->Print( @@ -1300,8 +1521,8 @@ void MessageGenerator::GenerateParsingConstructor(io::Printer* printer) { " }\n" " break;\n" "}\n", - "unknown_fields", HasUnknownFields(descriptor_) - ? " unknownFields," : ""); + "unknown_fields", UseUnknownFieldSet(descriptor_) + ? " unknownFields," : " unknownFieldsCodedOutput,"); for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = sorted_fields[i]; @@ -1362,9 +1583,18 @@ void MessageGenerator::GenerateParsingConstructor(io::Printer* printer) { } // Make unknown fields immutable. - if (HasUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_)) { printer->Print( "this.unknownFields = unknownFields.build();\n"); + } else { + printer->Print( + "try {\n" + " unknownFieldsCodedOutput.flush();\n" + "} catch (java.io.IOException e) {\n" + "// Should not happen\n" + "} finally {\n" + " unknownFields = unknownFieldsOutput.toByteString();\n" + "}\n"); } // Make extensions immutable. @@ -1379,7 +1609,7 @@ void MessageGenerator::GenerateParsingConstructor(io::Printer* printer) { } // =================================================================== -void MessageGenerator::GenerateParser(io::Printer* printer) { +void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) { printer->Print( "public static com.google.protobuf.Parser<$classname$> PARSER =\n" " new com.google.protobuf.AbstractParser<$classname$>() {\n", @@ -1429,6 +1659,7 @@ void MessageGenerator::GenerateParser(io::Printer* printer) { "classname", descriptor_->name()); } + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_message.h b/src/google/protobuf/compiler/java/java_message.h index a30f0202..406910dc 100644 --- a/src/google/protobuf/compiler/java/java_message.h +++ b/src/google/protobuf/compiler/java/java_message.h @@ -36,11 +36,17 @@ #define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__ #include <string> -#include <google/protobuf/stubs/common.h> +#include <map> #include <google/protobuf/compiler/java/java_field.h> namespace google { namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } namespace io { class Printer; // printer.h } @@ -53,26 +59,45 @@ namespace java { class MessageGenerator { public: explicit MessageGenerator(const Descriptor* descriptor); - ~MessageGenerator(); + virtual ~MessageGenerator(); // All static variables have to be declared at the top-level of the file // so that we can control initialization order, which is important for // DescriptorProto bootstrapping to work. - void GenerateStaticVariables(io::Printer* printer); + virtual void GenerateStaticVariables(io::Printer* printer) = 0; // Output code which initializes the static variables generated by // GenerateStaticVariables(). - void GenerateStaticVariableInitializers(io::Printer* printer); + virtual void GenerateStaticVariableInitializers(io::Printer* printer) = 0; // Generate the class itself. - void Generate(io::Printer* printer); + virtual void Generate(io::Printer* printer) = 0; // Generates the base interface that both the class and its builder implement - void GenerateInterface(io::Printer* printer); + virtual void GenerateInterface(io::Printer* printer) = 0; // Generate code to register all contained extensions with an // ExtensionRegistry. - void GenerateExtensionRegistrationCode(io::Printer* printer); + virtual void GenerateExtensionRegistrationCode(io::Printer* printer) = 0; + + protected: + const Descriptor* descriptor_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); +}; + +class ImmutableMessageGenerator : public MessageGenerator { + public: + explicit ImmutableMessageGenerator(const Descriptor* descriptor, + Context* context); + virtual ~ImmutableMessageGenerator(); + + 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 void GenerateStaticVariableInitializers(io::Printer* printer); private: enum UseMemoization { @@ -80,6 +105,9 @@ class MessageGenerator { DONT_MEMOIZE }; + void GenerateFieldAccessorTable(io::Printer* printer); + void GenerateFieldAccessorTableInitializer(io::Printer* printer); + void GenerateMessageSerializationMethods(io::Printer* printer); void GenerateParseFromMethods(io::Printer* printer); void GenerateSerializeOneField(io::Printer* printer, @@ -94,14 +122,14 @@ class MessageGenerator { void GenerateIsInitialized(io::Printer* printer, UseMemoization useMemoization); void GenerateEqualsAndHashCode(io::Printer* printer); - void GenerateParser(io::Printer* printer); void GenerateParsingConstructor(io::Printer* printer); - const Descriptor* descriptor_; - FieldGeneratorMap field_generators_; + Context* context_; + ClassNameResolver* name_resolver_; + FieldGeneratorMap<ImmutableFieldGenerator> field_generators_; - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableMessageGenerator); }; } // namespace java diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc index b0b284f7..80b9a382 100644 --- a/src/google/protobuf/compiler/java/java_message_field.cc +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -35,9 +35,11 @@ #include <map> #include <string> +#include <google/protobuf/compiler/java/java_context.h> #include <google/protobuf/compiler/java/java_message_field.h> #include <google/protobuf/compiler/java/java_doc_comment.h> #include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/wire_format.h> #include <google/protobuf/stubs/strutil.h> @@ -49,19 +51,18 @@ namespace java { namespace { -// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of -// repeat code between this and the other field types. void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, map<string, string>* variables) { - (*variables)["name"] = - UnderscoresToCamelCase(descriptor); - (*variables)["capitalized_name"] = - UnderscoresToCapitalizedCamelCase(descriptor); - (*variables)["constant_name"] = FieldConstantName(descriptor); - (*variables)["number"] = SimpleItoa(descriptor->number()); - (*variables)["type"] = ClassName(descriptor->message_type()); + 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"; @@ -72,14 +73,28 @@ void SetMessageVariables(const FieldDescriptor* descriptor, (*variables)["on_changed"] = HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; - // For singular messages and builders, one bit is used for the hasField bit. - (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); - (*variables)["set_has_field_bit_message"] = GenerateSetBit(messageBitIndex); + 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); + (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); + + // Note that these have a trailing ";". + (*variables)["set_has_field_bit_message"] = + GenerateSetBit(messageBitIndex) + ";"; + (*variables)["set_has_field_bit_builder"] = + GenerateSetBit(builderBitIndex) + ";"; + (*variables)["clear_has_field_bit_builder"] = + GenerateClearBit(builderBitIndex) + ";"; - (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); - (*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex); - (*variables)["clear_has_field_bit_builder"] = - GenerateClearBit(builderBitIndex); + (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex); + } else { + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["set_has_field_bit_builder"] = ""; + (*variables)["clear_has_field_bit_builder"] = ""; + + (*variables)["is_field_present_message"] = + (*variables)["name"] + "_ != null"; + } // For repated builders, one bit is used for whether the array is immutable. (*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex); @@ -103,35 +118,41 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // =================================================================== -MessageFieldGenerator:: -MessageFieldGenerator(const FieldDescriptor* descriptor, +ImmutableMessageFieldGenerator:: +ImmutableMessageFieldGenerator(const FieldDescriptor* descriptor, int messageBitIndex, - int builderBitIndex) + int builderBitIndex, + Context* context) : descriptor_(descriptor), messageBitIndex_(messageBitIndex), - builderBitIndex_(builderBitIndex) { - SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, - &variables_); + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); } -MessageFieldGenerator::~MessageFieldGenerator() {} +ImmutableMessageFieldGenerator::~ImmutableMessageFieldGenerator() {} -int MessageFieldGenerator::GetNumBitsForMessage() const { +int ImmutableMessageFieldGenerator::GetNumBitsForMessage() const { return 1; } -int MessageFieldGenerator::GetNumBitsForBuilder() const { +int ImmutableMessageFieldGenerator::GetNumBitsForBuilder() const { return 1; } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: 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. - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$boolean has$capitalized_name$();\n"); + 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"); @@ -143,31 +164,56 @@ GenerateInterfaceMembers(io::Printer* printer) const { } } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private $type$ $name$_;\n"); - 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"); + PrintExtraFieldInfo(variables_, printer); - if (HasNestedBuilders(descriptor_->containing_type())) { + if (SupportFieldPresence(descriptor_->file())) { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + "$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"); + + if (HasNestedBuilders(descriptor_->containing_type())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder " + "get$capitalized_name$OrBuilder() {\n" + " return $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"); + + 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"); + } } } -void MessageFieldGenerator::PrintNestedBuilderCondition( +void ImmutableMessageFieldGenerator::PrintNestedBuilderCondition( io::Printer* printer, const char* regular_case, const char* nested_builder_case) const { @@ -186,7 +232,7 @@ void MessageFieldGenerator::PrintNestedBuilderCondition( } } -void MessageFieldGenerator::PrintNestedBuilderFunction( +void ImmutableMessageFieldGenerator::PrintNestedBuilderFunction( io::Printer* printer, const char* method_prototype, const char* regular_case, @@ -203,15 +249,22 @@ void MessageFieldGenerator::PrintNestedBuilderFunction( printer->Print("}\n"); } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { // When using nested-builders, the code initially works just like the // non-nested builder case. It only creates a nested builder lazily on // demand and then forever delegates to it after creation. - printer->Print(variables_, - // Used when the builder is null. - "private $type$ $name$_ = $type$.getDefaultInstance();\n"); + bool support_field_presence = SupportFieldPresence(descriptor_->file()); + + if (support_field_presence) { + printer->Print(variables_, + // Used when the builder is null. + "private $type$ $name$_ = $type$.getDefaultInstance();\n"); + } else { + printer->Print(variables_, + "private $type$ $name$_ = null;\n"); + } if (HasNestedBuilders(descriptor_->containing_type())) { printer->Print(variables_, @@ -227,17 +280,26 @@ GenerateBuilderMembers(io::Printer* printer) const { // boolean hasField() WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public boolean has$capitalized_name$() {\n" - " return $get_has_field_bit_builder$;\n" - "}\n"); + if (support_field_presence) { + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_builder$;\n" + "}\n"); + } else { + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $name$Builder_ != null || $name$_ != null;\n" + "}\n"); + } // Field getField() WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public $type$ get$capitalized_name$()", - "return $name$_;\n", + support_field_presence + ? "return $name$_;\n" + : "return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n", "return $name$Builder_.getMessage();\n", @@ -256,7 +318,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "$name$Builder_.setMessage(value);\n", - "$set_has_field_bit_builder$;\n" + "$set_has_field_bit_builder$\n" "return this;\n"); // Field.Builder setField(Field.Builder builderForValue) @@ -270,7 +332,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "$name$Builder_.setMessage(builderForValue.build());\n", - "$set_has_field_bit_builder$;\n" + "$set_has_field_bit_builder$\n" "return this;\n"); // Field.Builder mergeField(Field value) @@ -278,18 +340,26 @@ GenerateBuilderMembers(io::Printer* printer) const { PrintNestedBuilderFunction(printer, "$deprecation$public Builder merge$capitalized_name$($type$ value)", - "if ($get_has_field_bit_builder$ &&\n" - " $name$_ != $type$.getDefaultInstance()) {\n" - " $name$_ =\n" - " $type$.newBuilder($name$_).mergeFrom(value).buildPartial();\n" - "} else {\n" - " $name$_ = value;\n" - "}\n" - "$on_changed$\n", + support_field_presence + ? "if ($get_has_field_bit_builder$ &&\n" + " $name$_ != $type$.getDefaultInstance()) {\n" + " $name$_ =\n" + " $type$.newBuilder($name$_).mergeFrom(value).buildPartial();\n" + "} else {\n" + " $name$_ = value;\n" + "}\n" + "$on_changed$\n" + : "if ($name$_ != null) {\n" + " $name$_ =\n" + " $type$.newBuilder($name$_).mergeFrom(value).buildPartial();\n" + "} else {\n" + " $name$_ = value;\n" + "}\n" + "$on_changed$\n", "$name$Builder_.mergeFrom(value);\n", - "$set_has_field_bit_builder$;\n" + "$set_has_field_bit_builder$\n" "return this;\n"); // Field.Builder clearField() @@ -297,19 +367,25 @@ GenerateBuilderMembers(io::Printer* printer) const { PrintNestedBuilderFunction(printer, "$deprecation$public Builder clear$capitalized_name$()", - "$name$_ = $type$.getDefaultInstance();\n" - "$on_changed$\n", + support_field_presence + ? "$name$_ = $type$.getDefaultInstance();\n" + "$on_changed$\n" + : "$name$_ = null;\n" + "$on_changed$\n", - "$name$Builder_.clear();\n", + support_field_presence + ? "$name$Builder_.clear();\n" + : "$name$_ = null;\n" + "$name$Builder_ = null;\n", - "$clear_has_field_bit_builder$;\n" + "$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" + " $set_has_field_bit_builder$\n" " $on_changed$\n" " return get$capitalized_name$FieldBuilder().getBuilder();\n" "}\n"); @@ -318,8 +394,16 @@ GenerateBuilderMembers(io::Printer* printer) const { "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" " if ($name$Builder_ != null) {\n" " return $name$Builder_.getMessageOrBuilder();\n" - " } else {\n" - " return $name$_;\n" + " } else {\n"); + if (support_field_presence) { + printer->Print(variables_, + " return $name$_;\n"); + } else { + printer->Print(variables_, + " return $name$_ == null ?\n" + " $type$.getDefaultInstance() : $name$_;\n"); + } + printer->Print(variables_, " }\n" "}\n"); WriteFieldDocComment(printer, descriptor_); @@ -330,7 +414,7 @@ GenerateBuilderMembers(io::Printer* printer) const { " if ($name$Builder_ == null) {\n" " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n" " $type$, $type$.Builder, $type$OrBuilder>(\n" - " $name$_,\n" + " get$capitalized_name$(),\n" " getParentForChildren(),\n" " isClean());\n" " $name$_ = null;\n" @@ -340,28 +424,40 @@ GenerateBuilderMembers(io::Printer* printer) const { } } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateFieldBuilderInitializationCode(io::Printer* printer) const { - printer->Print(variables_, - "get$capitalized_name$FieldBuilder();\n"); + if (SupportFieldPresence(descriptor_->file())) { + printer->Print(variables_, + "get$capitalized_name$FieldBuilder();\n"); + } } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { - printer->Print(variables_, "$name$_ = $type$.getDefaultInstance();\n"); + if (SupportFieldPresence(descriptor_->file())) { + printer->Print(variables_, "$name$_ = $type$.getDefaultInstance();\n"); + } } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateBuilderClearCode(io::Printer* printer) const { - PrintNestedBuilderCondition(printer, - "$name$_ = $type$.getDefaultInstance();\n", + if (SupportFieldPresence(descriptor_->file())) { + PrintNestedBuilderCondition(printer, + "$name$_ = $type$.getDefaultInstance();\n", - "$name$Builder_.clear();\n"); - printer->Print(variables_, "$clear_has_field_bit_builder$;\n"); + "$name$Builder_.clear();\n"); + printer->Print(variables_, "$clear_has_field_bit_builder$\n"); + } else { + PrintNestedBuilderCondition(printer, + "$name$_ = null;\n", + + "$name$_ = null;\n" + "$name$Builder_ = null;\n"); + } } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateMergingCode(io::Printer* printer) const { printer->Print(variables_, "if (other.has$capitalized_name$()) {\n" @@ -369,13 +465,14 @@ GenerateMergingCode(io::Printer* printer) const { "}\n"); } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { - - printer->Print(variables_, - "if ($get_has_field_bit_from_local$) {\n" - " $set_has_field_bit_to_local$;\n" - "}\n"); + if (SupportFieldPresence(descriptor_->file())) { + printer->Print(variables_, + "if ($get_has_field_bit_from_local$) {\n" + " $set_has_field_bit_to_local$;\n" + "}\n"); + } PrintNestedBuilderCondition(printer, "result.$name$_ = $name$_;\n", @@ -383,11 +480,11 @@ GenerateBuildingCode(io::Printer* printer) const { "result.$name$_ = $name$Builder_.build();\n"); } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, "$type$.Builder subBuilder = null;\n" - "if ($get_has_field_bit_message$) {\n" + "if ($is_field_present_message$) {\n" " subBuilder = $name$_.toBuilder();\n" "}\n"); @@ -404,74 +501,344 @@ GenerateParsingCode(io::Printer* printer) const { "if (subBuilder != null) {\n" " subBuilder.mergeFrom($name$_);\n" " $name$_ = subBuilder.buildPartial();\n" - "}\n"); - printer->Print(variables_, - "$set_has_field_bit_message$;\n"); + "}\n" + "$set_has_field_bit_message$\n"); } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateParsingDoneCode(io::Printer* printer) const { // noop for messages. } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, - "if ($get_has_field_bit_message$) {\n" + "if ($is_field_present_message$) {\n" " output.write$group_or_message$($number$, $name$_);\n" "}\n"); } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, - "if ($get_has_field_bit_message$) {\n" + "if ($is_field_present_message$) {\n" " size += com.google.protobuf.CodedOutputStream\n" " .compute$group_or_message$Size($number$, $name$_);\n" "}\n"); } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateEqualsCode(io::Printer* printer) const { printer->Print(variables_, "result = result && get$capitalized_name$()\n" " .equals(other.get$capitalized_name$());\n"); } -void MessageFieldGenerator:: +void ImmutableMessageFieldGenerator:: GenerateHashCode(io::Printer* printer) const { printer->Print(variables_, "hash = (37 * hash) + $constant_name$;\n" "hash = (53 * hash) + get$capitalized_name$().hashCode();\n"); } -string MessageFieldGenerator::GetBoxedType() const { - return ClassName(descriptor_->message_type()); +string ImmutableMessageFieldGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->message_type()); +} + +// =================================================================== + +ImmutableMessageOneofFieldGenerator:: +ImmutableMessageOneofFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableMessageFieldGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); +} + +ImmutableMessageOneofFieldGenerator:: +~ImmutableMessageOneofFieldGenerator() {} + +void ImmutableMessageOneofFieldGenerator:: +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"); + + 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"); + } +} + +void ImmutableMessageOneofFieldGenerator:: +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"); + } + + // 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 $has_oneof_case_message$;\n" + "}\n"); + } + + // Field getField() + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public $type$ get$capitalized_name$()", + + "if ($has_oneof_case_message$) {\n" + " return ($type$) $oneof_name$_;\n" + "}\n" + "return $type$.getDefaultInstance();\n", + + "if ($has_oneof_case_message$) {\n" + " return $name$Builder_.getMessage();\n" + "}\n" + "return $type$.getDefaultInstance();\n", + + NULL); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder set$capitalized_name$($type$ value)", + + "if (value == null) {\n" + " throw new NullPointerException();\n" + "}\n" + "$oneof_name$_ = value;\n" + "$on_changed$\n", + + "$name$Builder_.setMessage(value);\n", + + "$set_oneof_case_message$;\n" + "return this;\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder set$capitalized_name$(\n" + " $type$.Builder builderForValue)", + + "$oneof_name$_ = builderForValue.build();\n" + "$on_changed$\n", + + "$name$Builder_.setMessage(builderForValue.build());\n", + + "$set_oneof_case_message$;\n" + "return this;\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder merge$capitalized_name$($type$ value)", + + "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" + "$on_changed$\n", + + "if ($has_oneof_case_message$) {\n" + " $name$Builder_.mergeFrom(value);\n" + "}\n" + "$name$Builder_.setMessage(value);\n", + + "$set_oneof_case_message$;\n" + "return this;\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + PrintNestedBuilderFunction(printer, + "$deprecation$public Builder clear$capitalized_name$()", + + "if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " $on_changed$\n" + "}\n", + + "if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + "}\n" + "$name$Builder_.clear();\n", + + "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" + " return $name$Builder_;\n" + "}\n"); + } +} + +void ImmutableMessageOneofFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n"); + printer->Indent(); + + PrintNestedBuilderCondition(printer, + "result.$oneof_name$_ = $oneof_name$_;\n", + + "result.$oneof_name$_ = $name$Builder_.build();\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + +void ImmutableMessageOneofFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "merge$capitalized_name$(other.get$capitalized_name$());\n"); +} + +void ImmutableMessageOneofFieldGenerator:: +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 ImmutableMessageOneofFieldGenerator:: +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 ImmutableMessageOneofFieldGenerator:: +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"); } // =================================================================== -RepeatedMessageFieldGenerator:: -RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, - int builderBitIndex) +RepeatedImmutableMessageFieldGenerator:: +RepeatedImmutableMessageFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) : descriptor_(descriptor), messageBitIndex_(messageBitIndex), - builderBitIndex_(builderBitIndex) { + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, - &variables_); + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); } -RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {} +RepeatedImmutableMessageFieldGenerator:: +~RepeatedImmutableMessageFieldGenerator() {} -int RepeatedMessageFieldGenerator::GetNumBitsForMessage() const { +int RepeatedImmutableMessageFieldGenerator::GetNumBitsForMessage() const { return 0; } -int RepeatedMessageFieldGenerator::GetNumBitsForBuilder() const { +int RepeatedImmutableMessageFieldGenerator::GetNumBitsForBuilder() const { return 1; } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: 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 @@ -499,10 +866,11 @@ GenerateInterfaceMembers(io::Printer* printer) const { } } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private java.util.List<$type$> $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" @@ -533,7 +901,7 @@ GenerateMembers(io::Printer* printer) const { } -void RepeatedMessageFieldGenerator::PrintNestedBuilderCondition( +void RepeatedImmutableMessageFieldGenerator::PrintNestedBuilderCondition( io::Printer* printer, const char* regular_case, const char* nested_builder_case) const { @@ -552,7 +920,7 @@ void RepeatedMessageFieldGenerator::PrintNestedBuilderCondition( } } -void RepeatedMessageFieldGenerator::PrintNestedBuilderFunction( +void RepeatedImmutableMessageFieldGenerator::PrintNestedBuilderFunction( io::Printer* printer, const char* method_prototype, const char* regular_case, @@ -569,7 +937,7 @@ void RepeatedMessageFieldGenerator::PrintNestedBuilderFunction( printer->Print("}\n"); } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: 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 @@ -737,7 +1105,8 @@ GenerateBuilderMembers(io::Printer* printer) const { " java.lang.Iterable<? extends $type$> values)", "ensure$capitalized_name$IsMutable();\n" - "super.addAll(values, $name$_);\n" + "com.google.protobuf.AbstractMessageLite.Builder.addAll(\n" + " values, $name$_);\n" "$on_changed$\n", "$name$Builder_.addAllMessages(values);\n", @@ -836,18 +1205,18 @@ GenerateBuilderMembers(io::Printer* printer) const { } } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: GenerateFieldBuilderInitializationCode(io::Printer* printer) const { printer->Print(variables_, "get$capitalized_name$FieldBuilder();\n"); } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = java.util.Collections.emptyList();\n"); } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: GenerateBuilderClearCode(io::Printer* printer) const { PrintNestedBuilderCondition(printer, "$name$_ = java.util.Collections.emptyList();\n" @@ -856,7 +1225,7 @@ GenerateBuilderClearCode(io::Printer* printer) const { "$name$Builder_.clear();\n"); } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: 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 @@ -890,7 +1259,7 @@ GenerateMergingCode(io::Printer* printer) const { "}\n"); } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { // The code below (non-nested builder case) ensures that the result has an // immutable list. If our list is immutable, we can just reuse it. If not, @@ -905,7 +1274,7 @@ GenerateBuildingCode(io::Printer* printer) const { "result.$name$_ = $name$Builder_.build();\n"); } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, "if (!$get_mutable_bit_parser$) {\n" @@ -923,7 +1292,7 @@ GenerateParsingCode(io::Printer* printer) const { } } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: GenerateParsingDoneCode(io::Printer* printer) const { printer->Print(variables_, "if ($get_mutable_bit_parser$) {\n" @@ -931,7 +1300,7 @@ GenerateParsingDoneCode(io::Printer* printer) const { "}\n"); } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, "for (int i = 0; i < $name$_.size(); i++) {\n" @@ -939,7 +1308,7 @@ GenerateSerializationCode(io::Printer* printer) const { "}\n"); } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, "for (int i = 0; i < $name$_.size(); i++) {\n" @@ -948,14 +1317,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: GenerateEqualsCode(io::Printer* printer) const { printer->Print(variables_, "result = result && get$capitalized_name$List()\n" " .equals(other.get$capitalized_name$List());\n"); } -void RepeatedMessageFieldGenerator:: +void RepeatedImmutableMessageFieldGenerator:: GenerateHashCode(io::Printer* printer) const { printer->Print(variables_, "if (get$capitalized_name$Count() > 0) {\n" @@ -964,8 +1333,8 @@ GenerateHashCode(io::Printer* printer) const { "}\n"); } -string RepeatedMessageFieldGenerator::GetBoxedType() const { - return ClassName(descriptor_->message_type()); +string RepeatedImmutableMessageFieldGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->message_type()); } } // namespace java diff --git a/src/google/protobuf/compiler/java/java_message_field.h b/src/google/protobuf/compiler/java/java_message_field.h index 5c8078a1..f7b491e5 100644 --- a/src/google/protobuf/compiler/java/java_message_field.h +++ b/src/google/protobuf/compiler/java/java_message_field.h @@ -41,16 +41,26 @@ namespace google { namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } +} + +namespace protobuf { namespace compiler { namespace java { -class MessageFieldGenerator : public FieldGenerator { +class ImmutableMessageFieldGenerator : public ImmutableFieldGenerator { public: - explicit MessageFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, int builderBitIndex); - ~MessageFieldGenerator(); + explicit ImmutableMessageFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableMessageFieldGenerator(); - // implements FieldGenerator --------------------------------------- + // implements ImmutableFieldGenerator --------------------------------------- int GetNumBitsForMessage() const; int GetNumBitsForBuilder() const; void GenerateInterfaceMembers(io::Printer* printer) const; @@ -70,13 +80,13 @@ class MessageFieldGenerator : public FieldGenerator { string GetBoxedType() const; - private: + protected: const FieldDescriptor* descriptor_; map<string, string> variables_; const int messageBitIndex_; const int builderBitIndex_; - - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator); + Context* context_; + ClassNameResolver* name_resolver_; void PrintNestedBuilderCondition(io::Printer* printer, const char* regular_case, const char* nested_builder_case) const; @@ -84,15 +94,39 @@ class MessageFieldGenerator : public FieldGenerator { const char* method_prototype, const char* regular_case, const char* nested_builder_case, const char* trailing_code) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableMessageFieldGenerator); }; -class RepeatedMessageFieldGenerator : public FieldGenerator { +class ImmutableMessageOneofFieldGenerator + : public ImmutableMessageFieldGenerator { public: - explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, int builderBitIndex); - ~RepeatedMessageFieldGenerator(); + ImmutableMessageOneofFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableMessageOneofFieldGenerator(); - // implements FieldGenerator --------------------------------------- + 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(ImmutableMessageOneofFieldGenerator); +}; + +class RepeatedImmutableMessageFieldGenerator : public ImmutableFieldGenerator { + public: + explicit RepeatedImmutableMessageFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~RepeatedImmutableMessageFieldGenerator(); + + // implements ImmutableFieldGenerator --------------------------------------- int GetNumBitsForMessage() const; int GetNumBitsForBuilder() const; void GenerateInterfaceMembers(io::Printer* printer) const; @@ -112,13 +146,13 @@ class RepeatedMessageFieldGenerator : public FieldGenerator { string GetBoxedType() const; - private: + protected: const FieldDescriptor* descriptor_; map<string, string> variables_; const int messageBitIndex_; const int builderBitIndex_; - - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator); + Context* context_; + ClassNameResolver* name_resolver_; void PrintNestedBuilderCondition(io::Printer* printer, const char* regular_case, const char* nested_builder_case) const; @@ -126,6 +160,9 @@ class RepeatedMessageFieldGenerator : public FieldGenerator { const char* method_prototype, const char* regular_case, const char* nested_builder_case, const char* trailing_code) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableMessageFieldGenerator); }; } // namespace java diff --git a/src/google/protobuf/compiler/java/java_name_resolver.cc b/src/google/protobuf/compiler/java/java_name_resolver.cc new file mode 100644 index 00000000..7f52d234 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_name_resolver.cc @@ -0,0 +1,266 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <google/protobuf/compiler/java/java_name_resolver.h> + +#include <map> +#include <string> + +#include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/stubs/substitute.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { +// A suffix that will be appended to the file's outer class name if the name +// conflicts with some other types defined in the file. +const char* kOuterClassNameSuffix = "OuterClass"; + +// Strip package name from a descriptor's full name. +// For example: +// Full name : foo.Bar.Baz +// Package name: foo +// After strip : Bar.Baz +string StripPackageName(const string& full_name, + const FileDescriptor* file) { + if (file->package().empty()) { + return full_name; + } else { + // Strip package name + return full_name.substr(file->package().size() + 1); + } +} + +// Get the name of a message's Java class without package name prefix. +string ClassNameWithoutPackage(const Descriptor* descriptor, + bool immutable) { + return StripPackageName(descriptor->full_name(), + descriptor->file()); +} + +// Get the name of an enum's Java class without package name prefix. +string ClassNameWithoutPackage(const EnumDescriptor* descriptor, + bool immutable) { + // Doesn't append "Mutable" for enum type's name. + const Descriptor* message_descriptor = descriptor->containing_type(); + if (message_descriptor == NULL) { + return descriptor->name(); + } else { + return ClassNameWithoutPackage(message_descriptor, immutable) + + "." + descriptor->name(); + } +} + +// Get the name of a service's Java class without package name prefix. +string ClassNameWithoutPackage(const ServiceDescriptor* descriptor, + bool immutable) { + string full_name = StripPackageName(descriptor->full_name(), + descriptor->file()); + // We don't allow nested service definitions. + GOOGLE_CHECK(full_name.find('.') == string::npos); + return full_name; +} + +// Check whether a given message or its nested types has the given class name. +bool MessageHasConflictingClassName(const Descriptor* message, + const string& classname) { + if (message->name() == classname) return true; + for (int i = 0; i < message->nested_type_count(); ++i) { + if (MessageHasConflictingClassName(message->nested_type(i), classname)) { + return true; + } + } + for (int i = 0; i < message->enum_type_count(); ++i) { + if (message->enum_type(i)->name() == classname) { + return true; + } + } + return false; +} + +} // namespace + +ClassNameResolver::ClassNameResolver() { +} + +ClassNameResolver::~ClassNameResolver() { +} + +string ClassNameResolver::GetFileDefaultImmutableClassName( + const FileDescriptor* file) { + string basename; + string::size_type last_slash = file->name().find_last_of('/'); + if (last_slash == string::npos) { + basename = file->name(); + } else { + basename = file->name().substr(last_slash + 1); + } + return UnderscoresToCamelCase(StripProto(basename), true); +} + +string ClassNameResolver::GetFileImmutableClassName( + const FileDescriptor* file) { + string& class_name = file_immutable_outer_class_names_[file]; + if (class_name.empty()) { + if (file->options().has_java_outer_classname()) { + class_name = file->options().java_outer_classname(); + } else { + class_name = GetFileDefaultImmutableClassName(file); + if (HasConflictingClassName(file, class_name)) { + class_name += kOuterClassNameSuffix; + } + } + } + return class_name; +} + +string ClassNameResolver::GetFileClassName(const FileDescriptor* file, + bool immutable) { + if (immutable) { + return GetFileImmutableClassName(file); + } else { + return "Mutable" + GetFileImmutableClassName(file); + } +} + +// Check whether there is any type defined in the proto file that has +// the given class name. +bool ClassNameResolver::HasConflictingClassName( + const FileDescriptor* file, const string& classname) { + for (int i = 0; i < file->enum_type_count(); i++) { + if (file->enum_type(i)->name() == classname) { + return true; + } + } + for (int i = 0; i < file->service_count(); i++) { + if (file->service(i)->name() == classname) { + return true; + } + } + for (int i = 0; i < file->message_type_count(); i++) { + if (MessageHasConflictingClassName(file->message_type(i), classname)) { + return true; + } + } + return false; +} + +string ClassNameResolver::GetDescriptorClassName( + const FileDescriptor* descriptor) { + return GetFileImmutableClassName(descriptor) + "InternalDescriptors"; +} + +string ClassNameResolver::GetClassName(const FileDescriptor* descriptor, + bool immutable) { + string result = FileJavaPackage(descriptor, immutable); + if (!result.empty()) result += '.'; + result += GetFileClassName(descriptor, immutable); + return result; +} + +// Get the full name of a Java class by prepending the Java package name +// or outer class name. +string ClassNameResolver::GetClassFullName(const string& name_without_package, + const FileDescriptor* file, + bool immutable, + bool multiple_files) { + string result; + if (multiple_files) { + result = FileJavaPackage(file, immutable); + } else { + result = GetClassName(file, immutable); + } + if (!result.empty()) { + result += '.'; + } + result += name_without_package; + return result; +} + +string ClassNameResolver::GetClassName(const Descriptor* descriptor, + bool immutable) { + return GetClassFullName(ClassNameWithoutPackage(descriptor, immutable), + descriptor->file(), immutable, + MultipleJavaFiles(descriptor->file(), immutable)); +} + +string ClassNameResolver::GetClassName(const EnumDescriptor* descriptor, + bool immutable) { + return GetClassFullName(ClassNameWithoutPackage(descriptor, immutable), + descriptor->file(), immutable, + MultipleJavaFiles(descriptor->file(), immutable)); +} + +string ClassNameResolver::GetClassName(const ServiceDescriptor* descriptor, + bool immutable) { + return GetClassFullName(ClassNameWithoutPackage(descriptor, immutable), + descriptor->file(), immutable, + MultipleJavaFiles(descriptor->file(), immutable)); +} + +// Get the Java Class style full name of a message. +string ClassNameResolver::GetJavaClassFullName( + const string& name_without_package, + const FileDescriptor* file, + bool immutable) { + string result; + if (MultipleJavaFiles(file, immutable)) { + result = FileJavaPackage(file, immutable); + if (!result.empty()) result += '.'; + } else { + result = GetClassName(file, immutable); + if (!result.empty()) result += '$'; + } + result += StringReplace(name_without_package, ".", "$", true); + return result; +} + +string ClassNameResolver::GetExtensionIdentifierName( + const FieldDescriptor* descriptor, bool immutable) { + return GetClassName(descriptor->containing_type(), immutable) + "." + + descriptor->name(); +} + + +string ClassNameResolver::GetJavaImmutableClassName( + const Descriptor* descriptor) { + return GetJavaClassFullName( + ClassNameWithoutPackage(descriptor, true), + descriptor->file(), true); +} + + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_name_resolver.h b/src/google/protobuf/compiler/java/java_name_resolver.h new file mode 100644 index 00000000..1d3e185c --- /dev/null +++ b/src/google/protobuf/compiler/java/java_name_resolver.h @@ -0,0 +1,124 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_NAME_RESOLVER_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_NAME_RESOLVER_H__ + +#include <map> +#include <string> + +#include <google/protobuf/stubs/common.h> + +namespace google { +namespace protobuf { +class Descriptor; +class EnumDescriptor; +class FieldDescriptor; +class FileDescriptor; +class ServiceDescriptor; + +namespace compiler { +namespace java { + +// Used to get the Java class related names for a given descriptor. It caches +// the results to avoid redundant calculation across multiple name queries. +// Thread-safety note: This class is *not* thread-safe. +class ClassNameResolver { + public: + ClassNameResolver(); + ~ClassNameResolver(); + + // Gets the unqualified outer class name for the file. + string GetFileClassName(const FileDescriptor* file, bool immutable); + // Gets the unqualified immutable outer class name of a file. + string GetFileImmutableClassName(const FileDescriptor* file); + // Gets the unqualified default immutable outer class name of a file + // (converted from the proto file's name). + string GetFileDefaultImmutableClassName(const FileDescriptor* file); + + // Check whether there is any type defined in the proto file that has + // the given class name. + bool HasConflictingClassName(const FileDescriptor* file, + const string& classname); + + // Gets the name of the outer class that holds descriptor information. + // Descriptors are shared between immutable messages and mutable messages. + // Since both of them are generated optionally, the descriptors need to be + // put in another common place. + string GetDescriptorClassName(const FileDescriptor* file); + + // Gets the fully-qualified class name corresponding to the given descriptor. + string GetClassName(const Descriptor* descriptor, bool immutable); + string GetClassName(const EnumDescriptor* descriptor, bool immutable); + string GetClassName(const ServiceDescriptor* descriptor, bool immutable); + string GetClassName(const FileDescriptor* descriptor, bool immutable); + + template<class DescriptorType> + string GetImmutableClassName(const DescriptorType* descriptor) { + return GetClassName(descriptor, true); + } + template<class DescriptorType> + string GetMutableClassName(const DescriptorType* descriptor) { + return GetClassName(descriptor, false); + } + + // Gets the fully qualified name of an extension identifier. + string GetExtensionIdentifierName(const FieldDescriptor* descriptor, + bool immutable); + + // Gets the fully qualified name for generated classes in Java convention. + // Nested classes will be separated using '$' instead of '.' + // For example: + // com.package.OuterClass$OuterMessage$InnerMessage + string GetJavaImmutableClassName(const Descriptor* descriptor); + private: + // Get the full name of a Java class by prepending the Java package name + // or outer class name. + string GetClassFullName(const string& name_without_package, + const FileDescriptor* file, + bool immutable, + bool multiple_files); + // Get the Java Class style full name of a message. + string GetJavaClassFullName( + const string& name_without_package, + const FileDescriptor* file, + bool immutable); + // Caches the result to provide better performance. + map<const FileDescriptor*, string> file_immutable_outer_class_names_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ClassNameResolver); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_NAME_RESOLVER_H__ diff --git a/src/google/protobuf/compiler/java/java_plugin_unittest.cc b/src/google/protobuf/compiler/java/java_plugin_unittest.cc index ccc94c9d..52577626 100644 --- a/src/google/protobuf/compiler/java/java_plugin_unittest.cc +++ b/src/google/protobuf/compiler/java/java_plugin_unittest.cc @@ -34,6 +34,8 @@ // It seemed like parameterizing it would add more complexity than it is // worth. +#include <memory> + #include <google/protobuf/compiler/java/java_generator.h> #include <google/protobuf/compiler/command_line_interface.h> #include <google/protobuf/io/zero_copy_stream.h> @@ -71,7 +73,7 @@ class TestGenerator : public CodeGenerator { void TryInsert(const string& filename, const string& insertion_point, GeneratorContext* context) const { scoped_ptr<io::ZeroCopyOutputStream> output( - context->OpenForInsert(filename, insertion_point)); + context->OpenForInsert(filename, insertion_point)); io::Printer printer(output.get(), '$'); printer.Print("// inserted $name$\n", "name", insertion_point); } @@ -81,16 +83,16 @@ class TestGenerator : public CodeGenerator { // not verify that they are correctly-placed; that would require actually // compiling the output which is a bit more than I care to do for this test. TEST(JavaPluginTest, PluginTest) { - File::WriteStringToFileOrDie( - "syntax = \"proto2\";\n" - "package foo;\n" - "option java_package = \"\";\n" - "option java_outer_classname = \"Test\";\n" - "message Bar {\n" - " message Baz {}\n" - "}\n" - "enum Qux { BLAH = 1; }\n", - TestTempDir() + "/test.proto"); + GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/test.proto", + "syntax = \"proto2\";\n" + "package foo;\n" + "option java_package = \"\";\n" + "option java_outer_classname = \"Test\";\n" + "message Bar {\n" + " message Baz {}\n" + "}\n" + "enum Qux { BLAH = 1; }\n", + true)); google::protobuf::compiler::CommandLineInterface cli; cli.SetInputsAreProtoPathRelative(true); diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc index 0140e23f..031a1293 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -35,10 +35,12 @@ #include <map> #include <string> -#include <google/protobuf/compiler/java/java_primitive_field.h> -#include <google/protobuf/compiler/java/java_doc_comment.h> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/java/java_context.h> +#include <google/protobuf/compiler/java/java_doc_comment.h> #include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> +#include <google/protobuf/compiler/java/java_primitive_field.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/wire_format.h> #include <google/protobuf/stubs/strutil.h> @@ -73,107 +75,25 @@ const char* PrimitiveTypeName(JavaType type) { return NULL; } -bool IsReferenceType(JavaType type) { - switch (type) { - case JAVATYPE_INT : return false; - case JAVATYPE_LONG : return false; - case JAVATYPE_FLOAT : return false; - case JAVATYPE_DOUBLE : return false; - case JAVATYPE_BOOLEAN: return false; - case JAVATYPE_STRING : return true; - case JAVATYPE_BYTES : return true; - case JAVATYPE_ENUM : return true; - case JAVATYPE_MESSAGE: return true; - - // No default because we want the compiler to complain if any new - // JavaTypes are added. - } - - GOOGLE_LOG(FATAL) << "Can't get here."; - return false; -} - -const char* GetCapitalizedType(const FieldDescriptor* field) { - switch (GetType(field)) { - case FieldDescriptor::TYPE_INT32 : return "Int32" ; - case FieldDescriptor::TYPE_UINT32 : return "UInt32" ; - case FieldDescriptor::TYPE_SINT32 : return "SInt32" ; - case FieldDescriptor::TYPE_FIXED32 : return "Fixed32" ; - case FieldDescriptor::TYPE_SFIXED32: return "SFixed32"; - case FieldDescriptor::TYPE_INT64 : return "Int64" ; - case FieldDescriptor::TYPE_UINT64 : return "UInt64" ; - case FieldDescriptor::TYPE_SINT64 : return "SInt64" ; - case FieldDescriptor::TYPE_FIXED64 : return "Fixed64" ; - case FieldDescriptor::TYPE_SFIXED64: return "SFixed64"; - case FieldDescriptor::TYPE_FLOAT : return "Float" ; - case FieldDescriptor::TYPE_DOUBLE : return "Double" ; - case FieldDescriptor::TYPE_BOOL : return "Bool" ; - case FieldDescriptor::TYPE_STRING : return "String" ; - case FieldDescriptor::TYPE_BYTES : return "Bytes" ; - case FieldDescriptor::TYPE_ENUM : return "Enum" ; - case FieldDescriptor::TYPE_GROUP : return "Group" ; - case FieldDescriptor::TYPE_MESSAGE : return "Message" ; - - // No default because we want the compiler to complain if any new - // types are added. - } - - GOOGLE_LOG(FATAL) << "Can't get here."; - return NULL; -} - -// For encodings with fixed sizes, returns that size in bytes. Otherwise -// returns -1. -int FixedSize(FieldDescriptor::Type type) { - switch (type) { - case FieldDescriptor::TYPE_INT32 : return -1; - case FieldDescriptor::TYPE_INT64 : return -1; - case FieldDescriptor::TYPE_UINT32 : return -1; - case FieldDescriptor::TYPE_UINT64 : return -1; - case FieldDescriptor::TYPE_SINT32 : return -1; - case FieldDescriptor::TYPE_SINT64 : return -1; - case FieldDescriptor::TYPE_FIXED32 : return WireFormatLite::kFixed32Size; - case FieldDescriptor::TYPE_FIXED64 : return WireFormatLite::kFixed64Size; - case FieldDescriptor::TYPE_SFIXED32: return WireFormatLite::kSFixed32Size; - case FieldDescriptor::TYPE_SFIXED64: return WireFormatLite::kSFixed64Size; - case FieldDescriptor::TYPE_FLOAT : return WireFormatLite::kFloatSize; - case FieldDescriptor::TYPE_DOUBLE : return WireFormatLite::kDoubleSize; - - case FieldDescriptor::TYPE_BOOL : return WireFormatLite::kBoolSize; - case FieldDescriptor::TYPE_ENUM : return -1; - - case FieldDescriptor::TYPE_STRING : return -1; - case FieldDescriptor::TYPE_BYTES : return -1; - case FieldDescriptor::TYPE_GROUP : return -1; - case FieldDescriptor::TYPE_MESSAGE : return -1; - - // No default because we want the compiler to complain if any new - // types are added. - } - GOOGLE_LOG(FATAL) << "Can't get here."; - return -1; -} - void SetPrimitiveVariables(const FieldDescriptor* descriptor, int messageBitIndex, int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, map<string, string>* variables) { - (*variables)["name"] = - UnderscoresToCamelCase(descriptor); - (*variables)["capitalized_name"] = - UnderscoresToCapitalizedCamelCase(descriptor); - (*variables)["constant_name"] = FieldConstantName(descriptor); - (*variables)["number"] = SimpleItoa(descriptor->number()); + SetCommonFieldVariables(descriptor, info, variables); + (*variables)["type"] = PrimitiveTypeName(GetJavaType(descriptor)); (*variables)["boxed_type"] = BoxedPrimitiveTypeName(GetJavaType(descriptor)); (*variables)["field_type"] = (*variables)["type"]; (*variables)["field_list_type"] = "java.util.List<" + (*variables)["boxed_type"] + ">"; (*variables)["empty_list"] = "java.util.Collections.emptyList()"; - (*variables)["default"] = DefaultValue(descriptor); + (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); (*variables)["default_init"] = IsDefaultValueJavaDefault(descriptor) ? - "" : ("= " + DefaultValue(descriptor)); - (*variables)["capitalized_type"] = GetCapitalizedType(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))); @@ -196,14 +116,33 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, (*variables)["on_changed"] = HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; - // For singular messages and builders, one bit is used for the hasField bit. - (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); - (*variables)["set_has_field_bit_message"] = GenerateSetBit(messageBitIndex); + 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); + (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); - (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); - (*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex); - (*variables)["clear_has_field_bit_builder"] = - GenerateClearBit(builderBitIndex); + // Note that these have a trailing ";". + (*variables)["set_has_field_bit_message"] = + GenerateSetBit(messageBitIndex) + ";"; + (*variables)["set_has_field_bit_builder"] = + GenerateSetBit(builderBitIndex) + ";"; + (*variables)["clear_has_field_bit_builder"] = + GenerateClearBit(builderBitIndex) + ";"; + + (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex); + } else { + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["set_has_field_bit_builder"] = ""; + (*variables)["clear_has_field_bit_builder"] = ""; + + 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 repated builders, one bit is used for whether the array is immutable. (*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex); @@ -227,46 +166,53 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // =================================================================== -PrimitiveFieldGenerator:: -PrimitiveFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, - int builderBitIndex) +ImmutablePrimitiveFieldGenerator:: +ImmutablePrimitiveFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) : descriptor_(descriptor), messageBitIndex_(messageBitIndex), - builderBitIndex_(builderBitIndex) { + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, - &variables_); + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); } -PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {} +ImmutablePrimitiveFieldGenerator::~ImmutablePrimitiveFieldGenerator() {} -int PrimitiveFieldGenerator::GetNumBitsForMessage() const { +int ImmutablePrimitiveFieldGenerator::GetNumBitsForMessage() const { return 1; } -int PrimitiveFieldGenerator::GetNumBitsForBuilder() const { +int ImmutablePrimitiveFieldGenerator::GetNumBitsForBuilder() const { return 1; } -void PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$boolean has$capitalized_name$();\n"); + 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 PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private $field_type$ $name$_;\n"); - - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public boolean has$capitalized_name$() {\n" - " return $get_has_field_bit_message$;\n" - "}\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_, @@ -275,16 +221,18 @@ GenerateMembers(io::Printer* printer) const { "}\n"); } -void PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, "private $field_type$ $name$_ $default_init$;\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public boolean has$capitalized_name$() {\n" - " return $get_has_field_bit_builder$;\n" - "}\n"); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_builder$;\n" + "}\n"); + } WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, @@ -296,7 +244,7 @@ GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" "$null_check$" - " $set_has_field_bit_builder$;\n" + " $set_has_field_bit_builder$\n" " $name$_ = value;\n" " $on_changed$\n" " return this;\n" @@ -305,7 +253,7 @@ GenerateBuilderMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public Builder clear$capitalized_name$() {\n" - " $clear_has_field_bit_builder$;\n"); + " $clear_has_field_bit_builder$\n"); JavaType type = GetJavaType(descriptor_); if (type == JAVATYPE_STRING || type == JAVATYPE_BYTES) { // The default value is not a simple literal so we want to avoid executing @@ -322,70 +270,80 @@ GenerateBuilderMembers(io::Printer* printer) const { "}\n"); } -void PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateFieldBuilderInitializationCode(io::Printer* printer) const { // noop for primitives } -void PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = $default$;\n"); } -void PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateBuilderClearCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = $default$;\n" - "$clear_has_field_bit_builder$;\n"); + "$clear_has_field_bit_builder$\n"); } -void PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateMergingCode(io::Printer* printer) const { - printer->Print(variables_, - "if (other.has$capitalized_name$()) {\n" - " set$capitalized_name$(other.get$capitalized_name$());\n" - "}\n"); + 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 PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + printer->Print(variables_, + "if ($get_has_field_bit_from_local$) {\n" + " $set_has_field_bit_to_local$;\n" + "}\n"); + } printer->Print(variables_, - "if ($get_has_field_bit_from_local$) {\n" - " $set_has_field_bit_to_local$;\n" - "}\n" "result.$name$_ = $name$_;\n"); } -void PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, - "$set_has_field_bit_message$;\n" + "$set_has_field_bit_message$\n" "$name$_ = input.read$capitalized_type$();\n"); } -void PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateParsingDoneCode(io::Printer* printer) const { // noop for primitives. } -void PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, - "if ($get_has_field_bit_message$) {\n" + "if ($is_field_present_message$) {\n" " output.write$capitalized_type$($number$, $name$_);\n" "}\n"); } -void PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, - "if ($get_has_field_bit_message$) {\n" + "if ($is_field_present_message$) {\n" " size += com.google.protobuf.CodedOutputStream\n" " .compute$capitalized_type$Size($number$, $name$_);\n" "}\n"); } -void PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateEqualsCode(io::Printer* printer) const { switch (GetJavaType(descriptor_)) { case JAVATYPE_INT: @@ -398,14 +356,18 @@ GenerateEqualsCode(io::Printer* printer) const { case JAVATYPE_FLOAT: printer->Print(variables_, - "result = result && (Float.floatToIntBits(get$capitalized_name$())" - " == Float.floatToIntBits(other.get$capitalized_name$()));\n"); + "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 && (Double.doubleToLongBits(get$capitalized_name$())" - " == Double.doubleToLongBits(other.get$capitalized_name$()));\n"); + "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: @@ -423,7 +385,7 @@ GenerateEqualsCode(io::Printer* printer) const { } } -void PrimitiveFieldGenerator:: +void ImmutablePrimitiveFieldGenerator:: GenerateHashCode(io::Printer* printer) const { printer->Print(variables_, "hash = (37 * hash) + $constant_name$;\n"); @@ -435,24 +397,26 @@ GenerateHashCode(io::Printer* printer) const { case JAVATYPE_LONG: printer->Print(variables_, - "hash = (53 * hash) + hashLong(get$capitalized_name$());\n"); + "hash = (53 * hash) + com.google.protobuf.Internal.hashLong(\n" + " get$capitalized_name$());\n"); break; case JAVATYPE_BOOLEAN: printer->Print(variables_, - "hash = (53 * hash) + hashBoolean(get$capitalized_name$());\n"); + "hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(\n" + " get$capitalized_name$());\n"); break; case JAVATYPE_FLOAT: printer->Print(variables_, - "hash = (53 * hash) + Float.floatToIntBits(\n" + "hash = (53 * hash) + java.lang.Float.floatToIntBits(\n" " get$capitalized_name$());\n"); break; case JAVATYPE_DOUBLE: printer->Print(variables_, - "hash = (53 * hash) + hashLong(\n" - " Double.doubleToLongBits(get$capitalized_name$()));\n"); + "hash = (53 * hash) + com.google.protobuf.Internal.hashLong(\n" + " java.lang.Double.doubleToLongBits(get$capitalized_name$()));\n"); break; case JAVATYPE_STRING: @@ -469,33 +433,157 @@ GenerateHashCode(io::Printer* printer) const { } } -string PrimitiveFieldGenerator::GetBoxedType() const { +string ImmutablePrimitiveFieldGenerator::GetBoxedType() const { return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); } // =================================================================== -RepeatedPrimitiveFieldGenerator:: -RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, - int builderBitIndex) +ImmutablePrimitiveOneofFieldGenerator:: +ImmutablePrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutablePrimitiveFieldGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); +} + +ImmutablePrimitiveOneofFieldGenerator:: +~ImmutablePrimitiveOneofFieldGenerator() {} + +void ImmutablePrimitiveOneofFieldGenerator:: +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"); +} + + +void ImmutablePrimitiveOneofFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + 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_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + "$null_check$" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + " $on_changed$\n" + " return this;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " $on_changed$\n" + " }\n" + " return this;\n" + "}\n"); +} + +void ImmutablePrimitiveOneofFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " result.$oneof_name$_ = $oneof_name$_;\n" + "}\n"); +} + +void ImmutablePrimitiveOneofFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "set$capitalized_name$(other.get$capitalized_name$());\n"); +} + +void ImmutablePrimitiveOneofFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$set_oneof_case_message$;\n" + "$oneof_name$_ = input.read$capitalized_type$();\n"); +} + +void ImmutablePrimitiveOneofFieldGenerator:: +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 ImmutablePrimitiveOneofFieldGenerator:: +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"); +} + +// =================================================================== + +RepeatedImmutablePrimitiveFieldGenerator:: +RepeatedImmutablePrimitiveFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) : descriptor_(descriptor), messageBitIndex_(messageBitIndex), - builderBitIndex_(builderBitIndex) { + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, - &variables_); + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); } -RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {} +RepeatedImmutablePrimitiveFieldGenerator:: +~RepeatedImmutablePrimitiveFieldGenerator() {} -int RepeatedPrimitiveFieldGenerator::GetNumBitsForMessage() const { +int RepeatedImmutablePrimitiveFieldGenerator::GetNumBitsForMessage() const { return 0; } -int RepeatedPrimitiveFieldGenerator::GetNumBitsForBuilder() const { +int RepeatedImmutablePrimitiveFieldGenerator::GetNumBitsForBuilder() const { return 1; } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, @@ -509,10 +597,11 @@ GenerateInterfaceMembers(io::Printer* printer) const { } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: 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" @@ -537,7 +626,7 @@ GenerateMembers(io::Printer* printer) const { } } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { // One field is the list and the bit field keeps track of whether the // list is immutable. If it's immutable, the invariant is that it must @@ -603,7 +692,8 @@ GenerateBuilderMembers(io::Printer* printer) const { "$deprecation$public Builder addAll$capitalized_name$(\n" " java.lang.Iterable<? extends $boxed_type$> values) {\n" " ensure$capitalized_name$IsMutable();\n" - " super.addAll(values, $name$_);\n" + " com.google.protobuf.AbstractMessageLite.Builder.addAll(\n" + " values, $name$_);\n" " $on_changed$\n" " return this;\n" "}\n"); @@ -617,24 +707,24 @@ GenerateBuilderMembers(io::Printer* printer) const { "}\n"); } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateFieldBuilderInitializationCode(io::Printer* printer) const { // noop for primitives } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = $empty_list$;\n"); } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateBuilderClearCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = $empty_list$;\n" "$clear_mutable_bit_builder$;\n"); } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: 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 @@ -654,7 +744,7 @@ GenerateMergingCode(io::Printer* printer) const { "}\n"); } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { // The code below ensures that the result has an immutable list. If our // list is immutable, we can just reuse it. If not, we make it immutable. @@ -666,7 +756,7 @@ GenerateBuildingCode(io::Printer* printer) const { "result.$name$_ = $name$_;\n"); } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, "if (!$get_mutable_bit_parser$) {\n" @@ -676,7 +766,7 @@ GenerateParsingCode(io::Printer* printer) const { "$name$_.add(input.read$capitalized_type$());\n"); } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateParsingCodeFromPacked(io::Printer* printer) const { printer->Print(variables_, "int length = input.readRawVarint32();\n" @@ -691,7 +781,7 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const { "input.popLimit(limit);\n"); } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateParsingDoneCode(io::Printer* printer) const { printer->Print(variables_, "if ($get_mutable_bit_parser$) {\n" @@ -699,7 +789,7 @@ GenerateParsingDoneCode(io::Printer* printer) const { "}\n"); } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { if (descriptor_->options().packed()) { printer->Print(variables_, @@ -718,7 +808,7 @@ GenerateSerializationCode(io::Printer* printer) const { } } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, "{\n" @@ -761,14 +851,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print("}\n"); } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateEqualsCode(io::Printer* printer) const { printer->Print(variables_, "result = result && get$capitalized_name$List()\n" " .equals(other.get$capitalized_name$List());\n"); } -void RepeatedPrimitiveFieldGenerator:: +void RepeatedImmutablePrimitiveFieldGenerator:: GenerateHashCode(io::Printer* printer) const { printer->Print(variables_, "if (get$capitalized_name$Count() > 0) {\n" @@ -777,7 +867,7 @@ GenerateHashCode(io::Printer* printer) const { "}\n"); } -string RepeatedPrimitiveFieldGenerator::GetBoxedType() const { +string RepeatedImmutablePrimitiveFieldGenerator::GetBoxedType() const { return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); } diff --git a/src/google/protobuf/compiler/java/java_primitive_field.h b/src/google/protobuf/compiler/java/java_primitive_field.h index 1b5b6d95..489e1b20 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.h +++ b/src/google/protobuf/compiler/java/java_primitive_field.h @@ -41,16 +41,26 @@ namespace google { namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } +} + +namespace protobuf { namespace compiler { namespace java { -class PrimitiveFieldGenerator : public FieldGenerator { +class ImmutablePrimitiveFieldGenerator : public ImmutableFieldGenerator { public: - explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, int builderBitIndex); - ~PrimitiveFieldGenerator(); + explicit ImmutablePrimitiveFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutablePrimitiveFieldGenerator(); - // implements FieldGenerator --------------------------------------- + // implements ImmutableFieldGenerator --------------------------------------- int GetNumBitsForMessage() const; int GetNumBitsForBuilder() const; void GenerateInterfaceMembers(io::Printer* printer) const; @@ -70,22 +80,47 @@ class PrimitiveFieldGenerator : public FieldGenerator { string GetBoxedType() const; - private: + protected: const FieldDescriptor* descriptor_; map<string, string> variables_; const int messageBitIndex_; const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator); + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutablePrimitiveFieldGenerator); +}; + +class ImmutablePrimitiveOneofFieldGenerator + : public ImmutablePrimitiveFieldGenerator { + public: + ImmutablePrimitiveOneofFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutablePrimitiveOneofFieldGenerator(); + + 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(ImmutablePrimitiveOneofFieldGenerator); }; -class RepeatedPrimitiveFieldGenerator : public FieldGenerator { +class RepeatedImmutablePrimitiveFieldGenerator + : public ImmutableFieldGenerator { public: - explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, int builderBitIndex); - ~RepeatedPrimitiveFieldGenerator(); + explicit RepeatedImmutablePrimitiveFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + virtual ~RepeatedImmutablePrimitiveFieldGenerator(); - // implements FieldGenerator --------------------------------------- + // implements ImmutableFieldGenerator --------------------------------------- int GetNumBitsForMessage() const; int GetNumBitsForBuilder() const; void GenerateInterfaceMembers(io::Printer* printer) const; @@ -111,8 +146,10 @@ class RepeatedPrimitiveFieldGenerator : public FieldGenerator { map<string, string> variables_; const int messageBitIndex_; const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPrimitiveFieldGenerator); + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutablePrimitiveFieldGenerator); }; } // namespace java diff --git a/src/google/protobuf/compiler/java/java_service.cc b/src/google/protobuf/compiler/java/java_service.cc index bcd80359..aa5c5231 100644 --- a/src/google/protobuf/compiler/java/java_service.cc +++ b/src/google/protobuf/compiler/java/java_service.cc @@ -33,8 +33,11 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include <google/protobuf/compiler/java/java_service.h> + +#include <google/protobuf/compiler/java/java_context.h> #include <google/protobuf/compiler/java/java_doc_comment.h> #include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/descriptor.pb.h> #include <google/protobuf/stubs/strutil.h> @@ -49,8 +52,17 @@ ServiceGenerator::ServiceGenerator(const ServiceDescriptor* descriptor) ServiceGenerator::~ServiceGenerator() {} -void ServiceGenerator::Generate(io::Printer* printer) { - bool is_own_file = descriptor_->file()->options().java_multiple_files(); +// =================================================================== +ImmutableServiceGenerator::ImmutableServiceGenerator( + const ServiceDescriptor* descriptor, Context* context) + : ServiceGenerator(descriptor), context_(context), + name_resolver_(context->GetNameResolver()) {} + +ImmutableServiceGenerator::~ImmutableServiceGenerator() {} + +void ImmutableServiceGenerator::Generate(io::Printer* printer) { + bool is_own_file = + MultipleJavaFiles(descriptor_->file(), /* immutable = */ true); WriteServiceDocComment(printer, descriptor_); printer->Print( "public $static$ abstract class $classname$\n" @@ -77,7 +89,7 @@ void ServiceGenerator::Generate(io::Printer* printer) { " getDescriptor() {\n" " return $file$.getDescriptor().getServices().get($index$);\n" "}\n", - "file", ClassName(descriptor_->file()), + "file", name_resolver_->GetImmutableClassName(descriptor_->file()), "index", SimpleItoa(descriptor_->index())); GenerateGetDescriptorForType(printer); @@ -98,7 +110,8 @@ void ServiceGenerator::Generate(io::Printer* printer) { printer->Print("}\n\n"); } -void ServiceGenerator::GenerateGetDescriptorForType(io::Printer* printer) { +void ImmutableServiceGenerator::GenerateGetDescriptorForType( + io::Printer* printer) { printer->Print( "public final com.google.protobuf.Descriptors.ServiceDescriptor\n" " getDescriptorForType() {\n" @@ -106,7 +119,7 @@ void ServiceGenerator::GenerateGetDescriptorForType(io::Printer* printer) { "}\n"); } -void ServiceGenerator::GenerateInterface(io::Printer* printer) { +void ImmutableServiceGenerator::GenerateInterface(io::Printer* printer) { printer->Print("public interface Interface {\n"); printer->Indent(); GenerateAbstractMethods(printer); @@ -114,7 +127,7 @@ void ServiceGenerator::GenerateInterface(io::Printer* printer) { printer->Print("}\n\n"); } -void ServiceGenerator::GenerateNewReflectiveServiceMethod( +void ImmutableServiceGenerator::GenerateNewReflectiveServiceMethod( io::Printer* printer) { printer->Print( "public static com.google.protobuf.Service newReflectiveService(\n" @@ -141,7 +154,7 @@ void ServiceGenerator::GenerateNewReflectiveServiceMethod( printer->Print("}\n\n"); } -void ServiceGenerator::GenerateNewReflectiveBlockingServiceMethod( +void ImmutableServiceGenerator::GenerateNewReflectiveBlockingServiceMethod( io::Printer* printer) { printer->Print( "public static com.google.protobuf.BlockingService\n" @@ -162,7 +175,7 @@ void ServiceGenerator::GenerateNewReflectiveBlockingServiceMethod( printer->Print("}\n\n"); } -void ServiceGenerator::GenerateAbstractMethods(io::Printer* printer) { +void ImmutableServiceGenerator::GenerateAbstractMethods(io::Printer* printer) { for (int i = 0; i < descriptor_->method_count(); i++) { const MethodDescriptor* method = descriptor_->method(i); WriteMethodDocComment(printer, method); @@ -171,7 +184,7 @@ void ServiceGenerator::GenerateAbstractMethods(io::Printer* printer) { } } -void ServiceGenerator::GenerateCallMethod(io::Printer* printer) { +void ImmutableServiceGenerator::GenerateCallMethod(io::Printer* printer) { printer->Print( "\n" "public final void callMethod(\n" @@ -194,8 +207,10 @@ void ServiceGenerator::GenerateCallMethod(io::Printer* printer) { map<string, string> vars; vars["index"] = SimpleItoa(i); vars["method"] = UnderscoresToCamelCase(method); - vars["input"] = ClassName(method->input_type()); - vars["output"] = ClassName(method->output_type()); + vars["input"] = name_resolver_->GetImmutableClassName( + method->input_type()); + vars["output"] = name_resolver_->GetImmutableClassName( + method->output_type()); printer->Print(vars, "case $index$:\n" " this.$method$(controller, ($input$)request,\n" @@ -217,7 +232,8 @@ void ServiceGenerator::GenerateCallMethod(io::Printer* printer) { "\n"); } -void ServiceGenerator::GenerateCallBlockingMethod(io::Printer* printer) { +void ImmutableServiceGenerator::GenerateCallBlockingMethod( + io::Printer* printer) { printer->Print( "\n" "public final com.google.protobuf.Message callBlockingMethod(\n" @@ -239,8 +255,10 @@ void ServiceGenerator::GenerateCallBlockingMethod(io::Printer* printer) { map<string, string> vars; vars["index"] = SimpleItoa(i); vars["method"] = UnderscoresToCamelCase(method); - vars["input"] = ClassName(method->input_type()); - vars["output"] = ClassName(method->output_type()); + vars["input"] = name_resolver_->GetImmutableClassName( + method->input_type()); + vars["output"] = name_resolver_->GetImmutableClassName( + method->output_type()); printer->Print(vars, "case $index$:\n" " return impl.$method$(controller, ($input$)request);\n"); @@ -259,7 +277,7 @@ void ServiceGenerator::GenerateCallBlockingMethod(io::Printer* printer) { "\n"); } -void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which, +void ImmutableServiceGenerator::GenerateGetPrototype(RequestOrResponse which, io::Printer* printer) { /* * TODO(cpovirk): The exception message says "Service.foo" when it may be @@ -283,7 +301,7 @@ void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which, const MethodDescriptor* method = descriptor_->method(i); map<string, string> vars; vars["index"] = SimpleItoa(i); - vars["type"] = ClassName( + vars["type"] = name_resolver_->GetImmutableClassName( (which == REQUEST) ? method->input_type() : method->output_type()); printer->Print(vars, "case $index$:\n" @@ -303,7 +321,7 @@ void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which, "\n"); } -void ServiceGenerator::GenerateStub(io::Printer* printer) { +void ImmutableServiceGenerator::GenerateStub(io::Printer* printer) { printer->Print( "public static Stub newStub(\n" " com.google.protobuf.RpcChannel channel) {\n" @@ -312,7 +330,7 @@ void ServiceGenerator::GenerateStub(io::Printer* printer) { "\n" "public static final class Stub extends $classname$ implements Interface {" "\n", - "classname", ClassName(descriptor_)); + "classname", name_resolver_->GetImmutableClassName(descriptor_)); printer->Indent(); printer->Print( @@ -335,7 +353,8 @@ void ServiceGenerator::GenerateStub(io::Printer* printer) { map<string, string> vars; vars["index"] = SimpleItoa(i); - vars["output"] = ClassName(method->output_type()); + vars["output"] = name_resolver_->GetImmutableClassName( + method->output_type()); printer->Print(vars, "channel.callMethod(\n" " getDescriptor().getMethods().get($index$),\n" @@ -357,7 +376,7 @@ void ServiceGenerator::GenerateStub(io::Printer* printer) { "\n"); } -void ServiceGenerator::GenerateBlockingStub(io::Printer* printer) { +void ImmutableServiceGenerator::GenerateBlockingStub(io::Printer* printer) { printer->Print( "public static BlockingInterface newBlockingStub(\n" " com.google.protobuf.BlockingRpcChannel channel) {\n" @@ -399,7 +418,8 @@ void ServiceGenerator::GenerateBlockingStub(io::Printer* printer) { map<string, string> vars; vars["index"] = SimpleItoa(i); - vars["output"] = ClassName(method->output_type()); + vars["output"] = name_resolver_->GetImmutableClassName( + method->output_type()); printer->Print(vars, "return ($output$) channel.callBlockingMethod(\n" " getDescriptor().getMethods().get($index$),\n" @@ -417,13 +437,13 @@ void ServiceGenerator::GenerateBlockingStub(io::Printer* printer) { printer->Print("}\n"); } -void ServiceGenerator::GenerateMethodSignature(io::Printer* printer, +void ImmutableServiceGenerator::GenerateMethodSignature(io::Printer* printer, const MethodDescriptor* method, IsAbstract is_abstract) { map<string, string> vars; vars["name"] = UnderscoresToCamelCase(method); - vars["input"] = ClassName(method->input_type()); - vars["output"] = ClassName(method->output_type()); + vars["input"] = name_resolver_->GetImmutableClassName(method->input_type()); + vars["output"] = name_resolver_->GetImmutableClassName(method->output_type()); vars["abstract"] = (is_abstract == IS_ABSTRACT) ? "abstract" : ""; printer->Print(vars, "public $abstract$ void $name$(\n" @@ -432,13 +452,13 @@ void ServiceGenerator::GenerateMethodSignature(io::Printer* printer, " com.google.protobuf.RpcCallback<$output$> done)"); } -void ServiceGenerator::GenerateBlockingMethodSignature( +void ImmutableServiceGenerator::GenerateBlockingMethodSignature( io::Printer* printer, const MethodDescriptor* method) { map<string, string> vars; vars["method"] = UnderscoresToCamelCase(method); - vars["input"] = ClassName(method->input_type()); - vars["output"] = ClassName(method->output_type()); + vars["input"] = name_resolver_->GetImmutableClassName(method->input_type()); + vars["output"] = name_resolver_->GetImmutableClassName(method->output_type()); printer->Print(vars, "\n" "public $output$ $method$(\n" diff --git a/src/google/protobuf/compiler/java/java_service.h b/src/google/protobuf/compiler/java/java_service.h index e07eebf7..0d52325e 100644 --- a/src/google/protobuf/compiler/java/java_service.h +++ b/src/google/protobuf/compiler/java/java_service.h @@ -40,8 +40,14 @@ namespace google { namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } namespace io { - class Printer; // printer.h + class Printer; // printer.h } } @@ -52,9 +58,27 @@ namespace java { class ServiceGenerator { public: explicit ServiceGenerator(const ServiceDescriptor* descriptor); - ~ServiceGenerator(); + virtual ~ServiceGenerator(); + + virtual void Generate(io::Printer* printer) = 0; + + enum RequestOrResponse { REQUEST, RESPONSE }; + enum IsAbstract { IS_ABSTRACT, IS_CONCRETE }; + + protected: + const ServiceDescriptor* descriptor_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceGenerator); +}; - void Generate(io::Printer* printer); +class ImmutableServiceGenerator : public ServiceGenerator { + public: + explicit ImmutableServiceGenerator(const ServiceDescriptor* descriptor, + Context* context); + virtual ~ImmutableServiceGenerator(); + + virtual void Generate(io::Printer* printer); private: @@ -80,7 +104,6 @@ class ServiceGenerator { void GenerateCallBlockingMethod(io::Printer* printer); // Generate the implementations of Service.get{Request,Response}Prototype(). - enum RequestOrResponse { REQUEST, RESPONSE }; void GenerateGetPrototype(RequestOrResponse which, io::Printer* printer); // Generate a stub implementation of the service. @@ -88,7 +111,6 @@ class ServiceGenerator { // Generate a method signature, possibly abstract, without body or trailing // semicolon. - enum IsAbstract { IS_ABSTRACT, IS_CONCRETE }; void GenerateMethodSignature(io::Printer* printer, const MethodDescriptor* method, IsAbstract is_abstract); @@ -100,9 +122,9 @@ class ServiceGenerator { void GenerateBlockingMethodSignature(io::Printer* printer, const MethodDescriptor* method); - const ServiceDescriptor* descriptor_; - - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceGenerator); + Context* context_; + ClassNameResolver* name_resolver_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableServiceGenerator); }; } // namespace java diff --git a/src/google/protobuf/compiler/java/java_shared_code_generator.cc b/src/google/protobuf/compiler/java/java_shared_code_generator.cc new file mode 100644 index 00000000..c9f80abf --- /dev/null +++ b/src/google/protobuf/compiler/java/java_shared_code_generator.cc @@ -0,0 +1,223 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: xiaofeng@google.com (Feng Xiao) + +#include <google/protobuf/compiler/java/java_shared_code_generator.h> + +#include <memory> + +#include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> +#include <google/protobuf/compiler/code_generator.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/zero_copy_stream.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +SharedCodeGenerator::SharedCodeGenerator(const FileDescriptor* file) + : name_resolver_(new ClassNameResolver), file_(file) { +} + +SharedCodeGenerator::~SharedCodeGenerator() { +} + +void SharedCodeGenerator::Generate(GeneratorContext* context, + vector<string>* file_list) { + string java_package = FileJavaPackage(file_); + string package_dir = JavaPackageToDir(java_package); + + if (HasDescriptorMethods(file_)) { + // Generate descriptors. + string classname = name_resolver_->GetDescriptorClassName(file_); + string filename = package_dir + classname + ".java"; + file_list->push_back(filename); + scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename)); + scoped_ptr<io::Printer> printer(new io::Printer(output.get(), '$')); + + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "// source: $filename$\n" + "\n", + "filename", file_->name()); + if (!java_package.empty()) { + printer->Print( + "package $package$;\n" + "\n", + "package", java_package); + } + printer->Print( + "public final class $classname$ {\n", + "classname", classname); + printer->Indent(); + GenerateDescriptors(printer.get()); + printer->Outdent(); + printer->Print( + "}\n"); + + printer.reset(); + output.reset(); + } +} + + +void SharedCodeGenerator::GenerateDescriptors(io::Printer* printer) { + // Embed the descriptor. We simply serialize the entire FileDescriptorProto + // and embed it as a string literal, which is parsed and built into real + // descriptors at initialization time. We unfortunately have to put it in + // a string literal, not a byte array, because apparently using a literal + // byte array causes the Java compiler to generate *instructions* to + // initialize each and every byte of the array, e.g. as if you typed: + // b[0] = 123; b[1] = 456; b[2] = 789; + // This makes huge bytecode files and can easily hit the compiler's internal + // code size limits (error "code to large"). String literals are apparently + // embedded raw, which is what we want. + FileDescriptorProto file_proto; + file_->CopyTo(&file_proto); + + + string file_data; + file_proto.SerializeToString(&file_data); + + printer->Print( + "public static com.google.protobuf.Descriptors.FileDescriptor\n" + " descriptor;\n" + "static {\n" + " java.lang.String[] descriptorData = {\n"); + printer->Indent(); + printer->Indent(); + + // Only write 40 bytes per line. + static const int kBytesPerLine = 40; + for (int i = 0; i < file_data.size(); i += kBytesPerLine) { + if (i > 0) { + // Every 400 lines, start a new string literal, in order to avoid the + // 64k length limit. + if (i % 400 == 0) { + printer->Print(",\n"); + } else { + printer->Print(" +\n"); + } + } + printer->Print("\"$data$\"", + "data", CEscape(file_data.substr(i, kBytesPerLine))); + } + + printer->Outdent(); + printer->Print("\n};\n"); + + // ----------------------------------------------------------------- + // Create the InternalDescriptorAssigner. + + printer->Print( + "com.google.protobuf.Descriptors.FileDescriptor." + "InternalDescriptorAssigner assigner =\n" + " new com.google.protobuf.Descriptors.FileDescriptor." + " InternalDescriptorAssigner() {\n" + " public com.google.protobuf.ExtensionRegistry assignDescriptors(\n" + " com.google.protobuf.Descriptors.FileDescriptor root) {\n" + " descriptor = root;\n" + // Custom options will be handled when immutable messages' outer class is + // loaded. Here we just return null and let custom options be unknown + // fields. + " return null;\n" + " }\n" + " };\n"); + + // ----------------------------------------------------------------- + // Find out all dependencies. + vector<pair<string, string> > dependencies; + for (int i = 0; i < file_->dependency_count(); i++) { + if (ShouldIncludeDependency(file_->dependency(i))) { + string filename = file_->dependency(i)->name(); + string classname = FileJavaPackage(file_->dependency(i)) + "." + + name_resolver_->GetDescriptorClassName( + file_->dependency(i)); + dependencies.push_back(make_pair(filename, classname)); + } + } + + // ----------------------------------------------------------------- + // Invoke internalBuildGeneratedFileFrom() to build the file. + printer->Print( + "com.google.protobuf.Descriptors.FileDescriptor\n" + " .internalBuildGeneratedFileFrom(descriptorData,\n"); + + printer->Print( + " $classname$.class,\n" + " new java.lang.String[] {\n", + "classname", name_resolver_->GetDescriptorClassName(file_)); + for (int i = 0; i < dependencies.size(); i++) { + const string& dependency = dependencies[i].second; + printer->Print( + // Here we load the dependency FileDescriptors lazily via Java + // reflection. This is to avoid breaking proto1 targets who have + // genproto dependencies for which we can't generate the descriptor + // class. They will compile fine but when users try to call reflection + // functions upon them it will fail. Users will have to get rid of + // genproto dependencies before they can use proto2 reflection on + // proto1 messages. + " \"$dependency$\",\n", + "dependency", dependency); + } + + printer->Print( + " }, new java.lang.String[] {\n"); + + for (int i = 0; i < dependencies.size(); i++) { + const string& filename = dependencies[i].first; + printer->Print( + " \"$filename$\",\n", + "filename", filename); + } + + printer->Print( + " }, assigner);\n"); + + printer->Outdent(); + printer->Print( + "}\n"); +} + +bool SharedCodeGenerator::ShouldIncludeDependency( + const FileDescriptor* descriptor) { + return true; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_shared_code_generator.h b/src/google/protobuf/compiler/java/java_shared_code_generator.h new file mode 100644 index 00000000..c5457bf1 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_shared_code_generator.h @@ -0,0 +1,91 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: xiaofeng@google.com (Feng Xiao) +// +// Generators that generate shared code between immutable API and mutable API. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_SHARED_CODE_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_SHARED_CODE_GENERATOR_H__ + +#include <memory> +#include <string> +#include <vector> + +#include <google/protobuf/stubs/common.h> + +namespace google { +namespace protobuf { + class FileDescriptor; // descriptor.h + namespace compiler { + class GeneratorContext; // code_generator.h + namespace java { + class ClassNameResolver; // name_resolver.h + } + } + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +// A generator that generates code that are shared between immutable API +// and mutable API. Currently only descriptors are shared. +class SharedCodeGenerator { + public: + explicit SharedCodeGenerator(const FileDescriptor* file); + ~SharedCodeGenerator(); + + void Generate(GeneratorContext* generator_context, + vector<string>* file_list); + + private: + void GenerateDescriptors(io::Printer* printer); + + // Returns whether the dependency should be included in the output file. + // Always returns true for opensource, but used internally at Google to help + // improve compatibility with version 1 of protocol buffers. + bool ShouldIncludeDependency(const FileDescriptor* descriptor); + + scoped_ptr<ClassNameResolver> name_resolver_; + const FileDescriptor* file_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SharedCodeGenerator); +}; + + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_SHARED_CODE_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc index 4815663b..374e1d4e 100644 --- a/src/google/protobuf/compiler/java/java_string_field.cc +++ b/src/google/protobuf/compiler/java/java_string_field.cc @@ -36,10 +36,12 @@ #include <map> #include <string> -#include <google/protobuf/compiler/java/java_string_field.h> -#include <google/protobuf/compiler/java/java_doc_comment.h> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/java/java_context.h> +#include <google/protobuf/compiler/java/java_doc_comment.h> #include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> +#include <google/protobuf/compiler/java/java_string_field.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/wire_format.h> #include <google/protobuf/stubs/strutil.h> @@ -57,17 +59,16 @@ namespace { void SetPrimitiveVariables(const FieldDescriptor* descriptor, int messageBitIndex, int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, map<string, string>* variables) { - (*variables)["name"] = - UnderscoresToCamelCase(descriptor); - (*variables)["capitalized_name"] = - UnderscoresToCapitalizedCamelCase(descriptor); - (*variables)["constant_name"] = FieldConstantName(descriptor); - (*variables)["number"] = SimpleItoa(descriptor->number()); + SetCommonFieldVariables(descriptor, info, variables); + (*variables)["empty_list"] = "com.google.protobuf.LazyStringArrayList.EMPTY"; - (*variables)["default"] = DefaultValue(descriptor); - (*variables)["default_init"] = ("= " + DefaultValue(descriptor)); + (*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( @@ -84,16 +85,30 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, (*variables)["on_changed"] = HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; - // For singular messages and builders, one bit is used for the hasField bit. - (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); - (*variables)["set_has_field_bit_message"] = GenerateSetBit(messageBitIndex); + 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); + (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); + + // Note that these have a trailing ";". + (*variables)["set_has_field_bit_message"] = + GenerateSetBit(messageBitIndex) + ";"; + (*variables)["set_has_field_bit_builder"] = + GenerateSetBit(builderBitIndex) + ";"; + (*variables)["clear_has_field_bit_builder"] = + GenerateClearBit(builderBitIndex) + ";"; - (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); - (*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex); - (*variables)["clear_has_field_bit_builder"] = - GenerateClearBit(builderBitIndex); + (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex); + } else { + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["set_has_field_bit_builder"] = ""; + (*variables)["clear_has_field_bit_builder"] = ""; - // For repated builders, one bit is used for whether the array is immutable. + (*variables)["is_field_present_message"] = + "!get" + (*variables)["capitalized_name"] + "Bytes().isEmpty()"; + } + + // For repeated builders, one bit is used for whether the array is immutable. (*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex); (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); @@ -111,27 +126,34 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, GenerateSetBitToLocal(messageBitIndex); } +bool CheckUtf8(const FieldDescriptor* descriptor) { + return descriptor->file()->options().java_string_check_utf8(); +} + } // namespace // =================================================================== -StringFieldGenerator:: -StringFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, - int builderBitIndex) +ImmutableStringFieldGenerator:: +ImmutableStringFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) : descriptor_(descriptor), messageBitIndex_(messageBitIndex), - builderBitIndex_(builderBitIndex) { + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, - &variables_); + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); } -StringFieldGenerator::~StringFieldGenerator() {} +ImmutableStringFieldGenerator::~ImmutableStringFieldGenerator() {} -int StringFieldGenerator::GetNumBitsForMessage() const { +int ImmutableStringFieldGenerator::GetNumBitsForMessage() const { return 1; } -int StringFieldGenerator::GetNumBitsForBuilder() const { +int ImmutableStringFieldGenerator::GetNumBitsForBuilder() const { return 1; } @@ -167,11 +189,13 @@ int StringFieldGenerator::GetNumBitsForBuilder() const { // For single fields, the logic for this is done inside the generated code. For // repeated fields, the logic is done in LazyStringArrayList and // UnmodifiableLazyStringList. -void StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$boolean has$capitalized_name$();\n"); + 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"); @@ -181,15 +205,19 @@ GenerateInterfaceMembers(io::Printer* printer) const { " get$capitalized_name$Bytes();\n"); } -void StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private java.lang.Object $name$_;\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public boolean has$capitalized_name$() {\n" - " return $get_has_field_bit_message$;\n" - "}\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_, @@ -200,10 +228,17 @@ GenerateMembers(io::Printer* printer) const { " } else {\n" " com.google.protobuf.ByteString bs = \n" " (com.google.protobuf.ByteString) ref;\n" - " java.lang.String s = bs.toStringUtf8();\n" - " if (bs.isValidUtf8()) {\n" - " $name$_ = s;\n" - " }\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"); @@ -224,24 +259,36 @@ GenerateMembers(io::Printer* printer) const { "}\n"); } -void StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, "private java.lang.Object $name$_ $default_init$;\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public boolean has$capitalized_name$() {\n" - " return $get_has_field_bit_builder$;\n" - "}\n"); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_builder$;\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" - " java.lang.String s = ((com.google.protobuf.ByteString) ref)\n" - " .toStringUtf8();\n" - " $name$_ = s;\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" " } else {\n" " return (java.lang.String) ref;\n" @@ -269,7 +316,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "$deprecation$public Builder set$capitalized_name$(\n" " java.lang.String value) {\n" "$null_check$" - " $set_has_field_bit_builder$;\n" + " $set_has_field_bit_builder$\n" " $name$_ = value;\n" " $on_changed$\n" " return this;\n" @@ -277,7 +324,7 @@ GenerateBuilderMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public Builder clear$capitalized_name$() {\n" - " $clear_has_field_bit_builder$;\n"); + " $clear_has_field_bit_builder$\n"); // The default value is not a simple literal so we want to avoid executing // it multiple times. Instead, get the default out of the default instance. printer->Print(variables_, @@ -291,89 +338,113 @@ GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public Builder set$capitalized_name$Bytes(\n" " com.google.protobuf.ByteString value) {\n" - "$null_check$" - " $set_has_field_bit_builder$;\n" + "$null_check$"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " checkByteStringIsUtf8(value);\n"); + } + printer->Print(variables_, + " $set_has_field_bit_builder$\n" " $name$_ = value;\n" " $on_changed$\n" " return this;\n" "}\n"); } -void StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateFieldBuilderInitializationCode(io::Printer* printer) const { // noop for primitives } -void StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = $default$;\n"); } -void StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateBuilderClearCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = $default$;\n" - "$clear_has_field_bit_builder$;\n"); + "$clear_has_field_bit_builder$\n"); } -void StringFieldGenerator:: +void ImmutableStringFieldGenerator:: 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_, - "if (other.has$capitalized_name$()) {\n" - " $set_has_field_bit_builder$;\n" - " $name$_ = other.$name$_;\n" - " $on_changed$\n" - "}\n"); + 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_builder$\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 StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + printer->Print(variables_, + "if ($get_has_field_bit_from_local$) {\n" + " $set_has_field_bit_to_local$;\n" + "}\n"); + } printer->Print(variables_, - "if ($get_has_field_bit_from_local$) {\n" - " $set_has_field_bit_to_local$;\n" - "}\n" "result.$name$_ = $name$_;\n"); } -void StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { - printer->Print(variables_, - "$set_has_field_bit_message$;\n" - "$name$_ = input.readBytes();\n"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + "String s = input.readStringRequireUtf8();\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 StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateParsingDoneCode(io::Printer* printer) const { // noop for strings. } -void StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, - "if ($get_has_field_bit_message$) {\n" + "if ($is_field_present_message$) {\n" " output.writeBytes($number$, get$capitalized_name$Bytes());\n" "}\n"); } -void StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, - "if ($get_has_field_bit_message$) {\n" + "if ($is_field_present_message$) {\n" " size += com.google.protobuf.CodedOutputStream\n" " .computeBytesSize($number$, get$capitalized_name$Bytes());\n" "}\n"); } -void StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateEqualsCode(io::Printer* printer) const { printer->Print(variables_, "result = result && get$capitalized_name$()\n" " .equals(other.get$capitalized_name$());\n"); } -void StringFieldGenerator:: +void ImmutableStringFieldGenerator:: GenerateHashCode(io::Printer* printer) const { printer->Print(variables_, "hash = (37 * hash) + $constant_name$;\n"); @@ -381,39 +452,270 @@ GenerateHashCode(io::Printer* printer) const { "hash = (53 * hash) + get$capitalized_name$().hashCode();\n"); } -string StringFieldGenerator::GetBoxedType() const { +string ImmutableStringFieldGenerator::GetBoxedType() const { return "java.lang.String"; } +// =================================================================== + +ImmutableStringOneofFieldGenerator:: +ImmutableStringOneofFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableStringFieldGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); +} + +ImmutableStringOneofFieldGenerator:: +~ImmutableStringOneofFieldGenerator() {} + +void ImmutableStringOneofFieldGenerator:: +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"); +} + +void ImmutableStringOneofFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + 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" + " com.google.protobuf.ByteString bs =\n" + " (com.google.protobuf.ByteString) ref;\n" + " java.lang.String s = bs.toStringUtf8();\n" + " if ($has_oneof_case_message$) {\n"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " $oneof_name$_ = s;\n"); + } else { + printer->Print(variables_, + " if (bs.isValidUtf8()) {\n" + " $oneof_name$_ = s;\n" + " }\n"); + } + printer->Print(variables_, + " }\n" + " return s;\n" + " } else {\n" + " return (java.lang.String) ref;\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 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_, + "$deprecation$public Builder set$capitalized_name$(\n" + " java.lang.String value) {\n" + "$null_check$" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + " $on_changed$\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " $on_changed$\n" + " }\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" + "$null_check$"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " checkByteStringIsUtf8(value);\n"); + } + printer->Print(variables_, + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + " $on_changed$\n" + " return this;\n" + "}\n"); +} + +void ImmutableStringOneofFieldGenerator:: +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 ImmutableStringOneofFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " result.$oneof_name$_ = $oneof_name$_;\n" + "}\n"); +} + +void ImmutableStringOneofFieldGenerator:: +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}\n"); + } else { + printer->Print(variables_, + "com.google.protobuf.ByteString bs = input.readBytes();\n" + "$set_oneof_case_message$;\n" + "$oneof_name$_ = bs;\n"); + } +} + +void ImmutableStringOneofFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " output.writeBytes($number$, get$capitalized_name$Bytes());\n" + "}\n"); +} + +void ImmutableStringOneofFieldGenerator:: +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"); +} // =================================================================== -RepeatedStringFieldGenerator:: -RepeatedStringFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, - int builderBitIndex) +RepeatedImmutableStringFieldGenerator:: +RepeatedImmutableStringFieldGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) : descriptor_(descriptor), messageBitIndex_(messageBitIndex), - builderBitIndex_(builderBitIndex) { + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, - &variables_); + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); } -RepeatedStringFieldGenerator::~RepeatedStringFieldGenerator() {} +RepeatedImmutableStringFieldGenerator:: +~RepeatedImmutableStringFieldGenerator() {} -int RepeatedStringFieldGenerator::GetNumBitsForMessage() const { +int RepeatedImmutableStringFieldGenerator::GetNumBitsForMessage() const { return 0; } -int RepeatedStringFieldGenerator::GetNumBitsForBuilder() const { +int RepeatedImmutableStringFieldGenerator::GetNumBitsForBuilder() const { return 1; } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$java.util.List<java.lang.String>\n" - "get$capitalized_name$List();\n"); + "$deprecation$com.google.protobuf.ProtocolStringList\n" + " get$capitalized_name$List();\n"); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$int get$capitalized_name$Count();\n"); @@ -427,13 +729,14 @@ GenerateInterfaceMembers(io::Printer* printer) const { } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private com.google.protobuf.LazyStringList $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$public java.util.List<java.lang.String>\n" + "$deprecation$public com.google.protobuf.ProtocolStringList\n" " get$capitalized_name$List() {\n" " return $name$_;\n" // note: unmodifiable list "}\n"); @@ -461,7 +764,7 @@ GenerateMembers(io::Printer* printer) const { } } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { // One field is the list and the bit field keeps track of whether the // list is immutable. If it's immutable, the invariant is that it must @@ -489,9 +792,9 @@ GenerateBuilderMembers(io::Printer* printer) const { // immutable. WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$public java.util.List<java.lang.String>\n" + "$deprecation$public com.google.protobuf.ProtocolStringList\n" " get$capitalized_name$List() {\n" - " return java.util.Collections.unmodifiableList($name$_);\n" + " return $name$_.getUnmodifiableView();\n" "}\n"); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, @@ -534,7 +837,8 @@ GenerateBuilderMembers(io::Printer* printer) const { "$deprecation$public Builder addAll$capitalized_name$(\n" " java.lang.Iterable<java.lang.String> values) {\n" " ensure$capitalized_name$IsMutable();\n" - " super.addAll(values, $name$_);\n" + " com.google.protobuf.AbstractMessageLite.Builder.addAll(\n" + " values, $name$_);\n" " $on_changed$\n" " return this;\n" "}\n"); @@ -551,7 +855,12 @@ GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public Builder add$capitalized_name$Bytes(\n" " com.google.protobuf.ByteString value) {\n" - "$null_check$" + "$null_check$"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " checkByteStringIsUtf8(value);\n"); + } + printer->Print(variables_, " ensure$capitalized_name$IsMutable();\n" " $name$_.add(value);\n" " $on_changed$\n" @@ -559,24 +868,24 @@ GenerateBuilderMembers(io::Printer* printer) const { "}\n"); } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateFieldBuilderInitializationCode(io::Printer* printer) const { // noop for primitives } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateInitializationCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = $empty_list$;\n"); } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateBuilderClearCode(io::Printer* printer) const { printer->Print(variables_, "$name$_ = $empty_list$;\n" "$clear_mutable_bit_builder$;\n"); } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: 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 @@ -596,31 +905,43 @@ GenerateMergingCode(io::Printer* printer) const { "}\n"); } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { // The code below ensures that the result has an immutable list. If our // list is immutable, we can just reuse it. If not, we make it immutable. printer->Print(variables_, "if ($get_mutable_bit_builder$) {\n" - " $name$_ = new com.google.protobuf.UnmodifiableLazyStringList(\n" - " $name$_);\n" + " $name$_ = $name$_.getUnmodifiableView();\n" " $clear_mutable_bit_builder$;\n" "}\n" "result.$name$_ = $name$_;\n"); } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + "String s = input.readStringRequireUtf8();\n"); + } else { + printer->Print(variables_, + "com.google.protobuf.ByteString bs = input.readBytes();\n"); + } printer->Print(variables_, "if (!$get_mutable_bit_parser$) {\n" " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" " $set_mutable_bit_parser$;\n" - "}\n" - "$name$_.add(input.readBytes());\n"); + "}\n"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + "$name$_.add(s);\n"); + } else { + printer->Print(variables_, + "$name$_.add(bs);\n"); + } } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateParsingCodeFromPacked(io::Printer* printer) const { printer->Print(variables_, "int length = input.readRawVarint32();\n" @@ -629,21 +950,30 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const { " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" " $set_mutable_bit_parser$;\n" "}\n" - "while (input.getBytesUntilLimit() > 0) {\n" - " $name$.add(input.read$capitalized_type$());\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 RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateParsingDoneCode(io::Printer* printer) const { printer->Print(variables_, "if ($get_mutable_bit_parser$) {\n" - " $name$_ = new com.google.protobuf.UnmodifiableLazyStringList($name$_);\n" + " $name$_ = $name$_.getUnmodifiableView();\n" "}\n"); } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { if (descriptor_->options().packed()) { printer->Print(variables_, @@ -662,7 +992,7 @@ GenerateSerializationCode(io::Printer* printer) const { } } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, "{\n" @@ -700,14 +1030,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print("}\n"); } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateEqualsCode(io::Printer* printer) const { printer->Print(variables_, "result = result && get$capitalized_name$List()\n" " .equals(other.get$capitalized_name$List());\n"); } -void RepeatedStringFieldGenerator:: +void RepeatedImmutableStringFieldGenerator:: GenerateHashCode(io::Printer* printer) const { printer->Print(variables_, "if (get$capitalized_name$Count() > 0) {\n" @@ -716,7 +1046,7 @@ GenerateHashCode(io::Printer* printer) const { "}\n"); } -string RepeatedStringFieldGenerator::GetBoxedType() const { +string RepeatedImmutableStringFieldGenerator::GetBoxedType() const { return "String"; } diff --git a/src/google/protobuf/compiler/java/java_string_field.h b/src/google/protobuf/compiler/java/java_string_field.h index 4f7532f4..348cbcbd 100644 --- a/src/google/protobuf/compiler/java/java_string_field.h +++ b/src/google/protobuf/compiler/java/java_string_field.h @@ -42,16 +42,26 @@ namespace google { namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } +} + +namespace protobuf { namespace compiler { namespace java { -class StringFieldGenerator : public FieldGenerator { +class ImmutableStringFieldGenerator : public ImmutableFieldGenerator { public: - explicit StringFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, int builderBitIndex); - ~StringFieldGenerator(); + explicit ImmutableStringFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableStringFieldGenerator(); - // implements FieldGenerator --------------------------------------- + // implements ImmutableFieldGenerator --------------------------------------- int GetNumBitsForMessage() const; int GetNumBitsForBuilder() const; void GenerateInterfaceMembers(io::Printer* printer) const; @@ -68,24 +78,49 @@ class StringFieldGenerator : public FieldGenerator { void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; + string GetBoxedType() const; - private: + protected: const FieldDescriptor* descriptor_; map<string, string> variables_; const int messageBitIndex_; const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringFieldGenerator); + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableStringFieldGenerator); }; -class RepeatedStringFieldGenerator : public FieldGenerator { +class ImmutableStringOneofFieldGenerator + : public ImmutableStringFieldGenerator { public: - explicit RepeatedStringFieldGenerator(const FieldDescriptor* descriptor, - int messageBitIndex, int builderBitIndex); - ~RepeatedStringFieldGenerator(); + ImmutableStringOneofFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableStringOneofFieldGenerator(); - // implements FieldGenerator --------------------------------------- + private: + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableStringOneofFieldGenerator); +}; + +class RepeatedImmutableStringFieldGenerator : public ImmutableFieldGenerator { + public: + explicit RepeatedImmutableStringFieldGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~RepeatedImmutableStringFieldGenerator(); + + // implements ImmutableFieldGenerator --------------------------------------- int GetNumBitsForMessage() const; int GetNumBitsForBuilder() const; void GenerateInterfaceMembers(io::Printer* printer) const; @@ -103,6 +138,7 @@ class RepeatedStringFieldGenerator : public FieldGenerator { void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; + string GetBoxedType() const; private: @@ -110,8 +146,10 @@ class RepeatedStringFieldGenerator : public FieldGenerator { map<string, string> variables_; const int messageBitIndex_; const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedStringFieldGenerator); + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableStringFieldGenerator); }; } // namespace java diff --git a/src/google/protobuf/compiler/mock_code_generator.cc b/src/google/protobuf/compiler/mock_code_generator.cc index 0e35ed19..1f91817f 100644 --- a/src/google/protobuf/compiler/mock_code_generator.cc +++ b/src/google/protobuf/compiler/mock_code_generator.cc @@ -32,6 +32,8 @@ #include <google/protobuf/compiler/mock_code_generator.h> +#include <memory> + #include <google/protobuf/testing/file.h> #include <google/protobuf/descriptor.pb.h> #include <google/protobuf/descriptor.h> @@ -53,7 +55,7 @@ string CommaSeparatedList(const vector<const FileDescriptor*> all_files) { for (int i = 0; i < all_files.size(); i++) { names.push_back(all_files[i]->name()); } - return JoinStrings(names, ","); + return Join(names, ","); } static const char* kFirstInsertionPointName = "first_mock_insertion_point"; @@ -77,11 +79,11 @@ void MockCodeGenerator::ExpectGenerated( const string& first_parsed_file_name, const string& output_directory) { string content; - ASSERT_TRUE(File::ReadFileToString( - output_directory + "/" + GetOutputFileName(name, file), &content)); + GOOGLE_CHECK_OK( + File::GetContents(output_directory + "/" + GetOutputFileName(name, file), + &content, true)); - vector<string> lines; - SplitStringUsing(content, "\n", &lines); + vector<string> lines = Split(content, "\n", true); while (!lines.empty() && lines.back().empty()) { lines.pop_back(); @@ -95,7 +97,7 @@ void MockCodeGenerator::ExpectGenerated( SplitStringUsing(insertions, ",", &insertion_list); } - ASSERT_EQ(lines.size(), 3 + insertion_list.size() * 2); + EXPECT_EQ(lines.size(), 3 + insertion_list.size() * 2); EXPECT_EQ(GetOutputFileContent(name, parameter, file, first_parsed_file_name, first_message_name), lines[0]); @@ -155,10 +157,8 @@ bool MockCodeGenerator::Generate( for (int i = 0; i < insert_into.size(); i++) { { - scoped_ptr<io::ZeroCopyOutputStream> output( - context->OpenForInsert( - GetOutputFileName(insert_into[i], file), - kFirstInsertionPointName)); + scoped_ptr<io::ZeroCopyOutputStream> output(context->OpenForInsert( + GetOutputFileName(insert_into[i], file), kFirstInsertionPointName)); io::Printer printer(output.get(), '$'); printer.PrintRaw(GetOutputFileContent(name_, "first_insert", file, context)); @@ -170,9 +170,8 @@ bool MockCodeGenerator::Generate( { scoped_ptr<io::ZeroCopyOutputStream> output( - context->OpenForInsert( - GetOutputFileName(insert_into[i], file), - kSecondInsertionPointName)); + context->OpenForInsert(GetOutputFileName(insert_into[i], file), + kSecondInsertionPointName)); io::Printer printer(output.get(), '$'); printer.PrintRaw(GetOutputFileContent(name_, "second_insert", file, context)); diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc index 23aa01ce..7e0df7e1 100644 --- a/src/google/protobuf/compiler/parser.cc +++ b/src/google/protobuf/compiler/parser.cc @@ -46,7 +46,7 @@ #include <google/protobuf/io/tokenizer.h> #include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/strutil.h> -#include <google/protobuf/stubs/map-util.h> +#include <google/protobuf/stubs/map_util.h> namespace google { namespace protobuf { @@ -343,6 +343,11 @@ void Parser::LocationRecorder::StartAt(const io::Tokenizer::Token& token) { location_->set_span(1, token.column); } +void Parser::LocationRecorder::StartAt(const LocationRecorder& other) { + location_->set_span(0, other.location_->span(0)); + location_->set_span(1, other.location_->span(1)); +} + void Parser::LocationRecorder::EndAt(const io::Tokenizer::Token& token) { if (token.line != location_->span(0)) { location_->add_span(token.line); @@ -489,15 +494,15 @@ bool Parser::ParseTopLevelStatement(FileDescriptorProto* file, } else if (LookingAt("message")) { LocationRecorder location(root_location, FileDescriptorProto::kMessageTypeFieldNumber, file->message_type_size()); - return ParseMessageDefinition(file->add_message_type(), location); + return ParseMessageDefinition(file->add_message_type(), location, file); } else if (LookingAt("enum")) { LocationRecorder location(root_location, FileDescriptorProto::kEnumTypeFieldNumber, file->enum_type_size()); - return ParseEnumDefinition(file->add_enum_type(), location); + return ParseEnumDefinition(file->add_enum_type(), location, file); } else if (LookingAt("service")) { LocationRecorder location(root_location, FileDescriptorProto::kServiceFieldNumber, file->service_size()); - return ParseServiceDefinition(file->add_service(), location); + return ParseServiceDefinition(file->add_service(), location, file); } else if (LookingAt("extend")) { LocationRecorder location(root_location, FileDescriptorProto::kExtensionFieldNumber); @@ -505,18 +510,19 @@ bool Parser::ParseTopLevelStatement(FileDescriptorProto* file, file->mutable_message_type(), root_location, FileDescriptorProto::kMessageTypeFieldNumber, - location); + location, file); } else if (LookingAt("import")) { return ParseImport(file->mutable_dependency(), file->mutable_public_dependency(), file->mutable_weak_dependency(), - root_location); + root_location, file); } else if (LookingAt("package")) { - return ParsePackage(file, root_location); + return ParsePackage(file, root_location, file); } else if (LookingAt("option")) { LocationRecorder location(root_location, FileDescriptorProto::kOptionsFieldNumber); - return ParseOption(file->mutable_options(), location, OPTION_STATEMENT); + return ParseOption(file->mutable_options(), location, file, + OPTION_STATEMENT); } else { AddError("Expected top-level statement (e.g. \"message\")."); return false; @@ -526,8 +532,10 @@ bool Parser::ParseTopLevelStatement(FileDescriptorProto* file, // ------------------------------------------------------------------- // Messages -bool Parser::ParseMessageDefinition(DescriptorProto* message, - const LocationRecorder& message_location) { +bool Parser::ParseMessageDefinition( + DescriptorProto* message, + const LocationRecorder& message_location, + const FileDescriptorProto* containing_file) { DO(Consume("message")); { LocationRecorder location(message_location, @@ -536,7 +544,7 @@ bool Parser::ParseMessageDefinition(DescriptorProto* message, message, DescriptorPool::ErrorCollector::NAME); DO(ConsumeIdentifier(message->mutable_name(), "Expected message name.")); } - DO(ParseMessageBlock(message, message_location)); + DO(ParseMessageBlock(message, message_location, containing_file)); return true; } @@ -575,7 +583,8 @@ void AdjustExtensionRangesWithMaxEndNumber(DescriptorProto* message) { } // namespace bool Parser::ParseMessageBlock(DescriptorProto* message, - const LocationRecorder& message_location) { + const LocationRecorder& message_location, + const FileDescriptorProto* containing_file) { DO(ConsumeEndOfDeclaration("{", &message_location)); while (!TryConsumeEndOfDeclaration("}", NULL)) { @@ -584,7 +593,7 @@ bool Parser::ParseMessageBlock(DescriptorProto* message, return false; } - if (!ParseMessageStatement(message, message_location)) { + if (!ParseMessageStatement(message, message_location, containing_file)) { // This statement failed to parse. Skip it, but keep looping to parse // other statements. SkipStatement(); @@ -598,7 +607,8 @@ bool Parser::ParseMessageBlock(DescriptorProto* message, } bool Parser::ParseMessageStatement(DescriptorProto* message, - const LocationRecorder& message_location) { + const LocationRecorder& message_location, + const FileDescriptorProto* containing_file) { if (TryConsumeEndOfDeclaration(";", NULL)) { // empty statement; ignore return true; @@ -606,16 +616,18 @@ bool Parser::ParseMessageStatement(DescriptorProto* message, LocationRecorder location(message_location, DescriptorProto::kNestedTypeFieldNumber, message->nested_type_size()); - return ParseMessageDefinition(message->add_nested_type(), location); + return ParseMessageDefinition(message->add_nested_type(), location, + containing_file); } else if (LookingAt("enum")) { LocationRecorder location(message_location, DescriptorProto::kEnumTypeFieldNumber, message->enum_type_size()); - return ParseEnumDefinition(message->add_enum_type(), location); + return ParseEnumDefinition(message->add_enum_type(), location, + containing_file); } else if (LookingAt("extensions")) { LocationRecorder location(message_location, DescriptorProto::kExtensionRangeFieldNumber); - return ParseExtensions(message, location); + return ParseExtensions(message, location, containing_file); } else if (LookingAt("extend")) { LocationRecorder location(message_location, DescriptorProto::kExtensionFieldNumber); @@ -623,11 +635,21 @@ bool Parser::ParseMessageStatement(DescriptorProto* message, message->mutable_nested_type(), message_location, DescriptorProto::kNestedTypeFieldNumber, - location); + location, containing_file); } else if (LookingAt("option")) { LocationRecorder location(message_location, DescriptorProto::kOptionsFieldNumber); - return ParseOption(message->mutable_options(), location, OPTION_STATEMENT); + return ParseOption(message->mutable_options(), location, + containing_file, OPTION_STATEMENT); + } else if (LookingAt("oneof")) { + int oneof_index = message->oneof_decl_size(); + LocationRecorder oneof_location(message_location, + DescriptorProto::kOneofDeclFieldNumber, + oneof_index); + + return ParseOneof(message->add_oneof_decl(), message, + oneof_index, oneof_location, message_location, + containing_file); } else { LocationRecorder location(message_location, DescriptorProto::kFieldFieldNumber, @@ -636,7 +658,8 @@ bool Parser::ParseMessageStatement(DescriptorProto* message, message->mutable_nested_type(), message_location, DescriptorProto::kNestedTypeFieldNumber, - location); + location, + containing_file); } } @@ -644,17 +667,30 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field, RepeatedPtrField<DescriptorProto>* messages, const LocationRecorder& parent_location, int location_field_number_for_nested_type, - const LocationRecorder& field_location) { - // Parse label and type. - io::Tokenizer::Token label_token = input_->current(); + const LocationRecorder& field_location, + const FileDescriptorProto* containing_file) { { LocationRecorder location(field_location, FieldDescriptorProto::kLabelFieldNumber); FieldDescriptorProto::Label label; - DO(ParseLabel(&label)); + DO(ParseLabel(&label, containing_file)); field->set_label(label); } + return ParseMessageFieldNoLabel(field, messages, parent_location, + location_field_number_for_nested_type, + field_location, + containing_file); +} + +bool Parser::ParseMessageFieldNoLabel( + FieldDescriptorProto* field, + RepeatedPtrField<DescriptorProto>* messages, + const LocationRecorder& parent_location, + int location_field_number_for_nested_type, + const LocationRecorder& field_location, + const FileDescriptorProto* containing_file) { + // Parse type. { LocationRecorder location(field_location); // add path later location.RecordLegacyLocation(field, DescriptorPool::ErrorCollector::TYPE); @@ -693,14 +729,14 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field, } // Parse options. - DO(ParseFieldOptions(field, field_location)); + DO(ParseFieldOptions(field, field_location, containing_file)); // Deal with groups. if (field->has_type() && field->type() == FieldDescriptorProto::TYPE_GROUP) { // Awkward: Since a group declares both a message type and a field, we // have to create overlapping locations. LocationRecorder group_location(parent_location); - group_location.StartAt(label_token); + group_location.StartAt(field_location); group_location.AddPath(location_field_number_for_nested_type); group_location.AddPath(messages->size()); @@ -736,7 +772,7 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field, field->set_type_name(group->name()); if (LookingAt("{")) { - DO(ParseMessageBlock(group, group_location)); + DO(ParseMessageBlock(group, group_location, containing_file)); } else { AddError("Missing group body."); return false; @@ -749,7 +785,8 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field, } bool Parser::ParseFieldOptions(FieldDescriptorProto* field, - const LocationRecorder& field_location) { + const LocationRecorder& field_location, + const FileDescriptorProto* containing_file) { if (!LookingAt("[")) return true; LocationRecorder location(field_location, @@ -762,9 +799,10 @@ bool Parser::ParseFieldOptions(FieldDescriptorProto* field, if (LookingAt("default")) { // We intentionally pass field_location rather than location here, since // the default value is not actually an option. - DO(ParseDefaultAssignment(field, field_location)); + DO(ParseDefaultAssignment(field, field_location, containing_file)); } else { - DO(ParseOption(field->mutable_options(), location, OPTION_ASSIGNMENT)); + DO(ParseOption(field->mutable_options(), location, + containing_file, OPTION_ASSIGNMENT)); } } while (TryConsume(",")); @@ -772,8 +810,10 @@ bool Parser::ParseFieldOptions(FieldDescriptorProto* field, return true; } -bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field, - const LocationRecorder& field_location) { +bool Parser::ParseDefaultAssignment( + FieldDescriptorProto* field, + const LocationRecorder& field_location, + const FileDescriptorProto* containing_file) { if (field->has_default_value()) { AddError("Already set option \"default\"."); field->clear_default_value(); @@ -790,8 +830,16 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field, if (!field->has_type()) { // The field has a type name, but we don't know if it is a message or an - // enum yet. Assume an enum for now. - DO(ConsumeIdentifier(default_value, "Expected identifier.")); + // enum yet. (If it were a primitive type, |field| would have a type set + // already.) In this case, simply take the current string as the default + // value; we will catch the error later if it is not a valid enum value. + // (N.B. that we do not check whether the current token is an identifier: + // doing so throws strange errors when the user mistypes a primitive + // typename and we assume it's an enum. E.g.: "optional int foo = 1 [default + // = 42]". In such a case the fundamental error is really that "int" is not + // a type, not that "42" is not an identifier. See b/12533582.) + *default_value = input_->current().text; + input_->Next(); return true; } @@ -817,7 +865,8 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field, } // Parse the integer to verify that it is not out-of-range. uint64 value; - DO(ConsumeInteger64(max_value, &value, "Expected integer.")); + DO(ConsumeInteger64(max_value, &value, + "Expected integer for field default value.")); // And stringify it again. default_value->append(SimpleItoa(value)); break; @@ -839,7 +888,8 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field, } // Parse the integer to verify that it is not out-of-range. uint64 value; - DO(ConsumeInteger64(max_value, &value, "Expected integer.")); + DO(ConsumeInteger64(max_value, &value, + "Expected integer for field default value.")); // And stringify it again. default_value->append(SimpleItoa(value)); break; @@ -871,7 +921,11 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field, break; case FieldDescriptorProto::TYPE_STRING: - DO(ConsumeString(default_value, "Expected string.")); + // Note: When file opton java_string_check_utf8 is true, if a + // non-string representation (eg byte[]) is later supported, it must + // be checked for UTF-8-ness. + DO(ConsumeString(default_value, "Expected string for field default " + "value.")); break; case FieldDescriptorProto::TYPE_BYTES: @@ -880,7 +934,8 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field, break; case FieldDescriptorProto::TYPE_ENUM: - DO(ConsumeIdentifier(default_value, "Expected identifier.")); + DO(ConsumeIdentifier(default_value, "Expected enum identifier for field " + "default value.")); break; case FieldDescriptorProto::TYPE_MESSAGE: @@ -893,7 +948,8 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field, } bool Parser::ParseOptionNamePart(UninterpretedOption* uninterpreted_option, - const LocationRecorder& part_location) { + const LocationRecorder& part_location, + const FileDescriptorProto* containing_file) { UninterpretedOption::NamePart* name = uninterpreted_option->add_name(); string identifier; // We parse identifiers into this string. if (LookingAt("(")) { // This is an extension. @@ -957,6 +1013,7 @@ bool Parser::ParseUninterpretedBlock(string* value) { // UninterpretedOption, to be interpreted later. bool Parser::ParseOption(Message* options, const LocationRecorder& options_location, + const FileDescriptorProto* containing_file, OptionStyle style) { // Create an entry in the uninterpreted_option field. const FieldDescriptor* uninterpreted_option_field = options->GetDescriptor()-> @@ -988,14 +1045,16 @@ bool Parser::ParseOption(Message* options, { LocationRecorder part_location(name_location, uninterpreted_option->name_size()); - DO(ParseOptionNamePart(uninterpreted_option, part_location)); + DO(ParseOptionNamePart(uninterpreted_option, part_location, + containing_file)); } while (LookingAt(".")) { DO(Consume(".")); LocationRecorder part_location(name_location, uninterpreted_option->name_size()); - DO(ParseOptionNamePart(uninterpreted_option, part_location)); + DO(ParseOptionNamePart(uninterpreted_option, part_location, + containing_file)); } } @@ -1088,11 +1147,13 @@ bool Parser::ParseOption(Message* options, DO(ConsumeEndOfDeclaration(";", &location)); } + return true; } bool Parser::ParseExtensions(DescriptorProto* message, - const LocationRecorder& extensions_location) { + const LocationRecorder& extensions_location, + const FileDescriptorProto* containing_file) { // Parse the declaration. DO(Consume("extensions")); @@ -1150,7 +1211,8 @@ bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions, RepeatedPtrField<DescriptorProto>* messages, const LocationRecorder& parent_location, int location_field_number_for_nested_type, - const LocationRecorder& extend_location) { + const LocationRecorder& extend_location, + const FileDescriptorProto* containing_file) { DO(Consume("extend")); // Parse the extendee type. @@ -1192,7 +1254,65 @@ bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions, if (!ParseMessageField(field, messages, parent_location, location_field_number_for_nested_type, - location)) { + location, + containing_file)) { + // This statement failed to parse. Skip it, but keep looping to parse + // other statements. + SkipStatement(); + } + } while (!TryConsumeEndOfDeclaration("}", NULL)); + + return true; +} + +bool Parser::ParseOneof(OneofDescriptorProto* oneof_decl, + DescriptorProto* containing_type, + int oneof_index, + const LocationRecorder& oneof_location, + const LocationRecorder& containing_type_location, + const FileDescriptorProto* containing_file) { + DO(Consume("oneof")); + + { + LocationRecorder name_location(oneof_location, + OneofDescriptorProto::kNameFieldNumber); + DO(ConsumeIdentifier(oneof_decl->mutable_name(), "Expected oneof name.")); + } + + DO(ConsumeEndOfDeclaration("{", &oneof_location)); + + do { + if (AtEnd()) { + AddError("Reached end of input in oneof definition (missing '}')."); + return false; + } + + // Print a nice error if the user accidentally tries to place a label + // on an individual member of a oneof. + if (LookingAt("required") || + LookingAt("optional") || + LookingAt("repeated")) { + AddError("Fields in oneofs must not have labels (required / optional " + "/ repeated)."); + // We can continue parsing here because we understand what the user + // meant. The error report will still make parsing fail overall. + input_->Next(); + } + + LocationRecorder field_location(containing_type_location, + DescriptorProto::kFieldFieldNumber, + containing_type->field_size()); + + FieldDescriptorProto* field = containing_type->add_field(); + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL); + field->set_oneof_index(oneof_index); + + if (!ParseMessageFieldNoLabel(field, + containing_type->mutable_nested_type(), + containing_type_location, + DescriptorProto::kNestedTypeFieldNumber, + field_location, + containing_file)) { // This statement failed to parse. Skip it, but keep looping to parse // other statements. SkipStatement(); @@ -1206,7 +1326,8 @@ bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions, // Enums bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type, - const LocationRecorder& enum_location) { + const LocationRecorder& enum_location, + const FileDescriptorProto* containing_file) { DO(Consume("enum")); { @@ -1217,12 +1338,13 @@ bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type, DO(ConsumeIdentifier(enum_type->mutable_name(), "Expected enum name.")); } - DO(ParseEnumBlock(enum_type, enum_location)); + DO(ParseEnumBlock(enum_type, enum_location, containing_file)); return true; } bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type, - const LocationRecorder& enum_location) { + const LocationRecorder& enum_location, + const FileDescriptorProto* containing_file) { DO(ConsumeEndOfDeclaration("{", &enum_location)); while (!TryConsumeEndOfDeclaration("}", NULL)) { @@ -1231,7 +1353,7 @@ bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type, return false; } - if (!ParseEnumStatement(enum_type, enum_location)) { + if (!ParseEnumStatement(enum_type, enum_location, containing_file)) { // This statement failed to parse. Skip it, but keep looping to parse // other statements. SkipStatement(); @@ -1242,7 +1364,8 @@ bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type, } bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type, - const LocationRecorder& enum_location) { + const LocationRecorder& enum_location, + const FileDescriptorProto* containing_file) { if (TryConsumeEndOfDeclaration(";", NULL)) { // empty statement; ignore return true; @@ -1250,16 +1373,17 @@ bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type, LocationRecorder location(enum_location, EnumDescriptorProto::kOptionsFieldNumber); return ParseOption(enum_type->mutable_options(), location, - OPTION_STATEMENT); + containing_file, OPTION_STATEMENT); } else { LocationRecorder location(enum_location, EnumDescriptorProto::kValueFieldNumber, enum_type->value_size()); - return ParseEnumConstant(enum_type->add_value(), location); + return ParseEnumConstant(enum_type->add_value(), location, containing_file); } } bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value, - const LocationRecorder& enum_value_location) { + const LocationRecorder& enum_value_location, + const FileDescriptorProto* containing_file) { // Parse name. { LocationRecorder location(enum_value_location, @@ -1284,7 +1408,8 @@ bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value, enum_value->set_number(number); } - DO(ParseEnumConstantOptions(enum_value, enum_value_location)); + DO(ParseEnumConstantOptions(enum_value, enum_value_location, + containing_file)); DO(ConsumeEndOfDeclaration(";", &enum_value_location)); @@ -1293,7 +1418,8 @@ bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value, bool Parser::ParseEnumConstantOptions( EnumValueDescriptorProto* value, - const LocationRecorder& enum_value_location) { + const LocationRecorder& enum_value_location, + const FileDescriptorProto* containing_file) { if (!LookingAt("[")) return true; LocationRecorder location( @@ -1302,7 +1428,8 @@ bool Parser::ParseEnumConstantOptions( DO(Consume("[")); do { - DO(ParseOption(value->mutable_options(), location, OPTION_ASSIGNMENT)); + DO(ParseOption(value->mutable_options(), location, + containing_file, OPTION_ASSIGNMENT)); } while (TryConsume(",")); DO(Consume("]")); @@ -1312,8 +1439,10 @@ bool Parser::ParseEnumConstantOptions( // ------------------------------------------------------------------- // Services -bool Parser::ParseServiceDefinition(ServiceDescriptorProto* service, - const LocationRecorder& service_location) { +bool Parser::ParseServiceDefinition( + ServiceDescriptorProto* service, + const LocationRecorder& service_location, + const FileDescriptorProto* containing_file) { DO(Consume("service")); { @@ -1324,12 +1453,13 @@ bool Parser::ParseServiceDefinition(ServiceDescriptorProto* service, DO(ConsumeIdentifier(service->mutable_name(), "Expected service name.")); } - DO(ParseServiceBlock(service, service_location)); + DO(ParseServiceBlock(service, service_location, containing_file)); return true; } bool Parser::ParseServiceBlock(ServiceDescriptorProto* service, - const LocationRecorder& service_location) { + const LocationRecorder& service_location, + const FileDescriptorProto* containing_file) { DO(ConsumeEndOfDeclaration("{", &service_location)); while (!TryConsumeEndOfDeclaration("}", NULL)) { @@ -1338,7 +1468,7 @@ bool Parser::ParseServiceBlock(ServiceDescriptorProto* service, return false; } - if (!ParseServiceStatement(service, service_location)) { + if (!ParseServiceStatement(service, service_location, containing_file)) { // This statement failed to parse. Skip it, but keep looping to parse // other statements. SkipStatement(); @@ -1349,23 +1479,26 @@ bool Parser::ParseServiceBlock(ServiceDescriptorProto* service, } bool Parser::ParseServiceStatement(ServiceDescriptorProto* service, - const LocationRecorder& service_location) { + const LocationRecorder& service_location, + const FileDescriptorProto* containing_file) { if (TryConsumeEndOfDeclaration(";", NULL)) { // empty statement; ignore return true; } else if (LookingAt("option")) { LocationRecorder location( service_location, ServiceDescriptorProto::kOptionsFieldNumber); - return ParseOption(service->mutable_options(), location, OPTION_STATEMENT); + return ParseOption(service->mutable_options(), location, + containing_file, OPTION_STATEMENT); } else { LocationRecorder location(service_location, ServiceDescriptorProto::kMethodFieldNumber, service->method_size()); - return ParseServiceMethod(service->add_method(), location); + return ParseServiceMethod(service->add_method(), location, containing_file); } } bool Parser::ParseServiceMethod(MethodDescriptorProto* method, - const LocationRecorder& method_location) { + const LocationRecorder& method_location, + const FileDescriptorProto* containing_file) { DO(Consume("rpc")); { @@ -1402,6 +1535,7 @@ bool Parser::ParseServiceMethod(MethodDescriptorProto* method, if (LookingAt("{")) { // Options! DO(ParseOptions(method_location, + containing_file, MethodDescriptorProto::kOptionsFieldNumber, method->mutable_options())); } else { @@ -1413,6 +1547,7 @@ bool Parser::ParseServiceMethod(MethodDescriptorProto* method, bool Parser::ParseOptions(const LocationRecorder& parent_location, + const FileDescriptorProto* containing_file, const int optionsFieldNumber, Message* mutable_options) { // Options! @@ -1428,7 +1563,8 @@ bool Parser::ParseOptions(const LocationRecorder& parent_location, } else { LocationRecorder location(parent_location, optionsFieldNumber); - if (!ParseOption(mutable_options, location, OPTION_STATEMENT)) { + if (!ParseOption(mutable_options, location, containing_file, + OPTION_STATEMENT)) { // This statement failed to parse. Skip it, but keep looping to // parse other statements. SkipStatement(); @@ -1441,7 +1577,8 @@ bool Parser::ParseOptions(const LocationRecorder& parent_location, // ------------------------------------------------------------------- -bool Parser::ParseLabel(FieldDescriptorProto::Label* label) { +bool Parser::ParseLabel(FieldDescriptorProto::Label* label, + const FileDescriptorProto* containing_file) { if (TryConsume("optional")) { *label = FieldDescriptorProto::LABEL_OPTIONAL; return true; @@ -1510,7 +1647,8 @@ bool Parser::ParseUserDefinedType(string* type_name) { // =================================================================== bool Parser::ParsePackage(FileDescriptorProto* file, - const LocationRecorder& root_location) { + const LocationRecorder& root_location, + const FileDescriptorProto* containing_file) { if (file->has_package()) { AddError("Multiple package definitions."); // Don't append the new package to the old one. Just replace it. Not @@ -1544,7 +1682,8 @@ bool Parser::ParsePackage(FileDescriptorProto* file, bool Parser::ParseImport(RepeatedPtrField<string>* dependency, RepeatedField<int32>* public_dependency, RepeatedField<int32>* weak_dependency, - const LocationRecorder& root_location) { + const LocationRecorder& root_location, + const FileDescriptorProto* containing_file) { DO(Consume("import")); if (LookingAt("public")) { LocationRecorder location( diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h index cfd3649b..6bca7fa7 100644 --- a/src/google/protobuf/compiler/parser.h +++ b/src/google/protobuf/compiler/parser.h @@ -40,7 +40,6 @@ #include <map> #include <string> #include <utility> -#include <google/protobuf/stubs/common.h> #include <google/protobuf/descriptor.h> #include <google/protobuf/descriptor.pb.h> #include <google/protobuf/repeated_field.h> @@ -233,6 +232,9 @@ class LIBPROTOBUF_EXPORT Parser { // location to the given token instead. void StartAt(const io::Tokenizer::Token& token); + // Start at the same location as some other LocationRecorder. + void StartAt(const LocationRecorder& other); + // By default the location is considered to end at the previous token at // the time the LocationRecorder is destroyed. EndAt() sets the end // location to the given token instead. @@ -284,38 +286,50 @@ class LIBPROTOBUF_EXPORT Parser { // Parse various language high-level language construrcts. bool ParseMessageDefinition(DescriptorProto* message, - const LocationRecorder& message_location); + const LocationRecorder& message_location, + const FileDescriptorProto* containing_file); bool ParseEnumDefinition(EnumDescriptorProto* enum_type, - const LocationRecorder& enum_location); + const LocationRecorder& enum_location, + const FileDescriptorProto* containing_file); bool ParseServiceDefinition(ServiceDescriptorProto* service, - const LocationRecorder& service_location); + const LocationRecorder& service_location, + const FileDescriptorProto* containing_file); bool ParsePackage(FileDescriptorProto* file, - const LocationRecorder& root_location); + const LocationRecorder& root_location, + const FileDescriptorProto* containing_file); bool ParseImport(RepeatedPtrField<string>* dependency, RepeatedField<int32>* public_dependency, RepeatedField<int32>* weak_dependency, - const LocationRecorder& root_location); + const LocationRecorder& root_location, + const FileDescriptorProto* containing_file); bool ParseOption(Message* options, - const LocationRecorder& options_location); + const LocationRecorder& options_location, + const FileDescriptorProto* containing_file); // These methods parse the contents of a message, enum, or service type and // add them to the given object. They consume the entire block including // the beginning and ending brace. bool ParseMessageBlock(DescriptorProto* message, - const LocationRecorder& message_location); + const LocationRecorder& message_location, + const FileDescriptorProto* containing_file); bool ParseEnumBlock(EnumDescriptorProto* enum_type, - const LocationRecorder& enum_location); + const LocationRecorder& enum_location, + const FileDescriptorProto* containing_file); bool ParseServiceBlock(ServiceDescriptorProto* service, - const LocationRecorder& service_location); + const LocationRecorder& service_location, + const FileDescriptorProto* containing_file); // Parse one statement within a message, enum, or service block, inclunding // final semicolon. bool ParseMessageStatement(DescriptorProto* message, - const LocationRecorder& message_location); + const LocationRecorder& message_location, + const FileDescriptorProto* containing_file); bool ParseEnumStatement(EnumDescriptorProto* message, - const LocationRecorder& enum_location); + const LocationRecorder& enum_location, + const FileDescriptorProto* containing_file); bool ParseServiceStatement(ServiceDescriptorProto* message, - const LocationRecorder& service_location); + const LocationRecorder& service_location, + const FileDescriptorProto* containing_file); // Parse a field of a message. If the field is a group, its type will be // added to "messages". @@ -329,11 +343,22 @@ class LIBPROTOBUF_EXPORT Parser { RepeatedPtrField<DescriptorProto>* messages, const LocationRecorder& parent_location, int location_field_number_for_nested_type, - const LocationRecorder& field_location); + const LocationRecorder& field_location, + const FileDescriptorProto* containing_file); + + // Like ParseMessageField() but expects the label has already been filled in + // by the caller. + bool ParseMessageFieldNoLabel(FieldDescriptorProto* field, + RepeatedPtrField<DescriptorProto>* messages, + const LocationRecorder& parent_location, + int location_field_number_for_nested_type, + const LocationRecorder& field_location, + const FileDescriptorProto* containing_file); // Parse an "extensions" declaration. bool ParseExtensions(DescriptorProto* message, - const LocationRecorder& extensions_location); + const LocationRecorder& extensions_location, + const FileDescriptorProto* containing_file); // Parse an "extend" declaration. (See also comments for // ParseMessageField().) @@ -341,30 +366,46 @@ class LIBPROTOBUF_EXPORT Parser { RepeatedPtrField<DescriptorProto>* messages, const LocationRecorder& parent_location, int location_field_number_for_nested_type, - const LocationRecorder& extend_location); + const LocationRecorder& extend_location, + const FileDescriptorProto* containing_file); + + // Parse a "oneof" declaration. The caller is responsible for setting + // oneof_decl->label() since it will have had to parse the label before it + // knew it was parsing a oneof. + bool ParseOneof(OneofDescriptorProto* oneof_decl, + DescriptorProto* containing_type, + int oneof_index, + const LocationRecorder& oneof_location, + const LocationRecorder& containing_type_location, + const FileDescriptorProto* containing_file); // Parse a single enum value within an enum block. bool ParseEnumConstant(EnumValueDescriptorProto* enum_value, - const LocationRecorder& enum_value_location); + const LocationRecorder& enum_value_location, + const FileDescriptorProto* containing_file); // Parse enum constant options, i.e. the list in square brackets at the end // of the enum constant value definition. bool ParseEnumConstantOptions(EnumValueDescriptorProto* value, - const LocationRecorder& enum_value_location); + const LocationRecorder& enum_value_location, + const FileDescriptorProto* containing_file); // Parse a single method within a service definition. bool ParseServiceMethod(MethodDescriptorProto* method, - const LocationRecorder& method_location); + const LocationRecorder& method_location, + const FileDescriptorProto* containing_file); // Parse options of a single method or stream. bool ParseOptions(const LocationRecorder& parent_location, + const FileDescriptorProto* containing_file, const int optionsFieldNumber, Message* mutable_options); // Parse "required", "optional", or "repeated" and fill in "label" // with the value. - bool ParseLabel(FieldDescriptorProto::Label* label); + bool ParseLabel(FieldDescriptorProto::Label* label, + const FileDescriptorProto* containing_file); // Parse a type name and fill in "type" (if it is a primitive) or // "type_name" (if it is not) with the type parsed. @@ -377,12 +418,14 @@ class LIBPROTOBUF_EXPORT Parser { // Parses field options, i.e. the stuff in square brackets at the end // of a field definition. Also parses default value. bool ParseFieldOptions(FieldDescriptorProto* field, - const LocationRecorder& field_location); + const LocationRecorder& field_location, + const FileDescriptorProto* containing_file); // Parse the "default" option. This needs special handling because its // type is the field's type. bool ParseDefaultAssignment(FieldDescriptorProto* field, - const LocationRecorder& field_location); + const LocationRecorder& field_location, + const FileDescriptorProto* containing_file); enum OptionStyle { OPTION_ASSIGNMENT, // just "name = value" @@ -394,6 +437,7 @@ class LIBPROTOBUF_EXPORT Parser { // is set to the parsed value. bool ParseOption(Message* options, const LocationRecorder& options_location, + const FileDescriptorProto* containing_file, OptionStyle style); // Parses a single part of a multipart option name. A multipart name consists @@ -401,7 +445,8 @@ class LIBPROTOBUF_EXPORT Parser { // of identifiers separated by dots and enclosed in parentheses. E.g., // "foo.(bar.baz).qux". bool ParseOptionNamePart(UninterpretedOption* uninterpreted_option, - const LocationRecorder& part_location); + const LocationRecorder& part_location, + const FileDescriptorProto* containing_file); // Parses a string surrounded by balanced braces. Strips off the outer // braces and stores the enclosed string in *value. diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc index c61ac60e..847370a8 100644 --- a/src/google/protobuf/compiler/parser_unittest.cc +++ b/src/google/protobuf/compiler/parser_unittest.cc @@ -32,6 +32,7 @@ // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. +#include <memory> #include <vector> #include <algorithm> #include <map> @@ -47,7 +48,7 @@ #include <google/protobuf/unittest_custom_options.pb.h> #include <google/protobuf/stubs/strutil.h> #include <google/protobuf/stubs/substitute.h> -#include <google/protobuf/stubs/map-util.h> +#include <google/protobuf/stubs/map_util.h> #include <google/protobuf/testing/googletest.h> #include <gtest/gtest.h> @@ -436,6 +437,69 @@ TEST_F(ParseMessageTest, FieldOptions) { "}"); } +TEST_F(ParseMessageTest, Oneof) { + ExpectParsesTo( + "message TestMessage {\n" + " oneof foo {\n" + " int32 a = 1;\n" + " string b = 2;\n" + " TestMessage c = 3;\n" + " group D = 4 { optional int32 i = 5; }\n" + " }\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"a\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 " + " oneof_index:0 }" + " field { name:\"b\" label:LABEL_OPTIONAL type:TYPE_STRING number:2 " + " oneof_index:0 }" + " field { name:\"c\" label:LABEL_OPTIONAL type_name:\"TestMessage\" " + " number:3 oneof_index:0 }" + " field { name:\"d\" label:LABEL_OPTIONAL type:TYPE_GROUP " + " type_name:\"D\" number:4 oneof_index:0 }" + " oneof_decl {" + " name: \"foo\"" + " }" + " nested_type {" + " name: \"D\"" + " field { name:\"i\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 }" + " }" + "}"); +} + +TEST_F(ParseMessageTest, MultipleOneofs) { + ExpectParsesTo( + "message TestMessage {\n" + " oneof foo {\n" + " int32 a = 1;\n" + " string b = 2;\n" + " }\n" + " oneof bar {\n" + " int32 c = 3;\n" + " string d = 4;\n" + " }\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"a\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 " + " oneof_index:0 }" + " field { name:\"b\" label:LABEL_OPTIONAL type:TYPE_STRING number:2 " + " oneof_index:0 }" + " field { name:\"c\" label:LABEL_OPTIONAL type:TYPE_INT32 number:3 " + " oneof_index:1 }" + " field { name:\"d\" label:LABEL_OPTIONAL type:TYPE_STRING number:4 " + " oneof_index:1 }" + " oneof_decl {" + " name: \"foo\"" + " }" + " oneof_decl {" + " name: \"bar\"" + " }" + "}"); +} + TEST_F(ParseMessageTest, Group) { ExpectParsesTo( "message TestMessage {\n" @@ -575,6 +639,7 @@ TEST_F(ParseMessageTest, MultipleExtensionsOneExtendee) { " type_name:\"TestMessage\" extendee: \"Extendee1\" }"); } + // =================================================================== typedef ParserTest ParseEnumTest; @@ -681,6 +746,8 @@ TEST_F(ParseServiceTest, MethodsAndStreams) { "}"); } + + // =================================================================== // imports and packages @@ -870,7 +937,7 @@ TEST_F(ParseErrorTest, DefaultValueTypeMismatch) { "message TestMessage {\n" " optional uint32 foo = 1 [default=true];\n" "}\n", - "1:35: Expected integer.\n"); + "1:35: Expected integer for field default value.\n"); } TEST_F(ParseErrorTest, DefaultValueNotBoolean) { @@ -886,7 +953,7 @@ TEST_F(ParseErrorTest, DefaultValueNotString) { "message TestMessage {\n" " optional string foo = 1 [default=1];\n" "}\n", - "1:35: Expected string.\n"); + "1:35: Expected string for field default value.\n"); } TEST_F(ParseErrorTest, DefaultValueUnsignedNegative) { @@ -934,7 +1001,7 @@ TEST_F(ParseErrorTest, DefaultValueMissing) { "message TestMessage {\n" " optional uint32 foo = 1 [default=];\n" "}\n", - "1:35: Expected integer.\n"); + "1:35: Expected integer for field default value.\n"); } TEST_F(ParseErrorTest, DefaultValueForGroup) { @@ -953,6 +1020,27 @@ TEST_F(ParseErrorTest, DuplicateDefaultValue) { "1:37: Already set option \"default\".\n"); } +TEST_F(ParseErrorTest, MissingOneofName) { + ExpectHasErrors( + "message TestMessage {\n" + " oneof {\n" + " int32 bar = 1;\n" + " }\n" + "}\n", + "1:8: Expected oneof name.\n"); +} + +TEST_F(ParseErrorTest, LabelInOneof) { + ExpectHasErrors( + "message TestMessage {\n" + " oneof foo {\n" + " optional int32 bar = 1;\n" + " }\n" + "}\n", + "2:4: Fields in oneofs must not have labels (required / optional " + "/ repeated).\n"); +} + TEST_F(ParseErrorTest, GroupNotCapitalized) { ExpectHasErrors( "message TestMessage {\n" @@ -1244,17 +1332,93 @@ TEST_F(ParserValidationErrorTest, MethodOutputTypeError) { } +TEST_F(ParserValidationErrorTest, ResovledUndefinedError) { + // Create another file which defines symbol ".base.bar". + FileDescriptorProto other_file; + other_file.set_name("base.proto"); + other_file.set_package("base"); + other_file.add_message_type()->set_name("bar"); + EXPECT_TRUE(pool_.BuildFile(other_file) != NULL); + + // Define "foo.base" and try "base.bar". + // "base.bar" is resolved to "foo.base.bar" which is not defined. + ExpectHasValidationErrors( + "package foo.base;\n" + "import \"base.proto\";\n" + "message qux {\n" + " optional base.bar baz = 1;\n" + " optional .base.bar quz = 2;\n" + "}\n", + "3:11: \"base.bar\" is resolved to \"foo.base.bar\"," + " which is not defined. The innermost scope is searched first " + "in name resolution. Consider using a leading '.'(i.e., \".base.bar\")" + " to start from the outermost scope.\n"); +} + +TEST_F(ParserValidationErrorTest, ResovledUndefinedOptionError) { + // Build descriptor message in test pool + FileDescriptorProto descriptor_proto; + DescriptorProto::descriptor()->file()->CopyTo(&descriptor_proto); + ASSERT_TRUE(pool_.BuildFile(descriptor_proto) != NULL); + + // base2.proto: + // package baz + // import google/protobuf/descriptor.proto + // message Bar { optional int32 foo = 1; } + // extend FileOptions { optional Bar bar = 7672757; } + FileDescriptorProto other_file; + other_file.set_name("base2.proto"); + other_file.set_package("baz"); + other_file.add_dependency(); + other_file.set_dependency(0, descriptor_proto.name()); + + DescriptorProto* message(other_file.add_message_type()); + message->set_name("Bar"); + FieldDescriptorProto* field(message->add_field()); + field->set_name("foo"); + field->set_number(1); + field->set_label(FieldDescriptorProto_Label_LABEL_OPTIONAL); + field->set_type(FieldDescriptorProto_Type_TYPE_INT32); + + FieldDescriptorProto* extension(other_file.add_extension()); + extension->set_name("bar"); + extension->set_number(7672757); + extension->set_label(FieldDescriptorProto_Label_LABEL_OPTIONAL); + extension->set_type(FieldDescriptorProto_Type_TYPE_MESSAGE); + extension->set_type_name("Bar"); + extension->set_extendee("google.protobuf.FileOptions"); + + EXPECT_TRUE(pool_.BuildFile(other_file) != NULL); + + // qux.proto: + // package qux.baz + // option (baz.bar).foo = 1; + // + // Although "baz.bar" is already defined, the lookup code will try + // "qux.baz.bar", since it's the match from the innermost scope, + // which will cause a symbol not defined error. + ExpectHasValidationErrors( + "package qux.baz;\n" + "import \"base2.proto\";\n" + "option (baz.bar).foo = 1;\n", + "2:7: Option \"(baz.bar)\" is resolved to \"(qux.baz.bar)\"," + " which is not defined. The innermost scope is searched first " + "in name resolution. Consider using a leading '.'(i.e., \"(.baz.bar)\")" + " to start from the outermost scope.\n"); +} + // =================================================================== // Test that the output from FileDescriptor::DebugString() (and all other // descriptor types) is parseable, and results in the same Descriptor -// definitions again afoter parsing (not, however, that the order of messages +// definitions again afoter parsing (note, however, that the order of messages // cannot be guaranteed to be the same) typedef ParserTest ParseDecriptorDebugTest; class CompareDescriptorNames { public: - bool operator()(const DescriptorProto* left, const DescriptorProto* right) { + bool operator()(const DescriptorProto* left, + const DescriptorProto* right) const { return left->name() < right->name(); } }; @@ -1298,7 +1462,8 @@ TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) { FileDescriptorProto parsed; parser_->Parse(input_.get(), &parsed); EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); - ASSERT_EQ("", error_collector_.text_); + ASSERT_EQ("", error_collector_.text_) + << "Failed to parse:\n" << debug_string; // We now have a FileDescriptorProto, but to compare with the expected we // need to link to a FileDecriptor, then output back to a proto. We'll @@ -1317,6 +1482,8 @@ TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) { ASSERT_TRUE(pool_.BuildFile(import_proto) != NULL); const FileDescriptor* actual = pool_.BuildFile(parsed); parsed.Clear(); + ASSERT_TRUE(actual != NULL) + << "Failed to validate:\n" << debug_string; actual->CopyTo(&parsed); ASSERT_TRUE(actual != NULL); @@ -1871,6 +2038,31 @@ TEST_F(SourceInfoTest, ExtensionRanges) { EXPECT_TRUE(HasSpan(file_.message_type(0), "name")); } +TEST_F(SourceInfoTest, Oneofs) { + EXPECT_TRUE(Parse( + "message Foo {\n" + " $a$oneof $c$foo$d$ {\n" + " $e$int32$f$ $g$a$h$ = $i$1$j$;$k$\n" + " }$r$\n" + "}\n")); + + const OneofDescriptorProto& oneof_decl = file_.message_type(0).oneof_decl(0); + const FieldDescriptorProto& field = file_.message_type(0).field(0); + + EXPECT_TRUE(HasSpan('a', 'r', oneof_decl)); + EXPECT_TRUE(HasSpan('c', 'd', oneof_decl, "name")); + + EXPECT_TRUE(HasSpan('e', 'k', field)); + EXPECT_TRUE(HasSpan('e', 'f', field, "type")); + EXPECT_TRUE(HasSpan('g', 'h', field, "name")); + EXPECT_TRUE(HasSpan('i', 'j', field, "number")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(file_.message_type(0))); + EXPECT_TRUE(HasSpan(file_.message_type(0), "name")); +} + TEST_F(SourceInfoTest, NestedMessages) { EXPECT_TRUE(Parse( "message Foo {\n" @@ -2029,6 +2221,7 @@ TEST_F(SourceInfoTest, MethodsAndStreams) { EXPECT_TRUE(HasSpan(file_.service(0), "name")); } + TEST_F(SourceInfoTest, Options) { EXPECT_TRUE(Parse( "$a$option $b$foo$c$.$d$($e$bar.baz$f$)$g$ = " @@ -2365,6 +2558,51 @@ TEST_F(SourceInfoTest, DocComments3) { bar.options().uninterpreted_option(0), "aggregate_value")); } +TEST_F(SourceInfoTest, DocCommentsOneof) { + EXPECT_TRUE(Parse( + "// ignored\n" + "syntax = \"proto2\";\n" + "// Foo leading\n" + "$a$message Foo {\n" + " /* Foo trailing\n" + " */\n" + " // ignored\n" + " /* bar leading\n" + " * line 2 */\n" + " $b$oneof bar {\n" + " /* bar trailing\n" + " * line 2 */\n" + " // ignored\n" + " /* bar_int leading\n" + " */\n" + " $c$int32 bar_int = 1;$d$ // bar_int trailing\n" + " // ignored\n" + " }$e$\n" + "}$f$\n")); + + const DescriptorProto& foo = file_.message_type(0); + const OneofDescriptorProto& bar = foo.oneof_decl(0); + const FieldDescriptorProto& bar_int = foo.field(0); + + EXPECT_TRUE(HasSpanWithComment('a', 'f', foo, + " Foo leading\n", + " Foo trailing\n")); + EXPECT_TRUE(HasSpanWithComment('b', 'e', bar, + " bar leading\n line 2 ", + " bar trailing\n line 2 ")); + EXPECT_TRUE(HasSpanWithComment('c', 'd', bar_int, + " bar_int leading\n", + " bar_int trailing\n")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(foo, "name")); + EXPECT_TRUE(HasSpan(bar, "name")); + EXPECT_TRUE(HasSpan(bar_int, "type")); + EXPECT_TRUE(HasSpan(bar_int, "name")); + EXPECT_TRUE(HasSpan(bar_int, "number")); +} + // =================================================================== } // anonymous namespace diff --git a/src/google/protobuf/compiler/plugin.pb.cc b/src/google/protobuf/compiler/plugin.pb.cc index 6525d15c..b5cd01b5 100644 --- a/src/google/protobuf/compiler/plugin.pb.cc +++ b/src/google/protobuf/compiler/plugin.pb.cc @@ -171,6 +171,7 @@ const int CodeGeneratorRequest::kProtoFileFieldNumber; CodeGeneratorRequest::CodeGeneratorRequest() : ::google::protobuf::Message() { SharedCtor(); + // @@protoc_insertion_point(constructor:google.protobuf.compiler.CodeGeneratorRequest) } void CodeGeneratorRequest::InitAsDefaultInstance() { @@ -180,20 +181,23 @@ CodeGeneratorRequest::CodeGeneratorRequest(const CodeGeneratorRequest& from) : ::google::protobuf::Message() { SharedCtor(); MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:google.protobuf.compiler.CodeGeneratorRequest) } void CodeGeneratorRequest::SharedCtor() { + ::google::protobuf::internal::GetEmptyString(); _cached_size_ = 0; - parameter_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + parameter_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); ::memset(_has_bits_, 0, sizeof(_has_bits_)); } CodeGeneratorRequest::~CodeGeneratorRequest() { + // @@protoc_insertion_point(destructor:google.protobuf.compiler.CodeGeneratorRequest) SharedDtor(); } void CodeGeneratorRequest::SharedDtor() { - if (parameter_ != &::google::protobuf::internal::GetEmptyString()) { + if (parameter_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { delete parameter_; } if (this != default_instance_) { @@ -222,11 +226,9 @@ CodeGeneratorRequest* CodeGeneratorRequest::New() const { } void CodeGeneratorRequest::Clear() { - if (_has_bits_[1 / 32] & (0xffu << (1 % 32))) { - if (has_parameter()) { - if (parameter_ != &::google::protobuf::internal::GetEmptyString()) { - parameter_->clear(); - } + if (has_parameter()) { + if (parameter_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { + parameter_->clear(); } } file_to_generate_.Clear(); @@ -237,23 +239,27 @@ void CodeGeneratorRequest::Clear() { bool CodeGeneratorRequest::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) return false +#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; - while ((tag = input->ReadTag()) != 0) { + // @@protoc_insertion_point(parse_start:google.protobuf.compiler.CodeGeneratorRequest) + 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)) { // repeated string file_to_generate = 1; case 1: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + if (tag == 10) { parse_file_to_generate: DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->add_file_to_generate())); - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->file_to_generate(this->file_to_generate_size() - 1).data(), this->file_to_generate(this->file_to_generate_size() - 1).length(), - ::google::protobuf::internal::WireFormat::PARSE); + ::google::protobuf::internal::WireFormat::PARSE, + "file_to_generate"); } else { - goto handle_uninterpreted; + goto handle_unusual; } if (input->ExpectTag(10)) goto parse_file_to_generate; if (input->ExpectTag(18)) goto parse_parameter; @@ -262,16 +268,16 @@ bool CodeGeneratorRequest::MergePartialFromCodedStream( // optional string parameter = 2; case 2: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + if (tag == 18) { parse_parameter: DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->mutable_parameter())); - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->parameter().data(), this->parameter().length(), - ::google::protobuf::internal::WireFormat::PARSE); + ::google::protobuf::internal::WireFormat::PARSE, + "parameter"); } else { - goto handle_uninterpreted; + goto handle_unusual; } if (input->ExpectTag(122)) goto parse_proto_file; break; @@ -279,24 +285,24 @@ bool CodeGeneratorRequest::MergePartialFromCodedStream( // repeated .google.protobuf.FileDescriptorProto proto_file = 15; case 15: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + if (tag == 122) { parse_proto_file: DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( input, add_proto_file())); } else { - goto handle_uninterpreted; + goto handle_unusual; } if (input->ExpectTag(122)) goto parse_proto_file; - if (input->ExpectAtEnd()) return true; + if (input->ExpectAtEnd()) goto success; break; } default: { - handle_uninterpreted: - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { - return true; + goto success; } DO_(::google::protobuf::internal::WireFormat::SkipField( input, tag, mutable_unknown_fields())); @@ -304,27 +310,35 @@ bool CodeGeneratorRequest::MergePartialFromCodedStream( } } } +success: + // @@protoc_insertion_point(parse_success:google.protobuf.compiler.CodeGeneratorRequest) return true; +failure: + // @@protoc_insertion_point(parse_failure:google.protobuf.compiler.CodeGeneratorRequest) + return false; #undef DO_ } void CodeGeneratorRequest::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:google.protobuf.compiler.CodeGeneratorRequest) // repeated string file_to_generate = 1; for (int i = 0; i < this->file_to_generate_size(); i++) { - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->file_to_generate(i).data(), this->file_to_generate(i).length(), - ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormat::SERIALIZE, + "file_to_generate"); ::google::protobuf::internal::WireFormatLite::WriteString( 1, this->file_to_generate(i), output); } // optional string parameter = 2; if (has_parameter()) { - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->parameter().data(), this->parameter().length(), - ::google::protobuf::internal::WireFormat::SERIALIZE); - ::google::protobuf::internal::WireFormatLite::WriteString( + ::google::protobuf::internal::WireFormat::SERIALIZE, + "parameter"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( 2, this->parameter(), output); } @@ -338,24 +352,28 @@ void CodeGeneratorRequest::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormat::SerializeUnknownFields( unknown_fields(), output); } + // @@protoc_insertion_point(serialize_end:google.protobuf.compiler.CodeGeneratorRequest) } ::google::protobuf::uint8* CodeGeneratorRequest::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.compiler.CodeGeneratorRequest) // repeated string file_to_generate = 1; for (int i = 0; i < this->file_to_generate_size(); i++) { - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->file_to_generate(i).data(), this->file_to_generate(i).length(), - ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormat::SERIALIZE, + "file_to_generate"); target = ::google::protobuf::internal::WireFormatLite:: WriteStringToArray(1, this->file_to_generate(i), target); } // optional string parameter = 2; if (has_parameter()) { - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->parameter().data(), this->parameter().length(), - ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormat::SERIALIZE, + "parameter"); target = ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 2, this->parameter(), target); @@ -372,6 +390,7 @@ void CodeGeneratorRequest::SerializeWithCachedSizes( target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( unknown_fields(), target); } + // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.compiler.CodeGeneratorRequest) return target; } @@ -451,9 +470,7 @@ void CodeGeneratorRequest::CopyFrom(const CodeGeneratorRequest& from) { bool CodeGeneratorRequest::IsInitialized() const { - for (int i = 0; i < proto_file_size(); i++) { - if (!this->proto_file(i).IsInitialized()) return false; - } + if (!::google::protobuf::internal::AllAreInitialized(this->proto_file())) return false; return true; } @@ -488,6 +505,7 @@ const int CodeGeneratorResponse_File::kContentFieldNumber; CodeGeneratorResponse_File::CodeGeneratorResponse_File() : ::google::protobuf::Message() { SharedCtor(); + // @@protoc_insertion_point(constructor:google.protobuf.compiler.CodeGeneratorResponse.File) } void CodeGeneratorResponse_File::InitAsDefaultInstance() { @@ -497,28 +515,31 @@ CodeGeneratorResponse_File::CodeGeneratorResponse_File(const CodeGeneratorRespon : ::google::protobuf::Message() { SharedCtor(); MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:google.protobuf.compiler.CodeGeneratorResponse.File) } void CodeGeneratorResponse_File::SharedCtor() { + ::google::protobuf::internal::GetEmptyString(); _cached_size_ = 0; - name_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); - insertion_point_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); - content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + insertion_point_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); ::memset(_has_bits_, 0, sizeof(_has_bits_)); } CodeGeneratorResponse_File::~CodeGeneratorResponse_File() { + // @@protoc_insertion_point(destructor:google.protobuf.compiler.CodeGeneratorResponse.File) SharedDtor(); } void CodeGeneratorResponse_File::SharedDtor() { - if (name_ != &::google::protobuf::internal::GetEmptyString()) { + if (name_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { delete name_; } - if (insertion_point_ != &::google::protobuf::internal::GetEmptyString()) { + if (insertion_point_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { delete insertion_point_; } - if (content_ != &::google::protobuf::internal::GetEmptyString()) { + if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { delete content_; } if (this != default_instance_) { @@ -547,19 +568,19 @@ CodeGeneratorResponse_File* CodeGeneratorResponse_File::New() const { } void CodeGeneratorResponse_File::Clear() { - if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bits_[0 / 32] & 7) { if (has_name()) { - if (name_ != &::google::protobuf::internal::GetEmptyString()) { + if (name_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { name_->clear(); } } if (has_insertion_point()) { - if (insertion_point_ != &::google::protobuf::internal::GetEmptyString()) { + if (insertion_point_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { insertion_point_->clear(); } } if (has_content()) { - if (content_ != &::google::protobuf::internal::GetEmptyString()) { + if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { content_->clear(); } } @@ -570,21 +591,25 @@ void CodeGeneratorResponse_File::Clear() { bool CodeGeneratorResponse_File::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) return false +#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; - while ((tag = input->ReadTag()) != 0) { + // @@protoc_insertion_point(parse_start:google.protobuf.compiler.CodeGeneratorResponse.File) + 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 string name = 1; case 1: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + if (tag == 10) { DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->mutable_name())); - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->name().data(), this->name().length(), - ::google::protobuf::internal::WireFormat::PARSE); + ::google::protobuf::internal::WireFormat::PARSE, + "name"); } else { - goto handle_uninterpreted; + goto handle_unusual; } if (input->ExpectTag(18)) goto parse_insertion_point; break; @@ -592,16 +617,16 @@ bool CodeGeneratorResponse_File::MergePartialFromCodedStream( // optional string insertion_point = 2; case 2: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + if (tag == 18) { parse_insertion_point: DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->mutable_insertion_point())); - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->insertion_point().data(), this->insertion_point().length(), - ::google::protobuf::internal::WireFormat::PARSE); + ::google::protobuf::internal::WireFormat::PARSE, + "insertion_point"); } else { - goto handle_uninterpreted; + goto handle_unusual; } if (input->ExpectTag(122)) goto parse_content; break; @@ -609,26 +634,27 @@ bool CodeGeneratorResponse_File::MergePartialFromCodedStream( // optional string content = 15; case 15: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + if (tag == 122) { parse_content: DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->mutable_content())); - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->content().data(), this->content().length(), - ::google::protobuf::internal::WireFormat::PARSE); + ::google::protobuf::internal::WireFormat::PARSE, + "content"); } else { - goto handle_uninterpreted; + goto handle_unusual; } - if (input->ExpectAtEnd()) return true; + if (input->ExpectAtEnd()) goto success; break; } default: { - handle_uninterpreted: - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { - return true; + goto success; } DO_(::google::protobuf::internal::WireFormat::SkipField( input, tag, mutable_unknown_fields())); @@ -636,36 +662,45 @@ bool CodeGeneratorResponse_File::MergePartialFromCodedStream( } } } +success: + // @@protoc_insertion_point(parse_success:google.protobuf.compiler.CodeGeneratorResponse.File) return true; +failure: + // @@protoc_insertion_point(parse_failure:google.protobuf.compiler.CodeGeneratorResponse.File) + return false; #undef DO_ } void CodeGeneratorResponse_File::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:google.protobuf.compiler.CodeGeneratorResponse.File) // optional string name = 1; if (has_name()) { - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->name().data(), this->name().length(), - ::google::protobuf::internal::WireFormat::SERIALIZE); - ::google::protobuf::internal::WireFormatLite::WriteString( + ::google::protobuf::internal::WireFormat::SERIALIZE, + "name"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( 1, this->name(), output); } // optional string insertion_point = 2; if (has_insertion_point()) { - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->insertion_point().data(), this->insertion_point().length(), - ::google::protobuf::internal::WireFormat::SERIALIZE); - ::google::protobuf::internal::WireFormatLite::WriteString( + ::google::protobuf::internal::WireFormat::SERIALIZE, + "insertion_point"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( 2, this->insertion_point(), output); } // optional string content = 15; if (has_content()) { - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->content().data(), this->content().length(), - ::google::protobuf::internal::WireFormat::SERIALIZE); - ::google::protobuf::internal::WireFormatLite::WriteString( + ::google::protobuf::internal::WireFormat::SERIALIZE, + "content"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( 15, this->content(), output); } @@ -673,15 +708,18 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormat::SerializeUnknownFields( unknown_fields(), output); } + // @@protoc_insertion_point(serialize_end:google.protobuf.compiler.CodeGeneratorResponse.File) } ::google::protobuf::uint8* CodeGeneratorResponse_File::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.compiler.CodeGeneratorResponse.File) // optional string name = 1; if (has_name()) { - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->name().data(), this->name().length(), - ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormat::SERIALIZE, + "name"); target = ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 1, this->name(), target); @@ -689,9 +727,10 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( // optional string insertion_point = 2; if (has_insertion_point()) { - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->insertion_point().data(), this->insertion_point().length(), - ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormat::SERIALIZE, + "insertion_point"); target = ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 2, this->insertion_point(), target); @@ -699,9 +738,10 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( // optional string content = 15; if (has_content()) { - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->content().data(), this->content().length(), - ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormat::SERIALIZE, + "content"); target = ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 15, this->content(), target); @@ -711,6 +751,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( unknown_fields(), target); } + // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.compiler.CodeGeneratorResponse.File) return target; } @@ -826,6 +867,7 @@ const int CodeGeneratorResponse::kFileFieldNumber; CodeGeneratorResponse::CodeGeneratorResponse() : ::google::protobuf::Message() { SharedCtor(); + // @@protoc_insertion_point(constructor:google.protobuf.compiler.CodeGeneratorResponse) } void CodeGeneratorResponse::InitAsDefaultInstance() { @@ -835,20 +877,23 @@ CodeGeneratorResponse::CodeGeneratorResponse(const CodeGeneratorResponse& from) : ::google::protobuf::Message() { SharedCtor(); MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:google.protobuf.compiler.CodeGeneratorResponse) } void CodeGeneratorResponse::SharedCtor() { + ::google::protobuf::internal::GetEmptyString(); _cached_size_ = 0; - error_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + error_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); ::memset(_has_bits_, 0, sizeof(_has_bits_)); } CodeGeneratorResponse::~CodeGeneratorResponse() { + // @@protoc_insertion_point(destructor:google.protobuf.compiler.CodeGeneratorResponse) SharedDtor(); } void CodeGeneratorResponse::SharedDtor() { - if (error_ != &::google::protobuf::internal::GetEmptyString()) { + if (error_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { delete error_; } if (this != default_instance_) { @@ -877,11 +922,9 @@ CodeGeneratorResponse* CodeGeneratorResponse::New() const { } void CodeGeneratorResponse::Clear() { - if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (has_error()) { - if (error_ != &::google::protobuf::internal::GetEmptyString()) { - error_->clear(); - } + if (has_error()) { + if (error_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { + error_->clear(); } } file_.Clear(); @@ -891,21 +934,25 @@ void CodeGeneratorResponse::Clear() { bool CodeGeneratorResponse::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) return false +#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; - while ((tag = input->ReadTag()) != 0) { + // @@protoc_insertion_point(parse_start:google.protobuf.compiler.CodeGeneratorResponse) + 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 string error = 1; case 1: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + if (tag == 10) { DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->mutable_error())); - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->error().data(), this->error().length(), - ::google::protobuf::internal::WireFormat::PARSE); + ::google::protobuf::internal::WireFormat::PARSE, + "error"); } else { - goto handle_uninterpreted; + goto handle_unusual; } if (input->ExpectTag(122)) goto parse_file; break; @@ -913,24 +960,24 @@ bool CodeGeneratorResponse::MergePartialFromCodedStream( // repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15; case 15: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + if (tag == 122) { parse_file: DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( input, add_file())); } else { - goto handle_uninterpreted; + goto handle_unusual; } if (input->ExpectTag(122)) goto parse_file; - if (input->ExpectAtEnd()) return true; + if (input->ExpectAtEnd()) goto success; break; } default: { - handle_uninterpreted: - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { - return true; + goto success; } DO_(::google::protobuf::internal::WireFormat::SkipField( input, tag, mutable_unknown_fields())); @@ -938,18 +985,25 @@ bool CodeGeneratorResponse::MergePartialFromCodedStream( } } } +success: + // @@protoc_insertion_point(parse_success:google.protobuf.compiler.CodeGeneratorResponse) return true; +failure: + // @@protoc_insertion_point(parse_failure:google.protobuf.compiler.CodeGeneratorResponse) + return false; #undef DO_ } void CodeGeneratorResponse::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:google.protobuf.compiler.CodeGeneratorResponse) // optional string error = 1; if (has_error()) { - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->error().data(), this->error().length(), - ::google::protobuf::internal::WireFormat::SERIALIZE); - ::google::protobuf::internal::WireFormatLite::WriteString( + ::google::protobuf::internal::WireFormat::SERIALIZE, + "error"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( 1, this->error(), output); } @@ -963,15 +1017,18 @@ void CodeGeneratorResponse::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormat::SerializeUnknownFields( unknown_fields(), output); } + // @@protoc_insertion_point(serialize_end:google.protobuf.compiler.CodeGeneratorResponse) } ::google::protobuf::uint8* CodeGeneratorResponse::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.compiler.CodeGeneratorResponse) // optional string error = 1; if (has_error()) { - ::google::protobuf::internal::WireFormat::VerifyUTF8String( + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( this->error().data(), this->error().length(), - ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormat::SERIALIZE, + "error"); target = ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 1, this->error(), target); @@ -988,6 +1045,7 @@ void CodeGeneratorResponse::SerializeWithCachedSizes( target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( unknown_fields(), target); } + // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.compiler.CodeGeneratorResponse) return target; } diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h index d26a135e..b590d8df 100644 --- a/src/google/protobuf/compiler/plugin.pb.h +++ b/src/google/protobuf/compiler/plugin.pb.h @@ -89,7 +89,6 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message void SharedDtor(); void SetCachedSize(int size) const; public: - ::google::protobuf::Metadata GetMetadata() const; // nested types ---------------------------------------------------- @@ -143,13 +142,11 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message ::google::protobuf::UnknownFieldSet _unknown_fields_; + ::google::protobuf::uint32 _has_bits_[1]; + mutable int _cached_size_; ::google::protobuf::RepeatedPtrField< ::std::string> file_to_generate_; ::std::string* parameter_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto > proto_file_; - - mutable int _cached_size_; - ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; - friend void LIBPROTOC_EXPORT protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); friend void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); friend void protobuf_ShutdownFile_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); @@ -206,7 +203,6 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M void SharedDtor(); void SetCachedSize(int size) const; public: - ::google::protobuf::Metadata GetMetadata() const; // nested types ---------------------------------------------------- @@ -260,13 +256,11 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M ::google::protobuf::UnknownFieldSet _unknown_fields_; + ::google::protobuf::uint32 _has_bits_[1]; + mutable int _cached_size_; ::std::string* name_; ::std::string* insertion_point_; ::std::string* content_; - - mutable int _cached_size_; - ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; - friend void LIBPROTOC_EXPORT protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); friend void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); friend void protobuf_ShutdownFile_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); @@ -323,7 +317,6 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag void SharedDtor(); void SetCachedSize(int size) const; public: - ::google::protobuf::Metadata GetMetadata() const; // nested types ---------------------------------------------------- @@ -363,12 +356,10 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag ::google::protobuf::UnknownFieldSet _unknown_fields_; + ::google::protobuf::uint32 _has_bits_[1]; + mutable int _cached_size_; ::std::string* error_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File > file_; - - mutable int _cached_size_; - ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32]; - friend void LIBPROTOC_EXPORT protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); friend void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); friend void protobuf_ShutdownFile_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); @@ -391,39 +382,49 @@ inline void CodeGeneratorRequest::clear_file_to_generate() { file_to_generate_.Clear(); } inline const ::std::string& CodeGeneratorRequest::file_to_generate(int index) const { + // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) return file_to_generate_.Get(index); } inline ::std::string* CodeGeneratorRequest::mutable_file_to_generate(int index) { + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) return file_to_generate_.Mutable(index); } inline void CodeGeneratorRequest::set_file_to_generate(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) file_to_generate_.Mutable(index)->assign(value); } inline void CodeGeneratorRequest::set_file_to_generate(int index, const char* value) { file_to_generate_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) } inline void CodeGeneratorRequest::set_file_to_generate(int index, const char* value, size_t size) { file_to_generate_.Mutable(index)->assign( reinterpret_cast<const char*>(value), size); + // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) } inline ::std::string* CodeGeneratorRequest::add_file_to_generate() { return file_to_generate_.Add(); } inline void CodeGeneratorRequest::add_file_to_generate(const ::std::string& value) { file_to_generate_.Add()->assign(value); + // @@protoc_insertion_point(field_add:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) } inline void CodeGeneratorRequest::add_file_to_generate(const char* value) { file_to_generate_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) } inline void CodeGeneratorRequest::add_file_to_generate(const char* value, size_t size) { file_to_generate_.Add()->assign(reinterpret_cast<const char*>(value), size); + // @@protoc_insertion_point(field_add_pointer:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) } inline const ::google::protobuf::RepeatedPtrField< ::std::string>& CodeGeneratorRequest::file_to_generate() const { + // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) return file_to_generate_; } inline ::google::protobuf::RepeatedPtrField< ::std::string>* CodeGeneratorRequest::mutable_file_to_generate() { + // @@protoc_insertion_point(field_mutable_list:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) return &file_to_generate_; } @@ -438,54 +439,59 @@ inline void CodeGeneratorRequest::clear_has_parameter() { _has_bits_[0] &= ~0x00000002u; } inline void CodeGeneratorRequest::clear_parameter() { - if (parameter_ != &::google::protobuf::internal::GetEmptyString()) { + if (parameter_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { parameter_->clear(); } clear_has_parameter(); } inline const ::std::string& CodeGeneratorRequest::parameter() const { + // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorRequest.parameter) return *parameter_; } inline void CodeGeneratorRequest::set_parameter(const ::std::string& value) { set_has_parameter(); - if (parameter_ == &::google::protobuf::internal::GetEmptyString()) { + if (parameter_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { parameter_ = new ::std::string; } parameter_->assign(value); + // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorRequest.parameter) } inline void CodeGeneratorRequest::set_parameter(const char* value) { set_has_parameter(); - if (parameter_ == &::google::protobuf::internal::GetEmptyString()) { + if (parameter_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { parameter_ = new ::std::string; } parameter_->assign(value); + // @@protoc_insertion_point(field_set_char:google.protobuf.compiler.CodeGeneratorRequest.parameter) } inline void CodeGeneratorRequest::set_parameter(const char* value, size_t size) { set_has_parameter(); - if (parameter_ == &::google::protobuf::internal::GetEmptyString()) { + if (parameter_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { parameter_ = new ::std::string; } parameter_->assign(reinterpret_cast<const char*>(value), size); + // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorRequest.parameter) } inline ::std::string* CodeGeneratorRequest::mutable_parameter() { set_has_parameter(); - if (parameter_ == &::google::protobuf::internal::GetEmptyString()) { + if (parameter_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { parameter_ = new ::std::string; } + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorRequest.parameter) return parameter_; } inline ::std::string* CodeGeneratorRequest::release_parameter() { clear_has_parameter(); - if (parameter_ == &::google::protobuf::internal::GetEmptyString()) { + if (parameter_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { return NULL; } else { ::std::string* temp = parameter_; - parameter_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + parameter_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); return temp; } } inline void CodeGeneratorRequest::set_allocated_parameter(::std::string* parameter) { - if (parameter_ != &::google::protobuf::internal::GetEmptyString()) { + if (parameter_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { delete parameter_; } if (parameter) { @@ -493,8 +499,9 @@ inline void CodeGeneratorRequest::set_allocated_parameter(::std::string* paramet parameter_ = parameter; } else { clear_has_parameter(); - parameter_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + parameter_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } + // @@protoc_insertion_point(field_set_allocated:google.protobuf.compiler.CodeGeneratorRequest.parameter) } // repeated .google.protobuf.FileDescriptorProto proto_file = 15; @@ -505,20 +512,25 @@ inline void CodeGeneratorRequest::clear_proto_file() { proto_file_.Clear(); } inline const ::google::protobuf::FileDescriptorProto& CodeGeneratorRequest::proto_file(int index) const { + // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorRequest.proto_file) return proto_file_.Get(index); } inline ::google::protobuf::FileDescriptorProto* CodeGeneratorRequest::mutable_proto_file(int index) { + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorRequest.proto_file) return proto_file_.Mutable(index); } inline ::google::protobuf::FileDescriptorProto* CodeGeneratorRequest::add_proto_file() { + // @@protoc_insertion_point(field_add:google.protobuf.compiler.CodeGeneratorRequest.proto_file) return proto_file_.Add(); } inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& CodeGeneratorRequest::proto_file() const { + // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorRequest.proto_file) return proto_file_; } inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >* CodeGeneratorRequest::mutable_proto_file() { + // @@protoc_insertion_point(field_mutable_list:google.protobuf.compiler.CodeGeneratorRequest.proto_file) return &proto_file_; } @@ -537,54 +549,59 @@ inline void CodeGeneratorResponse_File::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } inline void CodeGeneratorResponse_File::clear_name() { - if (name_ != &::google::protobuf::internal::GetEmptyString()) { + if (name_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { name_->clear(); } clear_has_name(); } inline const ::std::string& CodeGeneratorResponse_File::name() const { + // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorResponse.File.name) return *name_; } inline void CodeGeneratorResponse_File::set_name(const ::std::string& value) { set_has_name(); - if (name_ == &::google::protobuf::internal::GetEmptyString()) { + if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { name_ = new ::std::string; } name_->assign(value); + // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorResponse.File.name) } inline void CodeGeneratorResponse_File::set_name(const char* value) { set_has_name(); - if (name_ == &::google::protobuf::internal::GetEmptyString()) { + if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { name_ = new ::std::string; } name_->assign(value); + // @@protoc_insertion_point(field_set_char:google.protobuf.compiler.CodeGeneratorResponse.File.name) } inline void CodeGeneratorResponse_File::set_name(const char* value, size_t size) { set_has_name(); - if (name_ == &::google::protobuf::internal::GetEmptyString()) { + if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { name_ = new ::std::string; } name_->assign(reinterpret_cast<const char*>(value), size); + // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorResponse.File.name) } inline ::std::string* CodeGeneratorResponse_File::mutable_name() { set_has_name(); - if (name_ == &::google::protobuf::internal::GetEmptyString()) { + if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { name_ = new ::std::string; } + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.File.name) return name_; } inline ::std::string* CodeGeneratorResponse_File::release_name() { clear_has_name(); - if (name_ == &::google::protobuf::internal::GetEmptyString()) { + if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { return NULL; } else { ::std::string* temp = name_; - name_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); return temp; } } inline void CodeGeneratorResponse_File::set_allocated_name(::std::string* name) { - if (name_ != &::google::protobuf::internal::GetEmptyString()) { + if (name_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { delete name_; } if (name) { @@ -592,8 +609,9 @@ inline void CodeGeneratorResponse_File::set_allocated_name(::std::string* name) name_ = name; } else { clear_has_name(); - name_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } + // @@protoc_insertion_point(field_set_allocated:google.protobuf.compiler.CodeGeneratorResponse.File.name) } // optional string insertion_point = 2; @@ -607,54 +625,59 @@ inline void CodeGeneratorResponse_File::clear_has_insertion_point() { _has_bits_[0] &= ~0x00000002u; } inline void CodeGeneratorResponse_File::clear_insertion_point() { - if (insertion_point_ != &::google::protobuf::internal::GetEmptyString()) { + if (insertion_point_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { insertion_point_->clear(); } clear_has_insertion_point(); } inline const ::std::string& CodeGeneratorResponse_File::insertion_point() const { + // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point) return *insertion_point_; } inline void CodeGeneratorResponse_File::set_insertion_point(const ::std::string& value) { set_has_insertion_point(); - if (insertion_point_ == &::google::protobuf::internal::GetEmptyString()) { + if (insertion_point_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { insertion_point_ = new ::std::string; } insertion_point_->assign(value); + // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point) } inline void CodeGeneratorResponse_File::set_insertion_point(const char* value) { set_has_insertion_point(); - if (insertion_point_ == &::google::protobuf::internal::GetEmptyString()) { + if (insertion_point_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { insertion_point_ = new ::std::string; } insertion_point_->assign(value); + // @@protoc_insertion_point(field_set_char:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point) } inline void CodeGeneratorResponse_File::set_insertion_point(const char* value, size_t size) { set_has_insertion_point(); - if (insertion_point_ == &::google::protobuf::internal::GetEmptyString()) { + if (insertion_point_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { insertion_point_ = new ::std::string; } insertion_point_->assign(reinterpret_cast<const char*>(value), size); + // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point) } inline ::std::string* CodeGeneratorResponse_File::mutable_insertion_point() { set_has_insertion_point(); - if (insertion_point_ == &::google::protobuf::internal::GetEmptyString()) { + if (insertion_point_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { insertion_point_ = new ::std::string; } + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point) return insertion_point_; } inline ::std::string* CodeGeneratorResponse_File::release_insertion_point() { clear_has_insertion_point(); - if (insertion_point_ == &::google::protobuf::internal::GetEmptyString()) { + if (insertion_point_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { return NULL; } else { ::std::string* temp = insertion_point_; - insertion_point_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + insertion_point_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); return temp; } } inline void CodeGeneratorResponse_File::set_allocated_insertion_point(::std::string* insertion_point) { - if (insertion_point_ != &::google::protobuf::internal::GetEmptyString()) { + if (insertion_point_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { delete insertion_point_; } if (insertion_point) { @@ -662,8 +685,9 @@ inline void CodeGeneratorResponse_File::set_allocated_insertion_point(::std::str insertion_point_ = insertion_point; } else { clear_has_insertion_point(); - insertion_point_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + insertion_point_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } + // @@protoc_insertion_point(field_set_allocated:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point) } // optional string content = 15; @@ -677,54 +701,59 @@ inline void CodeGeneratorResponse_File::clear_has_content() { _has_bits_[0] &= ~0x00000004u; } inline void CodeGeneratorResponse_File::clear_content() { - if (content_ != &::google::protobuf::internal::GetEmptyString()) { + if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { content_->clear(); } clear_has_content(); } inline const ::std::string& CodeGeneratorResponse_File::content() const { + // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorResponse.File.content) return *content_; } inline void CodeGeneratorResponse_File::set_content(const ::std::string& value) { set_has_content(); - if (content_ == &::google::protobuf::internal::GetEmptyString()) { + if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { content_ = new ::std::string; } content_->assign(value); + // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorResponse.File.content) } inline void CodeGeneratorResponse_File::set_content(const char* value) { set_has_content(); - if (content_ == &::google::protobuf::internal::GetEmptyString()) { + if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { content_ = new ::std::string; } content_->assign(value); + // @@protoc_insertion_point(field_set_char:google.protobuf.compiler.CodeGeneratorResponse.File.content) } inline void CodeGeneratorResponse_File::set_content(const char* value, size_t size) { set_has_content(); - if (content_ == &::google::protobuf::internal::GetEmptyString()) { + if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { content_ = new ::std::string; } content_->assign(reinterpret_cast<const char*>(value), size); + // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorResponse.File.content) } inline ::std::string* CodeGeneratorResponse_File::mutable_content() { set_has_content(); - if (content_ == &::google::protobuf::internal::GetEmptyString()) { + if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { content_ = new ::std::string; } + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.File.content) return content_; } inline ::std::string* CodeGeneratorResponse_File::release_content() { clear_has_content(); - if (content_ == &::google::protobuf::internal::GetEmptyString()) { + if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { return NULL; } else { ::std::string* temp = content_; - content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); return temp; } } inline void CodeGeneratorResponse_File::set_allocated_content(::std::string* content) { - if (content_ != &::google::protobuf::internal::GetEmptyString()) { + if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { delete content_; } if (content) { @@ -732,8 +761,9 @@ inline void CodeGeneratorResponse_File::set_allocated_content(::std::string* con content_ = content; } else { clear_has_content(); - content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } + // @@protoc_insertion_point(field_set_allocated:google.protobuf.compiler.CodeGeneratorResponse.File.content) } // ------------------------------------------------------------------- @@ -751,54 +781,59 @@ inline void CodeGeneratorResponse::clear_has_error() { _has_bits_[0] &= ~0x00000001u; } inline void CodeGeneratorResponse::clear_error() { - if (error_ != &::google::protobuf::internal::GetEmptyString()) { + if (error_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { error_->clear(); } clear_has_error(); } inline const ::std::string& CodeGeneratorResponse::error() const { + // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorResponse.error) return *error_; } inline void CodeGeneratorResponse::set_error(const ::std::string& value) { set_has_error(); - if (error_ == &::google::protobuf::internal::GetEmptyString()) { + if (error_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { error_ = new ::std::string; } error_->assign(value); + // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorResponse.error) } inline void CodeGeneratorResponse::set_error(const char* value) { set_has_error(); - if (error_ == &::google::protobuf::internal::GetEmptyString()) { + if (error_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { error_ = new ::std::string; } error_->assign(value); + // @@protoc_insertion_point(field_set_char:google.protobuf.compiler.CodeGeneratorResponse.error) } inline void CodeGeneratorResponse::set_error(const char* value, size_t size) { set_has_error(); - if (error_ == &::google::protobuf::internal::GetEmptyString()) { + if (error_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { error_ = new ::std::string; } error_->assign(reinterpret_cast<const char*>(value), size); + // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorResponse.error) } inline ::std::string* CodeGeneratorResponse::mutable_error() { set_has_error(); - if (error_ == &::google::protobuf::internal::GetEmptyString()) { + if (error_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { error_ = new ::std::string; } + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.error) return error_; } inline ::std::string* CodeGeneratorResponse::release_error() { clear_has_error(); - if (error_ == &::google::protobuf::internal::GetEmptyString()) { + if (error_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { return NULL; } else { ::std::string* temp = error_; - error_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + error_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); return temp; } } inline void CodeGeneratorResponse::set_allocated_error(::std::string* error) { - if (error_ != &::google::protobuf::internal::GetEmptyString()) { + if (error_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { delete error_; } if (error) { @@ -806,8 +841,9 @@ inline void CodeGeneratorResponse::set_allocated_error(::std::string* error) { error_ = error; } else { clear_has_error(); - error_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); + error_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } + // @@protoc_insertion_point(field_set_allocated:google.protobuf.compiler.CodeGeneratorResponse.error) } // repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15; @@ -818,20 +854,25 @@ inline void CodeGeneratorResponse::clear_file() { file_.Clear(); } inline const ::google::protobuf::compiler::CodeGeneratorResponse_File& CodeGeneratorResponse::file(int index) const { + // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorResponse.file) return file_.Get(index); } inline ::google::protobuf::compiler::CodeGeneratorResponse_File* CodeGeneratorResponse::mutable_file(int index) { + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.file) return file_.Mutable(index); } inline ::google::protobuf::compiler::CodeGeneratorResponse_File* CodeGeneratorResponse::add_file() { + // @@protoc_insertion_point(field_add:google.protobuf.compiler.CodeGeneratorResponse.file) return file_.Add(); } inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >& CodeGeneratorResponse::file() const { + // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorResponse.file) return file_; } inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >* CodeGeneratorResponse::mutable_file() { + // @@protoc_insertion_point(field_mutable_list:google.protobuf.compiler.CodeGeneratorResponse.file) return &file_; } diff --git a/src/google/protobuf/compiler/python/python_generator.cc b/src/google/protobuf/compiler/python/python_generator.cc index 211ac70c..067d856b 100644 --- a/src/google/protobuf/compiler/python/python_generator.cc +++ b/src/google/protobuf/compiler/python/python_generator.cc @@ -28,6 +28,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//#PY25 compatible generated code for GAE. +// Copyright 2007 Google Inc. All Rights Reserved. // Author: robinson@google.com (Will Robinson) // // This module outputs pure-Python protocol message classes that will @@ -45,6 +47,7 @@ #include <limits> #include <map> #include <utility> +#include <memory> #include <string> #include <vector> @@ -128,6 +131,7 @@ void PrintTopBoilerplate( printer->Print( "# Generated by the protocol buffer compiler. DO NOT EDIT!\n" "# source: $filename$\n" + "\nimport sys\n_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))" //##PY25 "\n", "filename", file->name()); if (HasTopLevelEnums(file)) { @@ -138,7 +142,8 @@ void PrintTopBoilerplate( "from google.protobuf import descriptor as _descriptor\n" "from google.protobuf import message as _message\n" "from google.protobuf import reflection as _reflection\n" - ); + "from google.protobuf import symbol_database as " + "_symbol_database\n"); if (HasGenericServices(file)) { printer->Print( "from google.protobuf import service as _service\n" @@ -151,7 +156,8 @@ void PrintTopBoilerplate( "from google.protobuf import descriptor_pb2\n"); } printer->Print( - "# @@protoc_insertion_point(imports)\n"); + "# @@protoc_insertion_point(imports)\n\n" + "_sym_db = _symbol_database.Default()\n"); printer->Print("\n\n"); } @@ -217,14 +223,14 @@ string StringifyDefaultValue(const FieldDescriptor& field) { case FieldDescriptor::CPPTYPE_ENUM: return SimpleItoa(field.default_value_enum()->number()); case FieldDescriptor::CPPTYPE_STRING: - if (field.type() == FieldDescriptor::TYPE_STRING) { - return "unicode(\"" + CEscape(field.default_value_string()) + - "\", \"utf-8\")"; - } else { - return "\"" + CEscape(field.default_value_string()) + "\""; - } - case FieldDescriptor::CPPTYPE_MESSAGE: - return "None"; +//##!PY25 return "b\"" + CEscape(field.default_value_string()) + +//##!PY25 (field.type() != FieldDescriptor::TYPE_STRING ? "\"" : +//##!PY25 "\".decode('utf-8')"); + return "_b(\"" + CEscape(field.default_value_string()) + //##PY25 + (field.type() != FieldDescriptor::TYPE_STRING ? "\")" : //##PY25 + "\").decode('utf-8')"); //##PY25 + case FieldDescriptor::CPPTYPE_MESSAGE: + return "None"; } // (We could add a default case above but then we wouldn't get the nice // compiler warning when a new type is added.) @@ -330,14 +336,25 @@ void Generator::PrintFileDescriptor() const { printer_->Print(m, file_descriptor_template); printer_->Indent(); printer_->Print( - "serialized_pb='$value$'", +//##!PY25 "serialized_pb=b'$value$'\n", + "serialized_pb=_b('$value$')\n", //##PY25 "value", strings::CHexEscape(file_descriptor_serialized_)); + if (file_->dependency_count() != 0) { + printer_->Print(",\ndependencies=["); + for (int i = 0; i < file_->dependency_count(); ++i) { + string module_name = ModuleName(file_->dependency(i)->name()); + printer_->Print("$module_name$.DESCRIPTOR,", "module_name", module_name); + } + printer_->Print("]"); + } // TODO(falk): Also print options and fix the message_type, enum_type, // service and extension later in the generation. printer_->Outdent(); printer_->Print(")\n"); + printer_->Print("_sym_db.RegisterFileDescriptor($name$)\n", "name", + kDescriptorKey); printer_->Print("\n"); } @@ -382,7 +399,9 @@ void Generator::PrintAllNestedEnumsInFile() const { // enum_descriptor. void Generator::PrintEnum(const EnumDescriptor& enum_descriptor) const { map<string, string> m; - m["descriptor_name"] = ModuleLevelDescriptorName(enum_descriptor); + string module_level_descriptor_name = + ModuleLevelDescriptorName(enum_descriptor); + m["descriptor_name"] = module_level_descriptor_name; m["name"] = enum_descriptor.name(); m["full_name"] = enum_descriptor.full_name(); m["file"] = kDescriptorKey; @@ -412,6 +431,8 @@ void Generator::PrintEnum(const EnumDescriptor& enum_descriptor) const { PrintSerializedPbInterval(enum_descriptor, edp); printer_->Outdent(); printer_->Print(")\n"); + printer_->Print("_sym_db.RegisterEnumDescriptor($name$)\n", "name", + module_level_descriptor_name); printer_->Print("\n"); } @@ -522,27 +543,36 @@ void Generator::PrintServiceDescriptor( void Generator::PrintServiceClass(const ServiceDescriptor& descriptor) const { // Print the service. - printer_->Print("class $class_name$(_service.Service):\n", + printer_->Print("$class_name$ = service_reflection.GeneratedServiceType(" + "'$class_name$', (_service.Service,), dict(\n", "class_name", descriptor.name()); printer_->Indent(); printer_->Print( - "__metaclass__ = service_reflection.GeneratedServiceType\n" - "$descriptor_key$ = $descriptor_name$\n", + "$descriptor_key$ = $descriptor_name$,\n", "descriptor_key", kDescriptorKey, "descriptor_name", ModuleLevelServiceDescriptorName(descriptor)); + printer_->Print( + "__module__ = '$module_name$'\n", + "module_name", ModuleName(file_->name())); + printer_->Print("))\n\n"); printer_->Outdent(); } void Generator::PrintServiceStub(const ServiceDescriptor& descriptor) const { // Print the service stub. - printer_->Print("class $class_name$_Stub($class_name$):\n", + printer_->Print("$class_name$_Stub = " + "service_reflection.GeneratedServiceStubType(" + "'$class_name$_Stub', ($class_name$,), dict(\n", "class_name", descriptor.name()); printer_->Indent(); printer_->Print( - "__metaclass__ = service_reflection.GeneratedServiceStubType\n" - "$descriptor_key$ = $descriptor_name$\n", + "$descriptor_key$ = $descriptor_name$,\n", "descriptor_key", kDescriptorKey, "descriptor_name", ModuleLevelServiceDescriptorName(descriptor)); + printer_->Print( + "__module__ = '$module_name$'\n", + "module_name", ModuleName(file_->name())); + printer_->Print("))\n\n"); printer_->Outdent(); } @@ -635,7 +665,12 @@ void Generator::PrintNestedDescriptors( // Prints all messages in |file|. void Generator::PrintMessages() const { for (int i = 0; i < file_->message_type_count(); ++i) { - PrintMessage(*file_->message_type(i)); + vector<string> to_register; + PrintMessage(*file_->message_type(i), "", &to_register); + for (int j = 0; j < to_register.size(); ++j) { + printer_->Print("_sym_db.RegisterMessage($name$)\n", "name", + to_register[j]); + } printer_->Print("\n"); } } @@ -647,33 +682,40 @@ void Generator::PrintMessages() const { // reflection.py will use to construct the meat of the class itself. // // Mutually recursive with PrintNestedMessages(). -void Generator::PrintMessage( - const Descriptor& message_descriptor) const { - printer_->Print("class $name$(_message.Message):\n", "name", - message_descriptor.name()); +// Collect nested message names to_register for the symbol_database. +void Generator::PrintMessage(const Descriptor& message_descriptor, + const string& prefix, + vector<string>* to_register) const { + string qualified_name(prefix + message_descriptor.name()); + to_register->push_back(qualified_name); + printer_->Print( + "$name$ = _reflection.GeneratedProtocolMessageType('$name$', " + "(_message.Message,), dict(\n", + "name", message_descriptor.name()); printer_->Indent(); - printer_->Print("__metaclass__ = _reflection.GeneratedProtocolMessageType\n"); - PrintNestedMessages(message_descriptor); + + PrintNestedMessages(message_descriptor, qualified_name + ".", to_register); map<string, string> m; m["descriptor_key"] = kDescriptorKey; m["descriptor_name"] = ModuleLevelDescriptorName(message_descriptor); - printer_->Print(m, "$descriptor_key$ = $descriptor_name$\n"); - - printer_->Print( - "\n" - "# @@protoc_insertion_point(class_scope:$full_name$)\n", - "full_name", message_descriptor.full_name()); - + printer_->Print(m, "$descriptor_key$ = $descriptor_name$,\n"); + printer_->Print("__module__ = '$module_name$'\n", + "module_name", ModuleName(file_->name())); + printer_->Print("# @@protoc_insertion_point(class_scope:$full_name$)\n", + "full_name", message_descriptor.full_name()); + printer_->Print("))\n"); printer_->Outdent(); } // Prints all nested messages within |containing_descriptor|. // Mutually recursive with PrintMessage(). -void Generator::PrintNestedMessages( - const Descriptor& containing_descriptor) const { +void Generator::PrintNestedMessages(const Descriptor& containing_descriptor, + const string& prefix, + vector<string>* to_register) const { for (int i = 0; i < containing_descriptor.nested_type_count(); ++i) { printer_->Print("\n"); - PrintMessage(*containing_descriptor.nested_type(i)); + PrintMessage(*containing_descriptor.nested_type(i), prefix, to_register); + printer_->Print(",\n"); } } @@ -714,6 +756,29 @@ void Generator::AddMessageToFileDescriptor(const Descriptor& descriptor) const { printer_->Print(m, file_descriptor_template); } +void Generator::AddEnumToFileDescriptor( + const EnumDescriptor& descriptor) const { + map<string, string> m; + m["descriptor_name"] = kDescriptorKey; + m["enum_name"] = descriptor.name(); + m["enum_descriptor_name"] = ModuleLevelDescriptorName(descriptor); + const char file_descriptor_template[] = + "$descriptor_name$.enum_types_by_name['$enum_name$'] = " + "$enum_descriptor_name$\n"; + printer_->Print(m, file_descriptor_template); +} + +void Generator::AddExtensionToFileDescriptor( + const FieldDescriptor& descriptor) const { + map<string, string> m; + m["descriptor_name"] = kDescriptorKey; + m["field_name"] = descriptor.name(); + const char file_descriptor_template[] = + "$descriptor_name$.extensions_by_name['$field_name$'] = " + "$field_name$\n"; + printer_->Print(m, file_descriptor_template); +} + // Sets any necessary message_type and enum_type attributes // for the Python version of |field|. // @@ -778,7 +843,7 @@ void Generator::FixContainingTypeInDescriptor( const string parent_name = ModuleLevelDescriptorName( *containing_descriptor); printer_->Print( - "$nested_name$.containing_type = $parent_name$;\n", + "$nested_name$.containing_type = $parent_name$\n", "nested_name", nested_name, "parent_name", parent_name); } @@ -795,6 +860,12 @@ void Generator::FixForeignFieldsInDescriptors() const { for (int i = 0; i < file_->message_type_count(); ++i) { AddMessageToFileDescriptor(*file_->message_type(i)); } + for (int i = 0; i < file_->enum_type_count(); ++i) { + AddEnumToFileDescriptor(*file_->enum_type(i)); + } + for (int i = 0; i < file_->extension_count(); ++i) { + AddExtensionToFileDescriptor(*file_->extension(i)); + } printer_->Print("\n"); } @@ -875,8 +946,10 @@ string Generator::OptionsValue( return "None"; } else { string full_class_name = "descriptor_pb2." + class_name; - return "_descriptor._ParseOptions(" + full_class_name + "(), '" - + CEscape(serialized_options)+ "')"; +//##!PY25 return "_descriptor._ParseOptions(" + full_class_name + "(), b'" +//##!PY25 + CEscape(serialized_options)+ "')"; + return "_descriptor._ParseOptions(" + full_class_name + "(), _b('" //##PY25 + + CEscape(serialized_options)+ "'))"; //##PY25 } } diff --git a/src/google/protobuf/compiler/python/python_generator.h b/src/google/protobuf/compiler/python/python_generator.h index a3f22cee..40dfddf3 100644 --- a/src/google/protobuf/compiler/python/python_generator.h +++ b/src/google/protobuf/compiler/python/python_generator.h @@ -94,8 +94,11 @@ class LIBPROTOC_EXPORT Generator : public CodeGenerator { void PrintNestedDescriptors(const Descriptor& containing_descriptor) const; void PrintMessages() const; - void PrintMessage(const Descriptor& message_descriptor) const; - void PrintNestedMessages(const Descriptor& containing_descriptor) const; + void PrintMessage(const Descriptor& message_descriptor, const string& prefix, + vector<string>* to_register) const; + void PrintNestedMessages(const Descriptor& containing_descriptor, + const string& prefix, + vector<string>* to_register) const; void FixForeignFieldsInDescriptors() const; void FixForeignFieldsInDescriptor( @@ -105,6 +108,8 @@ class LIBPROTOC_EXPORT Generator : public CodeGenerator { const FieldDescriptor& field, const string& python_dict_name) const; void AddMessageToFileDescriptor(const Descriptor& descriptor) const; + void AddEnumToFileDescriptor(const EnumDescriptor& descriptor) const; + void AddExtensionToFileDescriptor(const FieldDescriptor& descriptor) const; string FieldReferencingExpression(const Descriptor* containing_type, const FieldDescriptor& field, const string& python_dict_name) const; diff --git a/src/google/protobuf/compiler/python/python_plugin_unittest.cc b/src/google/protobuf/compiler/python/python_plugin_unittest.cc index da619ad3..77da8a87 100644 --- a/src/google/protobuf/compiler/python/python_plugin_unittest.cc +++ b/src/google/protobuf/compiler/python/python_plugin_unittest.cc @@ -34,6 +34,8 @@ // It seemed like parameterizing it would add more complexity than it is // worth. +#include <memory> + #include <google/protobuf/compiler/python/python_generator.h> #include <google/protobuf/compiler/command_line_interface.h> #include <google/protobuf/io/zero_copy_stream.h> @@ -68,7 +70,7 @@ class TestGenerator : public CodeGenerator { void TryInsert(const string& filename, const string& insertion_point, GeneratorContext* context) const { scoped_ptr<io::ZeroCopyOutputStream> output( - context->OpenForInsert(filename, insertion_point)); + context->OpenForInsert(filename, insertion_point)); io::Printer printer(output.get(), '$'); printer.Print("// inserted $name$\n", "name", insertion_point); } @@ -78,13 +80,13 @@ class TestGenerator : public CodeGenerator { // not verify that they are correctly-placed; that would require actually // compiling the output which is a bit more than I care to do for this test. TEST(PythonPluginTest, PluginTest) { - File::WriteStringToFileOrDie( - "syntax = \"proto2\";\n" - "package foo;\n" - "message Bar {\n" - " message Baz {}\n" - "}\n", - TestTempDir() + "/test.proto"); + GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/test.proto", + "syntax = \"proto2\";\n" + "package foo;\n" + "message Bar {\n" + " message Baz {}\n" + "}\n", + true)); google::protobuf::compiler::CommandLineInterface cli; cli.SetInputsAreProtoPathRelative(true); diff --git a/src/google/protobuf/compiler/subprocess.cc b/src/google/protobuf/compiler/subprocess.cc index 67da120d..ca4b874d 100644 --- a/src/google/protobuf/compiler/subprocess.cc +++ b/src/google/protobuf/compiler/subprocess.cc @@ -449,7 +449,7 @@ bool Subprocess::Communicate(const Message& input, Message* output, } if (!output->ParseFromString(output_data)) { - *error = "Plugin output is unparseable."; + *error = "Plugin output is unparseable: " + CEscape(output_data); return false; } |