diff options
author | xiaofeng@google.com <xiaofeng@google.com@630680e5-0e50-0410-840e-4b1c322b438d> | 2012-09-22 02:40:50 +0000 |
---|---|---|
committer | xiaofeng@google.com <xiaofeng@google.com@630680e5-0e50-0410-840e-4b1c322b438d> | 2012-09-22 02:40:50 +0000 |
commit | b55a20fa2c669b181f47ea9219b8e74d1263da19 (patch) | |
tree | 3936a0e7c22196587a6d8397372de41434fe2129 /src/google/protobuf/compiler | |
parent | 9ced30caf94bb4e7e9629c199679ff44e8ca7389 (diff) | |
download | protobuf-b55a20fa2c669b181f47ea9219b8e74d1263da19.tar.gz protobuf-b55a20fa2c669b181f47ea9219b8e74d1263da19.tar.bz2 protobuf-b55a20fa2c669b181f47ea9219b8e74d1263da19.zip |
Down-integrate from internal branch
Diffstat (limited to 'src/google/protobuf/compiler')
66 files changed, 3306 insertions, 1046 deletions
diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index 1c76994b..8dac5f42 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -59,12 +59,13 @@ #include <google/protobuf/descriptor.h> #include <google/protobuf/text_format.h> #include <google/protobuf/dynamic_message.h> +#include <google/protobuf/io/coded_stream.h> #include <google/protobuf/io/zero_copy_stream_impl.h> #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/stl_util-inl.h> +#include <google/protobuf/stubs/stl_util.h> namespace google { @@ -145,7 +146,7 @@ void AddTrailingSlash(string* path) { bool VerifyDirectoryExists(const string& path) { if (path.empty()) return true; - if (access(path.c_str(), W_OK) == -1) { + if (access(path.c_str(), F_OK) == -1) { cerr << path << ": " << strerror(errno) << endl; return false; } else { @@ -566,6 +567,7 @@ CommandLineInterface::CommandLineInterface() : mode_(MODE_COMPILE), error_format_(ERROR_FORMAT_GCC), imports_in_descriptor_set_(false), + source_info_in_descriptor_set_(false), disallow_services_(false), inputs_are_proto_path_relative_(false) {} CommandLineInterface::~CommandLineInterface() {} @@ -574,9 +576,23 @@ void CommandLineInterface::RegisterGenerator(const string& flag_name, CodeGenerator* generator, const string& help_text) { GeneratorInfo info; + info.flag_name = flag_name; info.generator = generator; info.help_text = help_text; - generators_[flag_name] = info; + generators_by_flag_name_[flag_name] = info; +} + +void CommandLineInterface::RegisterGenerator(const string& flag_name, + const string& option_flag_name, + CodeGenerator* generator, + const string& help_text) { + GeneratorInfo info; + info.flag_name = flag_name; + info.option_flag_name = option_flag_name; + info.generator = generator; + info.help_text = help_text; + generators_by_flag_name_[flag_name] = info; + generators_by_option_name_[option_flag_name] = info; } void CommandLineInterface::AllowPlugins(const string& exe_name_prefix) { @@ -585,7 +601,14 @@ void CommandLineInterface::AllowPlugins(const string& exe_name_prefix) { int CommandLineInterface::Run(int argc, const char* const argv[]) { Clear(); - if (!ParseArguments(argc, argv)) return 1; + switch (ParseArguments(argc, argv)) { + case PARSE_ARGUMENT_DONE_AND_EXIT: + return 0; + case PARSE_ARGUMENT_FAIL: + return 1; + case PARSE_ARGUMENT_DONE_AND_CONTINUE: + break; + } // Set up the source tree. DiskSourceTree source_tree; @@ -713,6 +736,7 @@ void CommandLineInterface::Clear() { mode_ = MODE_COMPILE; imports_in_descriptor_set_ = false; + source_info_in_descriptor_set_ = false; disallow_services_ = false; } @@ -755,7 +779,8 @@ bool CommandLineInterface::MakeInputsBeProtoPathRelative( return true; } -bool CommandLineInterface::ParseArguments(int argc, const char* const argv[]) { +CommandLineInterface::ParseArgumentStatus +CommandLineInterface::ParseArguments(int argc, const char* const argv[]) { executable_name_ = argv[0]; // Iterate through all arguments and parse them. @@ -769,41 +794,50 @@ bool CommandLineInterface::ParseArguments(int argc, const char* const argv[]) { if (name == "--decode") { cerr << "To decode an unknown message, use --decode_raw." << endl; } - return false; + return PARSE_ARGUMENT_FAIL; } else { ++i; value = argv[i]; } } - if (!InterpretArgument(name, value)) return false; + ParseArgumentStatus status = InterpretArgument(name, value); + if (status != PARSE_ARGUMENT_DONE_AND_CONTINUE) + return status; } // If no --proto_path was given, use the current working directory. if (proto_path_.empty()) { - proto_path_.push_back(make_pair<string, string>("", ".")); + // Don't use make_pair as the old/default standard library on Solaris + // doesn't support it without explicit template parameters, which are + // incompatible with C++0x's make_pair. + proto_path_.push_back(pair<string, string>("", ".")); } // Check some errror cases. bool decoding_raw = (mode_ == MODE_DECODE) && codec_type_.empty(); if (decoding_raw && !input_files_.empty()) { cerr << "When using --decode_raw, no input files should be given." << endl; - return false; + return PARSE_ARGUMENT_FAIL; } else if (!decoding_raw && input_files_.empty()) { cerr << "Missing input file." << endl; - return false; + return PARSE_ARGUMENT_FAIL; } if (mode_ == MODE_COMPILE && output_directives_.empty() && descriptor_set_name_.empty()) { cerr << "Missing output directives." << endl; - return false; + return PARSE_ARGUMENT_FAIL; } if (imports_in_descriptor_set_ && descriptor_set_name_.empty()) { cerr << "--include_imports only makes sense when combined with " "--descriptor_set_out." << endl; } + if (source_info_in_descriptor_set_ && descriptor_set_name_.empty()) { + cerr << "--include_source_info only makes sense when combined with " + "--descriptor_set_out." << endl; + } - return true; + return PARSE_ARGUMENT_DONE_AND_CONTINUE; } bool CommandLineInterface::ParseArgument(const char* arg, @@ -853,6 +887,7 @@ bool CommandLineInterface::ParseArgument(const char* arg, if (*name == "-h" || *name == "--help" || *name == "--disallow_services" || *name == "--include_imports" || + *name == "--include_source_info" || *name == "--version" || *name == "--decode_raw") { // HACK: These are the only flags that don't take a value. @@ -865,8 +900,9 @@ bool CommandLineInterface::ParseArgument(const char* arg, return true; } -bool CommandLineInterface::InterpretArgument(const string& name, - const string& value) { +CommandLineInterface::ParseArgumentStatus +CommandLineInterface::InterpretArgument(const string& name, + const string& value) { if (name.empty()) { // Not a flag. Just a filename. if (value.empty()) { @@ -874,7 +910,7 @@ bool CommandLineInterface::InterpretArgument(const string& name, "arguments to " << executable_name_ << ". This is actually " "sort of hard to do. Congrats. Unfortunately it is not valid " "input so the program is going to die now." << endl; - return false; + return PARSE_ARGUMENT_FAIL; } input_files_.push_back(value); @@ -902,7 +938,7 @@ bool CommandLineInterface::InterpretArgument(const string& name, if (disk_path.empty()) { cerr << "--proto_path passed empty directory name. (Use \".\" for " "current directory.)" << endl; - return false; + return PARSE_ARGUMENT_FAIL; } // Make sure disk path exists, warn otherwise. @@ -910,35 +946,45 @@ bool CommandLineInterface::InterpretArgument(const string& name, cerr << disk_path << ": warning: directory does not exist." << endl; } - proto_path_.push_back(make_pair<string, string>(virtual_path, disk_path)); + // Don't use make_pair as the old/default standard library on Solaris + // doesn't support it without explicit template parameters, which are + // incompatible with C++0x's make_pair. + proto_path_.push_back(pair<string, string>(virtual_path, disk_path)); } } else if (name == "-o" || name == "--descriptor_set_out") { if (!descriptor_set_name_.empty()) { cerr << name << " may only be passed once." << endl; - return false; + return PARSE_ARGUMENT_FAIL; } if (value.empty()) { cerr << name << " requires a non-empty value." << endl; - return false; + return PARSE_ARGUMENT_FAIL; } if (mode_ != MODE_COMPILE) { cerr << "Cannot use --encode or --decode and generate descriptors at the " "same time." << endl; - return false; + return PARSE_ARGUMENT_FAIL; } descriptor_set_name_ = value; } else if (name == "--include_imports") { if (imports_in_descriptor_set_) { cerr << name << " may only be passed once." << endl; - return false; + return PARSE_ARGUMENT_FAIL; } imports_in_descriptor_set_ = true; + } else if (name == "--include_source_info") { + if (source_info_in_descriptor_set_) { + cerr << name << " may only be passed once." << endl; + return PARSE_ARGUMENT_FAIL; + } + source_info_in_descriptor_set_ = true; + } else if (name == "-h" || name == "--help") { PrintHelpText(); - return false; // Exit without running compiler. + return PARSE_ARGUMENT_DONE_AND_EXIT; // Exit without running compiler. } else if (name == "--version") { if (!version_info_.empty()) { @@ -947,7 +993,7 @@ bool CommandLineInterface::InterpretArgument(const string& name, cout << "libprotoc " << protobuf::internal::VersionString(GOOGLE_PROTOBUF_VERSION) << endl; - return false; // Exit without running compiler. + return PARSE_ARGUMENT_DONE_AND_EXIT; // Exit without running compiler. } else if (name == "--disallow_services") { disallow_services_ = true; @@ -956,12 +1002,12 @@ bool CommandLineInterface::InterpretArgument(const string& name, name == "--decode_raw") { if (mode_ != MODE_COMPILE) { cerr << "Only one of --encode and --decode can be specified." << endl; - return false; + 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 false; + return PARSE_ARGUMENT_FAIL; } mode_ = (name == "--encode") ? MODE_ENCODE : MODE_DECODE; @@ -971,10 +1017,10 @@ bool CommandLineInterface::InterpretArgument(const string& name, if (name == "--decode") { cerr << "To decode an unknown message, use --decode_raw." << endl; } - return false; + return PARSE_ARGUMENT_FAIL; } else if (!value.empty() && name == "--decode_raw") { cerr << "--decode_raw does not take a parameter." << endl; - return false; + return PARSE_ARGUMENT_FAIL; } codec_type_ = value; @@ -986,16 +1032,16 @@ bool CommandLineInterface::InterpretArgument(const string& name, error_format_ = ERROR_FORMAT_MSVS; } else { cerr << "Unknown error format: " << value << endl; - return false; + return PARSE_ARGUMENT_FAIL; } } else if (name == "--plugin") { if (plugin_prefix_.empty()) { cerr << "This compiler does not support plugins." << endl; - return false; + return PARSE_ARGUMENT_FAIL; } - string name; + string plugin_name; string path; string::size_type equals_pos = value.find_first_of('='); @@ -1003,57 +1049,68 @@ bool CommandLineInterface::InterpretArgument(const string& name, // Use the basename of the file. string::size_type slash_pos = value.find_last_of('/'); if (slash_pos == string::npos) { - name = value; + plugin_name = value; } else { - name = value.substr(slash_pos + 1); + plugin_name = value.substr(slash_pos + 1); } path = value; } else { - name = value.substr(0, equals_pos); + plugin_name = value.substr(0, equals_pos); path = value.substr(equals_pos + 1); } - plugins_[name] = path; + plugins_[plugin_name] = path; } else { // Some other flag. Look it up in the generators list. - const GeneratorInfo* generator_info = FindOrNull(generators_, name); + const GeneratorInfo* generator_info = + FindOrNull(generators_by_flag_name_, name); if (generator_info == NULL && (plugin_prefix_.empty() || !HasSuffixString(name, "_out"))) { - cerr << "Unknown flag: " << name << endl; - return false; - } + // Check if it's a generator option flag. + generator_info = FindOrNull(generators_by_option_name_, name); + if (generator_info == NULL) { + cerr << "Unknown flag: " << name << endl; + return PARSE_ARGUMENT_FAIL; + } else { + string* parameters = &generator_parameters_[generator_info->flag_name]; + if (!parameters->empty()) { + parameters->append(","); + } + parameters->append(value); + } + } 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; + return PARSE_ARGUMENT_FAIL; + } - // 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; - return false; - } + OutputDirective directive; + directive.name = name; + if (generator_info == NULL) { + directive.generator = NULL; + } else { + directive.generator = generator_info->generator; + } - OutputDirective directive; - directive.name = name; - if (generator_info == NULL) { - directive.generator = NULL; - } else { - directive.generator = generator_info->generator; - } + // Split value at ':' to separate the generator parameter from the + // filename. However, avoid doing this if the colon is part of a valid + // Windows-style absolute path. + string::size_type colon_pos = value.find_first_of(':'); + if (colon_pos == string::npos || IsWindowsAbsolutePath(value)) { + directive.output_location = value; + } else { + directive.parameter = value.substr(0, colon_pos); + directive.output_location = value.substr(colon_pos + 1); + } - // Split value at ':' to separate the generator parameter from the - // filename. However, avoid doing this if the colon is part of a valid - // Windows-style absolute path. - string::size_type colon_pos = value.find_first_of(':'); - if (colon_pos == string::npos || IsWindowsAbsolutePath(value)) { - directive.output_location = value; - } else { - directive.parameter = value.substr(0, colon_pos); - directive.output_location = value.substr(colon_pos + 1); + output_directives_.push_back(directive); } - - output_directives_.push_back(directive); } - return true; + return PARSE_ARGUMENT_DONE_AND_CONTINUE; } void CommandLineInterface::PrintHelpText() { @@ -1086,6 +1143,12 @@ void CommandLineInterface::PrintHelpText() { " --include_imports When using --descriptor_set_out, also include\n" " all dependencies of the input files in the\n" " set, so that the set is self-contained.\n" +" --include_source_info When using --descriptor_set_out, do not strip\n" +" SourceCodeInfo from the FileDescriptorProto.\n" +" This results in vastly larger descriptors that\n" +" include information about the original\n" +" location of each decl in the source file as\n" +" 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; @@ -1101,8 +1164,8 @@ void CommandLineInterface::PrintHelpText() { " the executable's own name differs." << endl; } - for (GeneratorMap::iterator iter = generators_.begin(); - iter != generators_.end(); ++iter) { + for (GeneratorMap::iterator iter = generators_by_flag_name_.begin(); + iter != generators_by_flag_name_.end(); ++iter) { // FIXME(kenton): If the text is long enough it will wrap, which is ugly, // but fixing this nicely (e.g. splitting on spaces) is probably more // trouble than it's worth. @@ -1136,10 +1199,16 @@ bool CommandLineInterface::GenerateOutput( } } else { // Regular generator. + string parameters = output_directive.parameter; + if (!generator_parameters_[output_directive.name].empty()) { + if (!parameters.empty()) { + parameters.append(","); + } + parameters.append(generator_parameters_[output_directive.name]); + } for (int i = 0; i < parsed_files.size(); i++) { - if (!output_directive.generator->Generate( - parsed_files[i], output_directive.parameter, - generator_context, &error)) { + if (!output_directive.generator->Generate(parsed_files[i], parameters, + generator_context, &error)) { // Generator returned an error. cerr << output_directive.name << ": " << parsed_files[i]->name() << ": " << error << endl; @@ -1168,8 +1237,9 @@ bool CommandLineInterface::GeneratePluginOutput( set<const FileDescriptor*> already_seen; for (int i = 0; i < parsed_files.size(); i++) { request.add_file_to_generate(parsed_files[i]->name()); - GetTransitiveDependencies(parsed_files[i], &already_seen, - request.mutable_proto_file()); + GetTransitiveDependencies(parsed_files[i], + true, // Include source code info. + &already_seen, request.mutable_proto_file()); } // Invoke the plugin. @@ -1299,12 +1369,17 @@ bool CommandLineInterface::WriteDescriptorSet( if (imports_in_descriptor_set_) { set<const FileDescriptor*> already_seen; for (int i = 0; i < parsed_files.size(); i++) { - GetTransitiveDependencies( - parsed_files[i], &already_seen, file_set.mutable_file()); + GetTransitiveDependencies(parsed_files[i], + source_info_in_descriptor_set_, + &already_seen, file_set.mutable_file()); } } else { for (int i = 0; i < parsed_files.size(); i++) { - parsed_files[i]->CopyTo(file_set.add_file()); + FileDescriptorProto* file_proto = file_set.add_file(); + parsed_files[i]->CopyTo(file_proto); + if (source_info_in_descriptor_set_) { + parsed_files[i]->CopySourceCodeInfoTo(file_proto); + } } } @@ -1334,7 +1409,7 @@ bool CommandLineInterface::WriteDescriptorSet( } void CommandLineInterface::GetTransitiveDependencies( - const FileDescriptor* file, + const FileDescriptor* file, bool include_source_code_info, set<const FileDescriptor*>* already_seen, RepeatedPtrField<FileDescriptorProto>* output) { if (!already_seen->insert(file).second) { @@ -1344,11 +1419,16 @@ void CommandLineInterface::GetTransitiveDependencies( // Add all dependencies. for (int i = 0; i < file->dependency_count(); i++) { - GetTransitiveDependencies(file->dependency(i), already_seen, output); + GetTransitiveDependencies(file->dependency(i), include_source_code_info, + already_seen, output); } // Add this file. - file->CopyTo(output->Add()); + FileDescriptorProto* new_descriptor = output->Add(); + file->CopyTo(new_descriptor); + if (include_source_code_info) { + file->CopySourceCodeInfoTo(new_descriptor); + } } diff --git a/src/google/protobuf/compiler/command_line_interface.h b/src/google/protobuf/compiler/command_line_interface.h index 0b507d80..86ea9bde 100644 --- a/src/google/protobuf/compiler/command_line_interface.h +++ b/src/google/protobuf/compiler/command_line_interface.h @@ -112,6 +112,19 @@ class LIBPROTOC_EXPORT CommandLineInterface { CodeGenerator* generator, const string& help_text); + // Register a code generator for a language. + // Besides flag_name you can specify another option_flag_name that could be + // used to pass extra parameters to the registered code generator. + // Suppose you have registered a generator by calling: + // command_line_interface.RegisterGenerator("--foo_out", "--foo_opt", ...) + // Then you could invoke the compiler with a command like: + // protoc --foo_out=enable_bar:outdir --foo_opt=enable_baz + // This will pass "enable_bar,enable_baz" as the parameter to the generator. + void RegisterGenerator(const string& flag_name, + const string& option_flag_name, + CodeGenerator* generator, + const string& help_text); + // Enables "plugins". In this mode, if a command-line flag ends with "_out" // but does not match any registered generator, the compiler will attempt to // find a "plugin" to implement the generator. Plugins are just executables. @@ -186,8 +199,15 @@ class LIBPROTOC_EXPORT CommandLineInterface { bool MakeInputsBeProtoPathRelative( DiskSourceTree* source_tree); + // Return status for ParseArguments() and InterpretArgument(). + enum ParseArgumentStatus { + PARSE_ARGUMENT_DONE_AND_CONTINUE, + PARSE_ARGUMENT_DONE_AND_EXIT, + PARSE_ARGUMENT_FAIL + }; + // Parse all command-line arguments. - bool ParseArguments(int argc, const char* const argv[]); + ParseArgumentStatus ParseArguments(int argc, const char* const argv[]); // Parses a command-line argument into a name/value pair. Returns // true if the next argument in the argv should be used as the value, @@ -203,7 +223,8 @@ class LIBPROTOC_EXPORT CommandLineInterface { bool ParseArgument(const char* arg, string* name, string* value); // Interprets arguments parsed with ParseArgument. - bool InterpretArgument(const string& name, const string& value); + ParseArgumentStatus InterpretArgument(const string& name, + const string& value); // Print the --help text to stderr. void PrintHelpText(); @@ -230,9 +251,11 @@ class LIBPROTOC_EXPORT CommandLineInterface { // protos will be ordered such that every file is listed before any file that // depends on it, so that you can call DescriptorPool::BuildFile() on them // in order. Any files in *already_seen will not be added, and each file - // added will be inserted into *already_seen. + // added will be inserted into *already_seen. If include_source_code_info is + // true then include the source code information in the FileDescriptorProtos. static void GetTransitiveDependencies( const FileDescriptor* file, + bool include_source_code_info, set<const FileDescriptor*>* already_seen, RepeatedPtrField<FileDescriptorProto>* output); @@ -244,13 +267,21 @@ class LIBPROTOC_EXPORT CommandLineInterface { // Version info set with SetVersionInfo(). string version_info_; - // Map from flag names to registered generators. + // Registered generators. struct GeneratorInfo { + string flag_name; + string option_flag_name; CodeGenerator* generator; string help_text; }; typedef map<string, GeneratorInfo> GeneratorMap; - GeneratorMap generators_; + GeneratorMap generators_by_flag_name_; + GeneratorMap generators_by_option_name_; + // A map from generator names to the parameters specified using the option + // flag. For example, if the user invokes the compiler with: + // protoc --foo_out=outputdir --foo_opt=enable_bar ... + // Then there will be an entry ("--foo_out", "enable_bar") in this map. + map<string, string> generator_parameters_; // See AllowPlugins(). If this is empty, plugins aren't allowed. string plugin_prefix_; @@ -302,6 +333,10 @@ class LIBPROTOC_EXPORT CommandLineInterface { // the .proto files listed on the command-line are added. bool imports_in_descriptor_set_; + // True if --include_source_info was given, meaning that we should not strip + // SourceCodeInfo from the DescriptorSet. + bool source_info_in_descriptor_set_; + // Was the --disallow_services flag used? bool disallow_services_; diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index d5b3a1dc..16559923 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -122,6 +122,10 @@ class CommandLineInterfaceTest : public testing::Test { // substring. void ExpectErrorSubstring(const string& expected_substring); + // Like ExpectErrorSubstring, but checks that Run() returned zero. + void ExpectErrorSubstringWithZeroReturnCode( + const string& expected_substring); + // Returns true if ExpectErrorSubstring(expected_substring) would pass, but // does not fail otherwise. bool HasAlternateErrorSubstring(const string& expected_substring); @@ -225,7 +229,7 @@ void CommandLineInterfaceTest::SetUp() { // Register generators. CodeGenerator* generator = new MockCodeGenerator("test_generator"); mock_generators_to_delete_.push_back(generator); - cli_.RegisterGenerator("--test_out", generator, "Test output."); + cli_.RegisterGenerator("--test_out", "--test_opt", generator, "Test output."); cli_.RegisterGenerator("-t", generator, "Test output."); generator = new MockCodeGenerator("alt_generator"); @@ -345,6 +349,12 @@ void CommandLineInterfaceTest::ExpectErrorSubstring( EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_); } +void CommandLineInterfaceTest::ExpectErrorSubstringWithZeroReturnCode( + const string& expected_substring) { + EXPECT_EQ(0, return_code_); + EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_); +} + bool CommandLineInterfaceTest::HasAlternateErrorSubstring( const string& expected_substring) { EXPECT_NE(0, return_code_); @@ -544,6 +554,32 @@ TEST_F(CommandLineInterfaceTest, GeneratorParameters) { ExpectGenerated("test_plugin", "TestPluginParameter", "foo.proto", "Foo"); } +TEST_F(CommandLineInterfaceTest, ExtraGeneratorParameters) { + // Test that generator parameters specified with the option flag are + // correctly passed to the code generator. + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + // Create the "a" and "b" sub-directories. + CreateTempDir("a"); + CreateTempDir("b"); + + Run("protocol_compiler " + "--test_opt=foo1 " + "--test_out=bar:$tmpdir/a " + "--test_opt=foo2 " + "--test_out=baz:$tmpdir/b " + "--test_opt=foo3 " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated( + "test_generator", "bar,foo1,foo2,foo3", "foo.proto", "Foo", "a"); + ExpectGenerated( + "test_generator", "baz,foo1,foo2,foo3", "foo.proto", "Foo", "b"); +} + TEST_F(CommandLineInterfaceTest, Insert) { // Test running a generator that inserts code into another's output. @@ -779,6 +815,33 @@ TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) { if (HasFatalFailure()) return; ASSERT_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()); +} + +TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) { + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + CreateTempFile("bar.proto", + "syntax = \"proto2\";\n" + "import \"foo.proto\";\n" + "message Bar {\n" + " optional Foo foo = 1;\n" + "}\n"); + + Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set " + "--include_source_info --proto_path=$tmpdir bar.proto"); + + ExpectNoErrors(); + + FileDescriptorSet descriptor_set; + ReadDescriptorSet("descriptor_set", &descriptor_set); + if (HasFatalFailure()) return; + ASSERT_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()); } TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) { @@ -807,6 +870,40 @@ TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) { } EXPECT_EQ("foo.proto", descriptor_set.file(0).name()); EXPECT_EQ("bar.proto", descriptor_set.file(1).name()); + // Descriptor set should not have source code info. + EXPECT_FALSE(descriptor_set.file(0).has_source_code_info()); + EXPECT_FALSE(descriptor_set.file(1).has_source_code_info()); +} + +TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) { + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + CreateTempFile("bar.proto", + "syntax = \"proto2\";\n" + "import \"foo.proto\";\n" + "message Bar {\n" + " optional Foo foo = 1;\n" + "}\n"); + + Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set " + "--include_imports --include_source_info --proto_path=$tmpdir bar.proto"); + + ExpectNoErrors(); + + FileDescriptorSet descriptor_set; + ReadDescriptorSet("descriptor_set", &descriptor_set); + if (HasFatalFailure()) return; + ASSERT_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]); + } + EXPECT_EQ("foo.proto", descriptor_set.file(0).name()); + EXPECT_EQ("bar.proto", descriptor_set.file(1).name()); + // Source code info included. + EXPECT_TRUE(descriptor_set.file(0).has_source_code_info()); + EXPECT_TRUE(descriptor_set.file(1).has_source_code_info()); } // ------------------------------------------------------------------- @@ -1129,6 +1226,17 @@ TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) { #endif } +TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) { + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message MockCodeGenerator_HasSourceCodeInfo {}\n"); + + Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto"); + + ExpectErrorSubstring( + "Saw message type MockCodeGenerator_HasSourceCodeInfo: 1."); +} + TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) { // Test what happens if the plugin isn't found. @@ -1171,11 +1279,11 @@ TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) { TEST_F(CommandLineInterfaceTest, HelpText) { Run("test_exec_name --help"); - ExpectErrorSubstring("Usage: test_exec_name "); - ExpectErrorSubstring("--test_out=OUT_DIR"); - ExpectErrorSubstring("Test output."); - ExpectErrorSubstring("--alt_out=OUT_DIR"); - ExpectErrorSubstring("Alt output."); + ExpectErrorSubstringWithZeroReturnCode("Usage: test_exec_name "); + ExpectErrorSubstringWithZeroReturnCode("--test_out=OUT_DIR"); + ExpectErrorSubstringWithZeroReturnCode("Test output."); + ExpectErrorSubstringWithZeroReturnCode("--alt_out=OUT_DIR"); + ExpectErrorSubstringWithZeroReturnCode("Alt output."); } TEST_F(CommandLineInterfaceTest, GccFormatErrors) { diff --git a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc index bcfa5020..b7c1766b 100644 --- a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc @@ -48,8 +48,8 @@ #include <google/protobuf/compiler/importer.h> #include <google/protobuf/descriptor.h> #include <google/protobuf/io/zero_copy_stream_impl.h> -#include <google/protobuf/stubs/stl_util-inl.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> diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc index 76d2b798..67c12d7a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc @@ -46,10 +46,10 @@ namespace compiler { namespace cpp { EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, - const string& dllexport_decl) + const Options& options) : descriptor_(descriptor), classname_(ClassName(descriptor, false)), - dllexport_decl_(dllexport_decl) { + options_(options) { } EnumGenerator::~EnumGenerator() {} @@ -88,10 +88,10 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) { vars["min_name"] = min_value->name(); vars["max_name"] = max_value->name(); - if (dllexport_decl_.empty()) { + if (options_.dllexport_decl.empty()) { vars["dllexport"] = ""; } else { - vars["dllexport"] = dllexport_decl_ + " "; + vars["dllexport"] = options_.dllexport_decl + " "; } printer->Print(vars, diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.h b/src/google/protobuf/compiler/cpp/cpp_enum.h index 58f7721e..2e85a0bd 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.h +++ b/src/google/protobuf/compiler/cpp/cpp_enum.h @@ -36,8 +36,10 @@ #define GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__ #include <string> +#include <google/protobuf/compiler/cpp/cpp_options.h> #include <google/protobuf/descriptor.h> + namespace google { namespace protobuf { namespace io { @@ -53,7 +55,7 @@ class EnumGenerator { public: // See generator.cc for the meaning of dllexport_decl. explicit EnumGenerator(const EnumDescriptor* descriptor, - const string& dllexport_decl); + const Options& options); ~EnumGenerator(); // Header stuff. @@ -86,7 +88,7 @@ class EnumGenerator { private: const EnumDescriptor* descriptor_; string classname_; - string dllexport_decl_; + Options options_; 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 a369f417..6e1620d4 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -46,8 +46,9 @@ namespace cpp { namespace { void SetEnumVariables(const FieldDescriptor* descriptor, - map<string, string>* variables) { - SetCommonFieldVariables(descriptor, variables); + map<string, string>* variables, + const Options& options) { + 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()); @@ -58,9 +59,10 @@ void SetEnumVariables(const FieldDescriptor* descriptor, // =================================================================== EnumFieldGenerator:: -EnumFieldGenerator(const FieldDescriptor* descriptor) +EnumFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) : descriptor_(descriptor) { - SetEnumVariables(descriptor, &variables_); + SetEnumVariables(descriptor, &variables_, options); } EnumFieldGenerator::~EnumFieldGenerator() {} @@ -84,7 +86,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " return static_cast< $type$ >($name$_);\n" "}\n" "inline void $classname$::set_$name$($type$ value) {\n" - " GOOGLE_DCHECK($type$_IsValid(value));\n" + " assert($type$_IsValid(value));\n" " set_has_$name$();\n" " $name$_ = value;\n" "}\n"); @@ -152,9 +154,10 @@ GenerateByteSize(io::Printer* printer) const { // =================================================================== RepeatedEnumFieldGenerator:: -RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor) +RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) : descriptor_(descriptor) { - SetEnumVariables(descriptor, &variables_); + SetEnumVariables(descriptor, &variables_, options); } RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {} @@ -187,11 +190,11 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " return static_cast< $type$ >($name$_.Get(index));\n" "}\n" "inline void $classname$::set_$name$(int index, $type$ value) {\n" - " GOOGLE_DCHECK($type$_IsValid(value));\n" + " assert($type$_IsValid(value));\n" " $name$_.Set(index, value);\n" "}\n" "inline void $classname$::add_$name$($type$ value) {\n" - " GOOGLE_DCHECK($type$_IsValid(value));\n" + " assert($type$_IsValid(value));\n" " $name$_.Add(value);\n" "}\n"); printer->Print(variables_, @@ -345,7 +348,9 @@ GenerateByteSize(io::Printer* printer) const { " total_size += $tag_size$ +\n" " ::google::protobuf::internal::WireFormatLite::Int32Size(data_size);\n" "}\n" + "GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();\n" "_$name$_cached_byte_size_ = data_size;\n" + "GOOGLE_SAFE_CONCURRENT_WRITES_END();\n" "total_size += data_size;\n"); } else { printer->Print(variables_, diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.h b/src/google/protobuf/compiler/cpp/cpp_enum_field.h index 0793430c..65770083 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.h @@ -46,7 +46,8 @@ namespace cpp { class EnumFieldGenerator : public FieldGenerator { public: - explicit EnumFieldGenerator(const FieldDescriptor* descriptor); + explicit EnumFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~EnumFieldGenerator(); // implements FieldGenerator --------------------------------------- @@ -71,7 +72,8 @@ class EnumFieldGenerator : public FieldGenerator { class RepeatedEnumFieldGenerator : public FieldGenerator { public: - explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor); + explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~RepeatedEnumFieldGenerator(); // implements FieldGenerator --------------------------------------- diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.cc b/src/google/protobuf/compiler/cpp/cpp_extension.cc index 658a7077..ef56b5e5 100644 --- a/src/google/protobuf/compiler/cpp/cpp_extension.cc +++ b/src/google/protobuf/compiler/cpp/cpp_extension.cc @@ -57,9 +57,9 @@ string ExtendeeClassName(const FieldDescriptor* descriptor) { } // anonymous namespace ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor, - const string& dllexport_decl) + const Options& options) : descriptor_(descriptor), - dllexport_decl_(dllexport_decl) { + options_(options) { // Construct type_traits_. if (descriptor_->is_repeated()) { type_traits_ = "Repeated"; @@ -106,8 +106,8 @@ void ExtensionGenerator::GenerateDeclaration(io::Printer* printer) { // export/import specifier. if (descriptor_->extension_scope() == NULL) { vars["qualifier"] = "extern"; - if (!dllexport_decl_.empty()) { - vars["qualifier"] = dllexport_decl_ + " " + vars["qualifier"]; + if (!options_.dllexport_decl.empty()) { + vars["qualifier"] = options_.dllexport_decl + " " + vars["qualifier"]; } } else { vars["qualifier"] = "static"; diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.h b/src/google/protobuf/compiler/cpp/cpp_extension.h index 3068b091..50ad035b 100644 --- a/src/google/protobuf/compiler/cpp/cpp_extension.h +++ b/src/google/protobuf/compiler/cpp/cpp_extension.h @@ -37,6 +37,7 @@ #include <string> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/cpp/cpp_options.h> namespace google { namespace protobuf { @@ -56,8 +57,8 @@ namespace cpp { class ExtensionGenerator { public: // See generator.cc for the meaning of dllexport_decl. - explicit ExtensionGenerator(const FieldDescriptor* descriptor, - const string& dllexport_decl); + explicit ExtensionGenerator(const FieldDescriptor* desycriptor, + const Options& options); ~ExtensionGenerator(); // Header stuff. @@ -72,7 +73,7 @@ class ExtensionGenerator { private: const FieldDescriptor* descriptor_; string type_traits_; - string dllexport_decl_; + Options options_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator); }; diff --git a/src/google/protobuf/compiler/cpp/cpp_field.cc b/src/google/protobuf/compiler/cpp/cpp_field.cc index 103cac4a..0786176b 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_field.cc @@ -52,7 +52,8 @@ namespace cpp { using internal::WireFormat; void SetCommonFieldVariables(const FieldDescriptor* descriptor, - map<string, string>* variables) { + map<string, string>* variables, + const Options& options) { (*variables)["name"] = FieldName(descriptor); (*variables)["index"] = SimpleItoa(descriptor->index()); (*variables)["number"] = SimpleItoa(descriptor->number()); @@ -64,6 +65,7 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor, (*variables)["deprecation"] = descriptor->options().deprecated() ? " PROTOBUF_DEPRECATED" : ""; + (*variables)["cppget"] = "Get"; } FieldGenerator::~FieldGenerator() {} @@ -80,46 +82,47 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { } -FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor) +FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor, + const Options& options) : descriptor_(descriptor), - field_generators_( - new scoped_ptr<FieldGenerator>[descriptor->field_count()]) { + 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))); + field_generators_[i].reset(MakeGenerator(descriptor->field(i), options)); } } -FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field) { +FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field, + const Options& options) { if (field->is_repeated()) { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_MESSAGE: - return new RepeatedMessageFieldGenerator(field); + return new RepeatedMessageFieldGenerator(field, options); case FieldDescriptor::CPPTYPE_STRING: switch (field->options().ctype()) { default: // RepeatedStringFieldGenerator handles unknown ctypes. case FieldOptions::STRING: - return new RepeatedStringFieldGenerator(field); + return new RepeatedStringFieldGenerator(field, options); } case FieldDescriptor::CPPTYPE_ENUM: - return new RepeatedEnumFieldGenerator(field); + return new RepeatedEnumFieldGenerator(field, options); default: - return new RepeatedPrimitiveFieldGenerator(field); + return new RepeatedPrimitiveFieldGenerator(field, options); } } else { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_MESSAGE: - return new MessageFieldGenerator(field); + return new MessageFieldGenerator(field, options); case FieldDescriptor::CPPTYPE_STRING: switch (field->options().ctype()) { default: // StringFieldGenerator handles unknown ctypes. case FieldOptions::STRING: - return new StringFieldGenerator(field); + return new StringFieldGenerator(field, options); } case FieldDescriptor::CPPTYPE_ENUM: - return new EnumFieldGenerator(field); + return new EnumFieldGenerator(field, options); default: - return new PrimitiveFieldGenerator(field); + return new PrimitiveFieldGenerator(field, options); } } } diff --git a/src/google/protobuf/compiler/cpp/cpp_field.h b/src/google/protobuf/compiler/cpp/cpp_field.h index c303a337..f7d99b15 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_field.h @@ -40,6 +40,7 @@ #include <google/protobuf/stubs/common.h> #include <google/protobuf/descriptor.h> +#include <google/protobuf/compiler/cpp/cpp_options.h> namespace google { namespace protobuf { @@ -57,7 +58,8 @@ namespace cpp { // ['name', 'index', 'number', 'classname', 'declared_type', 'tag_size', // 'deprecation']. void SetCommonFieldVariables(const FieldDescriptor* descriptor, - map<string, string>* variables); + map<string, string>* variables, + const Options& options); class FieldGenerator { public: @@ -114,6 +116,13 @@ class FieldGenerator { // Most field types don't need this, so the default implementation is empty. virtual void GenerateDestructorCode(io::Printer* printer) const {} + // Generate code that allocates the fields's default instance. + virtual void GenerateDefaultInstanceAllocator(io::Printer* printer) const {} + + // Generate code that should be run when ShutdownProtobufLibrary() is called, + // to delete all dynamically-allocated objects. + virtual void GenerateShutdownCode(io::Printer* printer) const {} + // Generate lines to decode this field, which will be placed inside the // message's MergeFromCodedStream() method. virtual void GenerateMergeFromCodedStream(io::Printer* printer) const = 0; @@ -144,7 +153,7 @@ class FieldGenerator { // Convenience class which constructs FieldGenerators for a Descriptor. class FieldGeneratorMap { public: - explicit FieldGeneratorMap(const Descriptor* descriptor); + explicit FieldGeneratorMap(const Descriptor* descriptor, const Options& options); ~FieldGeneratorMap(); const FieldGenerator& get(const FieldDescriptor* field) const; @@ -153,7 +162,8 @@ class FieldGeneratorMap { const Descriptor* descriptor_; scoped_array<scoped_ptr<FieldGenerator> > field_generators_; - static FieldGenerator* MakeGenerator(const FieldDescriptor* field); + static FieldGenerator* MakeGenerator(const FieldDescriptor* field, + const Options& options); GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap); }; diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index b8e7708f..1bbc89c4 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -51,7 +51,7 @@ namespace cpp { // =================================================================== FileGenerator::FileGenerator(const FileDescriptor* file, - const string& dllexport_decl) + const Options& options) : file_(file), message_generators_( new scoped_ptr<MessageGenerator>[file->message_type_count()]), @@ -61,26 +61,26 @@ FileGenerator::FileGenerator(const FileDescriptor* file, new scoped_ptr<ServiceGenerator>[file->service_count()]), extension_generators_( new scoped_ptr<ExtensionGenerator>[file->extension_count()]), - dllexport_decl_(dllexport_decl) { + options_(options) { for (int i = 0; i < file->message_type_count(); i++) { message_generators_[i].reset( - new MessageGenerator(file->message_type(i), dllexport_decl)); + new MessageGenerator(file->message_type(i), options)); } for (int i = 0; i < file->enum_type_count(); i++) { enum_generators_[i].reset( - new EnumGenerator(file->enum_type(i), dllexport_decl)); + new EnumGenerator(file->enum_type(i), options)); } for (int i = 0; i < file->service_count(); i++) { service_generators_[i].reset( - new ServiceGenerator(file->service(i), dllexport_decl)); + new ServiceGenerator(file->service(i), options)); } for (int i = 0; i < file->extension_count(); i++) { extension_generators_[i].reset( - new ExtensionGenerator(file->extension(i), dllexport_decl)); + new ExtensionGenerator(file->extension(i), options)); } SplitStringUsing(file_->package(), ".", &package_parts_); @@ -104,6 +104,7 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { "filename", file_->name(), "filename_identifier", filename_identifier); + printer->Print( "#include <google/protobuf/stubs/common.h>\n" "\n"); @@ -128,13 +129,23 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { // OK, it's now safe to #include other files. printer->Print( - "#include <google/protobuf/generated_message_util.h>\n" + "#include <google/protobuf/generated_message_util.h>\n"); + if (file_->message_type_count() > 0) { + if (HasDescriptorMethods(file_)) { + printer->Print( + "#include <google/protobuf/message.h>\n"); + } else { + printer->Print( + "#include <google/protobuf/message_lite.h>\n"); + } + } + printer->Print( "#include <google/protobuf/repeated_field.h>\n" "#include <google/protobuf/extension_set.h>\n"); - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_) && HasEnumDefinitions(file_)) { printer->Print( - "#include <google/protobuf/generated_message_reflection.h>\n"); + "#include <google/protobuf/generated_enum_reflection.h>\n"); } if (HasGenericServices(file_)) { @@ -142,6 +153,11 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { "#include <google/protobuf/service.h>\n"); } + if (HasUnknownFields(file_) && file_->message_type_count() > 0) { + printer->Print( + "#include <google/protobuf/unknown_field_set.h>\n"); + } + for (int i = 0; i < file_->dependency_count(); i++) { printer->Print( @@ -149,9 +165,11 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { "dependency", StripProto(file_->dependency(i)->name())); } + printer->Print( "// @@protoc_insertion_point(includes)\n"); + // Open namespace. GenerateNamespaceOpeners(printer); @@ -162,7 +180,7 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { "// Internal implementation detail -- do not call these.\n" "void $dllexport_decl$ $adddescriptorsname$();\n", "adddescriptorsname", GlobalAddDescriptorsName(file_->name()), - "dllexport_decl", dllexport_decl_); + "dllexport_decl", options_.dllexport_decl); printer->Print( // Note that we don't put dllexport_decl on these because they are only @@ -282,6 +300,7 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { void FileGenerator::GenerateSource(io::Printer* printer) { printer->Print( "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "// source: $filename$\n" "\n" // The generated code calls accessors that might be deprecated. We don't @@ -294,11 +313,13 @@ void FileGenerator::GenerateSource(io::Printer* printer) { "#include <google/protobuf/stubs/once.h>\n" "#include <google/protobuf/io/coded_stream.h>\n" "#include <google/protobuf/wire_format_lite_inl.h>\n", + "filename", file_->name(), "basename", StripProto(file_->name())); if (HasDescriptorMethods(file_)) { printer->Print( "#include <google/protobuf/descriptor.h>\n" + "#include <google/protobuf/generated_message_reflection.h>\n" "#include <google/protobuf/reflection_ops.h>\n" "#include <google/protobuf/wire_format.h>\n"); } @@ -504,10 +525,12 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { " static bool already_here = false;\n" " if (already_here) return;\n" " already_here = true;\n" - " GOOGLE_PROTOBUF_VERIFY_VERSION;\n", + " GOOGLE_PROTOBUF_VERIFY_VERSION;\n" + "\n", // Without. "void $adddescriptorsname$_impl() {\n" - " GOOGLE_PROTOBUF_VERIFY_VERSION;\n", + " GOOGLE_PROTOBUF_VERIFY_VERSION;\n" + "\n", // Vars. "adddescriptorsname", GlobalAddDescriptorsName(file_->name())); @@ -521,9 +544,9 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { vector<string> dependency_package_parts; SplitStringUsing(dependency->package(), ".", &dependency_package_parts); printer->Print("::"); - for (int i = 0; i < dependency_package_parts.size(); i++) { + for (int j = 0; j < dependency_package_parts.size(); j++) { printer->Print("$name$::", - "name", dependency_package_parts[i]); + "name", dependency_package_parts[j]); } // Call its AddDescriptors function. printer->Print( @@ -547,10 +570,12 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { static const int kBytesPerLine = 40; for (int i = 0; i < file_data.size(); i += kBytesPerLine) { printer->Print("\n \"$data$\"", - "data", EscapeTrigraphs(CEscape(file_data.substr(i, kBytesPerLine)))); + "data", + EscapeTrigraphs( + CEscape(file_data.substr(i, kBytesPerLine)))); } printer->Print( - ", $size$);\n", + ", $size$);\n", "size", SimpleItoa(file_data.size())); // Call MessageFactory::InternalRegisterGeneratedFile(). @@ -594,7 +619,7 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { // Without. "GOOGLE_PROTOBUF_DECLARE_ONCE($adddescriptorsname$_once_);\n" "void $adddescriptorsname$() {\n" - " ::google::protobuf::GoogleOnceInit(&$adddescriptorsname$_once_,\n" + " ::google::protobuf::::google::protobuf::GoogleOnceInit(&$adddescriptorsname$_once_,\n" " &$adddescriptorsname$_impl);\n" "}\n", // Vars. diff --git a/src/google/protobuf/compiler/cpp/cpp_file.h b/src/google/protobuf/compiler/cpp/cpp_file.h index b4e01285..2deefaa8 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.h +++ b/src/google/protobuf/compiler/cpp/cpp_file.h @@ -39,6 +39,7 @@ #include <vector> #include <google/protobuf/stubs/common.h> #include <google/protobuf/compiler/cpp/cpp_field.h> +#include <google/protobuf/compiler/cpp/cpp_options.h> namespace google { namespace protobuf { @@ -61,7 +62,7 @@ class FileGenerator { public: // See generator.cc for the meaning of dllexport_decl. explicit FileGenerator(const FileDescriptor* file, - const string& dllexport_decl); + const Options& options); ~FileGenerator(); void GenerateHeader(io::Printer* printer); @@ -85,7 +86,7 @@ class FileGenerator { // E.g. if the package is foo.bar, package_parts_ is {"foo", "bar"}. vector<string> package_parts_; - string dllexport_decl_; + 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 bb84e2ab..1813510b 100644 --- a/src/google/protobuf/compiler/cpp/cpp_generator.cc +++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc @@ -78,11 +78,13 @@ bool CppGenerator::Generate(const FileDescriptor* file, // } // FOO_EXPORT is a macro which should expand to __declspec(dllexport) or // __declspec(dllimport) depending on what is being compiled. - string dllexport_decl; + Options file_options; for (int i = 0; i < options.size(); i++) { if (options[i].first == "dllexport_decl") { - dllexport_decl = options[i].second; + file_options.dllexport_decl = options[i].second; + } else if (options[i].first == "safe_boundary_check") { + file_options.safe_boundary_check = true; } else { *error = "Unknown generator option: " + options[i].first; return false; @@ -95,7 +97,7 @@ bool CppGenerator::Generate(const FileDescriptor* file, string basename = StripProto(file->name()); basename.append(".pb"); - FileGenerator file_generator(file, dllexport_decl); + FileGenerator file_generator(file, file_options); // Generate header. { diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc index c4b868c2..28911ab0 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc @@ -148,7 +148,7 @@ string ClassName(const Descriptor* descriptor, bool qualified) { string ClassName(const EnumDescriptor* enum_descriptor, bool qualified) { if (enum_descriptor->containing_type() == NULL) { if (qualified) { - return DotsToColons(enum_descriptor->full_name()); + return "::" + DotsToColons(enum_descriptor->full_name()); } else { return enum_descriptor->name(); } @@ -259,10 +259,23 @@ const char* DeclaredTypeMethodName(FieldDescriptor::Type type) { 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()); 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()) + ")"; case FieldDescriptor::CPPTYPE_UINT64: return "GOOGLE_ULONGLONG(" + SimpleItoa(field->default_value_uint64())+ ")"; @@ -308,8 +321,9 @@ string DefaultValue(const FieldDescriptor* field) { ClassName(field->enum_type(), true), field->default_value_enum()->number()); case FieldDescriptor::CPPTYPE_STRING: - return "\"" + EscapeTrigraphs(CEscape(field->default_value_string())) + - "\""; + return "\"" + EscapeTrigraphs( + CEscape(field->default_value_string())) + + "\""; case FieldDescriptor::CPPTYPE_MESSAGE: return FieldMessageTypeName(field) + "::default_instance()"; } @@ -401,6 +415,23 @@ void PrintHandlingOptionalStaticInitializers( } } + +static bool HasEnumDefinitions(const Descriptor* message_type) { + if (message_type->enum_type_count() > 0) return true; + for (int i = 0; i < message_type->nested_type_count(); ++i) { + if (HasEnumDefinitions(message_type->nested_type(i))) return true; + } + return false; +} + +bool HasEnumDefinitions(const FileDescriptor* file) { + if (file->enum_type_count() > 0) return true; + for (int i = 0; i < file->message_type_count(); ++i) { + if (HasEnumDefinitions(file->message_type(i))) return true; + } + 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 662a7c31..526e19cc 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.h +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h @@ -122,23 +122,27 @@ string GlobalShutdownFileName(const string& filename); string EscapeTrigraphs(const string& to_escape); // Do message classes in this file keep track of unknown fields? -inline bool HasUnknownFields(const FileDescriptor *file) { +inline bool HasUnknownFields(const FileDescriptor* file) { return file->options().optimize_for() != FileOptions::LITE_RUNTIME; } + +// Does this file have any enum type definitions? +bool HasEnumDefinitions(const FileDescriptor* file); + // Does this file have generated parsing, serialization, and other // standard methods for which reflection-based fallback implementations exist? -inline bool HasGeneratedMethods(const FileDescriptor *file) { +inline bool HasGeneratedMethods(const FileDescriptor* file) { return file->options().optimize_for() != FileOptions::CODE_SIZE; } -// Do message classes in this file have descriptor and refelction methods? -inline bool HasDescriptorMethods(const FileDescriptor *file) { +// Do message classes in this file have descriptor and reflection methods? +inline bool HasDescriptorMethods(const FileDescriptor* file) { return file->options().optimize_for() != FileOptions::LITE_RUNTIME; } // Should we generate generic services for this file? -inline bool HasGenericServices(const FileDescriptor *file) { +inline bool HasGenericServices(const FileDescriptor* file) { return file->service_count() > 0 && file->options().optimize_for() != FileOptions::LITE_RUNTIME && file->options().cc_generic_services(); @@ -173,6 +177,7 @@ void PrintHandlingOptionalStaticInitializers( io::Printer* printer, const char* with_static_init, const char* without_static_init); + } // 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 1c158be0..1ea4f13d 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -48,6 +48,7 @@ #include <google/protobuf/wire_format.h> #include <google/protobuf/descriptor.pb.h> + namespace google { namespace protobuf { namespace compiler { @@ -103,6 +104,13 @@ struct ExtensionRangeSorter { } }; +// Returns true if the "required" restriction check should be ignored for the +// given field. +inline static bool ShouldIgnoreRequiredFieldCheck( + const FieldDescriptor* field) { + return false; +} + // Returns true if the message type has any required fields. If it doesn't, // we can optimize out calls to its IsInitialized() method. // @@ -129,7 +137,8 @@ static bool HasRequiredFields( if (field->is_required()) { return true; } - if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && + !ShouldIgnoreRequiredFieldCheck(field)) { if (HasRequiredFields(field->message_type(), already_seen)) { return true; } @@ -280,11 +289,11 @@ void OptimizePadding(vector<const FieldDescriptor*>* fields) { // =================================================================== MessageGenerator::MessageGenerator(const Descriptor* descriptor, - const string& dllexport_decl) + const Options& options) : descriptor_(descriptor), classname_(ClassName(descriptor, false)), - dllexport_decl_(dllexport_decl), - field_generators_(descriptor), + options_(options), + field_generators_(descriptor, options), nested_generators_(new scoped_ptr<MessageGenerator>[ descriptor->nested_type_count()]), enum_generators_(new scoped_ptr<EnumGenerator>[ @@ -294,17 +303,17 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, for (int i = 0; i < descriptor->nested_type_count(); i++) { nested_generators_[i].reset( - new MessageGenerator(descriptor->nested_type(i), dllexport_decl)); + new MessageGenerator(descriptor->nested_type(i), options)); } for (int i = 0; i < descriptor->enum_type_count(); i++) { enum_generators_[i].reset( - new EnumGenerator(descriptor->enum_type(i), dllexport_decl)); + new EnumGenerator(descriptor->enum_type(i), options)); } for (int i = 0; i < descriptor->extension_count(); i++) { extension_generators_[i].reset( - new ExtensionGenerator(descriptor->extension(i), dllexport_decl)); + new ExtensionGenerator(descriptor->extension(i), options)); } } @@ -349,7 +358,7 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { PrintFieldComment(printer, field); map<string, string> vars; - SetCommonFieldVariables(field, &vars); + SetCommonFieldVariables(field, &vars, options_); vars["constant_name"] = FieldConstantName(field); if (field->is_repeated()) { @@ -386,7 +395,7 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) { PrintFieldComment(printer, field); map<string, string> vars; - SetCommonFieldVariables(field, &vars); + SetCommonFieldVariables(field, &vars, options_); // Generate has_$name$() or $name$_size(). if (field->is_repeated()) { @@ -446,10 +455,10 @@ GenerateClassDefinition(io::Printer* printer) { map<string, string> vars; vars["classname"] = classname_; vars["field_count"] = SimpleItoa(descriptor_->field_count()); - if (dllexport_decl_.empty()) { + if (options_.dllexport_decl.empty()) { vars["dllexport"] = ""; } else { - vars["dllexport"] = dllexport_decl_ + " "; + vars["dllexport"] = options_.dllexport_decl + " "; } vars["superclass"] = SuperClassName(descriptor_); @@ -507,6 +516,7 @@ GenerateClassDefinition(io::Printer* printer) { "\n"); } + printer->Print(vars, "void Swap($classname$* other);\n" "\n" @@ -605,6 +615,7 @@ GenerateClassDefinition(io::Printer* printer) { printer->Print(" private:\n"); printer->Indent(); + for (int i = 0; i < descriptor_->field_count(); i++) { if (!descriptor_->field(i)->is_repeated()) { printer->Print( @@ -680,7 +691,7 @@ GenerateClassDefinition(io::Printer* printer) { // Without. "friend void $dllexport_decl$ $adddescriptorsname$_impl();\n", // Vars. - "dllexport_decl", dllexport_decl_, + "dllexport_decl", options_.dllexport_decl, "adddescriptorsname", GlobalAddDescriptorsName(descriptor_->file()->name())); @@ -772,9 +783,11 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { printer->Print(vars, " -1,\n"); } + printer->Print( + " ::google::protobuf::DescriptorPool::generated_pool(),\n"); + printer->Print(vars, + " ::google::protobuf::MessageFactory::generated_factory(),\n"); printer->Print(vars, - " ::google::protobuf::DescriptorPool::generated_pool(),\n" - " ::google::protobuf::MessageFactory::generated_factory(),\n" " sizeof($classname$));\n"); // Handle nested types. @@ -803,6 +816,13 @@ GenerateTypeRegistrations(io::Printer* printer) { void MessageGenerator:: GenerateDefaultInstanceAllocator(io::Printer* printer) { + // Construct the default instances of all fields, as they will be used + // when creating the default instance of the entire message. + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateDefaultInstanceAllocator(printer); + } + // Construct the default instance. We can't call InitAsDefaultInstance() yet // because we need to make sure all default instances that this one might // depend on are constructed first. @@ -846,6 +866,12 @@ GenerateShutdownCode(io::Printer* printer) { "classname", classname_); } + // Handle default instances of fields. + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateShutdownCode(printer); + } + // Handle nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { nested_generators_[i]->GenerateShutdownCode(printer); @@ -1957,6 +1983,7 @@ GenerateIsInitialized(io::Printer* printer) { for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && + !ShouldIgnoreRequiredFieldCheck(field) && HasRequiredFields(field->message_type())) { if (field->is_repeated()) { printer->Print( diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h index 04778f6d..a7e43d9c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.h +++ b/src/google/protobuf/compiler/cpp/cpp_message.h @@ -38,6 +38,7 @@ #include <string> #include <google/protobuf/stubs/common.h> #include <google/protobuf/compiler/cpp/cpp_field.h> +#include <google/protobuf/compiler/cpp/cpp_options.h> namespace google { namespace protobuf { @@ -57,7 +58,7 @@ class MessageGenerator { public: // See generator.cc for the meaning of dllexport_decl. explicit MessageGenerator(const Descriptor* descriptor, - const string& dllexport_decl); + const Options& options); ~MessageGenerator(); // Header stuff. @@ -153,7 +154,7 @@ class MessageGenerator { const Descriptor* descriptor_; string classname_; - string dllexport_decl_; + Options options_; FieldGeneratorMap field_generators_; scoped_array<scoped_ptr<MessageGenerator> > nested_generators_; scoped_array<scoped_ptr<EnumGenerator> > enum_generators_; diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc index 7c785e7e..447f975f 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -45,8 +45,9 @@ namespace cpp { namespace { void SetMessageVariables(const FieldDescriptor* descriptor, - map<string, string>* variables) { - SetCommonFieldVariables(descriptor, variables); + map<string, string>* variables, + const Options& options) { + SetCommonFieldVariables(descriptor, variables, options); (*variables)["type"] = FieldMessageTypeName(descriptor); (*variables)["stream_writer"] = (*variables)["declared_type"] + (HasFastArraySerialization(descriptor->message_type()->file()) ? @@ -59,9 +60,10 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // =================================================================== MessageFieldGenerator:: -MessageFieldGenerator(const FieldDescriptor* descriptor) +MessageFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) : descriptor_(descriptor) { - SetMessageVariables(descriptor, &variables_); + SetMessageVariables(descriptor, &variables_, options); } MessageFieldGenerator::~MessageFieldGenerator() {} @@ -76,7 +78,8 @@ 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:: @@ -103,6 +106,15 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " $type$* temp = $name$_;\n" " $name$_ = NULL;\n" " return temp;\n" + "}\n" + "inline void $classname$::set_allocated_$name$($type$* $name$) {\n" + " delete $name$_;\n" + " $name$_ = $name$;\n" + " if ($name$) {\n" + " set_has_$name$();\n" + " } else {\n" + " clear_has_$name$();\n" + " }\n" "}\n"); } @@ -167,9 +179,10 @@ GenerateByteSize(io::Printer* printer) const { // =================================================================== RepeatedMessageFieldGenerator:: -RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor) +RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) : descriptor_(descriptor) { - SetMessageVariables(descriptor, &variables_); + SetMessageVariables(descriptor, &variables_, options); } RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {} @@ -197,7 +210,7 @@ void RepeatedMessageFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { printer->Print(variables_, "inline const $type$& $classname$::$name$(int index) const {\n" - " return $name$_.Get(index);\n" + " return $name$_.$cppget$(index);\n" "}\n" "inline $type$* $classname$::mutable_$name$(int index) {\n" " return $name$_.Mutable(index);\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.h b/src/google/protobuf/compiler/cpp/cpp_message_field.h index f5147278..a5ed68a5 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.h @@ -46,7 +46,8 @@ namespace cpp { class MessageFieldGenerator : public FieldGenerator { public: - explicit MessageFieldGenerator(const FieldDescriptor* descriptor); + explicit MessageFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~MessageFieldGenerator(); // implements FieldGenerator --------------------------------------- @@ -71,7 +72,8 @@ class MessageFieldGenerator : public FieldGenerator { class RepeatedMessageFieldGenerator : public FieldGenerator { public: - explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor); + explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~RepeatedMessageFieldGenerator(); // implements FieldGenerator --------------------------------------- diff --git a/src/google/protobuf/compiler/cpp/cpp_options.h b/src/google/protobuf/compiler/cpp/cpp_options.h new file mode 100644 index 00000000..78770662 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_options.h @@ -0,0 +1,58 @@ +// 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: rennie@google.com (Jeffrey Rennie) + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_OPTIONS_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_OPTIONS_H__ + +#include <string> + +#include <google/protobuf/stubs/common.h> +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +// Generator options: +struct Options { + Options() : safe_boundary_check(false) { + } + string dllexport_decl; + bool safe_boundary_check; +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_OPTIONS_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc index 5e8df0f4..1c35fefa 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc @@ -80,8 +80,9 @@ int FixedSize(FieldDescriptor::Type type) { } void SetPrimitiveVariables(const FieldDescriptor* descriptor, - map<string, string>* variables) { - SetCommonFieldVariables(descriptor, variables); + map<string, string>* variables, + const Options& options) { + SetCommonFieldVariables(descriptor, variables, options); (*variables)["type"] = PrimitiveTypeName(descriptor->cpp_type()); (*variables)["default"] = DefaultValue(descriptor); (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); @@ -99,9 +100,10 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // =================================================================== PrimitiveFieldGenerator:: -PrimitiveFieldGenerator(const FieldDescriptor* descriptor) +PrimitiveFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) : descriptor_(descriptor) { - SetPrimitiveVariables(descriptor, &variables_); + SetPrimitiveVariables(descriptor, &variables_, options); } PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {} @@ -190,9 +192,10 @@ GenerateByteSize(io::Printer* printer) const { // =================================================================== RepeatedPrimitiveFieldGenerator:: -RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor) +RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) : descriptor_(descriptor) { - SetPrimitiveVariables(descriptor, &variables_); + SetPrimitiveVariables(descriptor, &variables_, options); if (descriptor->options().packed()) { variables_["packed_reader"] = "ReadPackedPrimitive"; @@ -366,7 +369,9 @@ GenerateByteSize(io::Printer* printer) const { " total_size += $tag_size$ +\n" " ::google::protobuf::internal::WireFormatLite::Int32Size(data_size);\n" "}\n" + "GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();\n" "_$name$_cached_byte_size_ = data_size;\n" + "GOOGLE_SAFE_CONCURRENT_WRITES_END();\n" "total_size += data_size;\n"); } else { printer->Print(variables_, diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.h b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h index 8fcd74ae..48249c40 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h @@ -46,7 +46,8 @@ namespace cpp { class PrimitiveFieldGenerator : public FieldGenerator { public: - explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor); + explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~PrimitiveFieldGenerator(); // implements FieldGenerator --------------------------------------- @@ -71,7 +72,8 @@ class PrimitiveFieldGenerator : public FieldGenerator { class RepeatedPrimitiveFieldGenerator : public FieldGenerator { public: - explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor); + explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~RepeatedPrimitiveFieldGenerator(); // implements FieldGenerator --------------------------------------- diff --git a/src/google/protobuf/compiler/cpp/cpp_service.cc b/src/google/protobuf/compiler/cpp/cpp_service.cc index c2825683..d20018ea 100644 --- a/src/google/protobuf/compiler/cpp/cpp_service.cc +++ b/src/google/protobuf/compiler/cpp/cpp_service.cc @@ -43,14 +43,14 @@ namespace compiler { namespace cpp { ServiceGenerator::ServiceGenerator(const ServiceDescriptor* descriptor, - const string& dllexport_decl) + const Options& options) : descriptor_(descriptor) { vars_["classname"] = descriptor_->name(); vars_["full_name"] = descriptor_->full_name(); - if (dllexport_decl.empty()) { + if (options.dllexport_decl.empty()) { vars_["dllexport"] = ""; } else { - vars_["dllexport"] = dllexport_decl + " "; + vars_["dllexport"] = options.dllexport_decl + " "; } } diff --git a/src/google/protobuf/compiler/cpp/cpp_service.h b/src/google/protobuf/compiler/cpp/cpp_service.h index 10e9dd3c..820f9f5f 100644 --- a/src/google/protobuf/compiler/cpp/cpp_service.h +++ b/src/google/protobuf/compiler/cpp/cpp_service.h @@ -38,6 +38,7 @@ #include <map> #include <string> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/cpp/cpp_options.h> #include <google/protobuf/descriptor.h> namespace google { @@ -55,7 +56,7 @@ class ServiceGenerator { public: // See generator.cc for the meaning of dllexport_decl. explicit ServiceGenerator(const ServiceDescriptor* descriptor, - const string& dllexport_decl); + const Options& options); ~ServiceGenerator(); // Header stuff. diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc index 3cec2b6b..9c0911ac 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -46,11 +46,14 @@ namespace cpp { namespace { void SetStringVariables(const FieldDescriptor* descriptor, - map<string, string>* variables) { - SetCommonFieldVariables(descriptor, variables); + map<string, string>* variables, + const Options& options) { + SetCommonFieldVariables(descriptor, variables, options); (*variables)["default"] = DefaultValue(descriptor); + (*variables)["default_length"] = + SimpleItoa(descriptor->default_value_string().length()); (*variables)["default_variable"] = descriptor->default_value_string().empty() - ? "::google::protobuf::internal::kEmptyString" + ? "&::google::protobuf::internal::kEmptyString" : "_default_" + FieldName(descriptor) + "_"; (*variables)["pointer_type"] = descriptor->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char"; @@ -61,9 +64,10 @@ void SetStringVariables(const FieldDescriptor* descriptor, // =================================================================== StringFieldGenerator:: -StringFieldGenerator(const FieldDescriptor* descriptor) +StringFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) : descriptor_(descriptor) { - SetStringVariables(descriptor, &variables_); + SetStringVariables(descriptor, &variables_, options); } StringFieldGenerator::~StringFieldGenerator() {} @@ -72,7 +76,7 @@ void StringFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { printer->Print(variables_, "::std::string* $name$_;\n"); if (!descriptor_->default_value_string().empty()) { - printer->Print(variables_, "static const ::std::string $default_variable$;\n"); + printer->Print(variables_, "static ::std::string* $default_variable$;\n"); } } @@ -109,7 +113,9 @@ 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"); + if (descriptor_->options().ctype() != FieldOptions::STRING) { printer->Outdent(); @@ -126,14 +132,14 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { "}\n" "inline void $classname$::set_$name$(const ::std::string& value) {\n" " set_has_$name$();\n" - " if ($name$_ == &$default_variable$) {\n" + " if ($name$_ == $default_variable$) {\n" " $name$_ = new ::std::string;\n" " }\n" " $name$_->assign(value);\n" "}\n" "inline void $classname$::set_$name$(const char* value) {\n" " set_has_$name$();\n" - " if ($name$_ == &$default_variable$) {\n" + " if ($name$_ == $default_variable$) {\n" " $name$_ = new ::std::string;\n" " }\n" " $name$_->assign(value);\n" @@ -141,20 +147,20 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { "inline " "void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n" " set_has_$name$();\n" - " if ($name$_ == &$default_variable$) {\n" + " if ($name$_ == $default_variable$) {\n" " $name$_ = new ::std::string;\n" " }\n" " $name$_->assign(reinterpret_cast<const char*>(value), size);\n" "}\n" "inline ::std::string* $classname$::mutable_$name$() {\n" " set_has_$name$();\n" - " if ($name$_ == &$default_variable$) {\n"); + " if ($name$_ == $default_variable$) {\n"); if (descriptor_->default_value_string().empty()) { printer->Print(variables_, " $name$_ = new ::std::string;\n"); } else { printer->Print(variables_, - " $name$_ = new ::std::string($default_variable$);\n"); + " $name$_ = new ::std::string(*$default_variable$);\n"); } printer->Print(variables_, " }\n" @@ -162,21 +168,34 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { "}\n" "inline ::std::string* $classname$::release_$name$() {\n" " clear_has_$name$();\n" - " if ($name$_ == &$default_variable$) {\n" + " if ($name$_ == $default_variable$) {\n" " return NULL;\n" " } else {\n" " ::std::string* temp = $name$_;\n" - " $name$_ = const_cast< ::std::string*>(&$default_variable$);\n" + " $name$_ = const_cast< ::std::string*>($default_variable$);\n" " return temp;\n" " }\n" + "}\n" + "inline void $classname$::set_allocated_$name$(::std::string* $name$) {\n" + " if ($name$_ != $default_variable$) {\n" + " delete $name$_;\n" + " }\n" + " if ($name$) {\n" + " set_has_$name$();\n" + " $name$_ = $name$;\n" + " } else {\n" + " clear_has_$name$();\n" + " $name$_ = const_cast< ::std::string*>($default_variable$);\n" + " }\n" "}\n"); } void StringFieldGenerator:: GenerateNonInlineAccessorDefinitions(io::Printer* printer) const { if (!descriptor_->default_value_string().empty()) { + // Initialized in GenerateDefaultInstanceAllocator. printer->Print(variables_, - "const ::std::string $classname$::$default_variable$($default$);\n"); + "::std::string* $classname$::$default_variable$ = NULL;\n"); } } @@ -184,13 +203,13 @@ void StringFieldGenerator:: GenerateClearingCode(io::Printer* printer) const { if (descriptor_->default_value_string().empty()) { printer->Print(variables_, - "if ($name$_ != &$default_variable$) {\n" + "if ($name$_ != $default_variable$) {\n" " $name$_->clear();\n" "}\n"); } else { printer->Print(variables_, - "if ($name$_ != &$default_variable$) {\n" - " $name$_->assign($default_variable$);\n" + "if ($name$_ != $default_variable$) {\n" + " $name$_->assign(*$default_variable$);\n" "}\n"); } } @@ -208,18 +227,35 @@ GenerateSwappingCode(io::Printer* printer) const { void StringFieldGenerator:: GenerateConstructorCode(io::Printer* printer) const { printer->Print(variables_, - "$name$_ = const_cast< ::std::string*>(&$default_variable$);\n"); + "$name$_ = const_cast< ::std::string*>($default_variable$);\n"); } void StringFieldGenerator:: GenerateDestructorCode(io::Printer* printer) const { printer->Print(variables_, - "if ($name$_ != &$default_variable$) {\n" + "if ($name$_ != $default_variable$) {\n" " delete $name$_;\n" "}\n"); } void StringFieldGenerator:: +GenerateDefaultInstanceAllocator(io::Printer* printer) const { + if (!descriptor_->default_value_string().empty()) { + printer->Print(variables_, + "$classname$::$default_variable$ =\n" + " new ::std::string($default$, $default_length$);\n"); + } +} + +void StringFieldGenerator:: +GenerateShutdownCode(io::Printer* printer) const { + if (!descriptor_->default_value_string().empty()) { + printer->Print(variables_, + "delete $classname$::$default_variable$;\n"); + } +} + +void StringFieldGenerator:: GenerateMergeFromCodedStream(io::Printer* printer) const { printer->Print(variables_, "DO_(::google::protobuf::internal::WireFormatLite::Read$declared_type$(\n" @@ -273,9 +309,10 @@ GenerateByteSize(io::Printer* printer) const { // =================================================================== RepeatedStringFieldGenerator:: -RepeatedStringFieldGenerator(const FieldDescriptor* descriptor) +RepeatedStringFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) : descriptor_(descriptor) { - SetStringVariables(descriptor, &variables_); + SetStringVariables(descriptor, &variables_, options); } RepeatedStringFieldGenerator::~RepeatedStringFieldGenerator() {} @@ -328,7 +365,7 @@ void RepeatedStringFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { printer->Print(variables_, "inline const ::std::string& $classname$::$name$(int index) const {\n" - " return $name$_.Get(index);\n" + " return $name$_.$cppget$(index);\n" "}\n" "inline ::std::string* $classname$::mutable_$name$(int index) {\n" " return $name$_.Mutable(index);\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.h b/src/google/protobuf/compiler/cpp/cpp_string_field.h index 7f45107d..3264134a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.h @@ -46,7 +46,8 @@ namespace cpp { class StringFieldGenerator : public FieldGenerator { public: - explicit StringFieldGenerator(const FieldDescriptor* descriptor); + explicit StringFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~StringFieldGenerator(); // implements FieldGenerator --------------------------------------- @@ -59,6 +60,8 @@ class StringFieldGenerator : public FieldGenerator { void GenerateSwappingCode(io::Printer* printer) const; void GenerateConstructorCode(io::Printer* printer) const; void GenerateDestructorCode(io::Printer* printer) const; + void GenerateDefaultInstanceAllocator(io::Printer* printer) const; + void GenerateShutdownCode(io::Printer* printer) const; void GenerateMergeFromCodedStream(io::Printer* printer) const; void GenerateSerializeWithCachedSizes(io::Printer* printer) const; void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; @@ -73,7 +76,8 @@ class StringFieldGenerator : public FieldGenerator { class RepeatedStringFieldGenerator : public FieldGenerator { public: - explicit RepeatedStringFieldGenerator(const FieldDescriptor* descriptor); + explicit RepeatedStringFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~RepeatedStringFieldGenerator(); // implements FieldGenerator --------------------------------------- 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 54d830fc..e14a818c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto +++ b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto @@ -103,9 +103,19 @@ message TestConflictingSymbolNames { message DO {} optional DO do = 32; + // Some template parameter names for extensions. + optional int32 field_type = 33; + optional bool is_packed = 34; + extensions 1000 to max; } +message TestConflictingSymbolNamesExtension { + extend TestConflictingSymbolNames { + repeated int32 repeated_int32_ext = 20423638 [packed=true]; + } +} + message DummyMessage {} service TestConflictingMethodNames { diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc index 301a7ce6..b5f9ab58 100644 --- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -44,6 +44,8 @@ // correctly and produces the interfaces we expect, which is why this test // is written this way. +#include <google/protobuf/compiler/cpp/cpp_unittest.h> + #include <vector> #include <google/protobuf/unittest.pb.h> @@ -64,7 +66,7 @@ #include <google/protobuf/stubs/substitute.h> #include <google/protobuf/testing/googletest.h> #include <gtest/gtest.h> -#include <google/protobuf/stubs/stl_util-inl.h> +#include <google/protobuf/stubs/stl_util.h> namespace google { namespace protobuf { @@ -74,6 +76,8 @@ namespace cpp { // Can't use an anonymous namespace here due to brokenness of Tru64 compiler. namespace cpp_unittest { +namespace protobuf_unittest = ::protobuf_unittest; + class MockErrorCollector : public MultiFileErrorCollector { public: @@ -174,6 +178,15 @@ TEST(GeneratedMessageTest, Trigraph) { EXPECT_EQ("? ? ?? ?? ??? ?\?/ ?\?-", extreme_default.cpp_trigraph()); } +TEST(GeneratedMessageTest, ExtremeSmallIntegerDefault) { + const unittest::TestExtremeDefaultValues& extreme_default = + unittest::TestExtremeDefaultValues::default_instance(); + EXPECT_EQ(-0x80000000, kint32min); + EXPECT_EQ(GOOGLE_LONGLONG(-0x8000000000000000), kint64min); + EXPECT_EQ(kint32min, extreme_default.really_small_int32()); + EXPECT_EQ(kint64min, extreme_default.really_small_int64()); +} + TEST(GeneratedMessageTest, Accessors) { // Set every field to a unique value then go back and check all those // values. @@ -202,6 +215,13 @@ TEST(GeneratedMessageTest, MutableStringDefault) { EXPECT_EQ("hello", *message.mutable_default_string()); } +TEST(GeneratedMessageTest, StringDefaults) { + unittest::TestExtremeDefaultValues message; + // Check if '\000' can be used in default string value. + EXPECT_EQ(string("hel\000lo", 6), message.string_with_zero()); + EXPECT_EQ(string("wor\000ld", 6), message.bytes_with_zero()); +} + TEST(GeneratedMessageTest, ReleaseString) { // Check that release_foo() starts out NULL, and gives us a value // that we can delete after it's been set. @@ -244,6 +264,49 @@ TEST(GeneratedMessageTest, ReleaseMessage) { EXPECT_FALSE(message.has_optional_nested_message()); } +TEST(GeneratedMessageTest, SetAllocatedString) { + // Check that set_allocated_foo() works for strings. + unittest::TestAllTypes message; + + EXPECT_FALSE(message.has_optional_string()); + const string kHello("hello"); + message.set_optional_string(kHello); + EXPECT_TRUE(message.has_optional_string()); + + message.set_allocated_optional_string(NULL); + EXPECT_FALSE(message.has_optional_string()); + EXPECT_EQ("", message.optional_string()); + + message.set_allocated_optional_string(new string(kHello)); + EXPECT_TRUE(message.has_optional_string()); + EXPECT_EQ(kHello, message.optional_string()); +} + +TEST(GeneratedMessageTest, SetAllocatedMessage) { + // Check that set_allocated_foo() can be called in all cases. + unittest::TestAllTypes message; + + EXPECT_FALSE(message.has_optional_nested_message()); + + message.mutable_optional_nested_message()->set_bb(1); + EXPECT_TRUE(message.has_optional_nested_message()); + + message.set_allocated_optional_nested_message(NULL); + EXPECT_FALSE(message.has_optional_nested_message()); + EXPECT_EQ(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.optional_nested_message()); + + message.mutable_optional_nested_message()->set_bb(1); + unittest::TestAllTypes::NestedMessage* nest = + message.release_optional_nested_message(); + ASSERT_TRUE(nest != NULL); + EXPECT_FALSE(message.has_optional_nested_message()); + + message.set_allocated_optional_nested_message(nest); + EXPECT_TRUE(message.has_optional_nested_message()); + EXPECT_EQ(1, message.optional_nested_message().bb()); +} + TEST(GeneratedMessageTest, Clear) { // Set every field to a unique value, clear the message, then check that // it is cleared. @@ -695,6 +758,13 @@ TEST(GeneratedMessageTest, TestConflictingSymbolNames) { message.set_friend_(5); EXPECT_EQ(5, message.friend_()); + + // Instantiate extension template functions to test conflicting template + // parameter names. + typedef protobuf_unittest::TestConflictingSymbolNamesExtension ExtensionMessage; + message.AddExtension(ExtensionMessage::repeated_int32_ext, 123); + EXPECT_EQ(123, + message.GetExtension(ExtensionMessage::repeated_int32_ext, 0)); } #ifndef PROTOBUF_TEST_NO_DESCRIPTORS @@ -869,11 +939,10 @@ TEST(GeneratedEnumTest, MinAndMax) { EXPECT_NE(null_pointer, &unittest::ForeignEnum_MAX); EXPECT_NE(null_pointer, &unittest::ForeignEnum_ARRAYSIZE); - // Make sure we can use _MIN, _MAX and _ARRAYSIZE as switch cases. + // Make sure we can use _MIN and _MAX as switch cases. switch (unittest::SPARSE_A) { case unittest::TestSparseEnum_MIN: case unittest::TestSparseEnum_MAX: - case unittest::TestSparseEnum_ARRAYSIZE: break; default: break; diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.h b/src/google/protobuf/compiler/cpp/cpp_unittest.h new file mode 100644 index 00000000..a3a1d1ba --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.h @@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This header declares the namespace google::protobuf::protobuf_unittest in order to expose +// any problems with the generated class names. We use this header to ensure +// unittest.cc will declare the namespace prior to other includes, while obeying +// normal include ordering. +// +// When generating a class name of "foo.Bar" we must ensure we prefix the class +// name with "::", in case the namespace google::protobuf::foo exists. We intentionally +// trigger that case here by declaring google::protobuf::protobuf_unittest. +// +// See ClassName in helpers.h for more details. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_UNITTEST_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_UNITTEST_H__ + +namespace google { +namespace protobuf { +namespace protobuf_unittest {} +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_UNITTEST_H__ diff --git a/src/google/protobuf/compiler/importer.h b/src/google/protobuf/compiler/importer.h index 7a2efc29..7a62fa0e 100644 --- a/src/google/protobuf/compiler/importer.h +++ b/src/google/protobuf/compiler/importer.h @@ -280,8 +280,9 @@ class LIBPROTOBUF_EXPORT DiskSourceTree : public SourceTree { string virtual_path; string disk_path; - inline Mapping(const string& virtual_path, const string& disk_path) - : virtual_path(virtual_path), disk_path(disk_path) {} + inline Mapping(const string& virtual_path_param, + const string& disk_path_param) + : virtual_path(virtual_path_param), disk_path(disk_path_param) {} }; vector<Mapping> mappings_; diff --git a/src/google/protobuf/compiler/java/java_doc_comment.cc b/src/google/protobuf/compiler/java/java_doc_comment.cc new file mode 100644 index 00000000..60b4f2ac --- /dev/null +++ b/src/google/protobuf/compiler/java/java_doc_comment.cc @@ -0,0 +1,236 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include <google/protobuf/compiler/java/java_doc_comment.h> + +#include <vector> + +#include <google/protobuf/io/printer.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +string EscapeJavadoc(const string& input) { + string result; + result.reserve(input.size() * 2); + + char prev = '*'; + + for (string::size_type i = 0; i < input.size(); i++) { + char c = input[i]; + switch (c) { + case '*': + // Avoid "/*". + if (prev == '/') { + result.append("*"); + } else { + result.push_back(c); + } + break; + case '/': + // Avoid "*/". + if (prev == '*') { + result.append("/"); + } else { + result.push_back(c); + } + break; + case '@': + // "{@" starts Javadoc markup. + if (prev == '{') { + result.append("@"); + } else { + result.push_back(c); + } + break; + case '<': + // Avoid interpretation as HTML. + result.append("<"); + break; + case '>': + // Avoid interpretation as HTML. + result.append(">"); + break; + case '&': + // Avoid interpretation as HTML. + result.append("&"); + break; + case '\\': + // Java interprets Unicode escape sequences anywhere! + result.append("\"); + break; + default: + result.push_back(c); + break; + } + + prev = c; + } + + return result; +} + +static void WriteDocCommentBodyForLocation( + io::Printer* printer, const SourceLocation& location) { + string comments = location.leading_comments.empty() ? + location.trailing_comments : location.leading_comments; + if (!comments.empty()) { + // TODO(kenton): Ideally we should parse the comment text as Markdown and + // write it back as HTML, but this requires a Markdown parser. For now + // we just use <pre> to get fixed-width text formatting. + + // If the comment itself contains block comment start or end markers, + // HTML-escape them so that they don't accidentally close the doc comment. + comments = EscapeJavadoc(comments); + + vector<string> lines; + SplitStringAllowEmpty(comments, "\n", &lines); + while (!lines.empty() && lines.back().empty()) { + lines.pop_back(); + } + + printer->Print( + " *\n" + " * <pre>\n"); + for (int i = 0; i < lines.size(); i++) { + // Most lines should start with a space. Watch out for lines that start + // with a /, since putting that right after the leading asterisk will + // close the comment. + if (!lines[i].empty() && lines[i][0] == '/') { + printer->Print(" * $line$\n", "line", lines[i]); + } else { + printer->Print(" *$line$\n", "line", lines[i]); + } + } + printer->Print(" * </pre>\n"); + } +} + +template <typename DescriptorType> +static void WriteDocCommentBody( + io::Printer* printer, const DescriptorType* descriptor) { + SourceLocation location; + if (descriptor->GetSourceLocation(&location)) { + WriteDocCommentBodyForLocation(printer, location); + } +} + +static string FirstLineOf(const string& value) { + string result = value; + + string::size_type pos = result.find_first_of('\n'); + if (pos != string::npos) { + result.erase(pos); + } + + // If line ends in an opening brace, make it "{ ... }" so it looks nice. + if (!result.empty() && result[result.size() - 1] == '{') { + result.append(" ... }"); + } + + return result; +} + +void WriteMessageDocComment(io::Printer* printer, const Descriptor* message) { + printer->Print( + "/**\n" + " * Protobuf type {@code $fullname$}\n", + "fullname", EscapeJavadoc(message->full_name())); + WriteDocCommentBody(printer, message); + printer->Print(" */\n"); +} + +void WriteFieldDocComment(io::Printer* printer, const FieldDescriptor* field) { + // In theory we should have slightly different comments for setters, getters, + // etc., but in practice everyone already knows the difference between these + // so it's redundant information. + + // We use the field declaration as the first line of the comment, e.g.: + // optional string foo = 5; + // This communicates a lot of information about the field in a small space. + // If the field is a group, the debug string might end with {. + printer->Print( + "/**\n" + " * <code>$def$</code>\n", + "def", EscapeJavadoc(FirstLineOf(field->DebugString()))); + WriteDocCommentBody(printer, field); + printer->Print(" */\n"); +} + +void WriteEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_) { + printer->Print( + "/**\n" + " * Protobuf enum {@code $fullname$}\n", + "fullname", EscapeJavadoc(enum_->full_name())); + WriteDocCommentBody(printer, enum_); + printer->Print(" */\n"); +} + +void WriteEnumValueDocComment(io::Printer* printer, + const EnumValueDescriptor* value) { + printer->Print( + "/**\n" + " * <code>$def$</code>\n", + "def", EscapeJavadoc(FirstLineOf(value->DebugString()))); + WriteDocCommentBody(printer, value); + printer->Print(" */\n"); +} + +void WriteServiceDocComment(io::Printer* printer, + const ServiceDescriptor* service) { + printer->Print( + "/**\n" + " * Protobuf service {@code $fullname$}\n", + "fullname", EscapeJavadoc(service->full_name())); + WriteDocCommentBody(printer, service); + printer->Print(" */\n"); +} + +void WriteMethodDocComment(io::Printer* printer, + const MethodDescriptor* method) { + printer->Print( + "/**\n" + " * <code>$def$</code>\n", + "def", EscapeJavadoc(FirstLineOf(method->DebugString()))); + WriteDocCommentBody(printer, method); + printer->Print(" */\n"); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_doc_comment.h b/src/google/protobuf/compiler/java/java_doc_comment.h new file mode 100644 index 00000000..f77720b6 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_doc_comment.h @@ -0,0 +1,69 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_DOC_COMMENT_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_DOC_COMMENT_H__ + +#include <google/protobuf/descriptor.h> + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +void WriteMessageDocComment(io::Printer* printer, const Descriptor* message); +void WriteFieldDocComment(io::Printer* printer, const FieldDescriptor* field); +void WriteEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_); +void WriteEnumValueDocComment(io::Printer* printer, + const EnumValueDescriptor* value); +void WriteServiceDocComment(io::Printer* printer, + const ServiceDescriptor* service); +void WriteMethodDocComment(io::Printer* printer, + const MethodDescriptor* method); + +// Exposed for testing only. +string EscapeJavadoc(const string& input); + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_DOC_COMMENT_H__ diff --git a/src/google/protobuf/compiler/java/java_doc_comment_unittest.cc b/src/google/protobuf/compiler/java/java_doc_comment_unittest.cc new file mode 100644 index 00000000..28b6d8b4 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_doc_comment_unittest.cc @@ -0,0 +1,66 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) + +#include <google/protobuf/compiler/java/java_doc_comment.h> + +#include <gtest/gtest.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { +namespace { + +TEST(JavaDocCommentTest, Escaping) { + EXPECT_EQ("foo /* bar */ baz", EscapeJavadoc("foo /* bar */ baz")); + EXPECT_EQ("foo /*/ baz", EscapeJavadoc("foo /*/ baz")); + EXPECT_EQ("{@foo}", EscapeJavadoc("{@foo}")); + EXPECT_EQ("<i>&</i>", EscapeJavadoc("<i>&</i>")); + EXPECT_EQ("foo\u1234bar", EscapeJavadoc("foo\\u1234bar")); +} + +// TODO(kenton): It's hard to write a robust test of the doc comments -- we +// can only really compare the output against a golden value, which is a +// fairly tedious and fragile testing strategy. If we want to go that route, +// it probably makes sense to bite the bullet and write a test that compares +// the whole generated output for unittest.proto against a golden value, with +// a very simple script that can be run to regenerate it with the latest code. +// This would mean that updates to the golden file would have to be included +// in any change to the code generator, which would actually be fairly useful +// as it allows the reviewer to see clearly how the generated code is +// changing. + +} // namespace +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_enum.cc b/src/google/protobuf/compiler/java/java_enum.cc index 9d7bcab6..cfed815f 100644 --- a/src/google/protobuf/compiler/java/java_enum.cc +++ b/src/google/protobuf/compiler/java/java_enum.cc @@ -36,6 +36,7 @@ #include <string> #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/io/printer.h> #include <google/protobuf/descriptor.pb.h> @@ -67,6 +68,7 @@ EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor) EnumGenerator::~EnumGenerator() {} void EnumGenerator::Generate(io::Printer* printer) { + WriteEnumDocComment(printer, descriptor_); if (HasDescriptorMethods(descriptor_)) { printer->Print( "public enum $classname$\n" @@ -85,6 +87,7 @@ void EnumGenerator::Generate(io::Printer* printer) { vars["name"] = canonical_values_[i]->name(); vars["index"] = SimpleItoa(canonical_values_[i]->index()); vars["number"] = SimpleItoa(canonical_values_[i]->number()); + WriteEnumValueDocComment(printer, canonical_values_[i]); printer->Print(vars, "$name$($index$, $number$),\n"); } @@ -100,6 +103,7 @@ void EnumGenerator::Generate(io::Printer* printer) { vars["classname"] = descriptor_->name(); vars["name"] = aliases_[i].value->name(); vars["canonical_name"] = aliases_[i].canonical_value->name(); + WriteEnumValueDocComment(printer, aliases_[i].value); printer->Print(vars, "public static final $classname$ $name$ = $canonical_name$;\n"); } @@ -108,6 +112,7 @@ void EnumGenerator::Generate(io::Printer* printer) { map<string, string> vars; vars["name"] = descriptor_->value(i)->name(); vars["number"] = SimpleItoa(descriptor_->value(i)->number()); + WriteEnumValueDocComment(printer, descriptor_->value(i)); printer->Print(vars, "public static final int $name$_VALUE = $number$;\n"); } @@ -187,19 +192,30 @@ void EnumGenerator::Generate(io::Printer* printer) { printer->Print( "}\n" "\n" - "private static final $classname$[] VALUES = {\n" - " ", + "private static final $classname$[] VALUES = ", "classname", descriptor_->name()); - for (int i = 0; i < descriptor_->value_count(); i++) { - printer->Print("$name$, ", - "name", descriptor_->value(i)->name()); + if (CanUseEnumValues()) { + // If the constants we are going to output are exactly the ones we + // have declared in the Java enum in the same order, then we can use + // the values() method that the Java compiler automatically generates + // for every enum. + printer->Print("values();\n"); + } else { + printer->Print( + "{\n" + " "); + for (int i = 0; i < descriptor_->value_count(); i++) { + printer->Print("$name$, ", + "name", descriptor_->value(i)->name()); + } + printer->Print( + "\n" + "};\n"); } printer->Print( "\n" - "};\n" - "\n" "public static $classname$ valueOf(\n" " com.google.protobuf.Descriptors.EnumValueDescriptor desc) {\n" " if (desc.getType() != getDescriptor()) {\n" @@ -237,6 +253,18 @@ void EnumGenerator::Generate(io::Printer* printer) { printer->Print("}\n\n"); } +bool EnumGenerator::CanUseEnumValues() { + if (canonical_values_.size() != descriptor_->value_count()) { + return false; + } + for (int i = 0; i < descriptor_->value_count(); i++) { + if (descriptor_->value(i)->name() != canonical_values_[i]->name()) { + return false; + } + } + return true; +} + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_enum.h b/src/google/protobuf/compiler/java/java_enum.h index 05ece1f1..9a9e5742 100644 --- a/src/google/protobuf/compiler/java/java_enum.h +++ b/src/google/protobuf/compiler/java/java_enum.h @@ -73,6 +73,8 @@ class EnumGenerator { }; vector<Alias> aliases_; + 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 72caa10b..ec0b067e 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.cc +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -36,6 +36,7 @@ #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_helpers.h> #include <google/protobuf/io/printer.h> @@ -75,6 +76,7 @@ void SetEnumVariables(const FieldDescriptor* descriptor, // 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); (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); (*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex); @@ -86,6 +88,13 @@ void SetEnumVariables(const FieldDescriptor* descriptor, (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); + // For repeated fields, one bit is used for whether the array is immutable + // in the parsing constructor. + (*variables)["get_mutable_bit_parser"] = + GenerateGetBitMutableLocal(builderBitIndex); + (*variables)["set_mutable_bit_parser"] = + GenerateSetBitMutableLocal(builderBitIndex); + (*variables)["get_has_field_bit_from_local"] = GenerateGetBitFromLocal(builderBitIndex); (*variables)["set_has_field_bit_to_local"] = @@ -117,18 +126,25 @@ int EnumFieldGenerator::GetNumBitsForBuilder() const { void EnumFieldGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$boolean has$capitalized_name$();\n" "$deprecation$$type$ get$capitalized_name$();\n"); } void EnumFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private $type$ $name$_;\n" + "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" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" " return $name$_;\n" "}\n"); @@ -137,13 +153,19 @@ GenerateMembers(io::Printer* printer) const { void EnumFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, - "private $type$ $name$_ = $default$;\n" + "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" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" " return $name$_;\n" - "}\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" @@ -152,7 +174,9 @@ GenerateBuilderMembers(io::Printer* printer) const { " $name$_ = value;\n" " $on_changed$\n" " return this;\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder clear$capitalized_name$() {\n" " $clear_has_field_bit_builder$;\n" " $name$_ = $default$;\n" @@ -210,12 +234,17 @@ GenerateParsingCode(io::Printer* printer) const { "if (value != null) {\n"); } printer->Print(variables_, - " $set_has_field_bit_builder$;\n" + " $set_has_field_bit_message$;\n" " $name$_ = value;\n" "}\n"); } void EnumFieldGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // noop for enums +} + +void EnumFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, "if ($get_has_field_bit_message$) {\n" @@ -273,22 +302,33 @@ int RepeatedEnumFieldGenerator::GetNumBitsForBuilder() const { void RepeatedEnumFieldGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.util.List<$type$> get$capitalized_name$List();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Count();\n"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$java.util.List<$type$> get$capitalized_name$List();\n" - "$deprecation$int get$capitalized_name$Count();\n" "$deprecation$$type$ get$capitalized_name$(int index);\n"); } void RepeatedEnumFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private java.util.List<$type$> $name$_;\n" + "private java.util.List<$type$> $name$_;\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" " return $name$_;\n" // note: unmodifiable list - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public int get$capitalized_name$Count() {\n" " return $name$_.size();\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$(int index) {\n" " return $name$_.get(index);\n" "}\n"); @@ -320,21 +360,29 @@ GenerateBuilderMembers(io::Printer* printer) const { " $name$_ = new java.util.ArrayList<$type$>($name$_);\n" " $set_mutable_bit_builder$;\n" " }\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, // Note: We return an unmodifiable list because otherwise the caller // could hold on to the returned list and modify it after the message // has been built, thus mutating the message which is supposed to be // immutable. "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" " return java.util.Collections.unmodifiableList($name$_);\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public int get$capitalized_name$Count() {\n" " return $name$_.size();\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$(int index) {\n" " return $name$_.get(index);\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder set$capitalized_name$(\n" " int index, $type$ value) {\n" " if (value == null) {\n" @@ -344,7 +392,9 @@ GenerateBuilderMembers(io::Printer* printer) const { " $name$_.set(index, value);\n" " $on_changed$\n" " return this;\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder add$capitalized_name$($type$ value) {\n" " if (value == null) {\n" " throw new NullPointerException();\n" @@ -353,14 +403,18 @@ GenerateBuilderMembers(io::Printer* printer) const { " $name$_.add(value);\n" " $on_changed$\n" " return this;\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder addAll$capitalized_name$(\n" " java.lang.Iterable<? extends $type$> values) {\n" " ensure$capitalized_name$IsMutable();\n" " super.addAll(values, $name$_);\n" " $on_changed$\n" " return this;\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder clear$capitalized_name$() {\n" " $name$_ = java.util.Collections.emptyList();\n" " $clear_mutable_bit_builder$;\n" @@ -434,7 +488,11 @@ GenerateParsingCode(io::Printer* printer) const { "if (value != null) {\n"); } printer->Print(variables_, - " add$capitalized_name$(value);\n" + " if (!$get_mutable_bit_parser$) {\n" + " $name$_ = new java.util.ArrayList<$type$>();\n" + " $set_mutable_bit_parser$;\n" + " }\n" + " $name$_.add(value);\n" "}\n"); } @@ -457,6 +515,14 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const { } void RepeatedEnumFieldGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_mutable_bit_parser$) {\n" + " $name$_ = java.util.Collections.unmodifiableList($name$_);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { if (descriptor_->options().packed()) { printer->Print(variables_, diff --git a/src/google/protobuf/compiler/java/java_enum_field.h b/src/google/protobuf/compiler/java/java_enum_field.h index 0cad6be0..90fae639 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.h +++ b/src/google/protobuf/compiler/java/java_enum_field.h @@ -61,6 +61,7 @@ class EnumFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; @@ -96,6 +97,7 @@ class RepeatedEnumFieldGenerator : public FieldGenerator { void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateParsingCodeFromPacked(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/java/java_extension.cc b/src/google/protobuf/compiler/java/java_extension.cc index 9b147c77..921fe658 100644 --- a/src/google/protobuf/compiler/java/java_extension.cc +++ b/src/google/protobuf/compiler/java/java_extension.cc @@ -33,6 +33,7 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include <google/protobuf/compiler/java/java_extension.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/io/printer.h> @@ -130,6 +131,7 @@ void ExtensionGenerator::Generate(io::Printer* printer) { printer->Print(vars, "public static final int $constant_name$ = $number$;\n"); + WriteFieldDocComment(printer, descriptor_); if (HasDescriptorMethods(descriptor_->file())) { // Non-lite extensions if (descriptor_->extension_scope() == NULL) { diff --git a/src/google/protobuf/compiler/java/java_field.h b/src/google/protobuf/compiler/java/java_field.h index 6097f357..4dd0efd6 100644 --- a/src/google/protobuf/compiler/java/java_field.h +++ b/src/google/protobuf/compiler/java/java_field.h @@ -66,6 +66,7 @@ class FieldGenerator { virtual void GenerateBuildingCode(io::Printer* printer) const = 0; virtual void GenerateParsingCode(io::Printer* printer) const = 0; virtual void GenerateParsingCodeFromPacked(io::Printer* printer) const; + virtual void GenerateParsingDoneCode(io::Printer* printer) const = 0; virtual void GenerateSerializationCode(io::Printer* printer) const = 0; virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; virtual void GenerateFieldBuilderInitializationCode(io::Printer* printer) diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc index 8968069f..f43e5500 100644 --- a/src/google/protobuf/compiler/java/java_file.cc +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -42,6 +42,7 @@ #include <google/protobuf/io/printer.h> #include <google/protobuf/io/zero_copy_stream.h> #include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/dynamic_message.h> #include <google/protobuf/stubs/strutil.h> namespace google { @@ -51,18 +52,24 @@ namespace java { namespace { -// Recursively searches the given message to see if it contains any extensions. -bool UsesExtensions(const Message& message) { + +// Recursively searches the given message to collect extensions. +// Returns true if all the extensions can be recognized. The extensions will be +// appended in to the extensions parameter. +// Returns false when there are unknown fields, in which case the data in the +// extensions output parameter is not reliable and should be discarded. +bool CollectExtensions(const Message& message, + vector<const FieldDescriptor*>* extensions) { const Reflection* reflection = message.GetReflection(); - // We conservatively assume that unknown fields are extensions. - if (reflection->GetUnknownFields(message).field_count() > 0) return true; + // There are unknown fields that could be extensions, thus this call fails. + if (reflection->GetUnknownFields(message).field_count() > 0) return false; vector<const FieldDescriptor*> fields; reflection->ListFields(message, &fields); for (int i = 0; i < fields.size(); i++) { - if (fields[i]->is_extension()) return true; + if (fields[i]->is_extension()) extensions->push_back(fields[i]); if (GetJavaType(fields[i]) == JAVATYPE_MESSAGE) { if (fields[i]->is_repeated()) { @@ -70,16 +77,56 @@ bool UsesExtensions(const Message& message) { for (int j = 0; j < size; j++) { const Message& sub_message = reflection->GetRepeatedMessage(message, fields[i], j); - if (UsesExtensions(sub_message)) return true; + if (!CollectExtensions(sub_message, extensions)) return false; } } else { const Message& sub_message = reflection->GetMessage(message, fields[i]); - if (UsesExtensions(sub_message)) return true; + if (!CollectExtensions(sub_message, extensions)) return false; } } } - return false; + return true; +} + +// Finds all extensions in the given message and its sub-messages. If the +// message contains unknown fields (which could be extensions), then those +// extensions are defined in alternate_pool. +// The message will be converted to a DynamicMessage backed by alternate_pool +// in order to handle this case. +void CollectExtensions(const FileDescriptorProto& file_proto, + const DescriptorPool& alternate_pool, + vector<const FieldDescriptor*>* extensions, + const string& file_data) { + if (!CollectExtensions(file_proto, extensions)) { + // There are unknown fields in the file_proto, which are probably + // extensions. We need to parse the data into a dynamic message based on the + // builder-pool to find out all extensions. + const Descriptor* file_proto_desc = alternate_pool.FindMessageTypeByName( + file_proto.GetDescriptor()->full_name()); + GOOGLE_CHECK(file_proto_desc) + << "Find unknown fields in FileDescriptorProto when building " + << file_proto.name() + << ". It's likely that those fields are custom options, however, " + "descriptor.proto is not in the transitive dependencies. " + "This normally should not happen. Please report a bug."; + DynamicMessageFactory factory; + scoped_ptr<Message> dynamic_file_proto( + factory.GetPrototype(file_proto_desc)->New()); + GOOGLE_CHECK(dynamic_file_proto.get() != NULL); + GOOGLE_CHECK(dynamic_file_proto->ParseFromString(file_data)); + + // Collect the extensions again from the dynamic message. There should be no + // more unknown fields this time, i.e. all the custom options should be + // parsed as extensions now. + extensions->clear(); + GOOGLE_CHECK(CollectExtensions(*dynamic_file_proto, extensions)) + << "Find unknown fields in FileDescriptorProto when building " + << file_proto.name() + << ". It's likely that those fields are custom options, however, " + "those options cannot be recognized in the builder pool. " + "This normally should not happen. Please report a bug."; + } } @@ -306,19 +353,32 @@ void FileGenerator::GenerateEmbeddedDescriptor(io::Printer* printer) { .GenerateNonNestedInitializationCode(printer); } - if (UsesExtensions(file_proto)) { - // Must construct an ExtensionRegistry containing all possible extensions + // Proto compiler builds a DescriptorPool, which holds all the descriptors to + // generate, when processing the ".proto" files. We call this DescriptorPool + // the parsed pool (a.k.a. file_->pool()). + // + // Note that when users try to extend the (.*)DescriptorProto in their + // ".proto" files, it does not affect the pre-built FileDescriptorProto class + // in proto compiler. When we put the descriptor data in the file_proto, those + // extensions become unknown fields. + // + // Now we need to find out all the extension value to the (.*)DescriptorProto + // in the file_proto message, and prepare an ExtensionRegistry to return. + // + // 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 + 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. printer->Print( "com.google.protobuf.ExtensionRegistry registry =\n" - " com.google.protobuf.ExtensionRegistry.newInstance();\n" - "registerAllExtensions(registry);\n"); - for (int i = 0; i < file_->dependency_count(); i++) { - if (ShouldIncludeDependency(file_->dependency(i))) { - printer->Print( - "$dependency$.registerAllExtensions(registry);\n", - "dependency", ClassName(file_->dependency(i))); - } + " com.google.protobuf.ExtensionRegistry.newInstance();\n"); + for (int i = 0; i < extensions.size(); i++) { + ExtensionGenerator(extensions[i]).GenerateRegistrationCode(printer); } printer->Print( "return registry;\n"); @@ -375,7 +435,9 @@ static void GenerateSibling(const string& package_dir, printer.Print( "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" - "\n"); + "// source: $filename$\n" + "\n", + "filename", descriptor->file()->name()); if (!java_package.empty()) { printer.Print( "package $package$;\n" diff --git a/src/google/protobuf/compiler/java/java_helpers.cc b/src/google/protobuf/compiler/java/java_helpers.cc index 1b6f1653..cf241b8a 100644 --- a/src/google/protobuf/compiler/java/java_helpers.cc +++ b/src/google/protobuf/compiler/java/java_helpers.cc @@ -177,6 +177,18 @@ string ToJavaName(const string& full_name, const FileDescriptor* file) { return result; } +string ClassName(const Descriptor* descriptor) { + return ToJavaName(descriptor->full_name(), descriptor->file()); +} + +string ClassName(const EnumDescriptor* descriptor) { + return ToJavaName(descriptor->full_name(), descriptor->file()); +} + +string ClassName(const ServiceDescriptor* descriptor) { + return ToJavaName(descriptor->full_name(), descriptor->file()); +} + string ClassName(const FileDescriptor* descriptor) { string result = FileJavaPackage(descriptor); if (!result.empty()) result += '.'; @@ -326,14 +338,14 @@ string DefaultValue(const FieldDescriptor* field) { } else { // See comments in Internal.java for gory details. return strings::Substitute( - "com.google.protobuf.Internal.stringDefaultValue(\"$0\")", - CEscape(field->default_value_string())); + "com.google.protobuf.Internal.stringDefaultValue(\"$0\")", + CEscape(field->default_value_string())); } } case FieldDescriptor::CPPTYPE_ENUM: return ClassName(field->enum_type()) + "." + - field->default_value_enum()->name(); + field->default_value_enum()->name(); case FieldDescriptor::CPPTYPE_MESSAGE: return ClassName(field->message_type()) + ".getDefaultInstance()"; @@ -427,8 +439,10 @@ string GetBitFieldNameForBit(int bitIndex) { return GetBitFieldName(bitIndex / 32); } -string GenerateGetBit(int bitIndex) { - string varName = GetBitFieldNameForBit(bitIndex); +namespace { + +string GenerateGetBitInternal(const string& prefix, int bitIndex) { + string varName = prefix + GetBitFieldNameForBit(bitIndex); int bitInVarIndex = bitIndex % 32; string mask = bit_masks[bitInVarIndex]; @@ -436,8 +450,8 @@ string GenerateGetBit(int bitIndex) { return result; } -string GenerateSetBit(int bitIndex) { - string varName = GetBitFieldNameForBit(bitIndex); +string GenerateSetBitInternal(const string& prefix, int bitIndex) { + string varName = prefix + GetBitFieldNameForBit(bitIndex); int bitInVarIndex = bitIndex % 32; string mask = bit_masks[bitInVarIndex]; @@ -445,6 +459,16 @@ string GenerateSetBit(int bitIndex) { return result; } +} // namespace + +string GenerateGetBit(int bitIndex) { + return GenerateGetBitInternal("", bitIndex); +} + +string GenerateSetBit(int bitIndex) { + return GenerateSetBitInternal("", bitIndex); +} + string GenerateClearBit(int bitIndex) { string varName = GetBitFieldNameForBit(bitIndex); int bitInVarIndex = bitIndex % 32; @@ -455,21 +479,19 @@ string GenerateClearBit(int bitIndex) { } string GenerateGetBitFromLocal(int bitIndex) { - string varName = "from_" + GetBitFieldNameForBit(bitIndex); - int bitInVarIndex = bitIndex % 32; - - string mask = bit_masks[bitInVarIndex]; - string result = "((" + varName + " & " + mask + ") == " + mask + ")"; - return result; + return GenerateGetBitInternal("from_", bitIndex); } string GenerateSetBitToLocal(int bitIndex) { - string varName = "to_" + GetBitFieldNameForBit(bitIndex); - int bitInVarIndex = bitIndex % 32; + return GenerateSetBitInternal("to_", bitIndex); +} - string mask = bit_masks[bitInVarIndex]; - string result = varName + " |= " + mask; - return result; +string GenerateGetBitMutableLocal(int bitIndex) { + return GenerateGetBitInternal("mutable_", bitIndex); +} + +string GenerateSetBitMutableLocal(int bitIndex) { + return GenerateSetBitInternal("mutable_", bitIndex); } } // namespace java diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h index 4ae07f15..3937f069 100644 --- a/src/google/protobuf/compiler/java/java_helpers.h +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -78,19 +78,14 @@ string ToJavaName(const string& full_name, const FileDescriptor* file); // These return the fully-qualified class name corresponding to the given // descriptor. -inline string ClassName(const Descriptor* descriptor) { - return ToJavaName(descriptor->full_name(), descriptor->file()); -} -inline string ClassName(const EnumDescriptor* descriptor) { - return ToJavaName(descriptor->full_name(), descriptor->file()); -} -inline string ClassName(const ServiceDescriptor* descriptor) { - return ToJavaName(descriptor->full_name(), descriptor->file()); -} +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()); } -string ClassName(const FileDescriptor* descriptor); // Get the unqualified name that should be used for a field's field // number constant. @@ -205,6 +200,18 @@ string GenerateGetBitFromLocal(int bitIndex); // Example: "to_bitField1_ = (to_bitField1_ | 0x04)" string GenerateSetBitToLocal(int bitIndex); +// Does the same as GenerateGetBit but operates on the bit field on a local +// variable. This is used by the parsing constructor to record if a repeated +// field is mutable. +// Example: "((mutable_bitField1_ & 0x04) == 0x04)" +string GenerateGetBitMutableLocal(int bitIndex); + +// Does the same as GenerateSetBit but operates on the bit field on a local +// variable. This is used by the parsing constructor to record if a repeated +// field is mutable. +// Example: "mutable_bitField1_ = (mutable_bitField1_ | 0x04)" +string GenerateSetBitMutableLocal(int bitIndex); + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc index 4c087db5..9322e242 100644 --- a/src/google/protobuf/compiler/java/java_message.cc +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -32,17 +32,23 @@ // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. +#include <google/protobuf/compiler/java/java_message.h> + #include <algorithm> #include <google/protobuf/stubs/hash.h> -#include <google/protobuf/compiler/java/java_message.h> +#include <map> +#include <vector> + +#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_helpers.h> -#include <google/protobuf/stubs/strutil.h> -#include <google/protobuf/io/printer.h> #include <google/protobuf/io/coded_stream.h> -#include <google/protobuf/wire_format.h> +#include <google/protobuf/io/printer.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> namespace google { namespace protobuf { @@ -233,10 +239,8 @@ void MessageGenerator::GenerateStaticVariableInitializers( "field_name", UnderscoresToCapitalizedCamelCase(descriptor_->field(i))); } - printer->Print("},\n" - " $classname$.class,\n" - " $classname$.Builder.class);\n", - "classname", ClassName(descriptor_)); + printer->Print( + "});\n"); } // Generate static member initializers for all nested types. @@ -250,7 +254,6 @@ void MessageGenerator::GenerateStaticVariableInitializers( // =================================================================== void MessageGenerator::GenerateInterface(io::Printer* printer) { - if (descriptor_->extension_range_count() > 0) { if (HasDescriptorMethods(descriptor_)) { printer->Print( @@ -298,6 +301,10 @@ void MessageGenerator::Generate(io::Printer* printer) { descriptor_->containing_type() == NULL && descriptor_->file()->options().java_multiple_files(); + WriteMessageDocComment(printer, descriptor_); + + // The builder_type stores the super type name of the nested Builder class. + string builder_type; if (descriptor_->extension_range_count() > 0) { if (HasDescriptorMethods(descriptor_)) { printer->Print( @@ -306,6 +313,9 @@ void MessageGenerator::Generate(io::Printer* printer) { " $classname$> implements $classname$OrBuilder {\n", "static", is_own_file ? "" : "static", "classname", descriptor_->name()); + builder_type = strings::Substitute( + "com.google.protobuf.GeneratedMessage.ExtendableBuilder<$0, ?>", + ClassName(descriptor_)); } else { printer->Print( "public $static$ final class $classname$ extends\n" @@ -313,6 +323,9 @@ void MessageGenerator::Generate(io::Printer* printer) { " $classname$> implements $classname$OrBuilder {\n", "static", is_own_file ? "" : "static", "classname", descriptor_->name()); + builder_type = strings::Substitute( + "com.google.protobuf.GeneratedMessageLite.ExtendableBuilder<$0, ?>", + ClassName(descriptor_)); } } else { if (HasDescriptorMethods(descriptor_)) { @@ -322,6 +335,7 @@ void MessageGenerator::Generate(io::Printer* printer) { " implements $classname$OrBuilder {\n", "static", is_own_file ? "" : "static", "classname", descriptor_->name()); + builder_type = "com.google.protobuf.GeneratedMessage.Builder<?>"; } else { printer->Print( "public $static$ final class $classname$ extends\n" @@ -329,17 +343,28 @@ void MessageGenerator::Generate(io::Printer* printer) { " implements $classname$OrBuilder {\n", "static", is_own_file ? "" : "static", "classname", descriptor_->name()); + builder_type = "com.google.protobuf.GeneratedMessageLite.Builder"; } } printer->Indent(); + // Using builder_type, instead of Builder, prevents the Builder class from + // being loaded into PermGen space when the default instance is created. + // This optimizes the PermGen space usage for clients that do not modify + // messages. printer->Print( "// Use $classname$.newBuilder() to construct.\n" - "private $classname$(Builder builder) {\n" + "private $classname$($buildertype$ builder) {\n" " super(builder);\n" - "}\n" + "$set_unknown_fields$\n" + "}\n", + "classname", descriptor_->name(), + "buildertype", builder_type, + "set_unknown_fields", HasUnknownFields(descriptor_) + ? " 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. - "private $classname$(boolean noInit) {}\n" + "private $classname$(boolean noInit) {$set_default_unknown_fields$}\n" "\n" "private static final $classname$ defaultInstance;\n" "public static $classname$ getDefaultInstance() {\n" @@ -350,9 +375,28 @@ void MessageGenerator::Generate(io::Printer* printer) { " return defaultInstance;\n" "}\n" "\n", - "classname", descriptor_->name()); + "classname", descriptor_->name(), + "set_default_unknown_fields", HasUnknownFields(descriptor_) + ? " this.unknownFields =" + " com.google.protobuf.UnknownFieldSet.getDefaultInstance(); " : ""); + + if (HasUnknownFields(descriptor_)) { + printer->Print( + "private final com.google.protobuf.UnknownFieldSet unknownFields;\n" + "" + "@java.lang.Override\n" + "public final com.google.protobuf.UnknownFieldSet\n" + " getUnknownFields() {\n" + " return this.unknownFields;\n" + "}\n"); + } + + if (HasGeneratedMethods(descriptor_)) { + GenerateParsingConstructor(printer); + } GenerateDescriptorMethods(printer); + GenerateParser(printer); // Nested types for (int i = 0; i < descriptor_->enum_type_count(); i++) { @@ -567,68 +611,54 @@ GenerateParseFromMethods(io::Printer* printer) { "public static $classname$ parseFrom(\n" " com.google.protobuf.ByteString data)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return newBuilder().mergeFrom(data).buildParsed();\n" + " return PARSER.parseFrom(data);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.ByteString data,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return newBuilder().mergeFrom(data, extensionRegistry)\n" - " .buildParsed();\n" + " return PARSER.parseFrom(data, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(byte[] data)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return newBuilder().mergeFrom(data).buildParsed();\n" + " return PARSER.parseFrom(data);\n" "}\n" "public static $classname$ parseFrom(\n" " byte[] data,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return newBuilder().mergeFrom(data, extensionRegistry)\n" - " .buildParsed();\n" + " return PARSER.parseFrom(data, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(java.io.InputStream input)\n" " throws java.io.IOException {\n" - " return newBuilder().mergeFrom(input).buildParsed();\n" + " return PARSER.parseFrom(input);\n" "}\n" "public static $classname$ parseFrom(\n" " java.io.InputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return newBuilder().mergeFrom(input, extensionRegistry)\n" - " .buildParsed();\n" + " return PARSER.parseFrom(input, extensionRegistry);\n" "}\n" "public static $classname$ parseDelimitedFrom(java.io.InputStream input)\n" " throws java.io.IOException {\n" - " Builder builder = newBuilder();\n" - " if (builder.mergeDelimitedFrom(input)) {\n" - " return builder.buildParsed();\n" - " } else {\n" - " return null;\n" - " }\n" + " return PARSER.parseDelimitedFrom(input);\n" "}\n" "public static $classname$ parseDelimitedFrom(\n" " java.io.InputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " Builder builder = newBuilder();\n" - " if (builder.mergeDelimitedFrom(input, extensionRegistry)) {\n" - " return builder.buildParsed();\n" - " } else {\n" - " return null;\n" - " }\n" + " return PARSER.parseDelimitedFrom(input, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.CodedInputStream input)\n" " throws java.io.IOException {\n" - " return newBuilder().mergeFrom(input).buildParsed();\n" + " return PARSER.parseFrom(input);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.CodedInputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return newBuilder().mergeFrom(input, extensionRegistry)\n" - " .buildParsed();\n" + " return PARSER.parseFrom(input, extensionRegistry);\n" "}\n" "\n", "classname", ClassName(descriptor_)); @@ -669,6 +699,8 @@ void MessageGenerator::GenerateBuilder(io::Printer* printer) { "}\n"); } + WriteMessageDocComment(printer, descriptor_); + if (descriptor_->extension_range_count() > 0) { if (HasDescriptorMethods(descriptor_)) { printer->Print( @@ -739,17 +771,25 @@ void MessageGenerator::GenerateBuilder(io::Printer* printer) { void MessageGenerator::GenerateDescriptorMethods(io::Printer* printer) { if (HasDescriptorMethods(descriptor_)) { + if (!descriptor_->options().no_standard_descriptor_accessor()) { + printer->Print( + "public static final com.google.protobuf.Descriptors.Descriptor\n" + " getDescriptor() {\n" + " return $fileclass$.internal_$identifier$_descriptor;\n" + "}\n" + "\n", + "fileclass", ClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); + } printer->Print( - "public static final com.google.protobuf.Descriptors.Descriptor\n" - " getDescriptor() {\n" - " return $fileclass$.internal_$identifier$_descriptor;\n" - "}\n" - "\n" "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" " internalGetFieldAccessorTable() {\n" - " return $fileclass$.internal_$identifier$_fieldAccessorTable;\n" + " return $fileclass$.internal_$identifier$_fieldAccessorTable\n" + " .ensureFieldAccessorsInitialized(\n" + " $classname$.class, $classname$.Builder.class);\n" "}\n" "\n", + "classname", ClassName(descriptor_), "fileclass", ClassName(descriptor_->file()), "identifier", UniqueFileScopeIdentifier(descriptor_)); } @@ -768,7 +808,8 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { if (HasDescriptorMethods(descriptor_)) { printer->Print( - "private Builder(BuilderParent parent) {\n" + "private Builder(\n" + " com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n" " super(parent);\n" " maybeForceBuilderInitialization();\n" "}\n", @@ -830,10 +871,11 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { printer->Print( "public com.google.protobuf.Descriptors.Descriptor\n" " getDescriptorForType() {\n" - " return $classname$.getDescriptor();\n" + " return $fileclass$.internal_$identifier$_descriptor;\n" "}\n" "\n", - "classname", ClassName(descriptor_)); + "fileclass", ClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); } printer->Print( "public $classname$ getDefaultInstanceForType() {\n" @@ -853,16 +895,6 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { " return result;\n" "}\n" "\n" - "private $classname$ buildParsed()\n" - " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " $classname$ result = buildPartial();\n" - " if (!result.isInitialized()) {\n" - " throw newUninitializedMessageException(\n" - " result).asInvalidProtocolBufferException();\n" - " }\n" - " return result;\n" - "}\n" - "\n" "public $classname$ buildPartial() {\n" " $classname$ result = new $classname$(this);\n", "classname", ClassName(descriptor_)); @@ -969,108 +1001,25 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { // =================================================================== void MessageGenerator::GenerateBuilderParsingMethods(io::Printer* printer) { - scoped_array<const FieldDescriptor*> sorted_fields( - SortFieldsByNumber(descriptor_)); - printer->Print( "public Builder mergeFrom(\n" " com.google.protobuf.CodedInputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" - " throws java.io.IOException {\n"); - printer->Indent(); - - if (HasUnknownFields(descriptor_)) { - printer->Print( - "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n" - " com.google.protobuf.UnknownFieldSet.newBuilder(\n" - " this.getUnknownFields());\n"); - } - - printer->Print( - "while (true) {\n"); - printer->Indent(); - - printer->Print( - "int tag = input.readTag();\n" - "switch (tag) {\n"); - printer->Indent(); - - if (HasUnknownFields(descriptor_)) { - printer->Print( - "case 0:\n" // zero signals EOF / limit reached - " this.setUnknownFields(unknownFields.build());\n" - " $on_changed$\n" - " return this;\n" - "default: {\n" - " if (!parseUnknownField(input, unknownFields,\n" - " extensionRegistry, tag)) {\n" - " this.setUnknownFields(unknownFields.build());\n" - " $on_changed$\n" - " return this;\n" // it's an endgroup tag - " }\n" - " break;\n" - "}\n", - "on_changed", HasDescriptorMethods(descriptor_) ? "onChanged();" : ""); - } else { - printer->Print( - "case 0:\n" // zero signals EOF / limit reached - " $on_changed$\n" - " return this;\n" - "default: {\n" - " if (!parseUnknownField(input, extensionRegistry, tag)) {\n" - " $on_changed$\n" - " return this;\n" // it's an endgroup tag - " }\n" - " break;\n" - "}\n", - "on_changed", HasDescriptorMethods(descriptor_) ? "onChanged();" : ""); - } - - for (int i = 0; i < descriptor_->field_count(); i++) { - const FieldDescriptor* field = sorted_fields[i]; - uint32 tag = WireFormatLite::MakeTag(field->number(), - WireFormat::WireTypeForFieldType(field->type())); - - printer->Print( - "case $tag$: {\n", - "tag", SimpleItoa(tag)); - printer->Indent(); - - field_generators_.get(field).GenerateParsingCode(printer); - - printer->Outdent(); - printer->Print( - " break;\n" - "}\n"); - - if (field->is_packable()) { - // To make packed = true wire compatible, we generate parsing code from a - // packed version of this field regardless of field->options().packed(). - uint32 packed_tag = WireFormatLite::MakeTag(field->number(), - WireFormatLite::WIRETYPE_LENGTH_DELIMITED); - printer->Print( - "case $tag$: {\n", - "tag", SimpleItoa(packed_tag)); - printer->Indent(); - - field_generators_.get(field).GenerateParsingCodeFromPacked(printer); - - printer->Outdent(); - printer->Print( - " break;\n" - "}\n"); - } - } - - printer->Outdent(); - printer->Outdent(); - printer->Outdent(); - printer->Print( - " }\n" // switch (tag) - " }\n" // while (true) - "}\n" - - "\n"); + " throws java.io.IOException {\n" + " $classname$ parsedMessage = null;\n" + " try {\n" + " parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n" + " } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" + " parsedMessage = ($classname$) e.getUnfinishedMessage();\n" + " throw e;\n" + " } finally {\n" + " if (parsedMessage != null) {\n" + " mergeFrom(parsedMessage);\n" + " }\n" + " }\n" + " return this;\n" + "}\n", + "classname", ClassName(descriptor_)); } // =================================================================== @@ -1232,10 +1181,19 @@ 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(); printer->Print( + "if (memoizedHashCode != 0) {\n"); + printer->Indent(); + printer->Print( + "return memoizedHashCode;\n"); + printer->Outdent(); + printer->Print( + "}\n" "int hash = 41;\n" "hash = (19 * hash) + getDescriptorForType().hashCode();\n"); for (int i = 0; i < descriptor_->field_count(); i++) { @@ -1260,6 +1218,7 @@ void MessageGenerator::GenerateEqualsAndHashCode(io::Printer* printer) { } printer->Print( "hash = (29 * hash) + getUnknownFields().hashCode();\n" + "memoizedHashCode = hash;\n" "return hash;\n"); printer->Outdent(); printer->Print( @@ -1281,6 +1240,195 @@ void MessageGenerator::GenerateExtensionRegistrationCode(io::Printer* printer) { } } +// =================================================================== +void MessageGenerator::GenerateParsingConstructor(io::Printer* printer) { + scoped_array<const FieldDescriptor*> sorted_fields( + SortFieldsByNumber(descriptor_)); + + printer->Print( + "private $classname$(\n" + " com.google.protobuf.CodedInputStream input,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n", + "classname", descriptor_->name()); + printer->Indent(); + + // Initialize all fields to default. + printer->Print( + "initFields();\n"); + + // 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)); + totalBuilderBits += field.GetNumBitsForBuilder(); + } + int totalBuilderInts = (totalBuilderBits + 31) / 32; + for (int i = 0; i < totalBuilderInts; i++) { + printer->Print("int mutable_$bit_field_name$ = 0;\n", + "bit_field_name", GetBitFieldName(i)); + } + + if (HasUnknownFields(descriptor_)) { + printer->Print( + "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n" + " com.google.protobuf.UnknownFieldSet.newBuilder();\n"); + } + + printer->Print( + "try {\n"); + printer->Indent(); + + printer->Print( + "boolean done = false;\n" + "while (!done) {\n"); + printer->Indent(); + + printer->Print( + "int tag = input.readTag();\n" + "switch (tag) {\n"); + printer->Indent(); + + printer->Print( + "case 0:\n" // zero signals EOF / limit reached + " done = true;\n" + " break;\n" + "default: {\n" + " if (!parseUnknownField(input,$unknown_fields$\n" + " extensionRegistry, tag)) {\n" + " done = true;\n" // it's an endgroup tag + " }\n" + " break;\n" + "}\n", + "unknown_fields", HasUnknownFields(descriptor_) + ? " unknownFields," : ""); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = sorted_fields[i]; + uint32 tag = WireFormatLite::MakeTag(field->number(), + WireFormat::WireTypeForFieldType(field->type())); + + printer->Print( + "case $tag$: {\n", + "tag", SimpleItoa(tag)); + printer->Indent(); + + field_generators_.get(field).GenerateParsingCode(printer); + + printer->Outdent(); + printer->Print( + " break;\n" + "}\n"); + + if (field->is_packable()) { + // To make packed = true wire compatible, we generate parsing code from a + // packed version of this field regardless of field->options().packed(). + uint32 packed_tag = WireFormatLite::MakeTag(field->number(), + WireFormatLite::WIRETYPE_LENGTH_DELIMITED); + printer->Print( + "case $tag$: {\n", + "tag", SimpleItoa(packed_tag)); + printer->Indent(); + + field_generators_.get(field).GenerateParsingCodeFromPacked(printer); + + printer->Outdent(); + printer->Print( + " break;\n" + "}\n"); + } + } + + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" // switch (tag) + "}\n"); // while (!done) + + printer->Outdent(); + printer->Print( + "} catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" + " throw e.setUnfinishedMessage(this);\n" + "} catch (java.io.IOException e) {\n" + " throw new com.google.protobuf.InvalidProtocolBufferException(\n" + " e.getMessage()).setUnfinishedMessage(this);\n" + "} finally {\n"); + printer->Indent(); + + // Make repeated field list immutable. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = sorted_fields[i]; + field_generators_.get(field).GenerateParsingDoneCode(printer); + } + + // Make unknown fields immutable. + if (HasUnknownFields(descriptor_)) { + printer->Print( + "this.unknownFields = unknownFields.build();\n"); + } + + // Make extensions immutable. + printer->Print( + "makeExtensionsImmutable();\n"); + + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" // finally + "}\n"); +} + +// =================================================================== +void MessageGenerator::GenerateParser(io::Printer* printer) { + printer->Print( + "public static com.google.protobuf.Parser<$classname$> PARSER =\n" + " new com.google.protobuf.AbstractParser<$classname$>() {\n", + "classname", descriptor_->name()); + printer->Indent(); + printer->Print( + "public $classname$ parsePartialFrom(\n" + " com.google.protobuf.CodedInputStream input,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n", + "classname", descriptor_->name()); + if (HasGeneratedMethods(descriptor_)) { + printer->Print( + " return new $classname$(input, extensionRegistry);\n", + "classname", descriptor_->name()); + } else { + // When parsing constructor isn't generated, use builder to parse messages. + // Note, will fallback to use reflection based mergeFieldFrom() in + // AbstractMessage.Builder. + printer->Indent(); + printer->Print( + "Builder builder = newBuilder();\n" + "try {\n" + " builder.mergeFrom(input, extensionRegistry);\n" + "} catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" + " throw e.setUnfinishedMessage(builder.buildPartial());\n" + "} catch (java.io.IOException e) {\n" + " throw new com.google.protobuf.InvalidProtocolBufferException(\n" + " e.getMessage()).setUnfinishedMessage(builder.buildPartial());\n" + "}\n" + "return builder.buildPartial();\n"); + printer->Outdent(); + } + printer->Print( + "}\n"); + printer->Outdent(); + printer->Print( + "};\n" + "\n"); + + printer->Print( + "@java.lang.Override\n" + "public com.google.protobuf.Parser<$classname$> getParserForType() {\n" + " return PARSER;\n" + "}\n" + "\n", + "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 4c6fbbe5..a30f0202 100644 --- a/src/google/protobuf/compiler/java/java_message.h +++ b/src/google/protobuf/compiler/java/java_message.h @@ -95,6 +95,9 @@ class MessageGenerator { UseMemoization useMemoization); void GenerateEqualsAndHashCode(io::Printer* printer); + void GenerateParser(io::Printer* printer); + void GenerateParsingConstructor(io::Printer* printer); + const Descriptor* descriptor_; FieldGeneratorMap field_generators_; diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc index 251945af..b0b284f7 100644 --- a/src/google/protobuf/compiler/java/java_message_field.cc +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -36,6 +36,7 @@ #include <string> #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/io/printer.h> #include <google/protobuf/wire_format.h> @@ -73,6 +74,7 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // 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); (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); (*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex); @@ -84,6 +86,13 @@ void SetMessageVariables(const FieldDescriptor* descriptor, (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); + // For repeated fields, one bit is used for whether the array is immutable + // in the parsing constructor. + (*variables)["get_mutable_bit_parser"] = + GenerateGetBitMutableLocal(builderBitIndex); + (*variables)["set_mutable_bit_parser"] = + GenerateSetBitMutableLocal(builderBitIndex); + (*variables)["get_has_field_bit_from_local"] = GenerateGetBitFromLocal(builderBitIndex); (*variables)["set_has_field_bit_to_local"] = @@ -120,11 +129,15 @@ GenerateInterfaceMembers(io::Printer* printer) const { // 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"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$boolean has$capitalized_name$();\n" "$deprecation$$type$ get$capitalized_name$();\n"); if (HasNestedBuilders(descriptor_->containing_type())) { + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder();\n"); } @@ -133,15 +146,20 @@ GenerateInterfaceMembers(io::Printer* printer) const { void MessageFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private $type$ $name$_;\n" + "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" + "}\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" @@ -208,12 +226,14 @@ GenerateBuilderMembers(io::Printer* printer) const { // 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"); // Field getField() + WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public $type$ get$capitalized_name$()", @@ -224,6 +244,7 @@ GenerateBuilderMembers(io::Printer* printer) const { NULL); // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public Builder set$capitalized_name$($type$ value)", @@ -239,6 +260,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public Builder set$capitalized_name$(\n" " $type$.Builder builderForValue)", @@ -252,6 +274,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public Builder merge$capitalized_name$($type$ value)", @@ -270,6 +293,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public Builder clear$capitalized_name$()", @@ -282,19 +306,24 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); if (HasNestedBuilders(descriptor_->containing_type())) { + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n" " $set_has_field_bit_builder$;\n" " $on_changed$\n" " return get$capitalized_name$FieldBuilder().getBuilder();\n" - "}\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" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "private com.google.protobuf.SingleFieldBuilder<\n" " $type$, $type$.Builder, $type$OrBuilder> \n" " get$capitalized_name$FieldBuilder() {\n" @@ -357,21 +386,32 @@ GenerateBuildingCode(io::Printer* printer) const { void MessageFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, - "$type$.Builder subBuilder = $type$.newBuilder();\n" - "if (has$capitalized_name$()) {\n" - " subBuilder.mergeFrom(get$capitalized_name$());\n" + "$type$.Builder subBuilder = null;\n" + "if ($get_has_field_bit_message$) {\n" + " subBuilder = $name$_.toBuilder();\n" "}\n"); if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { printer->Print(variables_, - "input.readGroup($number$, subBuilder, extensionRegistry);\n"); + "$name$_ = input.readGroup($number$, $type$.PARSER,\n" + " extensionRegistry);\n"); } else { printer->Print(variables_, - "input.readMessage(subBuilder, extensionRegistry);\n"); + "$name$_ = input.readMessage($type$.PARSER, extensionRegistry);\n"); } printer->Print(variables_, - "set$capitalized_name$(subBuilder.buildPartial());\n"); + "if (subBuilder != null) {\n" + " subBuilder.mergeFrom($name$_);\n" + " $name$_ = subBuilder.buildPartial();\n" + "}\n"); + printer->Print(variables_, + "$set_has_field_bit_message$;\n"); +} + +void MessageFieldGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // noop for messages. } void MessageFieldGenerator:: @@ -437,15 +477,23 @@ GenerateInterfaceMembers(io::Printer* printer) const { // interface so that builders can choose dynamically to either return a // message or a nested builder, so that asking for the interface doesn't // cause a message to ever be built. + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$java.util.List<$type$> \n" - " get$capitalized_name$List();\n" - "$deprecation$$type$ get$capitalized_name$(int index);\n" + " get$capitalized_name$List();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$(int index);\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$int get$capitalized_name$Count();\n"); if (HasNestedBuilders(descriptor_->containing_type())) { + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$java.util.List<? extends $type$OrBuilder> \n" - " get$capitalized_name$OrBuilderList();\n" + " get$capitalized_name$OrBuilderList();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder(\n" " int index);\n"); } @@ -454,20 +502,30 @@ GenerateInterfaceMembers(io::Printer* printer) const { void RepeatedMessageFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private java.util.List<$type$> $name$_;\n" + "private java.util.List<$type$> $name$_;\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" " return $name$_;\n" // note: unmodifiable list - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public java.util.List<? extends $type$OrBuilder> \n" " get$capitalized_name$OrBuilderList() {\n" " return $name$_;\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public int get$capitalized_name$Count() {\n" " return $name$_.size();\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$(int index) {\n" " return $name$_.get(index);\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n" " int index) {\n" " return $name$_.get(index);\n" @@ -552,6 +610,7 @@ GenerateBuilderMembers(io::Printer* printer) const { // 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()", @@ -561,6 +620,7 @@ GenerateBuilderMembers(io::Printer* printer) const { NULL); // int getRepeatedFieldCount() + WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public int get$capitalized_name$Count()", @@ -570,6 +630,7 @@ GenerateBuilderMembers(io::Printer* printer) const { NULL); // Field getRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public $type$ get$capitalized_name$(int index)", @@ -580,6 +641,7 @@ GenerateBuilderMembers(io::Printer* printer) const { NULL); // Builder setRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public Builder set$capitalized_name$(\n" " int index, $type$ value)", @@ -593,6 +655,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "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)", @@ -606,6 +669,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); // Builder addRepeatedField(Field value) + WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public Builder add$capitalized_name$($type$ value)", @@ -622,6 +686,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "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)", @@ -638,6 +703,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); // Builder addRepeatedField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public Builder add$capitalized_name$(\n" " $type$.Builder builderForValue)", @@ -651,6 +717,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "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)", @@ -664,6 +731,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "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)", @@ -677,6 +745,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); // Builder clearAllRepeatedField() + WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public Builder clear$capitalized_name$()", @@ -689,6 +758,7 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); // Builder removeRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); PrintNestedBuilderFunction(printer, "$deprecation$public Builder remove$capitalized_name$(int index)", @@ -701,12 +771,15 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); if (HasNestedBuilders(descriptor_->containing_type())) { + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$.Builder get$capitalized_name$Builder(\n" " int index) {\n" " return get$capitalized_name$FieldBuilder().getBuilder(index);\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n" " int index) {\n" " if ($name$Builder_ == null) {\n" @@ -714,8 +787,10 @@ GenerateBuilderMembers(io::Printer* printer) const { " } else {\n" " return $name$Builder_.getMessageOrBuilder(index);\n" " }\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" @@ -723,17 +798,23 @@ GenerateBuilderMembers(io::Printer* printer) const { " } else {\n" " return java.util.Collections.unmodifiableList($name$_);\n" " }\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" + "}\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" + "}\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" @@ -827,18 +908,27 @@ GenerateBuildingCode(io::Printer* printer) const { void RepeatedMessageFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, - "$type$.Builder subBuilder = $type$.newBuilder();\n"); + "if (!$get_mutable_bit_parser$) {\n" + " $name$_ = new java.util.ArrayList<$type$>();\n" + " $set_mutable_bit_parser$;\n" + "}\n"); if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { printer->Print(variables_, - "input.readGroup($number$, subBuilder, extensionRegistry);\n"); + "$name$_.add(input.readGroup($number$, $type$.PARSER,\n" + " extensionRegistry));\n"); } else { printer->Print(variables_, - "input.readMessage(subBuilder, extensionRegistry);\n"); + "$name$_.add(input.readMessage($type$.PARSER, extensionRegistry));\n"); } +} +void RepeatedMessageFieldGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { printer->Print(variables_, - "add$capitalized_name$(subBuilder.buildPartial());\n"); + "if ($get_mutable_bit_parser$) {\n" + " $name$_ = java.util.Collections.unmodifiableList($name$_);\n" + "}\n"); } void RepeatedMessageFieldGenerator:: diff --git a/src/google/protobuf/compiler/java/java_message_field.h b/src/google/protobuf/compiler/java/java_message_field.h index 2efbcd97..5c8078a1 100644 --- a/src/google/protobuf/compiler/java/java_message_field.h +++ b/src/google/protobuf/compiler/java/java_message_field.h @@ -61,6 +61,7 @@ class MessageFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; @@ -102,6 +103,7 @@ class RepeatedMessageFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc index 712e047a..0140e23f 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -36,6 +36,7 @@ #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_helpers.h> #include <google/protobuf/io/printer.h> @@ -197,6 +198,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // 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); (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); (*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex); @@ -208,6 +210,13 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); + // For repeated fields, one bit is used for whether the array is immutable + // in the parsing constructor. + (*variables)["get_mutable_bit_parser"] = + GenerateGetBitMutableLocal(builderBitIndex); + (*variables)["set_mutable_bit_parser"] = + GenerateSetBitMutableLocal(builderBitIndex); + (*variables)["get_has_field_bit_from_local"] = GenerateGetBitFromLocal(builderBitIndex); (*variables)["set_has_field_bit_to_local"] = @@ -240,19 +249,26 @@ int PrimitiveFieldGenerator::GetNumBitsForBuilder() const { void PrimitiveFieldGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$boolean has$capitalized_name$();\n" "$deprecation$$type$ get$capitalized_name$();\n"); } void PrimitiveFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private $field_type$ $name$_;\n" + "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"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" " return $name$_;\n" @@ -262,16 +278,21 @@ GenerateMembers(io::Printer* printer) const { void PrimitiveFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, - "private $field_type$ $name$_ $default_init$;\n" + "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"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" " return $name$_;\n" "}\n"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" "$null_check$" @@ -279,7 +300,10 @@ GenerateBuilderMembers(io::Printer* printer) const { " $name$_ = value;\n" " $on_changed$\n" " return this;\n" - "}\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder clear$capitalized_name$() {\n" " $clear_has_field_bit_builder$;\n"); JavaType type = GetJavaType(descriptor_); @@ -335,11 +359,16 @@ GenerateBuildingCode(io::Printer* printer) const { void PrimitiveFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, - "$set_has_field_bit_builder$;\n" + "$set_has_field_bit_message$;\n" "$name$_ = input.read$capitalized_type$();\n"); } void PrimitiveFieldGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // noop for primitives. +} + +void PrimitiveFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, "if ($get_has_field_bit_message$) {\n" @@ -468,9 +497,14 @@ int RepeatedPrimitiveFieldGenerator::GetNumBitsForBuilder() const { void RepeatedPrimitiveFieldGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.util.List<$boxed_type$> get$capitalized_name$List();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Count();\n"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$java.util.List<$boxed_type$> get$capitalized_name$List();\n" - "$deprecation$int get$capitalized_name$Count();\n" "$deprecation$$type$ get$capitalized_name$(int index);\n"); } @@ -478,14 +512,20 @@ GenerateInterfaceMembers(io::Printer* printer) const { void RepeatedPrimitiveFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private $field_list_type$ $name$_;\n" + "private $field_list_type$ $name$_;\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public java.util.List<$boxed_type$>\n" " get$capitalized_name$List() {\n" " return $name$_;\n" // note: unmodifiable list - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public int get$capitalized_name$Count() {\n" " return $name$_.size();\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$(int index) {\n" " return $name$_.get(index);\n" "}\n"); @@ -523,17 +563,24 @@ GenerateBuilderMembers(io::Printer* printer) const { // could hold on to the returned list and modify it after the message // has been built, thus mutating the message which is supposed to be // immutable. + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public java.util.List<$boxed_type$>\n" " get$capitalized_name$List() {\n" " return java.util.Collections.unmodifiableList($name$_);\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public int get$capitalized_name$Count() {\n" " return $name$_.size();\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$(int index) {\n" " return $name$_.get(index);\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder set$capitalized_name$(\n" " int index, $type$ value) {\n" "$null_check$" @@ -541,21 +588,27 @@ GenerateBuilderMembers(io::Printer* printer) const { " $name$_.set(index, value);\n" " $on_changed$\n" " return this;\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder add$capitalized_name$($type$ value) {\n" "$null_check$" " ensure$capitalized_name$IsMutable();\n" " $name$_.add(value);\n" " $on_changed$\n" " return this;\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$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" " $on_changed$\n" " return this;\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder clear$capitalized_name$() {\n" " $name$_ = $empty_list$;\n" " $clear_mutable_bit_builder$;\n" @@ -616,7 +669,10 @@ GenerateBuildingCode(io::Printer* printer) const { void RepeatedPrimitiveFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, - "ensure$capitalized_name$IsMutable();\n" + "if (!$get_mutable_bit_parser$) {\n" + " $name$_ = new java.util.ArrayList<$boxed_type$>();\n" + " $set_mutable_bit_parser$;\n" + "}\n" "$name$_.add(input.read$capitalized_type$());\n"); } @@ -625,13 +681,25 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const { printer->Print(variables_, "int length = input.readRawVarint32();\n" "int limit = input.pushLimit(length);\n" + "if (!$get_mutable_bit_parser$ && input.getBytesUntilLimit() > 0) {\n" + " $name$_ = new java.util.ArrayList<$boxed_type$>();\n" + " $set_mutable_bit_parser$;\n" + "}\n" "while (input.getBytesUntilLimit() > 0) {\n" - " add$capitalized_name$(input.read$capitalized_type$());\n" + " $name$_.add(input.read$capitalized_type$());\n" "}\n" "input.popLimit(limit);\n"); } void RepeatedPrimitiveFieldGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_mutable_bit_parser$) {\n" + " $name$_ = java.util.Collections.unmodifiableList($name$_);\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { if (descriptor_->options().packed()) { printer->Print(variables_, diff --git a/src/google/protobuf/compiler/java/java_primitive_field.h b/src/google/protobuf/compiler/java/java_primitive_field.h index 7900fac5..1b5b6d95 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.h +++ b/src/google/protobuf/compiler/java/java_primitive_field.h @@ -61,6 +61,7 @@ class PrimitiveFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; @@ -96,6 +97,7 @@ class RepeatedPrimitiveFieldGenerator : public FieldGenerator { void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateParsingCodeFromPacked(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/java/java_service.cc b/src/google/protobuf/compiler/java/java_service.cc index 1ae4f461..bbd24806 100644 --- a/src/google/protobuf/compiler/java/java_service.cc +++ b/src/google/protobuf/compiler/java/java_service.cc @@ -33,6 +33,7 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include <google/protobuf/compiler/java/java_service.h> +#include <google/protobuf/compiler/java/java_doc_comment.h> #include <google/protobuf/compiler/java/java_helpers.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/descriptor.pb.h> @@ -50,6 +51,7 @@ ServiceGenerator::~ServiceGenerator() {} void ServiceGenerator::Generate(io::Printer* printer) { bool is_own_file = descriptor_->file()->options().java_multiple_files(); + WriteServiceDocComment(printer, descriptor_); printer->Print( "public $static$ abstract class $classname$\n" " implements com.google.protobuf.Service {\n", @@ -157,6 +159,7 @@ void ServiceGenerator::GenerateNewReflectiveBlockingServiceMethod( void ServiceGenerator::GenerateAbstractMethods(io::Printer* printer) { for (int i = 0; i < descriptor_->method_count(); i++) { const MethodDescriptor* method = descriptor_->method(i); + WriteMethodDocComment(printer, method); GenerateMethodSignature(printer, method, IS_ABSTRACT); printer->Print(";\n\n"); } diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc index 222285bd..4815663b 100644 --- a/src/google/protobuf/compiler/java/java_string_field.cc +++ b/src/google/protobuf/compiler/java/java_string_field.cc @@ -37,6 +37,7 @@ #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_helpers.h> #include <google/protobuf/io/printer.h> @@ -85,6 +86,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // 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); (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex); (*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex); @@ -96,6 +98,13 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); + // For repeated fields, one bit is used for whether the array is immutable + // in the parsing constructor. + (*variables)["get_mutable_bit_parser"] = + GenerateGetBitMutableLocal(builderBitIndex); + (*variables)["set_mutable_bit_parser"] = + GenerateSetBitMutableLocal(builderBitIndex); + (*variables)["get_has_field_bit_from_local"] = GenerateGetBitFromLocal(builderBitIndex); (*variables)["set_has_field_bit_to_local"] = @@ -160,19 +169,29 @@ int StringFieldGenerator::GetNumBitsForBuilder() const { // UnmodifiableLazyStringList. void StringFieldGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$boolean has$capitalized_name$();\n" "$deprecation$java.lang.String get$capitalized_name$();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes();\n"); } void StringFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private java.lang.Object $name$_;\n" + "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"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public java.lang.String get$capitalized_name$() {\n" " java.lang.Object ref = $name$_;\n" @@ -182,13 +201,16 @@ GenerateMembers(io::Printer* printer) const { " com.google.protobuf.ByteString bs = \n" " (com.google.protobuf.ByteString) ref;\n" " java.lang.String s = bs.toStringUtf8();\n" - " if (com.google.protobuf.Internal.isValidUtf8(bs)) {\n" + " if (bs.isValidUtf8()) {\n" " $name$_ = s;\n" " }\n" " return s;\n" " }\n" - "}\n" - "private com.google.protobuf.ByteString get$capitalized_name$Bytes() {\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes() {\n" " java.lang.Object ref = $name$_;\n" " if (ref instanceof java.lang.String) {\n" " com.google.protobuf.ByteString b = \n" @@ -205,11 +227,14 @@ GenerateMembers(io::Printer* printer) const { void StringFieldGenerator:: GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, - "private java.lang.Object $name$_ $default_init$;\n" + "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"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public java.lang.String get$capitalized_name$() {\n" " java.lang.Object ref = $name$_;\n" @@ -223,6 +248,23 @@ GenerateBuilderMembers(io::Printer* printer) const { " }\n" "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes() {\n" + " java.lang.Object ref = $name$_;\n" + " if (ref instanceof String) {\n" + " com.google.protobuf.ByteString b = \n" + " com.google.protobuf.ByteString.copyFromUtf8(\n" + " (java.lang.String) ref);\n" + " $name$_ = b;\n" + " return b;\n" + " } else {\n" + " return (com.google.protobuf.ByteString) ref;\n" + " }\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public Builder set$capitalized_name$(\n" " java.lang.String value) {\n" @@ -231,7 +273,9 @@ GenerateBuilderMembers(io::Printer* printer) const { " $name$_ = value;\n" " $on_changed$\n" " return this;\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder clear$capitalized_name$() {\n" " $clear_has_field_bit_builder$;\n"); // The default value is not a simple literal so we want to avoid executing @@ -243,11 +287,15 @@ GenerateBuilderMembers(io::Printer* printer) const { " return this;\n" "}\n"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "void set$capitalized_name$(com.google.protobuf.ByteString value) {\n" + "$deprecation$public Builder set$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + "$null_check$" " $set_has_field_bit_builder$;\n" " $name$_ = value;\n" " $on_changed$\n" + " return this;\n" "}\n"); } @@ -270,9 +318,13 @@ GenerateBuilderClearCode(io::Printer* printer) const { void StringFieldGenerator:: 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$capitalized_name$(other.get$capitalized_name$());\n" + " $set_has_field_bit_builder$;\n" + " $name$_ = other.$name$_;\n" + " $on_changed$\n" "}\n"); } @@ -288,11 +340,16 @@ GenerateBuildingCode(io::Printer* printer) const { void StringFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, - "$set_has_field_bit_builder$;\n" + "$set_has_field_bit_message$;\n" "$name$_ = input.readBytes();\n"); } void StringFieldGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // noop for strings. +} + +void StringFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, "if ($get_has_field_bit_message$) {\n" @@ -353,28 +410,49 @@ int RepeatedStringFieldGenerator::GetNumBitsForBuilder() const { void RepeatedStringFieldGenerator:: 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$int get$capitalized_name$Count();\n" + "get$capitalized_name$List();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Count();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$java.lang.String get$capitalized_name$(int index);\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes(int index);\n"); } void RepeatedStringFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private com.google.protobuf.LazyStringList $name$_;\n" + "private com.google.protobuf.LazyStringList $name$_;\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public java.util.List<java.lang.String>\n" " get$capitalized_name$List() {\n" " return $name$_;\n" // note: unmodifiable list - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public int get$capitalized_name$Count() {\n" " return $name$_.size();\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public java.lang.String get$capitalized_name$(int index) {\n" " return $name$_.get(index);\n" "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes(int index) {\n" + " return $name$_.getByteString(index);\n" + "}\n"); if (descriptor_->options().packed() && HasGeneratedMethods(descriptor_->containing_type())) { @@ -409,17 +487,30 @@ GenerateBuilderMembers(io::Printer* printer) const { // could hold on to the returned list and modify it after the message // has been built, thus mutating the message which is supposed to be // immutable. + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public java.util.List<java.lang.String>\n" " get$capitalized_name$List() {\n" " return java.util.Collections.unmodifiableList($name$_);\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public int get$capitalized_name$Count() {\n" " return $name$_.size();\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public java.lang.String get$capitalized_name$(int index) {\n" " return $name$_.get(index);\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes(int index) {\n" + " return $name$_.getByteString(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder set$capitalized_name$(\n" " int index, java.lang.String value) {\n" "$null_check$" @@ -427,7 +518,9 @@ GenerateBuilderMembers(io::Printer* printer) const { " $name$_.set(index, value);\n" " $on_changed$\n" " return this;\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder add$capitalized_name$(\n" " java.lang.String value) {\n" "$null_check$" @@ -435,14 +528,18 @@ GenerateBuilderMembers(io::Printer* printer) const { " $name$_.add(value);\n" " $on_changed$\n" " return this;\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$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" " $on_changed$\n" " return this;\n" - "}\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, "$deprecation$public Builder clear$capitalized_name$() {\n" " $name$_ = $empty_list$;\n" " $clear_mutable_bit_builder$;\n" @@ -450,11 +547,15 @@ GenerateBuilderMembers(io::Printer* printer) const { " return this;\n" "}\n"); + WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "void add$capitalized_name$(com.google.protobuf.ByteString value) {\n" + "$deprecation$public Builder add$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + "$null_check$" " ensure$capitalized_name$IsMutable();\n" " $name$_.add(value);\n" " $on_changed$\n" + " return this;\n" "}\n"); } @@ -512,7 +613,10 @@ GenerateBuildingCode(io::Printer* printer) const { void RepeatedStringFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, - "ensure$capitalized_name$IsMutable();\n" + "if (!$get_mutable_bit_parser$) {\n" + " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" + " $set_mutable_bit_parser$;\n" + "}\n" "$name$_.add(input.readBytes());\n"); } @@ -521,13 +625,25 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const { printer->Print(variables_, "int length = input.readRawVarint32();\n" "int limit = input.pushLimit(length);\n" + "if (!$get_mutable_bit_parser$ && input.getBytesUntilLimit() > 0) {\n" + " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" + " $set_mutable_bit_parser$;\n" + "}\n" "while (input.getBytesUntilLimit() > 0) {\n" - " add$capitalized_name$(input.read$capitalized_type$());\n" + " $name$.add(input.read$capitalized_type$());\n" "}\n" "input.popLimit(limit);\n"); } void RepeatedStringFieldGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_mutable_bit_parser$) {\n" + " $name$_ = new com.google.protobuf.UnmodifiableLazyStringList($name$_);\n" + "}\n"); +} + +void RepeatedStringFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { if (descriptor_->options().packed()) { printer->Print(variables_, diff --git a/src/google/protobuf/compiler/java/java_string_field.h b/src/google/protobuf/compiler/java/java_string_field.h index 8cb41469..4f7532f4 100644 --- a/src/google/protobuf/compiler/java/java_string_field.h +++ b/src/google/protobuf/compiler/java/java_string_field.h @@ -62,6 +62,7 @@ class StringFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; @@ -96,6 +97,7 @@ class RepeatedStringFieldGenerator : public FieldGenerator { void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateParsingCodeFromPacked(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc index d9b0c3f9..1afc5d61 100644 --- a/src/google/protobuf/compiler/main.cc +++ b/src/google/protobuf/compiler/main.cc @@ -43,7 +43,7 @@ int main(int argc, char* argv[]) { // Proto2 C++ google::protobuf::compiler::cpp::CppGenerator cpp_generator; - cli.RegisterGenerator("--cpp_out", &cpp_generator, + cli.RegisterGenerator("--cpp_out", "--cpp_opt", &cpp_generator, "Generate C++ header and source."); // Proto2 Java diff --git a/src/google/protobuf/compiler/mock_code_generator.cc b/src/google/protobuf/compiler/mock_code_generator.cc index 5b76af25..0e35ed19 100644 --- a/src/google/protobuf/compiler/mock_code_generator.cc +++ b/src/google/protobuf/compiler/mock_code_generator.cc @@ -33,13 +33,14 @@ #include <google/protobuf/compiler/mock_code_generator.h> #include <google/protobuf/testing/file.h> +#include <google/protobuf/descriptor.pb.h> #include <google/protobuf/descriptor.h> #include <google/protobuf/io/zero_copy_stream.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/stubs/strutil.h> #include <google/protobuf/stubs/substitute.h> #include <gtest/gtest.h> -#include <google/protobuf/stubs/stl_util-inl.h> +#include <google/protobuf/stubs/stl_util.h> namespace google { namespace protobuf { @@ -132,6 +133,15 @@ bool MockCodeGenerator::Generate( } else if (command == "Abort") { cerr << "Saw message type MockCodeGenerator_Abort." << endl; abort(); + } else if (command == "HasSourceCodeInfo") { + FileDescriptorProto file_descriptor_proto; + file->CopySourceCodeInfoTo(&file_descriptor_proto); + bool has_source_code_info = + file_descriptor_proto.has_source_code_info() && + file_descriptor_proto.source_code_info().location_size() > 0; + cerr << "Saw message type MockCodeGenerator_HasSourceCodeInfo: " + << has_source_code_info << "." << endl; + abort(); } else { GOOGLE_LOG(FATAL) << "Unknown MockCodeGenerator command: " << command; } diff --git a/src/google/protobuf/compiler/mock_code_generator.h b/src/google/protobuf/compiler/mock_code_generator.h index 5c7942bd..506fd207 100644 --- a/src/google/protobuf/compiler/mock_code_generator.h +++ b/src/google/protobuf/compiler/mock_code_generator.h @@ -59,6 +59,10 @@ namespace compiler { // MockCodeGenerator_Exit." to stderr and then calls exit(123). // MockCodeGenerator_Abort: Generate() prints "Saw message type // MockCodeGenerator_Abort." to stderr and then calls abort(). +// MockCodeGenerator_HasSourceCodeInfo: Causes Generate() to abort after +// printing "Saw message type MockCodeGenerator_HasSourceCodeInfo: FOO." to +// stderr, where FOO is "1" if the supplied FileDescriptorProto has source +// code info, and "0" otherwise. class MockCodeGenerator : public CodeGenerator { public: MockCodeGenerator(const string& name); diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc index 34317b1f..23aa01ce 100644 --- a/src/google/protobuf/compiler/parser.cc +++ b/src/google/protobuf/compiler/parser.cc @@ -174,6 +174,20 @@ bool Parser::ConsumeInteger(int* output, const char* error) { } } +bool Parser::ConsumeSignedInteger(int* output, const char* error) { + bool is_negative = false; + uint64 max_value = kint32max; + if (TryConsume("-")) { + is_negative = true; + max_value += 1; + } + uint64 value = 0; + DO(ConsumeInteger64(max_value, &value, error)); + if (is_negative) value *= -1; + *output = value; + return true; +} + bool Parser::ConsumeInteger64(uint64 max_value, uint64* output, const char* error) { if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) { @@ -237,6 +251,35 @@ bool Parser::ConsumeString(string* output, const char* error) { } } +bool Parser::TryConsumeEndOfDeclaration(const char* text, + const LocationRecorder* location) { + if (LookingAt(text)) { + string leading, trailing; + input_->NextWithComments(&trailing, NULL, &leading); + + // Save the leading comments for next time, and recall the leading comments + // from last time. + leading.swap(upcoming_doc_comments_); + + if (location != NULL) { + location->AttachComments(&leading, &trailing); + } + return true; + } else { + return false; + } +} + +bool Parser::ConsumeEndOfDeclaration(const char* text, + const LocationRecorder* location) { + if (TryConsumeEndOfDeclaration(text, location)) { + return true; + } else { + AddError("Expected \"" + string(text) + "\"."); + return false; + } +} + // ------------------------------------------------------------------- void Parser::AddError(int line, int column, const string& error) { @@ -315,6 +358,19 @@ void Parser::LocationRecorder::RecordLegacyLocation(const Message* descriptor, } } +void Parser::LocationRecorder::AttachComments( + string* leading, string* trailing) const { + GOOGLE_CHECK(!location_->has_leading_comments()); + GOOGLE_CHECK(!location_->has_trailing_comments()); + + if (!leading->empty()) { + location_->mutable_leading_comments()->swap(*leading); + } + if (!trailing->empty()) { + location_->mutable_trailing_comments()->swap(*trailing); + } +} + // ------------------------------------------------------------------- void Parser::SkipStatement() { @@ -322,7 +378,7 @@ void Parser::SkipStatement() { if (AtEnd()) { return; } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) { - if (TryConsume(";")) { + if (TryConsumeEndOfDeclaration(";", NULL)) { return; } else if (TryConsume("{")) { SkipRestOfBlock(); @@ -340,7 +396,7 @@ void Parser::SkipRestOfBlock() { if (AtEnd()) { return; } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) { - if (TryConsume("}")) { + if (TryConsumeEndOfDeclaration("}", NULL)) { return; } else if (TryConsume("{")) { SkipRestOfBlock(); @@ -366,7 +422,7 @@ bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) { if (LookingAtType(io::Tokenizer::TYPE_START)) { // Advance to first token. - input_->Next(); + input_->NextWithComments(NULL, NULL, &upcoming_doc_comments_); } { @@ -393,7 +449,7 @@ bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) { if (LookingAt("}")) { AddError("Unmatched \"}\"."); - input_->Next(); + input_->NextWithComments(NULL, NULL, &upcoming_doc_comments_); } } } @@ -411,7 +467,7 @@ bool Parser::ParseSyntaxIdentifier() { io::Tokenizer::Token syntax_token = input_->current(); string syntax; DO(ConsumeString(&syntax, "Expected syntax identifier.")); - DO(Consume(";")); + DO(ConsumeEndOfDeclaration(";", NULL)); syntax_identifier_ = syntax; @@ -427,7 +483,7 @@ bool Parser::ParseSyntaxIdentifier() { bool Parser::ParseTopLevelStatement(FileDescriptorProto* file, const LocationRecorder& root_location) { - if (TryConsume(";")) { + if (TryConsumeEndOfDeclaration(";", NULL)) { // empty statement; ignore return true; } else if (LookingAt("message")) { @@ -451,14 +507,16 @@ bool Parser::ParseTopLevelStatement(FileDescriptorProto* file, FileDescriptorProto::kMessageTypeFieldNumber, location); } else if (LookingAt("import")) { - int index = file->dependency_size(); - return ParseImport(file->add_dependency(), root_location, index); + return ParseImport(file->mutable_dependency(), + file->mutable_public_dependency(), + file->mutable_weak_dependency(), + root_location); } else if (LookingAt("package")) { return ParsePackage(file, root_location); } else if (LookingAt("option")) { LocationRecorder location(root_location, FileDescriptorProto::kOptionsFieldNumber); - return ParseOption(file->mutable_options(), location); + return ParseOption(file->mutable_options(), location, OPTION_STATEMENT); } else { AddError("Expected top-level statement (e.g. \"message\")."); return false; @@ -482,11 +540,45 @@ bool Parser::ParseMessageDefinition(DescriptorProto* message, return true; } +namespace { + +const int kMaxExtensionRangeSentinel = -1; + +bool IsMessageSetWireFormatMessage(const DescriptorProto& message) { + const MessageOptions& options = message.options(); + for (int i = 0; i < options.uninterpreted_option_size(); ++i) { + const UninterpretedOption& uninterpreted = options.uninterpreted_option(i); + if (uninterpreted.name_size() == 1 && + uninterpreted.name(0).name_part() == "message_set_wire_format" && + uninterpreted.identifier_value() == "true") { + return true; + } + } + return false; +} + +// Modifies any extension ranges that specified 'max' as the end of the +// extension range, and sets them to the type-specific maximum. The actual max +// tag number can only be determined after all options have been parsed. +void AdjustExtensionRangesWithMaxEndNumber(DescriptorProto* message) { + const bool is_message_set = IsMessageSetWireFormatMessage(*message); + const int max_extension_number = is_message_set ? + kint32max : + FieldDescriptor::kMaxNumber + 1; + for (int i = 0; i < message->extension_range_size(); ++i) { + if (message->extension_range(i).end() == kMaxExtensionRangeSentinel) { + message->mutable_extension_range(i)->set_end(max_extension_number); + } + } +} + +} // namespace + bool Parser::ParseMessageBlock(DescriptorProto* message, const LocationRecorder& message_location) { - DO(Consume("{")); + DO(ConsumeEndOfDeclaration("{", &message_location)); - while (!TryConsume("}")) { + while (!TryConsumeEndOfDeclaration("}", NULL)) { if (AtEnd()) { AddError("Reached end of input in message definition (missing '}')."); return false; @@ -499,12 +591,15 @@ bool Parser::ParseMessageBlock(DescriptorProto* message, } } + if (message->extension_range_size() > 0) { + AdjustExtensionRangesWithMaxEndNumber(message); + } return true; } bool Parser::ParseMessageStatement(DescriptorProto* message, const LocationRecorder& message_location) { - if (TryConsume(";")) { + if (TryConsumeEndOfDeclaration(";", NULL)) { // empty statement; ignore return true; } else if (LookingAt("message")) { @@ -532,7 +627,7 @@ bool Parser::ParseMessageStatement(DescriptorProto* message, } else if (LookingAt("option")) { LocationRecorder location(message_location, DescriptorProto::kOptionsFieldNumber); - return ParseOption(message->mutable_options(), location); + return ParseOption(message->mutable_options(), location, OPTION_STATEMENT); } else { LocationRecorder location(message_location, DescriptorProto::kFieldFieldNumber, @@ -647,7 +742,7 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field, return false; } } else { - DO(Consume(";")); + DO(ConsumeEndOfDeclaration(";", &field_location)); } return true; @@ -669,7 +764,7 @@ bool Parser::ParseFieldOptions(FieldDescriptorProto* field, // the default value is not actually an option. DO(ParseDefaultAssignment(field, field_location)); } else { - DO(ParseOptionAssignment(field->mutable_options(), location)); + DO(ParseOption(field->mutable_options(), location, OPTION_ASSIGNMENT)); } } while (TryConsume(",")); @@ -835,6 +930,8 @@ bool Parser::ParseOptionNamePart(UninterpretedOption* uninterpreted_option, bool Parser::ParseUninterpretedBlock(string* value) { // Note that enclosing braces are not added to *value. + // We do NOT use ConsumeEndOfStatement for this brace because it's delimiting + // an expression, not a block of statements. DO(Consume("{")); int brace_depth = 1; while (!AtEnd()) { @@ -858,8 +955,9 @@ bool Parser::ParseUninterpretedBlock(string* value) { // We don't interpret the option here. Instead we store it in an // UninterpretedOption, to be interpreted later. -bool Parser::ParseOptionAssignment(Message* options, - const LocationRecorder& options_location) { +bool Parser::ParseOption(Message* options, + const LocationRecorder& options_location, + OptionStyle style) { // Create an entry in the uninterpreted_option field. const FieldDescriptor* uninterpreted_option_field = options->GetDescriptor()-> FindFieldByName("uninterpreted_option"); @@ -872,6 +970,10 @@ bool Parser::ParseOptionAssignment(Message* options, options_location, uninterpreted_option_field->number(), reflection->FieldSize(*options, uninterpreted_option_field)); + if (style == OPTION_STATEMENT) { + DO(Consume("option")); + } + UninterpretedOption* uninterpreted_option = down_cast<UninterpretedOption*>( options->GetReflection()->AddMessage(options, uninterpreted_option_field)); @@ -899,82 +1001,91 @@ bool Parser::ParseOptionAssignment(Message* options, DO(Consume("=")); - LocationRecorder value_location(location); - value_location.RecordLegacyLocation( - uninterpreted_option, DescriptorPool::ErrorCollector::OPTION_VALUE); - - // All values are a single token, except for negative numbers, which consist - // of a single '-' symbol, followed by a positive number. - bool is_negative = TryConsume("-"); + { + LocationRecorder value_location(location); + value_location.RecordLegacyLocation( + uninterpreted_option, DescriptorPool::ErrorCollector::OPTION_VALUE); - switch (input_->current().type) { - case io::Tokenizer::TYPE_START: - GOOGLE_LOG(FATAL) << "Trying to read value before any tokens have been read."; - return false; + // All values are a single token, except for negative numbers, which consist + // of a single '-' symbol, followed by a positive number. + bool is_negative = TryConsume("-"); - case io::Tokenizer::TYPE_END: - AddError("Unexpected end of stream while parsing option value."); - return false; + switch (input_->current().type) { + case io::Tokenizer::TYPE_START: + GOOGLE_LOG(FATAL) << "Trying to read value before any tokens have been read."; + return false; - case io::Tokenizer::TYPE_IDENTIFIER: { - value_location.AddPath(UninterpretedOption::kIdentifierValueFieldNumber); - if (is_negative) { - AddError("Invalid '-' symbol before identifier."); + case io::Tokenizer::TYPE_END: + AddError("Unexpected end of stream while parsing option value."); return false; - } - string value; - DO(ConsumeIdentifier(&value, "Expected identifier.")); - uninterpreted_option->set_identifier_value(value); - break; - } - case io::Tokenizer::TYPE_INTEGER: { - uint64 value; - uint64 max_value = - is_negative ? static_cast<uint64>(kint64max) + 1 : kuint64max; - DO(ConsumeInteger64(max_value, &value, "Expected integer.")); - if (is_negative) { + case io::Tokenizer::TYPE_IDENTIFIER: { value_location.AddPath( - UninterpretedOption::kNegativeIntValueFieldNumber); - uninterpreted_option->set_negative_int_value(-static_cast<int64>(value)); - } else { - value_location.AddPath( - UninterpretedOption::kPositiveIntValueFieldNumber); - uninterpreted_option->set_positive_int_value(value); + UninterpretedOption::kIdentifierValueFieldNumber); + if (is_negative) { + AddError("Invalid '-' symbol before identifier."); + return false; + } + string value; + DO(ConsumeIdentifier(&value, "Expected identifier.")); + uninterpreted_option->set_identifier_value(value); + break; } - break; - } - case io::Tokenizer::TYPE_FLOAT: { - value_location.AddPath(UninterpretedOption::kDoubleValueFieldNumber); - double value; - DO(ConsumeNumber(&value, "Expected number.")); - uninterpreted_option->set_double_value(is_negative ? -value : value); - break; - } + case io::Tokenizer::TYPE_INTEGER: { + uint64 value; + uint64 max_value = + is_negative ? static_cast<uint64>(kint64max) + 1 : kuint64max; + DO(ConsumeInteger64(max_value, &value, "Expected integer.")); + if (is_negative) { + value_location.AddPath( + UninterpretedOption::kNegativeIntValueFieldNumber); + uninterpreted_option->set_negative_int_value( + -static_cast<int64>(value)); + } else { + value_location.AddPath( + UninterpretedOption::kPositiveIntValueFieldNumber); + uninterpreted_option->set_positive_int_value(value); + } + break; + } - case io::Tokenizer::TYPE_STRING: { - value_location.AddPath(UninterpretedOption::kStringValueFieldNumber); - if (is_negative) { - AddError("Invalid '-' symbol before string."); - return false; + case io::Tokenizer::TYPE_FLOAT: { + value_location.AddPath(UninterpretedOption::kDoubleValueFieldNumber); + double value; + DO(ConsumeNumber(&value, "Expected number.")); + uninterpreted_option->set_double_value(is_negative ? -value : value); + break; } - string value; - DO(ConsumeString(&value, "Expected string.")); - uninterpreted_option->set_string_value(value); - break; - } - case io::Tokenizer::TYPE_SYMBOL: - if (LookingAt("{")) { - value_location.AddPath(UninterpretedOption::kAggregateValueFieldNumber); - DO(ParseUninterpretedBlock( - uninterpreted_option->mutable_aggregate_value())); - } else { - AddError("Expected option value."); - return false; + case io::Tokenizer::TYPE_STRING: { + value_location.AddPath(UninterpretedOption::kStringValueFieldNumber); + if (is_negative) { + AddError("Invalid '-' symbol before string."); + return false; + } + string value; + DO(ConsumeString(&value, "Expected string.")); + uninterpreted_option->set_string_value(value); + break; } - break; + + case io::Tokenizer::TYPE_SYMBOL: + if (LookingAt("{")) { + value_location.AddPath( + UninterpretedOption::kAggregateValueFieldNumber); + DO(ParseUninterpretedBlock( + uninterpreted_option->mutable_aggregate_value())); + } else { + AddError("Expected option value."); + return false; + } + break; + } + } + + if (style == OPTION_STATEMENT) { + DO(ConsumeEndOfDeclaration(";", &location)); } return true; @@ -1008,7 +1119,10 @@ bool Parser::ParseExtensions(DescriptorProto* message, LocationRecorder end_location( location, DescriptorProto::ExtensionRange::kEndFieldNumber); if (TryConsume("max")) { - end = FieldDescriptor::kMaxNumber; + // Set to the sentinel value - 1 since we increment the value below. + // The actual value of the end of the range should be set with + // AdjustExtensionRangesWithMaxEndNumber. + end = kMaxExtensionRangeSentinel - 1; } else { DO(ConsumeInteger(&end, "Expected integer.")); } @@ -1028,7 +1142,7 @@ bool Parser::ParseExtensions(DescriptorProto* message, range->set_end(end); } while (TryConsume(",")); - DO(Consume(";")); + DO(ConsumeEndOfDeclaration(";", &extensions_location)); return true; } @@ -1046,7 +1160,7 @@ bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions, io::Tokenizer::Token extendee_end = input_->previous(); // Parse the block. - DO(Consume("{")); + DO(ConsumeEndOfDeclaration("{", &extend_location)); bool is_first = true; @@ -1083,7 +1197,7 @@ bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions, // other statements. SkipStatement(); } - } while(!TryConsume("}")); + } while (!TryConsumeEndOfDeclaration("}", NULL)); return true; } @@ -1109,9 +1223,9 @@ bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type, bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type, const LocationRecorder& enum_location) { - DO(Consume("{")); + DO(ConsumeEndOfDeclaration("{", &enum_location)); - while (!TryConsume("}")) { + while (!TryConsumeEndOfDeclaration("}", NULL)) { if (AtEnd()) { AddError("Reached end of input in enum definition (missing '}')."); return false; @@ -1129,13 +1243,14 @@ bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type, bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type, const LocationRecorder& enum_location) { - if (TryConsume(";")) { + if (TryConsumeEndOfDeclaration(";", NULL)) { // empty statement; ignore return true; } else if (LookingAt("option")) { LocationRecorder location(enum_location, EnumDescriptorProto::kOptionsFieldNumber); - return ParseOption(enum_type->mutable_options(), location); + return ParseOption(enum_type->mutable_options(), location, + OPTION_STATEMENT); } else { LocationRecorder location(enum_location, EnumDescriptorProto::kValueFieldNumber, enum_type->value_size()); @@ -1164,16 +1279,14 @@ bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value, location.RecordLegacyLocation( enum_value, DescriptorPool::ErrorCollector::NUMBER); - bool is_negative = TryConsume("-"); int number; - DO(ConsumeInteger(&number, "Expected integer.")); - if (is_negative) number *= -1; + DO(ConsumeSignedInteger(&number, "Expected integer.")); enum_value->set_number(number); } DO(ParseEnumConstantOptions(enum_value, enum_value_location)); - DO(Consume(";")); + DO(ConsumeEndOfDeclaration(";", &enum_value_location)); return true; } @@ -1189,7 +1302,7 @@ bool Parser::ParseEnumConstantOptions( DO(Consume("[")); do { - DO(ParseOptionAssignment(value->mutable_options(), location)); + DO(ParseOption(value->mutable_options(), location, OPTION_ASSIGNMENT)); } while (TryConsume(",")); DO(Consume("]")); @@ -1217,9 +1330,9 @@ bool Parser::ParseServiceDefinition(ServiceDescriptorProto* service, bool Parser::ParseServiceBlock(ServiceDescriptorProto* service, const LocationRecorder& service_location) { - DO(Consume("{")); + DO(ConsumeEndOfDeclaration("{", &service_location)); - while (!TryConsume("}")) { + while (!TryConsumeEndOfDeclaration("}", NULL)) { if (AtEnd()) { AddError("Reached end of input in service definition (missing '}')."); return false; @@ -1237,13 +1350,13 @@ bool Parser::ParseServiceBlock(ServiceDescriptorProto* service, bool Parser::ParseServiceStatement(ServiceDescriptorProto* service, const LocationRecorder& service_location) { - if (TryConsume(";")) { + if (TryConsumeEndOfDeclaration(";", NULL)) { // empty statement; ignore return true; } else if (LookingAt("option")) { LocationRecorder location( service_location, ServiceDescriptorProto::kOptionsFieldNumber); - return ParseOption(service->mutable_options(), location); + return ParseOption(service->mutable_options(), location, OPTION_STATEMENT); } else { LocationRecorder location(service_location, ServiceDescriptorProto::kMethodFieldNumber, service->method_size()); @@ -1286,28 +1399,41 @@ bool Parser::ParseServiceMethod(MethodDescriptorProto* method, } DO(Consume(")")); - if (TryConsume("{")) { + if (LookingAt("{")) { // Options! - while (!TryConsume("}")) { - if (AtEnd()) { - AddError("Reached end of input in method options (missing '}')."); - return false; - } + DO(ParseOptions(method_location, + MethodDescriptorProto::kOptionsFieldNumber, + method->mutable_options())); + } else { + DO(ConsumeEndOfDeclaration(";", &method_location)); + } - if (TryConsume(";")) { - // empty statement; ignore - } else { - LocationRecorder location(method_location, - MethodDescriptorProto::kOptionsFieldNumber); - if (!ParseOption(method->mutable_options(), location)) { - // This statement failed to parse. Skip it, but keep looping to - // parse other statements. - SkipStatement(); - } + return true; +} + + +bool Parser::ParseOptions(const LocationRecorder& parent_location, + const int optionsFieldNumber, + Message* mutable_options) { + // Options! + ConsumeEndOfDeclaration("{", &parent_location); + while (!TryConsumeEndOfDeclaration("}", NULL)) { + if (AtEnd()) { + AddError("Reached end of input in method options (missing '}')."); + return false; + } + + if (TryConsumeEndOfDeclaration(";", NULL)) { + // empty statement; ignore + } else { + LocationRecorder location(parent_location, + optionsFieldNumber); + if (!ParseOption(mutable_options, location, OPTION_STATEMENT)) { + // This statement failed to parse. Skip it, but keep looping to + // parse other statements. + SkipStatement(); } } - } else { - DO(Consume(";")); } return true; @@ -1406,32 +1532,44 @@ bool Parser::ParsePackage(FileDescriptorProto* file, if (!TryConsume(".")) break; file->mutable_package()->append("."); } + + location.EndAt(input_->previous()); + + DO(ConsumeEndOfDeclaration(";", &location)); } - DO(Consume(";")); return true; } -bool Parser::ParseImport(string* import_filename, - const LocationRecorder& root_location, - int index) { +bool Parser::ParseImport(RepeatedPtrField<string>* dependency, + RepeatedField<int32>* public_dependency, + RepeatedField<int32>* weak_dependency, + const LocationRecorder& root_location) { DO(Consume("import")); + if (LookingAt("public")) { + LocationRecorder location( + root_location, FileDescriptorProto::kPublicDependencyFieldNumber, + public_dependency->size()); + DO(Consume("public")); + *public_dependency->Add() = dependency->size(); + } else if (LookingAt("weak")) { + LocationRecorder location( + root_location, FileDescriptorProto::kWeakDependencyFieldNumber, + weak_dependency->size()); + DO(Consume("weak")); + *weak_dependency->Add() = dependency->size(); + } { LocationRecorder location(root_location, FileDescriptorProto::kDependencyFieldNumber, - index); - DO(ConsumeString(import_filename, + dependency->size()); + DO(ConsumeString(dependency->Add(), "Expected a string naming the file to import.")); - } - DO(Consume(";")); - return true; -} -bool Parser::ParseOption(Message* options, - const LocationRecorder& options_location) { - DO(Consume("option")); - DO(ParseOptionAssignment(options, options_location)); - DO(Consume(";")); + location.EndAt(input_->previous()); + + DO(ConsumeEndOfDeclaration(";", &location)); + } return true; } diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h index 4cc90a29..cfd3649b 100644 --- a/src/google/protobuf/compiler/parser.h +++ b/src/google/protobuf/compiler/parser.h @@ -116,6 +116,8 @@ class LIBPROTOBUF_EXPORT Parser { } private: + class LocationRecorder; + // ================================================================= // Error recovery helpers @@ -164,6 +166,8 @@ class LIBPROTOBUF_EXPORT Parser { bool ConsumeIdentifier(string* output, const char* error); // Consume an integer and store its value in "output". bool ConsumeInteger(int* output, const char* error); + // Consume a signed integer and store its value in "output". + bool ConsumeSignedInteger(int* output, const char* error); // Consume a 64-bit integer and store its value in "output". If the value // is greater than max_value, an error will be reported. bool ConsumeInteger64(uint64 max_value, uint64* output, const char* error); @@ -173,6 +177,20 @@ class LIBPROTOBUF_EXPORT Parser { // Consume a string literal and store its (unescaped) value in "output". bool ConsumeString(string* output, const char* error); + // Consume a token representing the end of the statement. Comments between + // this token and the next will be harvested for documentation. The given + // LocationRecorder should refer to the declaration that was just parsed; + // it will be populated with these comments. + // + // TODO(kenton): The LocationRecorder is const because historically locations + // have been passed around by const reference, for no particularly good + // reason. We should probably go through and change them all to mutable + // pointer to make this more intuitive. + bool TryConsumeEndOfDeclaration(const char* text, + const LocationRecorder* location); + bool ConsumeEndOfDeclaration(const char* text, + const LocationRecorder* location); + // ----------------------------------------------------------------- // Error logging helpers @@ -227,6 +245,14 @@ class LIBPROTOBUF_EXPORT Parser { void RecordLegacyLocation(const Message* descriptor, DescriptorPool::ErrorCollector::ErrorLocation location); + // Attaches leading and trailing comments to the location. The two strings + // will be swapped into place, so after this is called *leading and + // *trailing will be empty. + // + // TODO(kenton): See comment on TryConsumeEndOfDeclaration(), above, for + // why this is const. + void AttachComments(string* leading, string* trailing) const; + private: Parser* parser_; SourceCodeInfo::Location* location_; @@ -265,9 +291,10 @@ class LIBPROTOBUF_EXPORT Parser { const LocationRecorder& service_location); bool ParsePackage(FileDescriptorProto* file, const LocationRecorder& root_location); - bool ParseImport(string* import_filename, - const LocationRecorder& root_location, - int index); + bool ParseImport(RepeatedPtrField<string>* dependency, + RepeatedField<int32>* public_dependency, + RepeatedField<int32>* weak_dependency, + const LocationRecorder& root_location); bool ParseOption(Message* options, const LocationRecorder& options_location); @@ -329,6 +356,12 @@ class LIBPROTOBUF_EXPORT Parser { bool ParseServiceMethod(MethodDescriptorProto* method, const LocationRecorder& method_location); + + // Parse options of a single method or stream. + bool ParseOptions(const LocationRecorder& parent_location, + const int optionsFieldNumber, + Message* mutable_options); + // Parse "required", "optional", or "repeated" and fill in "label" // with the value. bool ParseLabel(FieldDescriptorProto::Label* label); @@ -351,11 +384,17 @@ class LIBPROTOBUF_EXPORT Parser { bool ParseDefaultAssignment(FieldDescriptorProto* field, const LocationRecorder& field_location); + enum OptionStyle { + OPTION_ASSIGNMENT, // just "name = value" + OPTION_STATEMENT // "option name = value;" + }; + // Parse a single option name/value pair, e.g. "ctype = CORD". The name // identifies a field of the given Message, and the value of that field // is set to the parsed value. - bool ParseOptionAssignment(Message* options, - const LocationRecorder& options_location); + bool ParseOption(Message* options, + const LocationRecorder& options_location, + OptionStyle style); // Parses a single part of a multipart option name. A multipart name consists // of names separated by dots. Each name is either an identifier or a series @@ -387,6 +426,10 @@ class LIBPROTOBUF_EXPORT Parser { bool stop_after_syntax_identifier_; string syntax_identifier_; + // Leading doc comments for the next declaration. These are not complete + // yet; use ConsumeEndOfDeclaration() to get the complete comments. + string upcoming_doc_comments_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Parser); }; diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc index 156c0dc3..c61ac60e 100644 --- a/src/google/protobuf/compiler/parser_unittest.cc +++ b/src/google/protobuf/compiler/parser_unittest.cc @@ -44,6 +44,7 @@ #include <google/protobuf/wire_format.h> #include <google/protobuf/text_format.h> #include <google/protobuf/unittest.pb.h> +#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> @@ -509,6 +510,31 @@ TEST_F(ParseMessageTest, CompoundExtensionRange) { "}"); } +TEST_F(ParseMessageTest, LargerMaxForMessageSetWireFormatMessages) { + // Messages using the message_set_wire_format option can accept larger + // extension numbers, as the numbers are not encoded as int32 field values + // rather than tags. + ExpectParsesTo( + "message TestMessage {\n" + " extensions 4 to max;\n" + " option message_set_wire_format = true;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " extension_range { start:4 end: 0x7fffffff }" + " options {\n" + " uninterpreted_option { \n" + " name {\n" + " name_part: \"message_set_wire_format\"\n" + " is_extension: false\n" + " }\n" + " identifier_value: \"true\"\n" + " }\n" + " }\n" + "}"); +} + TEST_F(ParseMessageTest, Extensions) { ExpectParsesTo( "extend Extendee1 { optional int32 foo = 12; }\n" @@ -571,6 +597,10 @@ TEST_F(ParseEnumTest, Values) { " FOO = 13;\n" " BAR = -10;\n" " BAZ = 500;\n" + " HEX_MAX = 0x7FFFFFFF;\n" + " HEX_MIN = -0x80000000;\n" + " INT_MAX = 2147483647;\n" + " INT_MIN = -2147483648;\n" "}\n", "enum_type {" @@ -578,6 +608,10 @@ TEST_F(ParseEnumTest, Values) { " value { name:\"FOO\" number:13 }" " value { name:\"BAR\" number:-10 }" " value { name:\"BAZ\" number:500 }" + " value { name:\"HEX_MAX\" number:2147483647 }" + " value { name:\"HEX_MIN\" number:-2147483648 }" + " value { name:\"INT_MAX\" number:2147483647 }" + " value { name:\"INT_MIN\" number:-2147483648 }" "}"); } @@ -631,7 +665,7 @@ TEST_F(ParseServiceTest, SimpleService) { "}"); } -TEST_F(ParseServiceTest, Methods) { +TEST_F(ParseServiceTest, MethodsAndStreams) { ExpectParsesTo( "service TestService {\n" " rpc Foo(In1) returns (Out1);\n" @@ -668,6 +702,20 @@ TEST_F(ParseMiscTest, ParseMultipleImports) { "dependency: \"baz.proto\""); } +TEST_F(ParseMiscTest, ParsePublicImports) { + ExpectParsesTo( + "import \"foo.proto\";\n" + "import public \"bar.proto\";\n" + "import \"baz.proto\";\n" + "import public \"qux.proto\";\n", + "dependency: \"foo.proto\"" + "dependency: \"bar.proto\"" + "dependency: \"baz.proto\"" + "dependency: \"qux.proto\"" + "public_dependency: 1 " + "public_dependency: 3 "); +} + TEST_F(ParseMiscTest, ParsePackage) { ExpectParsesTo( "package foo.bar.baz;\n", @@ -867,6 +915,20 @@ TEST_F(ParseErrorTest, DefaultValueTooLarge) { "6:36: Integer out of range.\n"); } +TEST_F(ParseErrorTest, EnumValueOutOfRange) { + ExpectHasErrors( + "enum TestEnum {\n" + " HEX_TOO_BIG = 0x80000000;\n" + " HEX_TOO_SMALL = -0x80000001;\n" + " INT_TOO_BIG = 2147483648;\n" + " INT_TOO_SMALL = -2147483649;\n" + "}\n", + "1:19: Integer out of range.\n" + "2:19: Integer out of range.\n" + "3:19: Integer out of range.\n" + "4:19: Integer out of range.\n"); +} + TEST_F(ParseErrorTest, DefaultValueMissing) { ExpectHasErrors( "message TestMessage {\n" @@ -976,6 +1038,7 @@ TEST_F(ParseErrorTest, ServiceMethodPrimitiveParams) { "1:26: Expected message type.\n"); } + TEST_F(ParseErrorTest, EofInMethodOptions) { ExpectHasErrors( "service TestService {\n" @@ -984,6 +1047,7 @@ TEST_F(ParseErrorTest, EofInMethodOptions) { "1:29: Reached end of input in service definition (missing '}').\n"); } + TEST_F(ParseErrorTest, PrimitiveMethodInput) { ExpectHasErrors( "service TestService {\n" @@ -992,6 +1056,7 @@ TEST_F(ParseErrorTest, PrimitiveMethodInput) { "1:10: Expected message type.\n"); } + TEST_F(ParseErrorTest, MethodOptionTypeError) { // This used to cause an infinite loop. ExpectHasErrors( @@ -1002,6 +1067,7 @@ TEST_F(ParseErrorTest, MethodOptionTypeError) { "2:45: Expected \"=\".\n"); } + // ------------------------------------------------------------------- // Import and package errors @@ -1157,6 +1223,7 @@ TEST_F(ParserValidationErrorTest, MethodNameError) { "3:6: \"Bar\" is already defined in \"Foo\".\n"); } + TEST_F(ParserValidationErrorTest, MethodInputTypeError) { ExpectHasValidationErrors( "message Baz {}\n" @@ -1166,6 +1233,7 @@ TEST_F(ParserValidationErrorTest, MethodInputTypeError) { "2:10: \"Qux\" is not defined.\n"); } + TEST_F(ParserValidationErrorTest, MethodOutputTypeError) { ExpectHasValidationErrors( "message Baz {}\n" @@ -1175,6 +1243,7 @@ TEST_F(ParserValidationErrorTest, MethodOutputTypeError) { "2:23: \"Qux\" is not defined.\n"); } + // =================================================================== // Test that the output from FileDescriptor::DebugString() (and all other // descriptor types) is parseable, and results in the same Descriptor @@ -1236,6 +1305,11 @@ TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) { // also need to give it the same name as the original. parsed.set_name("google/protobuf/unittest.proto"); // We need the imported dependency before we can build our parsed proto + const FileDescriptor* public_import = + protobuf_unittest_import::PublicImportMessage::descriptor()->file(); + FileDescriptorProto public_import_proto; + public_import->CopyTo(&public_import_proto); + ASSERT_TRUE(pool_.BuildFile(public_import_proto) != NULL); const FileDescriptor* import = protobuf_unittest_import::ImportMessage::descriptor()->file(); FileDescriptorProto import_proto; @@ -1258,6 +1332,45 @@ TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) { EXPECT_EQ(expected.DebugString(), parsed.DebugString()); } +TEST_F(ParseDecriptorDebugTest, TestCustomOptions) { + const FileDescriptor* original_file = + protobuf_unittest::AggregateMessage::descriptor()->file(); + FileDescriptorProto expected; + original_file->CopyTo(&expected); + + string debug_string = original_file->DebugString(); + + // Parse the debug string + SetupParser(debug_string.c_str()); + FileDescriptorProto parsed; + parser_->Parse(input_.get(), &parsed); + EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); + ASSERT_EQ("", error_collector_.text_); + + // 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 + // also need to give it the same name as the original. + parsed.set_name(original_file->name()); + + // unittest_custom_options.proto depends on descriptor.proto. + const FileDescriptor* import = FileDescriptorProto::descriptor()->file(); + FileDescriptorProto import_proto; + import->CopyTo(&import_proto); + ASSERT_TRUE(pool_.BuildFile(import_proto) != NULL); + const FileDescriptor* actual = pool_.BuildFile(parsed); + ASSERT_TRUE(actual != NULL); + parsed.Clear(); + actual->CopyTo(&parsed); + + // The messages might be in different orders, making them hard to compare. + // So, sort the messages in the descriptor protos (including nested messages, + // recursively). + SortMessages(&expected); + SortMessages(&parsed); + + EXPECT_EQ(expected.DebugString(), parsed.DebugString()); +} + // =================================================================== // SourceCodeInfo tests. @@ -1353,67 +1466,6 @@ bool FollowPath(const Message& root, } } -// Split some text on line breaks. The line breaks are retained in the output, -// so each line (except the last) ends with a '\n', and the lines can be -// concatenated to produce the original text. -// -// I couldn't find the proper string utility function for this. Our -// split-on-delimiter functions don't include the delimiter in the output. -void SplitLines(const string& text, vector<string>* lines) { - string::size_type pos = 0; - - while (pos != string::npos) { - string::size_type last_pos = pos; - pos = text.find_first_of('\n', pos); - if (pos != string::npos) ++pos; - lines->push_back(text.substr(last_pos, pos - last_pos)); - } -} - -// Look for the given tags in the given text and construct a span (as defined -// by SourceCodeInfo.Location.span) from them. E.g. for text like: -// /*a*/message /*b*/Foo/*c*/ {}/*d*/ -// There are four tags: "a", "b", "c", and "d". The constructed span starts -// immediately after the start tag's trailing '/' and ends immediately before -// the end tags leading '/'. -void MakeExpectedSpan(const vector<string>& lines, - const string& start_tag, const string& end_tag, - RepeatedField<int>* output) { - string start_comment = "/*" + start_tag + "*/"; - string end_comment = "/*" + end_tag + "*/"; - - int start_line = -1; - int start_column = -1; - for (int i = 0; i < lines.size(); i++) { - string::size_type pos = lines[i].find(start_comment); - if (pos != string::npos) { - start_line = i; - start_column = pos + start_comment.size(); - break; - } - } - ASSERT_NE(start_line, -1) - << "Tag \"" << start_comment << "\" not found in text."; - - int end_line = -1; - int end_column = -1; - for (int i = start_line; i < lines.size(); i++) { - string::size_type pos = lines[i].find(end_comment); - if (pos != string::npos) { - end_line = i; - end_column = pos; - break; - } - } - ASSERT_NE(end_line, -1) - << "Tag \"" << end_comment << "\" not found in text."; - - output->Add(start_line); - output->Add(start_column); - if (end_line != start_line) output->Add(end_line); - output->Add(end_column); -} - // Check if two spans are equal. bool CompareSpans(const RepeatedField<int>& span1, const RepeatedField<int>& span2) { @@ -1434,8 +1486,8 @@ class SourceInfoTest : public ParserTest { // Parse the given text as a .proto file and populate the spans_ map with // all the source location spans in its SourceCodeInfo table. bool Parse(const char* text) { - SetupParser(text); - SplitLines(text, &lines_); + ExtractMarkers(text); + SetupParser(text_without_markers_.c_str()); if (!parser_->Parse(input_.get(), &file_)) { return false; } @@ -1470,19 +1522,38 @@ class SourceInfoTest : public ParserTest { // part of the FileDescriptorProto. (If unclear, look at the actual tests; // it should quickly become obvious.) - bool HasSpan(const char* start_tag, const char* end_tag, + bool HasSpan(char start_marker, char end_marker, const Message& descriptor_proto) { - return HasSpan(start_tag, end_tag, descriptor_proto, NULL, -1); + return HasSpanWithComment( + start_marker, end_marker, descriptor_proto, NULL, -1, NULL, NULL); } - bool HasSpan(const char* start_tag, const char* end_tag, + bool HasSpanWithComment(char start_marker, char end_marker, + const Message& descriptor_proto, + const char* expected_leading_comments, + const char* expected_trailing_comments) { + return HasSpanWithComment( + start_marker, end_marker, descriptor_proto, NULL, -1, + expected_leading_comments, expected_trailing_comments); + } + + bool HasSpan(char start_marker, char end_marker, const Message& descriptor_proto, const string& field_name) { - return HasSpan(start_tag, end_tag, descriptor_proto, field_name, -1); + return HasSpan(start_marker, end_marker, descriptor_proto, field_name, -1); } - bool HasSpan(const char* start_tag, const char* end_tag, + bool HasSpan(char start_marker, char end_marker, const Message& descriptor_proto, const string& field_name, int index) { + return HasSpan(start_marker, end_marker, descriptor_proto, + field_name, index, NULL, NULL); + } + + bool HasSpan(char start_marker, char end_marker, + const Message& descriptor_proto, + const string& field_name, int index, + const char* expected_leading_comments, + const char* expected_trailing_comments) { const FieldDescriptor* field = descriptor_proto.GetDescriptor()->FindFieldByName(field_name); if (field == NULL) { @@ -1491,29 +1562,34 @@ class SourceInfoTest : public ParserTest { return false; } - return HasSpan(start_tag, end_tag, descriptor_proto, field, index); + return HasSpanWithComment( + start_marker, end_marker, descriptor_proto, field, index, + expected_leading_comments, expected_trailing_comments); } bool HasSpan(const Message& descriptor_proto) { - return HasSpan(NULL, NULL, descriptor_proto, NULL, -1); + return HasSpanWithComment( + '\0', '\0', descriptor_proto, NULL, -1, NULL, NULL); } bool HasSpan(const Message& descriptor_proto, const string& field_name) { - return HasSpan(NULL, NULL, descriptor_proto, field_name, -1); + return HasSpan('\0', '\0', descriptor_proto, field_name, -1); } bool HasSpan(const Message& descriptor_proto, const string& field_name, int index) { - return HasSpan(NULL, NULL, descriptor_proto, field_name, index); + return HasSpan('\0', '\0', descriptor_proto, field_name, index); } - bool HasSpan(const char* start_tag, const char* end_tag, - const Message& descriptor_proto, const FieldDescriptor* field, - int index) { + bool HasSpanWithComment(char start_marker, char end_marker, + const Message& descriptor_proto, + const FieldDescriptor* field, int index, + const char* expected_leading_comments, + const char* expected_trailing_comments) { pair<SpanMap::iterator, SpanMap::iterator> range = spans_.equal_range(SpanKey(descriptor_proto, field, index)); - if (start_tag == NULL) { + if (start_marker == '\0') { if (range.first == range.second) { return false; } else { @@ -1521,11 +1597,34 @@ class SourceInfoTest : public ParserTest { return true; } } else { + pair<int, int> start_pos = FindOrDie(markers_, start_marker); + pair<int, int> end_pos = FindOrDie(markers_, end_marker); + RepeatedField<int> expected_span; - MakeExpectedSpan(lines_, start_tag, end_tag, &expected_span); + expected_span.Add(start_pos.first); + expected_span.Add(start_pos.second); + if (end_pos.first != start_pos.first) { + expected_span.Add(end_pos.first); + } + expected_span.Add(end_pos.second); for (SpanMap::iterator iter = range.first; iter != range.second; ++iter) { if (CompareSpans(expected_span, iter->second->span())) { + if (expected_leading_comments == NULL) { + EXPECT_FALSE(iter->second->has_leading_comments()); + } else { + EXPECT_TRUE(iter->second->has_leading_comments()); + EXPECT_EQ(expected_leading_comments, + iter->second->leading_comments()); + } + if (expected_trailing_comments == NULL) { + EXPECT_FALSE(iter->second->has_trailing_comments()); + } else { + EXPECT_TRUE(iter->second->has_trailing_comments()); + EXPECT_EQ(expected_trailing_comments, + iter->second->trailing_comments()); + } + spans_.erase(iter); return true; } @@ -1542,10 +1641,11 @@ class SourceInfoTest : public ParserTest { int index; inline SpanKey() {} - inline SpanKey(const Message& descriptor_proto, - const FieldDescriptor* field, - int index) - : descriptor_proto(&descriptor_proto), field(field), index(index) {} + inline SpanKey(const Message& descriptor_proto_param, + const FieldDescriptor* field_param, + int index_param) + : descriptor_proto(&descriptor_proto_param), field(field_param), + index(index_param) {} inline bool operator<(const SpanKey& other) const { if (descriptor_proto < other.descriptor_proto) return true; @@ -1558,32 +1658,63 @@ class SourceInfoTest : public ParserTest { typedef multimap<SpanKey, const SourceCodeInfo::Location*> SpanMap; SpanMap spans_; - vector<string> lines_; + map<char, pair<int, int> > markers_; + string text_without_markers_; + + void ExtractMarkers(const char* text) { + markers_.clear(); + text_without_markers_.clear(); + int line = 0; + int column = 0; + while (*text != '\0') { + if (*text == '$') { + ++text; + GOOGLE_CHECK_NE('\0', *text); + if (*text == '$') { + text_without_markers_ += '$'; + ++column; + } else { + markers_[*text] = make_pair(line, column); + ++text; + GOOGLE_CHECK_EQ('$', *text); + } + } else if (*text == '\n') { + ++line; + column = 0; + text_without_markers_ += *text; + } else { + text_without_markers_ += *text; + ++column; + } + ++text; + } + } }; TEST_F(SourceInfoTest, BasicFileDecls) { EXPECT_TRUE(Parse( - "/*a*/syntax = \"proto2\";\n" - "package /*b*/foo.bar/*c*/;\n" - "import /*d*/\"baz.proto\"/*e*/;\n" - "import /*f*/\"qux.proto\"/*g*/;/*h*/\n" + "$a$syntax = \"proto2\";\n" + "package $b$foo.bar$c$;\n" + "import $d$\"baz.proto\"$e$;\n" + "import $f$\"qux.proto\"$g$;$h$\n" + "\n" "// comment ignored\n")); - EXPECT_TRUE(HasSpan("a", "h", file_)); - EXPECT_TRUE(HasSpan("b", "c", file_, "package")); - EXPECT_TRUE(HasSpan("d", "e", file_, "dependency", 0)); - EXPECT_TRUE(HasSpan("f", "g", file_, "dependency", 1)); + EXPECT_TRUE(HasSpan('a', 'h', file_)); + EXPECT_TRUE(HasSpan('b', 'c', file_, "package")); + EXPECT_TRUE(HasSpan('d', 'e', file_, "dependency", 0)); + EXPECT_TRUE(HasSpan('f', 'g', file_, "dependency", 1)); } TEST_F(SourceInfoTest, Messages) { EXPECT_TRUE(Parse( - "/*a*/message /*b*/Foo/*c*/ {}/*d*/\n" - "/*e*/message /*f*/Bar/*g*/ {}/*h*/\n")); + "$a$message $b$Foo$c$ {}$d$\n" + "$e$message $f$Bar$g$ {}$h$\n")); - EXPECT_TRUE(HasSpan("a", "d", file_.message_type(0))); - EXPECT_TRUE(HasSpan("b", "c", file_.message_type(0), "name")); - EXPECT_TRUE(HasSpan("e", "h", file_.message_type(1))); - EXPECT_TRUE(HasSpan("f", "g", file_.message_type(1), "name")); + EXPECT_TRUE(HasSpan('a', 'd', file_.message_type(0))); + EXPECT_TRUE(HasSpan('b', 'c', file_.message_type(0), "name")); + EXPECT_TRUE(HasSpan('e', 'h', file_.message_type(1))); + EXPECT_TRUE(HasSpan('f', 'g', file_.message_type(1), "name")); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -1592,24 +1723,24 @@ TEST_F(SourceInfoTest, Messages) { TEST_F(SourceInfoTest, Fields) { EXPECT_TRUE(Parse( "message Foo {\n" - " /*a*/optional/*b*/ /*c*/int32/*d*/ /*e*/bar/*f*/ = /*g*/1/*h*/;/*i*/\n" - " /*j*/repeated/*k*/ /*l*/X.Y/*m*/ /*n*/baz/*o*/ = /*p*/2/*q*/;/*r*/\n" + " $a$optional$b$ $c$int32$d$ $e$bar$f$ = $g$1$h$;$i$\n" + " $j$repeated$k$ $l$X.Y$m$ $n$baz$o$ = $p$2$q$;$r$\n" "}\n")); const FieldDescriptorProto& field1 = file_.message_type(0).field(0); const FieldDescriptorProto& field2 = file_.message_type(0).field(1); - EXPECT_TRUE(HasSpan("a", "i", field1)); - EXPECT_TRUE(HasSpan("a", "b", field1, "label")); - EXPECT_TRUE(HasSpan("c", "d", field1, "type")); - EXPECT_TRUE(HasSpan("e", "f", field1, "name")); - EXPECT_TRUE(HasSpan("g", "h", field1, "number")); + EXPECT_TRUE(HasSpan('a', 'i', field1)); + EXPECT_TRUE(HasSpan('a', 'b', field1, "label")); + EXPECT_TRUE(HasSpan('c', 'd', field1, "type")); + EXPECT_TRUE(HasSpan('e', 'f', field1, "name")); + EXPECT_TRUE(HasSpan('g', 'h', field1, "number")); - EXPECT_TRUE(HasSpan("j", "r", field2)); - EXPECT_TRUE(HasSpan("j", "k", field2, "label")); - EXPECT_TRUE(HasSpan("l", "m", field2, "type_name")); - EXPECT_TRUE(HasSpan("n", "o", field2, "name")); - EXPECT_TRUE(HasSpan("p", "q", field2, "number")); + EXPECT_TRUE(HasSpan('j', 'r', field2)); + EXPECT_TRUE(HasSpan('j', 'k', field2, "label")); + EXPECT_TRUE(HasSpan('l', 'm', field2, "type_name")); + EXPECT_TRUE(HasSpan('n', 'o', field2, "name")); + EXPECT_TRUE(HasSpan('p', 'q', field2, "number")); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -1619,31 +1750,31 @@ TEST_F(SourceInfoTest, Fields) { TEST_F(SourceInfoTest, Extensions) { EXPECT_TRUE(Parse( - "/*a*/extend /*b*/Foo/*c*/ {\n" - " /*d*/optional/*e*/ int32 bar = 1;/*f*/\n" - " /*g*/repeated/*h*/ X.Y baz = 2;/*i*/\n" - "}/*j*/\n" - "/*k*/extend /*l*/Bar/*m*/ {\n" - " /*n*/optional int32 qux = 1;/*o*/\n" - "}/*p*/\n")); + "$a$extend $b$Foo$c$ {\n" + " $d$optional$e$ int32 bar = 1;$f$\n" + " $g$repeated$h$ X.Y baz = 2;$i$\n" + "}$j$\n" + "$k$extend $l$Bar$m$ {\n" + " $n$optional int32 qux = 1;$o$\n" + "}$p$\n")); const FieldDescriptorProto& field1 = file_.extension(0); const FieldDescriptorProto& field2 = file_.extension(1); const FieldDescriptorProto& field3 = file_.extension(2); - EXPECT_TRUE(HasSpan("a", "j", file_, "extension")); - EXPECT_TRUE(HasSpan("k", "p", file_, "extension")); + EXPECT_TRUE(HasSpan('a', 'j', file_, "extension")); + EXPECT_TRUE(HasSpan('k', 'p', file_, "extension")); - EXPECT_TRUE(HasSpan("d", "f", field1)); - EXPECT_TRUE(HasSpan("d", "e", field1, "label")); - EXPECT_TRUE(HasSpan("b", "c", field1, "extendee")); + EXPECT_TRUE(HasSpan('d', 'f', field1)); + EXPECT_TRUE(HasSpan('d', 'e', field1, "label")); + EXPECT_TRUE(HasSpan('b', 'c', field1, "extendee")); - EXPECT_TRUE(HasSpan("g", "i", field2)); - EXPECT_TRUE(HasSpan("g", "h", field2, "label")); - EXPECT_TRUE(HasSpan("b", "c", field2, "extendee")); + EXPECT_TRUE(HasSpan('g', 'i', field2)); + EXPECT_TRUE(HasSpan('g', 'h', field2, "label")); + EXPECT_TRUE(HasSpan('b', 'c', field2, "extendee")); - EXPECT_TRUE(HasSpan("n", "o", field3)); - EXPECT_TRUE(HasSpan("l", "m", field3, "extendee")); + EXPECT_TRUE(HasSpan('n', 'o', field3)); + EXPECT_TRUE(HasSpan('l', 'm', field3, "extendee")); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -1662,32 +1793,32 @@ TEST_F(SourceInfoTest, Extensions) { TEST_F(SourceInfoTest, NestedExtensions) { EXPECT_TRUE(Parse( "message Message {\n" - " /*a*/extend /*b*/Foo/*c*/ {\n" - " /*d*/optional/*e*/ int32 bar = 1;/*f*/\n" - " /*g*/repeated/*h*/ X.Y baz = 2;/*i*/\n" - " }/*j*/\n" - " /*k*/extend /*l*/Bar/*m*/ {\n" - " /*n*/optional int32 qux = 1;/*o*/\n" - " }/*p*/\n" + " $a$extend $b$Foo$c$ {\n" + " $d$optional$e$ int32 bar = 1;$f$\n" + " $g$repeated$h$ X.Y baz = 2;$i$\n" + " }$j$\n" + " $k$extend $l$Bar$m$ {\n" + " $n$optional int32 qux = 1;$o$\n" + " }$p$\n" "}\n")); const FieldDescriptorProto& field1 = file_.message_type(0).extension(0); const FieldDescriptorProto& field2 = file_.message_type(0).extension(1); const FieldDescriptorProto& field3 = file_.message_type(0).extension(2); - EXPECT_TRUE(HasSpan("a", "j", file_.message_type(0), "extension")); - EXPECT_TRUE(HasSpan("k", "p", file_.message_type(0), "extension")); + EXPECT_TRUE(HasSpan('a', 'j', file_.message_type(0), "extension")); + EXPECT_TRUE(HasSpan('k', 'p', file_.message_type(0), "extension")); - EXPECT_TRUE(HasSpan("d", "f", field1)); - EXPECT_TRUE(HasSpan("d", "e", field1, "label")); - EXPECT_TRUE(HasSpan("b", "c", field1, "extendee")); + EXPECT_TRUE(HasSpan('d', 'f', field1)); + EXPECT_TRUE(HasSpan('d', 'e', field1, "label")); + EXPECT_TRUE(HasSpan('b', 'c', field1, "extendee")); - EXPECT_TRUE(HasSpan("g", "i", field2)); - EXPECT_TRUE(HasSpan("g", "h", field2, "label")); - EXPECT_TRUE(HasSpan("b", "c", field2, "extendee")); + EXPECT_TRUE(HasSpan('g', 'i', field2)); + EXPECT_TRUE(HasSpan('g', 'h', field2, "label")); + EXPECT_TRUE(HasSpan('b', 'c', field2, "extendee")); - EXPECT_TRUE(HasSpan("n", "o", field3)); - EXPECT_TRUE(HasSpan("l", "m", field3, "extendee")); + EXPECT_TRUE(HasSpan('n', 'o', field3)); + EXPECT_TRUE(HasSpan('l', 'm', field3, "extendee")); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -1708,8 +1839,8 @@ TEST_F(SourceInfoTest, NestedExtensions) { TEST_F(SourceInfoTest, ExtensionRanges) { EXPECT_TRUE(Parse( "message Message {\n" - " /*a*/extensions /*b*/1/*c*/ to /*d*/4/*e*/, /*f*/6/*g*/;/*h*/\n" - " /*i*/extensions /*j*/8/*k*/ to /*l*/max/*m*/;/*n*/\n" + " $a$extensions $b$1$c$ to $d$4$e$, $f$6$g$;$h$\n" + " $i$extensions $j$8$k$ to $l$max$m$;$n$\n" "}\n")); const DescriptorProto::ExtensionRange& range1 = @@ -1719,20 +1850,20 @@ TEST_F(SourceInfoTest, ExtensionRanges) { const DescriptorProto::ExtensionRange& range3 = file_.message_type(0).extension_range(2); - EXPECT_TRUE(HasSpan("a", "h", file_.message_type(0), "extension_range")); - EXPECT_TRUE(HasSpan("i", "n", file_.message_type(0), "extension_range")); + EXPECT_TRUE(HasSpan('a', 'h', file_.message_type(0), "extension_range")); + EXPECT_TRUE(HasSpan('i', 'n', file_.message_type(0), "extension_range")); - EXPECT_TRUE(HasSpan("b", "e", range1)); - EXPECT_TRUE(HasSpan("b", "c", range1, "start")); - EXPECT_TRUE(HasSpan("d", "e", range1, "end")); + EXPECT_TRUE(HasSpan('b', 'e', range1)); + EXPECT_TRUE(HasSpan('b', 'c', range1, "start")); + EXPECT_TRUE(HasSpan('d', 'e', range1, "end")); - EXPECT_TRUE(HasSpan("f", "g", range2)); - EXPECT_TRUE(HasSpan("f", "g", range2, "start")); - EXPECT_TRUE(HasSpan("f", "g", range2, "end")); + EXPECT_TRUE(HasSpan('f', 'g', range2)); + EXPECT_TRUE(HasSpan('f', 'g', range2, "start")); + EXPECT_TRUE(HasSpan('f', 'g', range2, "end")); - EXPECT_TRUE(HasSpan("j", "m", range3)); - EXPECT_TRUE(HasSpan("j", "k", range3, "start")); - EXPECT_TRUE(HasSpan("l", "m", range3, "end")); + EXPECT_TRUE(HasSpan('j', 'm', range3)); + EXPECT_TRUE(HasSpan('j', 'k', range3, "start")); + EXPECT_TRUE(HasSpan('l', 'm', range3, "end")); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -1743,22 +1874,22 @@ TEST_F(SourceInfoTest, ExtensionRanges) { TEST_F(SourceInfoTest, NestedMessages) { EXPECT_TRUE(Parse( "message Foo {\n" - " /*a*/message /*b*/Bar/*c*/ {\n" - " /*d*/message /*e*/Baz/*f*/ {}/*g*/\n" - " }/*h*/\n" - " /*i*/message /*j*/Qux/*k*/ {}/*l*/\n" + " $a$message $b$Bar$c$ {\n" + " $d$message $e$Baz$f$ {}$g$\n" + " }$h$\n" + " $i$message $j$Qux$k$ {}$l$\n" "}\n")); const DescriptorProto& bar = file_.message_type(0).nested_type(0); const DescriptorProto& baz = bar.nested_type(0); const DescriptorProto& qux = file_.message_type(0).nested_type(1); - EXPECT_TRUE(HasSpan("a", "h", bar)); - EXPECT_TRUE(HasSpan("b", "c", bar, "name")); - EXPECT_TRUE(HasSpan("d", "g", baz)); - EXPECT_TRUE(HasSpan("e", "f", baz, "name")); - EXPECT_TRUE(HasSpan("i", "l", qux)); - EXPECT_TRUE(HasSpan("j", "k", qux, "name")); + EXPECT_TRUE(HasSpan('a', 'h', bar)); + EXPECT_TRUE(HasSpan('b', 'c', bar, "name")); + EXPECT_TRUE(HasSpan('d', 'g', baz)); + EXPECT_TRUE(HasSpan('e', 'f', baz, "name")); + EXPECT_TRUE(HasSpan('i', 'l', qux)); + EXPECT_TRUE(HasSpan('j', 'k', qux, "name")); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -1770,9 +1901,9 @@ TEST_F(SourceInfoTest, Groups) { EXPECT_TRUE(Parse( "message Foo {\n" " message Bar {}\n" - " /*a*/optional/*b*/ /*c*/group/*d*/ /*e*/Baz/*f*/ = /*g*/1/*h*/ {\n" - " /*i*/message Qux {}/*j*/\n" - " }/*k*/\n" + " $a$optional$b$ $c$group$d$ $e$Baz$f$ = $g$1$h$ {\n" + " $i$message Qux {}$j$\n" + " }$k$\n" "}\n")); const DescriptorProto& bar = file_.message_type(0).nested_type(0); @@ -1780,16 +1911,16 @@ TEST_F(SourceInfoTest, Groups) { const DescriptorProto& qux = baz.nested_type(0); const FieldDescriptorProto& field = file_.message_type(0).field(0); - EXPECT_TRUE(HasSpan("a", "k", field)); - EXPECT_TRUE(HasSpan("a", "b", field, "label")); - EXPECT_TRUE(HasSpan("c", "d", field, "type")); - EXPECT_TRUE(HasSpan("e", "f", field, "name")); - EXPECT_TRUE(HasSpan("e", "f", field, "type_name")); - EXPECT_TRUE(HasSpan("g", "h", field, "number")); + EXPECT_TRUE(HasSpan('a', 'k', field)); + EXPECT_TRUE(HasSpan('a', 'b', field, "label")); + EXPECT_TRUE(HasSpan('c', 'd', field, "type")); + EXPECT_TRUE(HasSpan('e', 'f', field, "name")); + EXPECT_TRUE(HasSpan('e', 'f', field, "type_name")); + EXPECT_TRUE(HasSpan('g', 'h', field, "number")); - EXPECT_TRUE(HasSpan("a", "k", baz)); - EXPECT_TRUE(HasSpan("e", "f", baz, "name")); - EXPECT_TRUE(HasSpan("i", "j", qux)); + EXPECT_TRUE(HasSpan('a', 'k', baz)); + EXPECT_TRUE(HasSpan('e', 'f', baz, "name")); + EXPECT_TRUE(HasSpan('i', 'j', qux)); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -1802,13 +1933,13 @@ TEST_F(SourceInfoTest, Groups) { TEST_F(SourceInfoTest, Enums) { EXPECT_TRUE(Parse( - "/*a*/enum /*b*/Foo/*c*/ {}/*d*/\n" - "/*e*/enum /*f*/Bar/*g*/ {}/*h*/\n")); + "$a$enum $b$Foo$c$ {}$d$\n" + "$e$enum $f$Bar$g$ {}$h$\n")); - EXPECT_TRUE(HasSpan("a", "d", file_.enum_type(0))); - EXPECT_TRUE(HasSpan("b", "c", file_.enum_type(0), "name")); - EXPECT_TRUE(HasSpan("e", "h", file_.enum_type(1))); - EXPECT_TRUE(HasSpan("f", "g", file_.enum_type(1), "name")); + EXPECT_TRUE(HasSpan('a', 'd', file_.enum_type(0))); + EXPECT_TRUE(HasSpan('b', 'c', file_.enum_type(0), "name")); + EXPECT_TRUE(HasSpan('e', 'h', file_.enum_type(1))); + EXPECT_TRUE(HasSpan('f', 'g', file_.enum_type(1), "name")); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -1817,19 +1948,19 @@ TEST_F(SourceInfoTest, Enums) { TEST_F(SourceInfoTest, EnumValues) { EXPECT_TRUE(Parse( "enum Foo {\n" - " /*a*/BAR/*b*/ = /*c*/1/*d*/;/*e*/\n" - " /*f*/BAZ/*g*/ = /*h*/2/*i*/;/*j*/\n" + " $a$BAR$b$ = $c$1$d$;$e$\n" + " $f$BAZ$g$ = $h$2$i$;$j$\n" "}")); const EnumValueDescriptorProto& bar = file_.enum_type(0).value(0); const EnumValueDescriptorProto& baz = file_.enum_type(0).value(1); - EXPECT_TRUE(HasSpan("a", "e", bar)); - EXPECT_TRUE(HasSpan("a", "b", bar, "name")); - EXPECT_TRUE(HasSpan("c", "d", bar, "number")); - EXPECT_TRUE(HasSpan("f", "j", baz)); - EXPECT_TRUE(HasSpan("f", "g", baz, "name")); - EXPECT_TRUE(HasSpan("h", "i", baz, "number")); + EXPECT_TRUE(HasSpan('a', 'e', bar)); + EXPECT_TRUE(HasSpan('a', 'b', bar, "name")); + EXPECT_TRUE(HasSpan('c', 'd', bar, "number")); + EXPECT_TRUE(HasSpan('f', 'j', baz)); + EXPECT_TRUE(HasSpan('f', 'g', baz, "name")); + EXPECT_TRUE(HasSpan('h', 'i', baz, "number")); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -1840,17 +1971,17 @@ TEST_F(SourceInfoTest, EnumValues) { TEST_F(SourceInfoTest, NestedEnums) { EXPECT_TRUE(Parse( "message Foo {\n" - " /*a*/enum /*b*/Bar/*c*/ {}/*d*/\n" - " /*e*/enum /*f*/Baz/*g*/ {}/*h*/\n" + " $a$enum $b$Bar$c$ {}$d$\n" + " $e$enum $f$Baz$g$ {}$h$\n" "}\n")); const EnumDescriptorProto& bar = file_.message_type(0).enum_type(0); const EnumDescriptorProto& baz = file_.message_type(0).enum_type(1); - EXPECT_TRUE(HasSpan("a", "d", bar)); - EXPECT_TRUE(HasSpan("b", "c", bar, "name")); - EXPECT_TRUE(HasSpan("e", "h", baz)); - EXPECT_TRUE(HasSpan("f", "g", baz, "name")); + EXPECT_TRUE(HasSpan('a', 'd', bar)); + EXPECT_TRUE(HasSpan('b', 'c', bar, "name")); + EXPECT_TRUE(HasSpan('e', 'h', baz)); + EXPECT_TRUE(HasSpan('f', 'g', baz, "name")); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -1860,37 +1991,37 @@ TEST_F(SourceInfoTest, NestedEnums) { TEST_F(SourceInfoTest, Services) { EXPECT_TRUE(Parse( - "/*a*/service /*b*/Foo/*c*/ {}/*d*/\n" - "/*e*/service /*f*/Bar/*g*/ {}/*h*/\n")); + "$a$service $b$Foo$c$ {}$d$\n" + "$e$service $f$Bar$g$ {}$h$\n")); - EXPECT_TRUE(HasSpan("a", "d", file_.service(0))); - EXPECT_TRUE(HasSpan("b", "c", file_.service(0), "name")); - EXPECT_TRUE(HasSpan("e", "h", file_.service(1))); - EXPECT_TRUE(HasSpan("f", "g", file_.service(1), "name")); + EXPECT_TRUE(HasSpan('a', 'd', file_.service(0))); + EXPECT_TRUE(HasSpan('b', 'c', file_.service(0), "name")); + EXPECT_TRUE(HasSpan('e', 'h', file_.service(1))); + EXPECT_TRUE(HasSpan('f', 'g', file_.service(1), "name")); // Ignore these. EXPECT_TRUE(HasSpan(file_)); } -TEST_F(SourceInfoTest, Methods) { +TEST_F(SourceInfoTest, MethodsAndStreams) { EXPECT_TRUE(Parse( "service Foo {\n" - " /*a*/rpc /*b*/Bar/*c*/(/*d*/X/*e*/) returns(/*f*/Y/*g*/);/*h*/" - " /*i*/rpc /*j*/Baz/*k*/(/*l*/Z/*m*/) returns(/*n*/W/*o*/);/*p*/" + " $a$rpc $b$Bar$c$($d$X$e$) returns($f$Y$g$);$h$" + " $i$rpc $j$Baz$k$($l$Z$m$) returns($n$W$o$);$p$" "}")); const MethodDescriptorProto& bar = file_.service(0).method(0); const MethodDescriptorProto& baz = file_.service(0).method(1); - EXPECT_TRUE(HasSpan("a", "h", bar)); - EXPECT_TRUE(HasSpan("b", "c", bar, "name")); - EXPECT_TRUE(HasSpan("d", "e", bar, "input_type")); - EXPECT_TRUE(HasSpan("f", "g", bar, "output_type")); + EXPECT_TRUE(HasSpan('a', 'h', bar)); + EXPECT_TRUE(HasSpan('b', 'c', bar, "name")); + EXPECT_TRUE(HasSpan('d', 'e', bar, "input_type")); + EXPECT_TRUE(HasSpan('f', 'g', bar, "output_type")); - EXPECT_TRUE(HasSpan("i", "p", baz)); - EXPECT_TRUE(HasSpan("j", "k", baz, "name")); - EXPECT_TRUE(HasSpan("l", "m", baz, "input_type")); - EXPECT_TRUE(HasSpan("n", "o", baz, "output_type")); + EXPECT_TRUE(HasSpan('i', 'p', baz)); + EXPECT_TRUE(HasSpan('j', 'k', baz, "name")); + EXPECT_TRUE(HasSpan('l', 'm', baz, "input_type")); + EXPECT_TRUE(HasSpan('n', 'o', baz, "output_type")); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -1900,13 +2031,13 @@ TEST_F(SourceInfoTest, Methods) { TEST_F(SourceInfoTest, Options) { EXPECT_TRUE(Parse( - "/*a*/option /*b*/foo/*c*/./*d*/(/*e*/bar.baz/*f*/)/*g*/ = " - "/*h*/123/*i*/;/*j*/\n" - "/*k*/option qux = /*l*/-123/*m*/;/*n*/\n" - "/*o*/option corge = /*p*/abc/*q*/;/*r*/\n" - "/*s*/option grault = /*t*/'blah'/*u*/;/*v*/\n" - "/*w*/option garply = /*x*/{ yadda yadda }/*y*/;/*z*/\n" - "/*0*/option waldo = /*1*/123.0/*2*/;/*3*/\n" + "$a$option $b$foo$c$.$d$($e$bar.baz$f$)$g$ = " + "$h$123$i$;$j$\n" + "$k$option qux = $l$-123$m$;$n$\n" + "$o$option corge = $p$abc$q$;$r$\n" + "$s$option grault = $t$'blah'$u$;$v$\n" + "$w$option garply = $x${ yadda yadda }$y$;$z$\n" + "$0$option waldo = $1$123.0$2$;$3$\n" )); const UninterpretedOption& option1 = file_.options().uninterpreted_option(0); @@ -1916,29 +2047,29 @@ TEST_F(SourceInfoTest, Options) { const UninterpretedOption& option5 = file_.options().uninterpreted_option(4); const UninterpretedOption& option6 = file_.options().uninterpreted_option(5); - EXPECT_TRUE(HasSpan("a", "j", file_.options())); - EXPECT_TRUE(HasSpan("b", "i", option1)); - EXPECT_TRUE(HasSpan("b", "g", option1, "name")); - EXPECT_TRUE(HasSpan("b", "c", option1.name(0))); - EXPECT_TRUE(HasSpan("b", "c", option1.name(0), "name_part")); - EXPECT_TRUE(HasSpan("d", "g", option1.name(1))); - EXPECT_TRUE(HasSpan("e", "f", option1.name(1), "name_part")); - EXPECT_TRUE(HasSpan("h", "i", option1, "positive_int_value")); + EXPECT_TRUE(HasSpan('a', 'j', file_.options())); + EXPECT_TRUE(HasSpan('a', 'j', option1)); + EXPECT_TRUE(HasSpan('b', 'g', option1, "name")); + EXPECT_TRUE(HasSpan('b', 'c', option1.name(0))); + EXPECT_TRUE(HasSpan('b', 'c', option1.name(0), "name_part")); + EXPECT_TRUE(HasSpan('d', 'g', option1.name(1))); + EXPECT_TRUE(HasSpan('e', 'f', option1.name(1), "name_part")); + EXPECT_TRUE(HasSpan('h', 'i', option1, "positive_int_value")); - EXPECT_TRUE(HasSpan("k", "n", file_.options())); - EXPECT_TRUE(HasSpan("l", "m", option2, "negative_int_value")); + EXPECT_TRUE(HasSpan('k', 'n', file_.options())); + EXPECT_TRUE(HasSpan('l', 'm', option2, "negative_int_value")); - EXPECT_TRUE(HasSpan("o", "r", file_.options())); - EXPECT_TRUE(HasSpan("p", "q", option3, "identifier_value")); + EXPECT_TRUE(HasSpan('o', 'r', file_.options())); + EXPECT_TRUE(HasSpan('p', 'q', option3, "identifier_value")); - EXPECT_TRUE(HasSpan("s", "v", file_.options())); - EXPECT_TRUE(HasSpan("t", "u", option4, "string_value")); + EXPECT_TRUE(HasSpan('s', 'v', file_.options())); + EXPECT_TRUE(HasSpan('t', 'u', option4, "string_value")); - EXPECT_TRUE(HasSpan("w", "z", file_.options())); - EXPECT_TRUE(HasSpan("x", "y", option5, "aggregate_value")); + EXPECT_TRUE(HasSpan('w', 'z', file_.options())); + EXPECT_TRUE(HasSpan('x', 'y', option5, "aggregate_value")); - EXPECT_TRUE(HasSpan("0", "3", file_.options())); - EXPECT_TRUE(HasSpan("1", "2", option6, "double_value")); + EXPECT_TRUE(HasSpan('0', '3', file_.options())); + EXPECT_TRUE(HasSpan('1', '2', option6, "double_value")); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -1967,22 +2098,22 @@ TEST_F(SourceInfoTest, Options) { TEST_F(SourceInfoTest, ScopedOptions) { EXPECT_TRUE(Parse( "message Foo {\n" - " /*a*/option mopt = 1;/*b*/\n" + " $a$option mopt = 1;$b$\n" "}\n" "enum Bar {\n" - " /*c*/option eopt = 1;/*d*/\n" + " $c$option eopt = 1;$d$\n" "}\n" "service Baz {\n" - " /*e*/option sopt = 1;/*f*/\n" + " $e$option sopt = 1;$f$\n" " rpc M(X) returns(Y) {\n" - " /*g*/option mopt = 1;/*h*/\n" + " $g$option mopt = 1;$h$\n" " }\n" "}\n")); - EXPECT_TRUE(HasSpan("a", "b", file_.message_type(0).options())); - EXPECT_TRUE(HasSpan("c", "d", file_.enum_type(0).options())); - EXPECT_TRUE(HasSpan("e", "f", file_.service(0).options())); - EXPECT_TRUE(HasSpan("g", "h", file_.service(0).method(0).options())); + EXPECT_TRUE(HasSpan('a', 'b', file_.message_type(0).options())); + EXPECT_TRUE(HasSpan('c', 'd', file_.enum_type(0).options())); + EXPECT_TRUE(HasSpan('e', 'f', file_.service(0).options())); + EXPECT_TRUE(HasSpan('g', 'h', file_.service(0).method(0).options())); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -2045,8 +2176,8 @@ TEST_F(SourceInfoTest, FieldOptions) { EXPECT_TRUE(Parse( "message Foo {" " optional int32 bar = 1 " - "/*a*/[default=/*b*/123/*c*/,/*d*/opt1=123/*e*/," - "/*f*/opt2='hi'/*g*/]/*h*/;" + "$a$[default=$b$123$c$,$d$opt1=123$e$," + "$f$opt2='hi'$g$]$h$;" "}\n" )); @@ -2054,10 +2185,10 @@ TEST_F(SourceInfoTest, FieldOptions) { const UninterpretedOption& option1 = field.options().uninterpreted_option(0); const UninterpretedOption& option2 = field.options().uninterpreted_option(1); - EXPECT_TRUE(HasSpan("a", "h", field.options())); - EXPECT_TRUE(HasSpan("b", "c", field, "default_value")); - EXPECT_TRUE(HasSpan("d", "e", option1)); - EXPECT_TRUE(HasSpan("f", "g", option2)); + EXPECT_TRUE(HasSpan('a', 'h', field.options())); + EXPECT_TRUE(HasSpan('b', 'c', field, "default_value")); + EXPECT_TRUE(HasSpan('d', 'e', option1)); + EXPECT_TRUE(HasSpan('f', 'g', option2)); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -2084,7 +2215,7 @@ TEST_F(SourceInfoTest, EnumValueOptions) { // syntax used for enum options is understood. EXPECT_TRUE(Parse( "enum Foo {" - " BAR = 1 /*a*/[/*b*/opt1=123/*c*/,/*d*/opt2='hi'/*e*/]/*f*/;" + " BAR = 1 $a$[$b$opt1=123$c$,$d$opt2='hi'$e$]$f$;" "}\n" )); @@ -2092,9 +2223,9 @@ TEST_F(SourceInfoTest, EnumValueOptions) { const UninterpretedOption& option1 = value.options().uninterpreted_option(0); const UninterpretedOption& option2 = value.options().uninterpreted_option(1); - EXPECT_TRUE(HasSpan("a", "f", value.options())); - EXPECT_TRUE(HasSpan("b", "c", option1)); - EXPECT_TRUE(HasSpan("d", "e", option2)); + EXPECT_TRUE(HasSpan('a', 'f', value.options())); + EXPECT_TRUE(HasSpan('b', 'c', option1)); + EXPECT_TRUE(HasSpan('d', 'e', option2)); // Ignore these. EXPECT_TRUE(HasSpan(file_)); @@ -2113,6 +2244,127 @@ TEST_F(SourceInfoTest, EnumValueOptions) { EXPECT_TRUE(HasSpan(option2, "string_value")); } +TEST_F(SourceInfoTest, DocComments) { + EXPECT_TRUE(Parse( + "// Foo leading\n" + "// line 2\n" + "$a$message Foo {\n" + " // Foo trailing\n" + " // line 2\n" + "\n" + " // ignored\n" + "\n" + " // bar leading\n" + " $b$optional int32 bar = 1;$c$\n" + " // bar trailing\n" + "}$d$\n" + "// ignored\n" + )); + + const DescriptorProto& foo = file_.message_type(0); + const FieldDescriptorProto& bar = foo.field(0); + + EXPECT_TRUE(HasSpanWithComment('a', 'd', foo, + " Foo leading\n line 2\n", + " Foo trailing\n line 2\n")); + EXPECT_TRUE(HasSpanWithComment('b', 'c', bar, + " bar leading\n", + " bar trailing\n")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(foo, "name")); + EXPECT_TRUE(HasSpan(bar, "label")); + EXPECT_TRUE(HasSpan(bar, "type")); + EXPECT_TRUE(HasSpan(bar, "name")); + EXPECT_TRUE(HasSpan(bar, "number")); +} + +TEST_F(SourceInfoTest, DocComments2) { + EXPECT_TRUE(Parse( + "// ignored\n" + "syntax = \"proto2\";\n" + "// Foo leading\n" + "// line 2\n" + "$a$message Foo {\n" + " /* Foo trailing\n" + " * line 2 */\n" + " // ignored\n" + " /* bar leading\n" + " */" + " $b$optional int32 bar = 1;$c$ // bar trailing\n" + " // ignored\n" + "}$d$\n" + "// ignored\n" + "\n" + "// option leading\n" + "$e$option baz = 123;$f$\n" + "// option trailing\n" + )); + + const DescriptorProto& foo = file_.message_type(0); + const FieldDescriptorProto& bar = foo.field(0); + const UninterpretedOption& baz = file_.options().uninterpreted_option(0); + + EXPECT_TRUE(HasSpanWithComment('a', 'd', foo, + " Foo leading\n line 2\n", + " Foo trailing\n line 2 ")); + EXPECT_TRUE(HasSpanWithComment('b', 'c', bar, + " bar leading\n", + " bar trailing\n")); + EXPECT_TRUE(HasSpanWithComment('e', 'f', baz, + " option leading\n", + " option trailing\n")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(foo, "name")); + EXPECT_TRUE(HasSpan(bar, "label")); + EXPECT_TRUE(HasSpan(bar, "type")); + EXPECT_TRUE(HasSpan(bar, "name")); + EXPECT_TRUE(HasSpan(bar, "number")); + EXPECT_TRUE(HasSpan(file_.options())); + EXPECT_TRUE(HasSpan(baz, "name")); + EXPECT_TRUE(HasSpan(baz.name(0))); + EXPECT_TRUE(HasSpan(baz.name(0), "name_part")); + EXPECT_TRUE(HasSpan(baz, "positive_int_value")); +} + +TEST_F(SourceInfoTest, DocComments3) { + EXPECT_TRUE(Parse( + "$a$message Foo {\n" + " // bar leading\n" + " $b$optional int32 bar = 1 [(baz.qux) = {}];$c$\n" + " // bar trailing\n" + "}$d$\n" + "// ignored\n" + )); + + const DescriptorProto& foo = file_.message_type(0); + const FieldDescriptorProto& bar = foo.field(0); + + EXPECT_TRUE(HasSpanWithComment('b', 'c', bar, + " bar leading\n", + " bar trailing\n")); + + // Ignore these. + EXPECT_TRUE(HasSpan(file_)); + EXPECT_TRUE(HasSpan(foo)); + EXPECT_TRUE(HasSpan(foo, "name")); + EXPECT_TRUE(HasSpan(bar, "label")); + EXPECT_TRUE(HasSpan(bar, "type")); + EXPECT_TRUE(HasSpan(bar, "name")); + EXPECT_TRUE(HasSpan(bar, "number")); + EXPECT_TRUE(HasSpan(bar.options())); + EXPECT_TRUE(HasSpan(bar.options().uninterpreted_option(0))); + EXPECT_TRUE(HasSpan(bar.options().uninterpreted_option(0), "name")); + EXPECT_TRUE(HasSpan(bar.options().uninterpreted_option(0).name(0))); + EXPECT_TRUE(HasSpan( + bar.options().uninterpreted_option(0).name(0), "name_part")); + EXPECT_TRUE(HasSpan( + bar.options().uninterpreted_option(0), "aggregate_value")); +} + // =================================================================== } // anonymous namespace diff --git a/src/google/protobuf/compiler/plugin.h b/src/google/protobuf/compiler/plugin.h index 7c403332..6fa2de12 100644 --- a/src/google/protobuf/compiler/plugin.h +++ b/src/google/protobuf/compiler/plugin.h @@ -56,7 +56,6 @@ #define GOOGLE_PROTOBUF_COMPILER_PLUGIN_H__ #include <google/protobuf/stubs/common.h> - namespace google { namespace protobuf { namespace compiler { diff --git a/src/google/protobuf/compiler/plugin.pb.cc b/src/google/protobuf/compiler/plugin.pb.cc index 05ff2f04..bc0c1f17 100644 --- a/src/google/protobuf/compiler/plugin.pb.cc +++ b/src/google/protobuf/compiler/plugin.pb.cc @@ -1,4 +1,5 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! +// source: google/protobuf/compiler/plugin.proto #define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION #include "google/protobuf/compiler/plugin.pb.h" @@ -9,6 +10,7 @@ #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> #include <google/protobuf/descriptor.h> +#include <google/protobuf/generated_message_reflection.h> #include <google/protobuf/reflection_ops.h> #include <google/protobuf/wire_format.h> // @@protoc_insertion_point(includes) @@ -124,6 +126,7 @@ void protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto() { if (already_here) return; already_here = true; GOOGLE_PROTOBUF_VERIFY_VERSION; + ::google::protobuf::protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( "\n%google/protobuf/compiler/plugin.proto\022" @@ -253,7 +256,7 @@ bool CodeGeneratorRequest::MergePartialFromCodedStream( if (input->ExpectTag(18)) goto parse_parameter; break; } - + // optional string parameter = 2; case 2: { if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == @@ -270,7 +273,7 @@ bool CodeGeneratorRequest::MergePartialFromCodedStream( if (input->ExpectTag(122)) goto parse_proto_file; break; } - + // repeated .google.protobuf.FileDescriptorProto proto_file = 15; case 15: { if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == @@ -285,7 +288,7 @@ bool CodeGeneratorRequest::MergePartialFromCodedStream( if (input->ExpectAtEnd()) return true; break; } - + default: { handle_uninterpreted: if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == @@ -312,7 +315,7 @@ void CodeGeneratorRequest::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteString( 1, this->file_to_generate(i), output); } - + // optional string parameter = 2; if (has_parameter()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( @@ -321,13 +324,13 @@ void CodeGeneratorRequest::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteString( 2, this->parameter(), output); } - + // repeated .google.protobuf.FileDescriptorProto proto_file = 15; for (int i = 0; i < this->proto_file_size(); i++) { ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( 15, this->proto_file(i), output); } - + if (!unknown_fields().empty()) { ::google::protobuf::internal::WireFormat::SerializeUnknownFields( unknown_fields(), output); @@ -344,7 +347,7 @@ void CodeGeneratorRequest::SerializeWithCachedSizes( 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( @@ -354,14 +357,14 @@ void CodeGeneratorRequest::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 2, this->parameter(), target); } - + // repeated .google.protobuf.FileDescriptorProto proto_file = 15; for (int i = 0; i < this->proto_file_size(); i++) { target = ::google::protobuf::internal::WireFormatLite:: WriteMessageNoVirtualToArray( 15, this->proto_file(i), target); } - + if (!unknown_fields().empty()) { target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( unknown_fields(), target); @@ -371,7 +374,7 @@ void CodeGeneratorRequest::SerializeWithCachedSizes( int CodeGeneratorRequest::ByteSize() const { int total_size = 0; - + if (_has_bits_[1 / 32] & (0xffu << (1 % 32))) { // optional string parameter = 2; if (has_parameter()) { @@ -379,7 +382,7 @@ int CodeGeneratorRequest::ByteSize() const { ::google::protobuf::internal::WireFormatLite::StringSize( this->parameter()); } - + } // repeated string file_to_generate = 1; total_size += 1 * this->file_to_generate_size(); @@ -387,7 +390,7 @@ int CodeGeneratorRequest::ByteSize() const { total_size += ::google::protobuf::internal::WireFormatLite::StringSize( this->file_to_generate(i)); } - + // repeated .google.protobuf.FileDescriptorProto proto_file = 15; total_size += 1 * this->proto_file_size(); for (int i = 0; i < this->proto_file_size(); i++) { @@ -395,7 +398,7 @@ int CodeGeneratorRequest::ByteSize() const { ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( this->proto_file(i)); } - + if (!unknown_fields().empty()) { total_size += ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( @@ -444,7 +447,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; } @@ -583,7 +586,7 @@ bool CodeGeneratorResponse_File::MergePartialFromCodedStream( if (input->ExpectTag(18)) goto parse_insertion_point; break; } - + // optional string insertion_point = 2; case 2: { if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == @@ -600,7 +603,7 @@ bool CodeGeneratorResponse_File::MergePartialFromCodedStream( if (input->ExpectTag(122)) goto parse_content; break; } - + // optional string content = 15; case 15: { if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == @@ -617,7 +620,7 @@ bool CodeGeneratorResponse_File::MergePartialFromCodedStream( if (input->ExpectAtEnd()) return true; break; } - + default: { handle_uninterpreted: if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == @@ -644,7 +647,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteString( 1, this->name(), output); } - + // optional string insertion_point = 2; if (has_insertion_point()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( @@ -653,7 +656,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteString( 2, this->insertion_point(), output); } - + // optional string content = 15; if (has_content()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( @@ -662,7 +665,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteString( 15, this->content(), output); } - + if (!unknown_fields().empty()) { ::google::protobuf::internal::WireFormat::SerializeUnknownFields( unknown_fields(), output); @@ -680,7 +683,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 1, this->name(), target); } - + // optional string insertion_point = 2; if (has_insertion_point()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( @@ -690,7 +693,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 2, this->insertion_point(), target); } - + // optional string content = 15; if (has_content()) { ::google::protobuf::internal::WireFormat::VerifyUTF8String( @@ -700,7 +703,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 15, this->content(), target); } - + if (!unknown_fields().empty()) { target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( unknown_fields(), target); @@ -710,7 +713,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( int CodeGeneratorResponse_File::ByteSize() const { int total_size = 0; - + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { // optional string name = 1; if (has_name()) { @@ -718,21 +721,21 @@ int CodeGeneratorResponse_File::ByteSize() const { ::google::protobuf::internal::WireFormatLite::StringSize( this->name()); } - + // optional string insertion_point = 2; if (has_insertion_point()) { total_size += 1 + ::google::protobuf::internal::WireFormatLite::StringSize( this->insertion_point()); } - + // optional string content = 15; if (has_content()) { total_size += 1 + ::google::protobuf::internal::WireFormatLite::StringSize( this->content()); } - + } if (!unknown_fields().empty()) { total_size += @@ -786,7 +789,7 @@ void CodeGeneratorResponse_File::CopyFrom(const CodeGeneratorResponse_File& from } bool CodeGeneratorResponse_File::IsInitialized() const { - + return true; } @@ -904,7 +907,7 @@ bool CodeGeneratorResponse::MergePartialFromCodedStream( if (input->ExpectTag(122)) goto parse_file; break; } - + // repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15; case 15: { if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == @@ -919,7 +922,7 @@ bool CodeGeneratorResponse::MergePartialFromCodedStream( if (input->ExpectAtEnd()) return true; break; } - + default: { handle_uninterpreted: if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == @@ -946,13 +949,13 @@ void CodeGeneratorResponse::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteString( 1, this->error(), output); } - + // repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15; for (int i = 0; i < this->file_size(); i++) { ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( 15, this->file(i), output); } - + if (!unknown_fields().empty()) { ::google::protobuf::internal::WireFormat::SerializeUnknownFields( unknown_fields(), output); @@ -970,14 +973,14 @@ void CodeGeneratorResponse::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 1, this->error(), target); } - + // repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15; for (int i = 0; i < this->file_size(); i++) { target = ::google::protobuf::internal::WireFormatLite:: WriteMessageNoVirtualToArray( 15, this->file(i), target); } - + if (!unknown_fields().empty()) { target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( unknown_fields(), target); @@ -987,7 +990,7 @@ void CodeGeneratorResponse::SerializeWithCachedSizes( int CodeGeneratorResponse::ByteSize() const { int total_size = 0; - + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { // optional string error = 1; if (has_error()) { @@ -995,7 +998,7 @@ int CodeGeneratorResponse::ByteSize() const { ::google::protobuf::internal::WireFormatLite::StringSize( this->error()); } - + } // repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15; total_size += 1 * this->file_size(); @@ -1004,7 +1007,7 @@ int CodeGeneratorResponse::ByteSize() const { ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( this->file(i)); } - + if (!unknown_fields().empty()) { total_size += ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( @@ -1052,7 +1055,7 @@ void CodeGeneratorResponse::CopyFrom(const CodeGeneratorResponse& from) { } bool CodeGeneratorResponse::IsInitialized() const { - + return true; } diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h index 1cf8a556..46478c73 100644 --- a/src/google/protobuf/compiler/plugin.pb.h +++ b/src/google/protobuf/compiler/plugin.pb.h @@ -20,9 +20,10 @@ #endif #include <google/protobuf/generated_message_util.h> +#include <google/protobuf/message.h> #include <google/protobuf/repeated_field.h> #include <google/protobuf/extension_set.h> -#include <google/protobuf/generated_message_reflection.h> +#include <google/protobuf/unknown_field_set.h> #include "google/protobuf/descriptor.pb.h" // @@protoc_insertion_point(includes) @@ -45,29 +46,29 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message public: CodeGeneratorRequest(); virtual ~CodeGeneratorRequest(); - + CodeGeneratorRequest(const CodeGeneratorRequest& from); - + inline CodeGeneratorRequest& operator=(const CodeGeneratorRequest& from) { CopyFrom(from); return *this; } - + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { return _unknown_fields_; } - + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { return &_unknown_fields_; } - + static const ::google::protobuf::Descriptor* descriptor(); static const CodeGeneratorRequest& default_instance(); - + void Swap(CodeGeneratorRequest* other); - + // implements Message ---------------------------------------------- - + CodeGeneratorRequest* New() const; void CopyFrom(const ::google::protobuf::Message& from); void MergeFrom(const ::google::protobuf::Message& from); @@ -75,7 +76,7 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message void MergeFrom(const CodeGeneratorRequest& from); void Clear(); bool IsInitialized() const; - + int ByteSize() const; bool MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input); @@ -88,13 +89,13 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message void SharedDtor(); void SetCachedSize(int size) const; public: - + ::google::protobuf::Metadata GetMetadata() const; - + // nested types ---------------------------------------------------- - + // accessors ------------------------------------------------------- - + // repeated string file_to_generate = 1; inline int file_to_generate_size() const; inline void clear_file_to_generate(); @@ -110,7 +111,7 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message inline void add_file_to_generate(const char* value, size_t size); inline const ::google::protobuf::RepeatedPtrField< ::std::string>& file_to_generate() const; inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_file_to_generate(); - + // optional string parameter = 2; inline bool has_parameter() const; inline void clear_parameter(); @@ -121,7 +122,8 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message inline void set_parameter(const char* value, size_t size); inline ::std::string* mutable_parameter(); inline ::std::string* release_parameter(); - + inline void set_allocated_parameter(::std::string* parameter); + // repeated .google.protobuf.FileDescriptorProto proto_file = 15; inline int proto_file_size() const; inline void clear_proto_file(); @@ -133,25 +135,25 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message proto_file() const; inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >* mutable_proto_file(); - + // @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorRequest) private: inline void set_has_parameter(); inline void clear_has_parameter(); - + ::google::protobuf::UnknownFieldSet _unknown_fields_; - + ::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(); - + void InitAsDefaultInstance(); static CodeGeneratorRequest* default_instance_; }; @@ -161,29 +163,29 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M public: CodeGeneratorResponse_File(); virtual ~CodeGeneratorResponse_File(); - + CodeGeneratorResponse_File(const CodeGeneratorResponse_File& from); - + inline CodeGeneratorResponse_File& operator=(const CodeGeneratorResponse_File& from) { CopyFrom(from); return *this; } - + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { return _unknown_fields_; } - + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { return &_unknown_fields_; } - + static const ::google::protobuf::Descriptor* descriptor(); static const CodeGeneratorResponse_File& default_instance(); - + void Swap(CodeGeneratorResponse_File* other); - + // implements Message ---------------------------------------------- - + CodeGeneratorResponse_File* New() const; void CopyFrom(const ::google::protobuf::Message& from); void MergeFrom(const ::google::protobuf::Message& from); @@ -191,7 +193,7 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M void MergeFrom(const CodeGeneratorResponse_File& from); void Clear(); bool IsInitialized() const; - + int ByteSize() const; bool MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input); @@ -204,13 +206,13 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M void SharedDtor(); void SetCachedSize(int size) const; public: - + ::google::protobuf::Metadata GetMetadata() const; - + // nested types ---------------------------------------------------- - + // accessors ------------------------------------------------------- - + // optional string name = 1; inline bool has_name() const; inline void clear_name(); @@ -221,7 +223,8 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M inline void set_name(const char* value, size_t size); inline ::std::string* mutable_name(); inline ::std::string* release_name(); - + inline void set_allocated_name(::std::string* name); + // optional string insertion_point = 2; inline bool has_insertion_point() const; inline void clear_insertion_point(); @@ -232,7 +235,8 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M inline void set_insertion_point(const char* value, size_t size); inline ::std::string* mutable_insertion_point(); inline ::std::string* release_insertion_point(); - + inline void set_allocated_insertion_point(::std::string* insertion_point); + // optional string content = 15; inline bool has_content() const; inline void clear_content(); @@ -243,7 +247,8 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M inline void set_content(const char* value, size_t size); inline ::std::string* mutable_content(); inline ::std::string* release_content(); - + inline void set_allocated_content(::std::string* content); + // @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorResponse.File) private: inline void set_has_name(); @@ -252,20 +257,20 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M inline void clear_has_insertion_point(); inline void set_has_content(); inline void clear_has_content(); - + ::google::protobuf::UnknownFieldSet _unknown_fields_; - + ::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(); - + void InitAsDefaultInstance(); static CodeGeneratorResponse_File* default_instance_; }; @@ -275,29 +280,29 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag public: CodeGeneratorResponse(); virtual ~CodeGeneratorResponse(); - + CodeGeneratorResponse(const CodeGeneratorResponse& from); - + inline CodeGeneratorResponse& operator=(const CodeGeneratorResponse& from) { CopyFrom(from); return *this; } - + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { return _unknown_fields_; } - + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { return &_unknown_fields_; } - + static const ::google::protobuf::Descriptor* descriptor(); static const CodeGeneratorResponse& default_instance(); - + void Swap(CodeGeneratorResponse* other); - + // implements Message ---------------------------------------------- - + CodeGeneratorResponse* New() const; void CopyFrom(const ::google::protobuf::Message& from); void MergeFrom(const ::google::protobuf::Message& from); @@ -305,7 +310,7 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag void MergeFrom(const CodeGeneratorResponse& from); void Clear(); bool IsInitialized() const; - + int ByteSize() const; bool MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input); @@ -318,15 +323,15 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag void SharedDtor(); void SetCachedSize(int size) const; public: - + ::google::protobuf::Metadata GetMetadata() const; - + // nested types ---------------------------------------------------- - + typedef CodeGeneratorResponse_File File; - + // accessors ------------------------------------------------------- - + // optional string error = 1; inline bool has_error() const; inline void clear_error(); @@ -337,7 +342,8 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag inline void set_error(const char* value, size_t size); inline ::std::string* mutable_error(); inline ::std::string* release_error(); - + inline void set_allocated_error(::std::string* error); + // repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15; inline int file_size() const; inline void clear_file(); @@ -349,24 +355,24 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag file() const; inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >* mutable_file(); - + // @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorResponse) private: inline void set_has_error(); inline void clear_has_error(); - + ::google::protobuf::UnknownFieldSet _unknown_fields_; - + ::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(); - + void InitAsDefaultInstance(); static CodeGeneratorResponse* default_instance_; }; @@ -478,6 +484,18 @@ inline ::std::string* CodeGeneratorRequest::release_parameter() { return temp; } } +inline void CodeGeneratorRequest::set_allocated_parameter(::std::string* parameter) { + if (parameter_ != &::google::protobuf::internal::kEmptyString) { + delete parameter_; + } + if (parameter) { + set_has_parameter(); + parameter_ = parameter; + } else { + clear_has_parameter(); + parameter_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} // repeated .google.protobuf.FileDescriptorProto proto_file = 15; inline int CodeGeneratorRequest::proto_file_size() const { @@ -565,6 +583,18 @@ inline ::std::string* CodeGeneratorResponse_File::release_name() { return temp; } } +inline void CodeGeneratorResponse_File::set_allocated_name(::std::string* name) { + if (name_ != &::google::protobuf::internal::kEmptyString) { + delete name_; + } + if (name) { + set_has_name(); + name_ = name; + } else { + clear_has_name(); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} // optional string insertion_point = 2; inline bool CodeGeneratorResponse_File::has_insertion_point() const { @@ -623,6 +653,18 @@ inline ::std::string* CodeGeneratorResponse_File::release_insertion_point() { return temp; } } +inline void CodeGeneratorResponse_File::set_allocated_insertion_point(::std::string* insertion_point) { + if (insertion_point_ != &::google::protobuf::internal::kEmptyString) { + delete insertion_point_; + } + if (insertion_point) { + set_has_insertion_point(); + insertion_point_ = insertion_point; + } else { + clear_has_insertion_point(); + insertion_point_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} // optional string content = 15; inline bool CodeGeneratorResponse_File::has_content() const { @@ -681,6 +723,18 @@ inline ::std::string* CodeGeneratorResponse_File::release_content() { return temp; } } +inline void CodeGeneratorResponse_File::set_allocated_content(::std::string* content) { + if (content_ != &::google::protobuf::internal::kEmptyString) { + delete content_; + } + if (content) { + set_has_content(); + content_ = content; + } else { + clear_has_content(); + content_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} // ------------------------------------------------------------------- @@ -743,6 +797,18 @@ inline ::std::string* CodeGeneratorResponse::release_error() { return temp; } } +inline void CodeGeneratorResponse::set_allocated_error(::std::string* error) { + if (error_ != &::google::protobuf::internal::kEmptyString) { + delete error_; + } + if (error) { + set_has_error(); + error_ = error; + } else { + clear_has_error(); + error_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} // repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15; inline int CodeGeneratorResponse::file_size() const { diff --git a/src/google/protobuf/compiler/python/python_generator.cc b/src/google/protobuf/compiler/python/python_generator.cc index 9b109378..211ac70c 100644 --- a/src/google/protobuf/compiler/python/python_generator.cc +++ b/src/google/protobuf/compiler/python/python_generator.cc @@ -52,6 +52,7 @@ #include <google/protobuf/descriptor.pb.h> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/stringprintf.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/descriptor.h> #include <google/protobuf/io/zero_copy_stream.h> @@ -106,6 +107,12 @@ string NamePrefixedWithNestedTypes(const DescriptorT& descriptor, const char kDescriptorKey[] = "DESCRIPTOR"; +// Does the file have top-level enums? +inline bool HasTopLevelEnums(const FileDescriptor *file) { + return file->enum_type_count() > 0; +} + + // Should we generate generic services for this file? inline bool HasGenericServices(const FileDescriptor *file) { return file->service_count() > 0 && @@ -120,13 +127,21 @@ void PrintTopBoilerplate( // TODO(robinson): Allow parameterization of Python version? printer->Print( "# Generated by the protocol buffer compiler. DO NOT EDIT!\n" - "\n" - "from google.protobuf import descriptor\n" - "from google.protobuf import message\n" - "from google.protobuf import reflection\n"); + "# source: $filename$\n" + "\n", + "filename", file->name()); + if (HasTopLevelEnums(file)) { + printer->Print( + "from google.protobuf.internal import enum_type_wrapper\n"); + } + printer->Print( + "from google.protobuf import descriptor as _descriptor\n" + "from google.protobuf import message as _message\n" + "from google.protobuf import reflection as _reflection\n" + ); if (HasGenericServices(file)) { printer->Print( - "from google.protobuf import service\n" + "from google.protobuf import service as _service\n" "from google.protobuf import service_reflection\n"); } @@ -204,12 +219,12 @@ string StringifyDefaultValue(const FieldDescriptor& field) { case FieldDescriptor::CPPTYPE_STRING: if (field.type() == FieldDescriptor::TYPE_STRING) { return "unicode(\"" + CEscape(field.default_value_string()) + - "\", \"utf-8\")"; + "\", \"utf-8\")"; } else { return "\"" + CEscape(field.default_value_string()) + "\""; } - case FieldDescriptor::CPPTYPE_MESSAGE: - return "None"; + 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.) @@ -270,6 +285,11 @@ bool Generator::Generate(const FileDescriptor* file, // since they need to call static RegisterExtension() methods on these // classes. FixForeignFieldsInExtensions(); + // Descriptor options may have custom extensions. These custom options + // can only be successfully parsed after we register corresponding + // extensions. Therefore we parse all options again here to recognize + // custom options that may be unknown when we define the descriptors. + FixAllDescriptorOptions(); if (HasGenericServices(file)) { PrintServices(); } @@ -288,6 +308,13 @@ void Generator::PrintImports() const { module_name); } printer_->Print("\n"); + + // Print public imports. + for (int i = 0; i < file_->public_dependency_count(); ++i) { + string module_name = ModuleName(file_->public_dependency(i)->name()); + printer_->Print("from $module$ import *\n", "module", module_name); + } + printer_->Print("\n"); } // Prints the single file descriptor for this file. @@ -297,7 +324,7 @@ void Generator::PrintFileDescriptor() const { m["name"] = file_->name(); m["package"] = file_->package(); const char file_descriptor_template[] = - "$descriptor_name$ = descriptor.FileDescriptor(\n" + "$descriptor_name$ = _descriptor.FileDescriptor(\n" " name='$name$',\n" " package='$package$',\n"; printer_->Print(m, file_descriptor_template); @@ -321,6 +348,11 @@ void Generator::PrintTopLevelEnums() const { for (int i = 0; i < file_->enum_type_count(); ++i) { const EnumDescriptor& enum_descriptor = *file_->enum_type(i); PrintEnum(enum_descriptor); + printer_->Print("$name$ = " + "enum_type_wrapper.EnumTypeWrapper($descriptor_name$)", + "name", enum_descriptor.name(), + "descriptor_name", + ModuleLevelDescriptorName(enum_descriptor)); printer_->Print("\n"); for (int j = 0; j < enum_descriptor.value_count(); ++j) { @@ -355,7 +387,7 @@ void Generator::PrintEnum(const EnumDescriptor& enum_descriptor) const { m["full_name"] = enum_descriptor.full_name(); m["file"] = kDescriptorKey; const char enum_descriptor_template[] = - "$descriptor_name$ = descriptor.EnumDescriptor(\n" + "$descriptor_name$ = _descriptor.EnumDescriptor(\n" " name='$name$',\n" " full_name='$full_name$',\n" " filename=None,\n" @@ -436,7 +468,7 @@ void Generator::PrintServiceDescriptor( descriptor.options().SerializeToString(&options_string); printer_->Print( - "$service_name$ = descriptor.ServiceDescriptor(\n", + "$service_name$ = _descriptor.ServiceDescriptor(\n", "service_name", service_name); printer_->Indent(); map<string, string> m; @@ -459,7 +491,6 @@ void Generator::PrintServiceDescriptor( printer_->Print("methods=[\n"); for (int i = 0; i < descriptor.method_count(); ++i) { const MethodDescriptor* method = descriptor.method(i); - string options_string; method->options().SerializeToString(&options_string); m.clear(); @@ -470,7 +501,7 @@ void Generator::PrintServiceDescriptor( m["input_type"] = ModuleLevelDescriptorName(*(method->input_type())); m["output_type"] = ModuleLevelDescriptorName(*(method->output_type())); m["options_value"] = OptionsValue("MethodOptions", options_string); - printer_->Print("descriptor.MethodDescriptor(\n"); + printer_->Print("_descriptor.MethodDescriptor(\n"); printer_->Indent(); printer_->Print( m, @@ -491,7 +522,7 @@ void Generator::PrintServiceDescriptor( void Generator::PrintServiceClass(const ServiceDescriptor& descriptor) const { // Print the service. - printer_->Print("class $class_name$(service.Service):\n", + printer_->Print("class $class_name$(_service.Service):\n", "class_name", descriptor.name()); printer_->Indent(); printer_->Print( @@ -523,7 +554,7 @@ void Generator::PrintDescriptor(const Descriptor& message_descriptor) const { PrintNestedDescriptors(message_descriptor); printer_->Print("\n"); - printer_->Print("$descriptor_name$ = descriptor.Descriptor(\n", + printer_->Print("$descriptor_name$ = _descriptor.Descriptor(\n", "descriptor_name", ModuleLevelDescriptorName(message_descriptor)); printer_->Indent(); @@ -618,10 +649,10 @@ void Generator::PrintMessages() const { // Mutually recursive with PrintNestedMessages(). void Generator::PrintMessage( const Descriptor& message_descriptor) const { - printer_->Print("class $name$(message.Message):\n", "name", + printer_->Print("class $name$(_message.Message):\n", "name", message_descriptor.name()); printer_->Indent(); - printer_->Print("__metaclass__ = reflection.GeneratedProtocolMessageType\n"); + printer_->Print("__metaclass__ = _reflection.GeneratedProtocolMessageType\n"); PrintNestedMessages(message_descriptor); map<string, string> m; m["descriptor_key"] = kDescriptorKey; @@ -779,6 +810,7 @@ void Generator::FixForeignFieldsInExtensions() const { for (int i = 0; i < file_->message_type_count(); ++i) { FixForeignFieldsInNestedExtensions(*file_->message_type(i)); } + printer_->Print("\n"); } void Generator::FixForeignFieldsInExtension( @@ -829,7 +861,7 @@ void Generator::PrintEnumValueDescriptor( m["options"] = OptionsValue("EnumValueOptions", options_string); printer_->Print( m, - "descriptor.EnumValueDescriptor(\n" + "_descriptor.EnumValueDescriptor(\n" " name='$name$', index=$index$, number=$number$,\n" " options=$options$,\n" " type=None)"); @@ -843,7 +875,7 @@ string Generator::OptionsValue( return "None"; } else { string full_class_name = "descriptor_pb2." + class_name; - return "descriptor._ParseOptions(" + full_class_name + "(), '" + return "_descriptor._ParseOptions(" + full_class_name + "(), '" + CEscape(serialized_options)+ "')"; } } @@ -869,7 +901,7 @@ void Generator::PrintFieldDescriptor( // these fields in correctly after all referenced descriptors have been // defined and/or imported (see FixForeignFieldsInDescriptors()). const char field_descriptor_decl[] = - "descriptor.FieldDescriptor(\n" + "_descriptor.FieldDescriptor(\n" " name='$name$', full_name='$full_name$', index=$index$,\n" " number=$number$, type=$type$, cpp_type=$cpp_type$, label=$label$,\n" " has_default_value=$has_default_value$, default_value=$default_value$,\n" @@ -1000,6 +1032,125 @@ void Generator::PrintSerializedPbInterval( "serialized_end", SimpleItoa(offset + sp.size())); } +namespace { +void PrintDescriptorOptionsFixingCode(const string& descriptor, + const string& options, + io::Printer* printer) { + // TODO(xiaofeng): I have added a method _SetOptions() to DescriptorBase + // in proto2 python runtime but it couldn't be used here because appengine + // uses a snapshot version of the library in which the new method is not + // yet present. After appengine has synced their runtime library, the code + // below should be cleaned up to use _SetOptions(). + printer->Print( + "$descriptor$.has_options = True\n" + "$descriptor$._options = $options$\n", + "descriptor", descriptor, "options", options); +} +} // namespace + +// Prints expressions that set the options field of all descriptors. +void Generator::FixAllDescriptorOptions() const { + // Prints an expression that sets the file descriptor's options. + string file_options = OptionsValue( + "FileOptions", file_->options().SerializeAsString()); + if (file_options != "None") { + PrintDescriptorOptionsFixingCode(kDescriptorKey, file_options, printer_); + } + // Prints expressions that set the options for all top level enums. + for (int i = 0; i < file_->enum_type_count(); ++i) { + const EnumDescriptor& enum_descriptor = *file_->enum_type(i); + FixOptionsForEnum(enum_descriptor); + } + // Prints expressions that set the options for all top level extensions. + for (int i = 0; i < file_->extension_count(); ++i) { + const FieldDescriptor& field = *file_->extension(i); + FixOptionsForField(field); + } + // Prints expressions that set the options for all messages, nested enums, + // nested extensions and message fields. + for (int i = 0; i < file_->message_type_count(); ++i) { + FixOptionsForMessage(*file_->message_type(i)); + } +} + +// Prints expressions that set the options for an enum descriptor and its +// value descriptors. +void Generator::FixOptionsForEnum(const EnumDescriptor& enum_descriptor) const { + string descriptor_name = ModuleLevelDescriptorName(enum_descriptor); + string enum_options = OptionsValue( + "EnumOptions", enum_descriptor.options().SerializeAsString()); + if (enum_options != "None") { + PrintDescriptorOptionsFixingCode(descriptor_name, enum_options, printer_); + } + for (int i = 0; i < enum_descriptor.value_count(); ++i) { + const EnumValueDescriptor& value_descriptor = *enum_descriptor.value(i); + string value_options = OptionsValue( + "EnumValueOptions", value_descriptor.options().SerializeAsString()); + if (value_options != "None") { + PrintDescriptorOptionsFixingCode( + StringPrintf("%s.values_by_name[\"%s\"]", descriptor_name.c_str(), + value_descriptor.name().c_str()), + value_options, printer_); + } + } +} + +// Prints expressions that set the options for field descriptors (including +// extensions). +void Generator::FixOptionsForField( + const FieldDescriptor& field) const { + string field_options = OptionsValue( + "FieldOptions", field.options().SerializeAsString()); + if (field_options != "None") { + string field_name; + if (field.is_extension()) { + if (field.extension_scope() == NULL) { + // Top level extensions. + field_name = field.name(); + } else { + field_name = FieldReferencingExpression( + field.extension_scope(), field, "extensions_by_name"); + } + } else { + field_name = FieldReferencingExpression( + field.containing_type(), field, "fields_by_name"); + } + PrintDescriptorOptionsFixingCode(field_name, field_options, printer_); + } +} + +// Prints expressions that set the options for a message and all its inner +// types (nested messages, nested enums, extensions, fields). +void Generator::FixOptionsForMessage(const Descriptor& descriptor) const { + // Nested messages. + for (int i = 0; i < descriptor.nested_type_count(); ++i) { + FixOptionsForMessage(*descriptor.nested_type(i)); + } + // Enums. + for (int i = 0; i < descriptor.enum_type_count(); ++i) { + FixOptionsForEnum(*descriptor.enum_type(i)); + } + // Fields. + for (int i = 0; i < descriptor.field_count(); ++i) { + const FieldDescriptor& field = *descriptor.field(i); + FixOptionsForField(field); + } + // Extensions. + for (int i = 0; i < descriptor.extension_count(); ++i) { + const FieldDescriptor& field = *descriptor.extension(i); + FixOptionsForField(field); + } + // Message option for this message. + string message_options = OptionsValue( + "MessageOptions", descriptor.options().SerializeAsString()); + if (message_options != "None") { + string descriptor_name = ModuleLevelDescriptorName(descriptor); + PrintDescriptorOptionsFixingCode(descriptor_name, + message_options, + printer_); + } +} + } // namespace python } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/python/python_generator.h b/src/google/protobuf/compiler/python/python_generator.h index 84eaf8ab..a3f22cee 100644 --- a/src/google/protobuf/compiler/python/python_generator.h +++ b/src/google/protobuf/compiler/python/python_generator.h @@ -138,6 +138,11 @@ class LIBPROTOC_EXPORT Generator : public CodeGenerator { void PrintSerializedPbInterval( const DescriptorT& descriptor, DescriptorProtoT& proto) const; + void FixAllDescriptorOptions() const; + void FixOptionsForField(const FieldDescriptor& field) const; + void FixOptionsForEnum(const EnumDescriptor& descriptor) const; + void FixOptionsForMessage(const Descriptor& descriptor) const; + // Very coarse-grained lock to ensure that Generate() is reentrant. // Guards file_, printer_ and file_descriptor_serialized_. mutable Mutex mutex_; diff --git a/src/google/protobuf/compiler/subprocess.cc b/src/google/protobuf/compiler/subprocess.cc index 5fb5d5cb..860fc875 100644 --- a/src/google/protobuf/compiler/subprocess.cc +++ b/src/google/protobuf/compiler/subprocess.cc @@ -33,6 +33,7 @@ #include <google/protobuf/compiler/subprocess.h> #include <algorithm> +#include <iostream> #ifndef _WIN32 #include <errno.h> diff --git a/src/google/protobuf/compiler/subprocess.h b/src/google/protobuf/compiler/subprocess.h index 00564964..de9fce9e 100644 --- a/src/google/protobuf/compiler/subprocess.h +++ b/src/google/protobuf/compiler/subprocess.h @@ -53,7 +53,7 @@ class Message; namespace compiler { // Utility class for launching sub-processes. -class LIBPROTOC_EXPORT Subprocess { +class Subprocess { public: Subprocess(); ~Subprocess(); |