diff options
author | Jisi Liu <jisi.liu@gmail.com> | 2016-03-30 11:39:59 -0700 |
---|---|---|
committer | Jisi Liu <jisi.liu@gmail.com> | 2016-03-30 11:39:59 -0700 |
commit | 3b3c8abb9635eb3ea078a821a99c9ef29d66dff7 (patch) | |
tree | 7d2ec154f15c9f9153d890e76b6cf30e471ea488 /src/google/protobuf/compiler | |
parent | 78105897a8f01c7be9cf8502b6c58d47eb1ccdd7 (diff) | |
download | protobuf-3b3c8abb9635eb3ea078a821a99c9ef29d66dff7.tar.gz protobuf-3b3c8abb9635eb3ea078a821a99c9ef29d66dff7.tar.bz2 protobuf-3b3c8abb9635eb3ea078a821a99c9ef29d66dff7.zip |
Integrate google internal changes.
Diffstat (limited to 'src/google/protobuf/compiler')
56 files changed, 1596 insertions, 727 deletions
diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index 3a816b05..87843f62 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -49,7 +49,7 @@ #include <iostream> #include <ctype.h> -#ifdef GOOGLE_PROTOBUF_ARCH_SPARC +#ifdef GOOGLE_PROTOBUF_ARCH_SPARC #include <limits.h> //For PATH_MAX #endif diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index dda007d4..e3dd2295 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -63,13 +63,13 @@ #include <google/protobuf/testing/googletest.h> #include <gtest/gtest.h> -namespace google { -namespace protobuf { -namespace compiler { // Disable the whole test when we use tcmalloc for "draconian" heap checks, in // which case tcmalloc will print warnings that fail the plugin tests. #if !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN +namespace google { +namespace protobuf { +namespace compiler { #if defined(_WIN32) #ifndef STDIN_FILENO diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc index 5ee6f000..415ae60f 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc @@ -69,11 +69,12 @@ EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, EnumGenerator::~EnumGenerator() {} -void EnumGenerator::FillForwardDeclaration(set<string>* enum_names) { +void EnumGenerator::FillForwardDeclaration( + map<string, const EnumDescriptor*>* enum_names) { if (!options_.proto_h) { return; } - enum_names->insert(classname_); + (*enum_names)[classname_] = descriptor_; } void EnumGenerator::GenerateDefinition(io::Printer* printer) { @@ -83,6 +84,7 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) { vars["enumbase"] = classname_ + (options_.proto_h ? " : int" : ""); printer->Print(vars, "enum $enumbase$ {\n"); + printer->Annotate("enumbase", descriptor_); printer->Indent(); const EnumValueDescriptor* min_value = descriptor_->value(0); diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.h b/src/google/protobuf/compiler/cpp/cpp_enum.h index f3aa72e4..61e40346 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.h +++ b/src/google/protobuf/compiler/cpp/cpp_enum.h @@ -64,8 +64,10 @@ class EnumGenerator { // Fills the name to use when declaring the enum. This is for use when // generating other .proto.h files. This code should be placed within the // enum's package namespace, but NOT within any class, even for nested - // enums. - void FillForwardDeclaration(set<string>* enum_names); + // enums. A given key in enum_names will map from an enum class name to the + // EnumDescriptor that was responsible for its inclusion in the map. This can + // be used to associate the descriptor with the code generated for it. + void FillForwardDeclaration(map<string, const EnumDescriptor*>* enum_names); // Generate header code defining the enum. This code should be placed // within the enum's package namespace, but NOT within any class, even for diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index 37e4bae4..d48171b0 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -94,7 +94,8 @@ FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options) FileGenerator::~FileGenerator() {} -void FileGenerator::GenerateProtoHeader(io::Printer* printer) { +void FileGenerator::GenerateProtoHeader(io::Printer* printer, + const string& info_path) { if (!options_.proto_h) { return; } @@ -114,6 +115,8 @@ void FileGenerator::GenerateProtoHeader(io::Printer* printer) { "dependency", dependency); } + GenerateMetadataPragma(printer, info_path); + printer->Print( "// @@protoc_insertion_point(includes)\n"); @@ -167,7 +170,8 @@ void FileGenerator::GenerateProtoHeader(io::Printer* printer) { GenerateBottomHeaderGuard(printer, filename_identifier); } -void FileGenerator::GeneratePBHeader(io::Printer* printer) { +void FileGenerator::GeneratePBHeader(io::Printer* printer, + const string& info_path) { string filename_identifier = FilenameIdentifier(file_->name() + (options_.proto_h ? ".pb.h" : "")); GenerateTopHeaderGuard(printer, filename_identifier); @@ -179,6 +183,7 @@ void FileGenerator::GeneratePBHeader(io::Printer* printer) { GenerateLibraryIncludes(printer); } GenerateDependencyIncludes(printer); + GenerateMetadataPragma(printer, info_path); printer->Print( "// @@protoc_insertion_point(includes)\n"); @@ -237,7 +242,7 @@ void FileGenerator::GeneratePBHeader(io::Printer* printer) { } void FileGenerator::GenerateSource(io::Printer* printer) { - bool well_known = IsWellKnownMessage(file_); + const bool use_system_include = IsWellKnownMessage(file_); string header = StripProto(file_->name()) + (options_.proto_h ? ".proto.h" : ".pb.h"); printer->Print( @@ -258,8 +263,8 @@ void FileGenerator::GenerateSource(io::Printer* printer) { "#include <google/protobuf/wire_format_lite_inl.h>\n", "filename", file_->name(), "header", header, - "left", well_known ? "<" : "\"", - "right", well_known ? ">" : "\""); + "left", use_system_include ? "<" : "\"", + "right", use_system_include ? ">" : "\""); // Unknown fields implementation in lite mode uses StringOutputStream if (!UseUnknownFieldSet(file_) && file_->message_type_count() > 0) { @@ -401,20 +406,24 @@ class FileGenerator::ForwardDeclarations { return ns; } - set<string>& classes() { return classes_; } - set<string>& enums() { return enums_; } + map<string, const Descriptor*>& classes() { return classes_; } + map<string, const EnumDescriptor*>& enums() { return enums_; } void Print(io::Printer* printer) const { - for (set<string>::const_iterator it = enums_.begin(), end = enums_.end(); + for (map<string, const EnumDescriptor *>::const_iterator + it = enums_.begin(), + end = enums_.end(); it != end; ++it) { - printer->Print("enum $enumname$ : int;\n" - "bool $enumname$_IsValid(int value);\n", - "enumname", it->c_str()); + printer->Print("enum $enumname$ : int;\n", "enumname", it->first); + printer->Annotate("enumname", it->second); + printer->Print("bool $enumname$_IsValid(int value);\n", "enumname", + it->first); } - for (set<string>::const_iterator it = classes_.begin(), - end = classes_.end(); + for (map<string, const Descriptor *>::const_iterator it = classes_.begin(), + end = classes_.end(); it != end; ++it) { - printer->Print("class $classname$;\n", "classname", it->c_str()); + printer->Print("class $classname$;\n", "classname", it->first); + printer->Annotate("classname", it->second); } for (map<string, ForwardDeclarations *>::const_iterator it = namespaces_.begin(), @@ -431,8 +440,8 @@ class FileGenerator::ForwardDeclarations { private: map<string, ForwardDeclarations*> namespaces_; - set<string> classes_; - set<string> enums_; + map<string, const Descriptor*> classes_; + map<string, const EnumDescriptor*> enums_; }; void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { @@ -854,6 +863,19 @@ void FileGenerator::GenerateLibraryIncludes(io::Printer* printer) { } } +void FileGenerator::GenerateMetadataPragma(io::Printer* printer, + const string& info_path) { + if (!info_path.empty() && !options_.annotation_pragma_name.empty() && + !options_.annotation_guard_name.empty()) { + printer->Print( + "#ifdef $guard$\n" + "#pragma $pragma$ \"$info_path$\"\n" + "#endif // $guard$\n", + "guard", options_.annotation_guard_name, "pragma", + options_.annotation_pragma_name, "info_path", info_path); + } +} + void FileGenerator::GenerateDependencyIncludes(io::Printer* printer) { set<string> public_import_names; for (int i = 0; i < file_->public_dependency_count(); i++) { @@ -861,16 +883,17 @@ void FileGenerator::GenerateDependencyIncludes(io::Printer* printer) { } for (int i = 0; i < file_->dependency_count(); i++) { - bool well_known = IsWellKnownMessage(file_->dependency(i)); + const bool use_system_include = IsWellKnownMessage(file_->dependency(i)); const string& name = file_->dependency(i)->name(); bool public_import = (public_import_names.count(name) != 0); + printer->Print( "#include $left$$dependency$.pb.h$right$$iwyu$\n", "dependency", StripProto(name), "iwyu", (public_import) ? " // IWYU pragma: export" : "", - "left", well_known ? "<" : "\"", - "right", well_known ? ">" : "\""); + "left", use_system_include ? "<" : "\"", + "right", use_system_include ? ">" : "\""); } } @@ -897,13 +920,15 @@ void FileGenerator::GenerateGlobalStateFunctionDeclarations( } void FileGenerator::GenerateMessageForwardDeclarations(io::Printer* printer) { - set<string> classes; + map<string, const Descriptor*> classes; for (int i = 0; i < file_->message_type_count(); i++) { message_generators_[i]->FillMessageForwardDeclarations(&classes); } - for (set<string>::const_iterator it = classes.begin(), end = classes.end(); + for (map<string, const Descriptor *>::const_iterator it = classes.begin(), + end = classes.end(); it != end; ++it) { - printer->Print("class $classname$;\n", "classname", it->c_str()); + printer->Print("class $classname$;\n", "classname", it->first); + printer->Annotate("classname", it->second); } } diff --git a/src/google/protobuf/compiler/cpp/cpp_file.h b/src/google/protobuf/compiler/cpp/cpp_file.h index 29cdaea5..ebe990c2 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.h +++ b/src/google/protobuf/compiler/cpp/cpp_file.h @@ -69,8 +69,14 @@ class FileGenerator { const Options& options); ~FileGenerator(); - void GenerateProtoHeader(io::Printer* printer); - void GeneratePBHeader(io::Printer* printer); + // info_path, if non-empty, should be the path (relative to printer's output) + // to the metadata file describing this proto header. + void GenerateProtoHeader(io::Printer* printer, + const string& info_path); + // info_path, if non-empty, should be the path (relative to printer's output) + // to the metadata file describing this PB header. + void GeneratePBHeader(io::Printer* printer, + const string& info_path); void GenerateSource(io::Printer* printer); private: @@ -102,6 +108,10 @@ class FileGenerator { void GenerateLibraryIncludes(io::Printer* printer); void GenerateDependencyIncludes(io::Printer* printer); + // Generate a pragma to pull in metadata using the given info_path (if + // non-empty). info_path should be relative to printer's output. + void GenerateMetadataPragma(io::Printer* printer, const string& info_path); + // Generates a couple of different pieces before definitions: void GenerateGlobalStateFunctionDeclarations(io::Printer* printer); diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.cc b/src/google/protobuf/compiler/cpp/cpp_generator.cc index 781526b5..c7aec93a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_generator.cc +++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc @@ -90,6 +90,12 @@ bool CppGenerator::Generate(const FileDescriptor* file, file_options.dllexport_decl = options[i].second; } else if (options[i].first == "safe_boundary_check") { file_options.safe_boundary_check = true; + } else if (options[i].first == "annotate_headers") { + file_options.annotate_headers = true; + } else if (options[i].first == "annotation_pragma_name") { + file_options.annotation_pragma_name = options[i].second; + } else if (options[i].first == "annotation_guard_name") { + file_options.annotation_guard_name = options[i].second; } else { *error = "Unknown generator option: " + options[i].first; return false; @@ -107,16 +113,40 @@ bool CppGenerator::Generate(const FileDescriptor* file, if (file_options.proto_h) { google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( generator_context->Open(basename + ".proto.h")); - io::Printer printer(output.get(), '$'); - file_generator.GenerateProtoHeader(&printer); + GeneratedCodeInfo annotations; + io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector( + &annotations); + string info_path = basename + ".proto.h.meta"; + io::Printer printer(output.get(), '$', file_options.annotate_headers + ? &annotation_collector + : NULL); + file_generator.GenerateProtoHeader( + &printer, file_options.annotate_headers ? info_path : ""); + if (file_options.annotate_headers) { + scoped_ptr<io::ZeroCopyOutputStream> info_output( + generator_context->Open(info_path)); + annotations.SerializeToZeroCopyStream(info_output.get()); + } } basename.append(".pb"); { google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( generator_context->Open(basename + ".h")); - io::Printer printer(output.get(), '$'); - file_generator.GeneratePBHeader(&printer); + GeneratedCodeInfo annotations; + io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector( + &annotations); + string info_path = basename + ".h.meta"; + io::Printer printer(output.get(), '$', file_options.annotate_headers + ? &annotation_collector + : NULL); + file_generator.GeneratePBHeader( + &printer, file_options.annotate_headers ? info_path : ""); + if (file_options.annotate_headers) { + scoped_ptr<io::ZeroCopyOutputStream> info_output( + generator_context->Open(info_path)); + annotations.SerializeToZeroCopyStream(info_output.get()); + } } // Generate cc file. diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index 8304ebbd..c3166611 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -423,8 +423,8 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, MessageGenerator::~MessageGenerator() {} void MessageGenerator:: -FillMessageForwardDeclarations(set<string>* class_names) { - class_names->insert(classname_); +FillMessageForwardDeclarations(map<string, const Descriptor*>* class_names) { + (*class_names)[classname_] = descriptor_; for (int i = 0; i < descriptor_->nested_type_count(); i++) { // map entry message doesn't need forward declaration. Since map entry @@ -436,7 +436,7 @@ FillMessageForwardDeclarations(set<string>* class_names) { } void MessageGenerator:: -FillEnumForwardDeclarations(set<string>* enum_names) { +FillEnumForwardDeclarations(map<string, const EnumDescriptor*>* enum_names) { for (int i = 0; i < descriptor_->nested_type_count(); i++) { nested_generators_[i]->FillEnumForwardDeclarations(enum_names); } @@ -892,6 +892,7 @@ GenerateClassDefinition(io::Printer* printer) { } printer->Print(vars, "class $dllexport$$classname$ : public $superclass$ {\n"); + printer->Annotate("classname", descriptor_); if (use_dependent_base_) { printer->Print(vars, " friend class $superclass$;\n"); } @@ -1027,6 +1028,8 @@ GenerateClassDefinition(io::Printer* printer) { "// implements Any -----------------------------------------------\n" "\n" "void PackFrom(const ::google::protobuf::Message& message);\n" + "void PackFrom(const ::google::protobuf::Message& message,\n" + " const ::std::string& type_url_prefix);\n" "bool UnpackTo(::google::protobuf::Message* message) const;\n" "template<typename T> bool Is() const {\n" " return _any_metadata_.Is<T>();\n" @@ -1789,6 +1792,11 @@ GenerateClassMethods(io::Printer* printer) { " _any_metadata_.PackFrom(message);\n" "}\n" "\n" + "void $classname$::PackFrom(const ::google::protobuf::Message& message,\n" + " const ::std::string& type_url_prefix) {\n" + " _any_metadata_.PackFrom(message, type_url_prefix);\n" + "}\n" + "\n" "bool $classname$::UnpackTo(::google::protobuf::Message* message) const {\n" " return _any_metadata_.UnpackTo(message);\n" "}\n" @@ -1899,11 +1907,10 @@ GenerateClassMethods(io::Printer* printer) { void MessageGenerator:: GenerateOffsets(io::Printer* printer) { - printer->Print( - "static const int $classname$_offsets_[$field_count$] = {\n", - "classname", classname_, - "field_count", SimpleItoa(max( - 1, descriptor_->field_count() + descriptor_->oneof_decl_count()))); + printer->Print("static const int $classname$_offsets_[$field_count$] = {\n", + "classname", classname_, "field_count", + SimpleItoa(std::max(1, descriptor_->field_count() + + descriptor_->oneof_decl_count()))); printer->Indent(); for (int i = 0; i < descriptor_->field_count(); i++) { @@ -2907,7 +2914,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) { // on the CodedOutputStream. printer->Print( " ::google::protobuf::io::LazyStringOutputStream unknown_fields_string(\n" - " ::google::protobuf::internal::NewPermanentCallback(\n" + " google::protobuf::internal::NewPermanentCallback(\n" " &MutableUnknownFieldsFor$classname$, this));\n" " ::google::protobuf::io::CodedOutputStream unknown_fields_stream(\n" " &unknown_fields_string, false);\n", diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h index 8e19a3f0..31223e13 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.h +++ b/src/google/protobuf/compiler/cpp/cpp_message.h @@ -67,9 +67,13 @@ class MessageGenerator { // Header stuff. // Return names for foward declarations of this class and all its nested - // types. - void FillMessageForwardDeclarations(set<string>* class_names); - void FillEnumForwardDeclarations(set<string>* enum_names); + // types. A given key in {class,enum}_names will map from a class name to the + // descriptor that was responsible for its inclusion in the map. This can be + // used to associate the descriptor with the code generated for it. + void FillMessageForwardDeclarations( + map<string, const Descriptor*>* class_names); + void FillEnumForwardDeclarations( + map<string, const EnumDescriptor*>* enum_names); // Generate definitions of all nested enums (must come before class // definitions because those classes use the enums definitions). diff --git a/src/google/protobuf/compiler/cpp/cpp_options.h b/src/google/protobuf/compiler/cpp/cpp_options.h index 4463f200..e908362b 100644 --- a/src/google/protobuf/compiler/cpp/cpp_options.h +++ b/src/google/protobuf/compiler/cpp/cpp_options.h @@ -43,11 +43,14 @@ namespace cpp { // Generator options (see generator.cc for a description of each): struct Options { - Options() : safe_boundary_check(false), proto_h(false) { - } + Options() + : safe_boundary_check(false), proto_h(false), annotate_headers(false) {} string dllexport_decl; bool safe_boundary_check; bool proto_h; + bool annotate_headers; + string annotation_pragma_name; + string annotation_guard_name; }; } // namespace cpp diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc index 9942a343..148da883 100644 --- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -82,7 +82,6 @@ namespace google { namespace protobuf { -using internal::NewPermanentCallback; namespace compiler { namespace cpp { @@ -1253,7 +1252,7 @@ class GeneratedServiceTest : public testing::Test { foo_(descriptor_->FindMethodByName("Foo")), bar_(descriptor_->FindMethodByName("Bar")), stub_(&mock_channel_), - done_(NewPermanentCallback(&DoNothing)) {} + done_(google::protobuf::internal::NewPermanentCallback(&DoNothing)) {} virtual void SetUp() { ASSERT_TRUE(foo_ != NULL); diff --git a/src/google/protobuf/compiler/cpp/metadata_test.cc b/src/google/protobuf/compiler/cpp/metadata_test.cc index 422eb73b..61dc283a 100644 --- a/src/google/protobuf/compiler/cpp/metadata_test.cc +++ b/src/google/protobuf/compiler/cpp/metadata_test.cc @@ -51,6 +51,189 @@ namespace compiler { namespace cpp { namespace { +// A CodeGenerator that captures the FileDescriptor it's passed as a +// FileDescriptorProto. +class DescriptorCapturingGenerator : public CodeGenerator { + public: + // Does not own file; file must outlive the Generator. + explicit DescriptorCapturingGenerator(FileDescriptorProto* file) + : file_(file) {} + + virtual bool Generate(const FileDescriptor* file, const string& parameter, + GeneratorContext* context, string* error) const { + file->CopyTo(file_); + return true; + } + + private: + FileDescriptorProto* file_; +}; + +class CppMetadataTest : public ::testing::Test { + public: + // Adds a file with name `filename` and content `data`. + void AddFile(const string& filename, const string& data) { + GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/" + filename, data, + true)); + } + + // Tries to capture a FileDescriptorProto, GeneratedCodeInfo, and output + // code from the previously added file with name `filename`. Returns true on + // success. If pb_h is non-null, expects a .pb.h and a .pb.h.meta (copied to + // pb_h and pb_h_info respecfively); similarly for proto_h and proto_h_info. + bool CaptureMetadata(const string& filename, FileDescriptorProto* file, + string* pb_h, GeneratedCodeInfo* pb_h_info, + string* proto_h, GeneratedCodeInfo* proto_h_info, + string* pb_cc) { + google::protobuf::compiler::CommandLineInterface cli; + cli.SetInputsAreProtoPathRelative(true); + + CppGenerator cpp_generator; + DescriptorCapturingGenerator capturing_generator(file); + cli.RegisterGenerator("--cpp_out", &cpp_generator, ""); + cli.RegisterGenerator("--capture_out", &capturing_generator, ""); + + string proto_path = "-I" + TestTempDir(); + string cpp_out = + "--cpp_out=annotate_headers=true," + "annotation_pragma_name=pragma_name," + "annotation_guard_name=guard_name:" + + TestTempDir(); + string capture_out = "--capture_out=" + TestTempDir(); + + const char* argv[] = {"protoc", proto_path.c_str(), cpp_out.c_str(), + capture_out.c_str(), filename.c_str()}; + + if (cli.Run(5, argv) != 0) { + return false; + } + + string output_base = TestTempDir() + "/" + StripProto(filename); + + if (pb_cc != NULL) { + GOOGLE_CHECK_OK( + File::GetContents(output_base + ".pb.cc", pb_cc, true)); + } + + if (pb_h != NULL && pb_h_info != NULL) { + GOOGLE_CHECK_OK( + File::GetContents(output_base + ".pb.h", pb_h, true)); + if (!DecodeMetadata(output_base + ".pb.h.meta", pb_h_info)) { + return false; + } + } + + if (proto_h != NULL && proto_h_info != NULL) { + GOOGLE_CHECK_OK(File::GetContents(output_base + ".proto.h", proto_h, + true)); + if (!DecodeMetadata(output_base + ".proto.h.meta", proto_h_info)) { + return false; + } + } + + return true; + } + + private: + // Decodes GeneratedCodeInfo stored in path and copies it to info. + // Returns true on success. + bool DecodeMetadata(const string& path, GeneratedCodeInfo* info) { + string data; + GOOGLE_CHECK_OK(File::GetContents(path, &data, true)); + io::ArrayInputStream input(data.data(), data.size()); + return info->ParseFromZeroCopyStream(&input); + } +}; + +const char kSmallTestFile[] = + "syntax = \"proto2\";\n" + "package foo;\n" + "enum Enum { VALUE = 0; }\n" + "message Message { }\n"; + +// Finds the Annotation for a given source file and path (or returns null if it +// couldn't). +const GeneratedCodeInfo::Annotation* FindAnnotationOnPath( + const GeneratedCodeInfo& info, const string& source_file, + const vector<int>& path) { + for (int i = 0; i < info.annotation_size(); ++i) { + const GeneratedCodeInfo::Annotation* annotation = &info.annotation(i); + if (annotation->source_file() != source_file || + annotation->path_size() != path.size()) { + continue; + } + int node = 0; + for (; node < path.size(); ++node) { + if (annotation->path(node) != path[node]) { + break; + } + } + if (node == path.size()) { + return annotation; + } + } + return NULL; +} + +// Returns true if the provided annotation covers a given substring in +// file_content. +bool AnnotationMatchesSubstring(const string& file_content, + const GeneratedCodeInfo::Annotation* annotation, + const string& expected_text) { + uint32 begin = annotation->begin(); + uint32 end = annotation->end(); + if (end < begin || end > file_content.size()) { + return false; + } + return file_content.substr(begin, end - begin) == expected_text; +} + +TEST_F(CppMetadataTest, CapturesEnumNames) { + FileDescriptorProto file; + GeneratedCodeInfo info; + string pb_h; + AddFile("test.proto", kSmallTestFile); + EXPECT_TRUE( + CaptureMetadata("test.proto", &file, &pb_h, &info, NULL, NULL, NULL)); + EXPECT_EQ("Enum", file.enum_type(0).name()); + vector<int> enum_path; + enum_path.push_back(FileDescriptorProto::kEnumTypeFieldNumber); + enum_path.push_back(0); + const GeneratedCodeInfo::Annotation* enum_annotation = + FindAnnotationOnPath(info, "test.proto", enum_path); + EXPECT_TRUE(NULL != enum_annotation); + EXPECT_TRUE(AnnotationMatchesSubstring(pb_h, enum_annotation, "Enum")); +} + +TEST_F(CppMetadataTest, AddsPragma) { + FileDescriptorProto file; + GeneratedCodeInfo info; + string pb_h; + AddFile("test.proto", kSmallTestFile); + EXPECT_TRUE( + CaptureMetadata("test.proto", &file, &pb_h, &info, NULL, NULL, NULL)); + EXPECT_TRUE(pb_h.find("#ifdef guard_name") != string::npos); + EXPECT_TRUE(pb_h.find("#pragma pragma_name \"test.pb.h.meta\"") != + string::npos); +} + +TEST_F(CppMetadataTest, CapturesMessageNames) { + FileDescriptorProto file; + GeneratedCodeInfo info; + string pb_h; + AddFile("test.proto", kSmallTestFile); + EXPECT_TRUE( + CaptureMetadata("test.proto", &file, &pb_h, &info, NULL, NULL, NULL)); + EXPECT_EQ("Message", file.message_type(0).name()); + vector<int> message_path; + message_path.push_back(FileDescriptorProto::kMessageTypeFieldNumber); + message_path.push_back(0); + const GeneratedCodeInfo::Annotation* message_annotation = + FindAnnotationOnPath(info, "test.proto", message_path); + EXPECT_TRUE(NULL != message_annotation); + EXPECT_TRUE(AnnotationMatchesSubstring(pb_h, message_annotation, "Message")); +} + } // namespace } // namespace cpp } // namespace compiler diff --git a/src/google/protobuf/compiler/java/java_context.cc b/src/google/protobuf/compiler/java/java_context.cc index 7d21fe61..0a112888 100644 --- a/src/google/protobuf/compiler/java/java_context.cc +++ b/src/google/protobuf/compiler/java/java_context.cc @@ -43,7 +43,7 @@ namespace compiler { namespace java { Context::Context(const FileDescriptor* file) - : name_resolver_(new ClassNameResolver) { + : name_resolver_(new ClassNameResolver), enforce_lite_(false) { InitializeFieldGeneratorInfo(file); } @@ -189,6 +189,13 @@ const OneofGeneratorInfo* Context::GetOneofGeneratorInfo( return result; } +// Does this message class have generated parsing, serialization, and other +// standard methods for which reflection-based fallback implementations exist? +bool Context::HasGeneratedMethods(const Descriptor* descriptor) const { + return enforce_lite_ || descriptor->file()->options().optimize_for() != + FileOptions::CODE_SIZE; +} + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_context.h b/src/google/protobuf/compiler/java/java_context.h index 5b595d07..a480e45d 100644 --- a/src/google/protobuf/compiler/java/java_context.h +++ b/src/google/protobuf/compiler/java/java_context.h @@ -46,6 +46,7 @@ namespace protobuf { class FieldDescriptor; class OneofDescriptor; class Descriptor; + class EnumDescriptor; namespace compiler { namespace java { class ClassNameResolver; // name_resolver.h @@ -78,6 +79,20 @@ class Context { const OneofGeneratorInfo* GetOneofGeneratorInfo( const OneofDescriptor* oneof) const; + // Enforces all the files (including transitive dependencies) to use + // LiteRuntime. + void SetEnforceLite(bool enforce_lite) { + enforce_lite_ = enforce_lite; + } + + bool EnforceLite() const { + return enforce_lite_; + } + + // Does this message class have generated parsing, serialization, and other + // standard methods for which reflection-based fallback implementations exist? + bool HasGeneratedMethods(const Descriptor* descriptor) const; + private: void InitializeFieldGeneratorInfo(const FileDescriptor* file); void InitializeFieldGeneratorInfoForMessage(const Descriptor* message); @@ -87,6 +102,7 @@ class Context { google::protobuf::scoped_ptr<ClassNameResolver> name_resolver_; map<const FieldDescriptor*, FieldGeneratorInfo> field_generator_info_map_; map<const OneofDescriptor*, OneofGeneratorInfo> oneof_generator_info_map_; + bool enforce_lite_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Context); }; diff --git a/src/google/protobuf/compiler/java/java_enum.cc b/src/google/protobuf/compiler/java/java_enum.cc index 5fc9b002..9eea873a 100644 --- a/src/google/protobuf/compiler/java/java_enum.cc +++ b/src/google/protobuf/compiler/java/java_enum.cc @@ -64,6 +64,7 @@ EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, bool immutable_api, Context* context) : descriptor_(descriptor), immutable_api_(immutable_api), + context_(context), name_resolver_(context->GetNameResolver()) { for (int i = 0; i < descriptor_->value_count(); i++) { const EnumValueDescriptor* value = descriptor_->value(i); @@ -150,7 +151,15 @@ void EnumGenerator::Generate(io::Printer* printer) { " return value;\n" "}\n" "\n" + "/**\n" + " * @deprecated Use {@link #forNumber(int)} instead.\n" + " */\n" + "@java.lang.Deprecated\n" "public static $classname$ valueOf(int value) {\n" + " return forNumber(value);\n" + "}\n" + "\n" + "public static $classname$ forNumber(int value) {\n" " switch (value) {\n", "classname", descriptor_->name()); printer->Indent(); @@ -178,7 +187,7 @@ void EnumGenerator::Generate(io::Printer* printer) { " $classname$> internalValueMap =\n" " new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n" " public $classname$ findValueByNumber(int number) {\n" - " return $classname$.valueOf(number);\n" + " return $classname$.forNumber(number);\n" " }\n" " };\n" "\n", @@ -187,7 +196,7 @@ void EnumGenerator::Generate(io::Printer* printer) { // ----------------------------------------------------------------- // Reflection - if (HasDescriptorMethods(descriptor_)) { + if (HasDescriptorMethods(descriptor_, context_->EnforceLite())) { printer->Print( "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n" " getValueDescriptor() {\n" @@ -229,7 +238,7 @@ void EnumGenerator::Generate(io::Printer* printer) { " (com.google.protobuf.Descriptors.FileDescriptor)\n" " m.invoke(immutableFileClass);\n" " return file.getEnumTypes().get($index$);\n" - "} catch (Exception e) {\n" + "} catch (java.lang.Exception e) {\n" // Immutable classes cannot be found. Proceed as if custom options // don't exist. "}\n", @@ -313,7 +322,7 @@ void EnumGenerator::Generate(io::Printer* printer) { "private final int value;\n\n" "private $classname$(int index, int value) {\n", "classname", descriptor_->name()); - if (HasDescriptorMethods(descriptor_)) { + if (HasDescriptorMethods(descriptor_, context_->EnforceLite())) { printer->Print(" this.index = index;\n"); } printer->Print( diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc index 558da968..3e54be3d 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.cc +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -68,21 +68,14 @@ void SetEnumVariables(const FieldDescriptor* descriptor, (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); (*variables)["default_number"] = SimpleItoa( descriptor->default_value_enum()->number()); - if (descriptor->is_packed()) { - (*variables)["tag"] = SimpleItoa(internal::WireFormatLite::MakeTag( - descriptor->number(), - internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED)); - } else { - (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); - } + (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); (*variables)["tag_size"] = SimpleItoa( internal::WireFormat::TagSize(descriptor->number(), GetType(descriptor))); // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + (*variables)["on_changed"] = "onChanged();"; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -198,7 +191,7 @@ GenerateMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" - " $type$ result = $type$.valueOf($name$_);\n" + " $type$ result = $type$.forNumber($name$_);\n" " return result == null ? $unknown$ : result;\n" "}\n"); } @@ -231,7 +224,7 @@ GenerateBuilderMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" - " $type$ result = $type$.valueOf($name$_);\n" + " $type$ result = $type$.forNumber($name$_);\n" " return result == null ? $unknown$ : result;\n" "}\n"); WriteFieldDocComment(printer, descriptor_); @@ -311,7 +304,7 @@ GenerateParsingCode(io::Printer* printer) const { } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n" + "$type$ value = $type$.forNumber(rawValue);\n" "if (value == null) {\n"); if (PreserveUnknownFields(descriptor_->containing_type())) { printer->Print(variables_, @@ -405,7 +398,7 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" " if ($has_oneof_case_message$) {\n" - " $type$ result = $type$.valueOf((java.lang.Integer) $oneof_name$_);\n" + " $type$ result = $type$.forNumber((java.lang.Integer) $oneof_name$_);\n" " return result == null ? $unknown$ : result;\n" " }\n" " return $default$;\n" @@ -443,7 +436,7 @@ GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" " if ($has_oneof_case_message$) {\n" - " $type$ result = $type$.valueOf((java.lang.Integer) $oneof_name$_);\n" + " $type$ result = $type$.forNumber((java.lang.Integer) $oneof_name$_);\n" " return result == null ? $unknown$ : result;\n" " }\n" " return $default$;\n" @@ -500,7 +493,7 @@ GenerateParsingCode(io::Printer* printer) const { } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n" + "$type$ value = $type$.forNumber(rawValue);\n" "if (value == null) {\n"); if (PreserveUnknownFields(descriptor_->containing_type())) { printer->Print(variables_, @@ -613,7 +606,7 @@ GenerateMembers(io::Printer* printer) const { " new com.google.protobuf.Internal.ListAdapter.Converter<\n" " java.lang.Integer, $type$>() {\n" " public $type$ convert(java.lang.Integer from) {\n" - " $type$ result = $type$.valueOf(from);\n" + " $type$ result = $type$.forNumber(from);\n" " return result == null ? $unknown$ : result;\n" " }\n" " };\n"); @@ -649,7 +642,7 @@ GenerateMembers(io::Printer* printer) const { } if (descriptor_->is_packed() && - HasGeneratedMethods(descriptor_->containing_type())) { + context_->HasGeneratedMethods(descriptor_->containing_type())) { printer->Print(variables_, "private int $name$MemoizedSerializedSize;\n"); } @@ -846,7 +839,7 @@ GenerateParsingCode(io::Printer* printer) const { } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n" + "$type$ value = $type$.forNumber(rawValue);\n" "if (value == null) {\n"); if (PreserveUnknownFields(descriptor_->containing_type())) { printer->Print(variables_, diff --git a/src/google/protobuf/compiler/java/java_enum_field_lite.cc b/src/google/protobuf/compiler/java/java_enum_field_lite.cc index e3e87c58..5b98637b 100644 --- a/src/google/protobuf/compiler/java/java_enum_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.cc @@ -75,8 +75,6 @@ void SetEnumVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -179,7 +177,7 @@ GenerateMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" - " $type$ result = $type$.valueOf($name$_);\n" + " $type$ result = $type$.forNumber($name$_);\n" " return result == null ? $unknown$ : result;\n" "}\n"); @@ -228,7 +226,7 @@ GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public Builder set$capitalized_name$Value(int value) {\n" " copyOnWrite();\n" - " instance.set$capitalized_name$Value(int value);\n" + " instance.set$capitalized_name$Value(value);\n" " return this;\n" "}\n"); } @@ -295,7 +293,7 @@ GenerateParsingCode(io::Printer* printer) const { } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n" + "$type$ value = $type$.forNumber(rawValue);\n" "if (value == null) {\n"); if (PreserveUnknownFields(descriptor_->containing_type())) { printer->Print(variables_, @@ -389,7 +387,7 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" " if ($has_oneof_case_message$) {\n" - " $type$ result = $type$.valueOf((java.lang.Integer) $oneof_name$_);\n" + " $type$ result = $type$.forNumber((java.lang.Integer) $oneof_name$_);\n" " return result == null ? $unknown$ : result;\n" " }\n" " return $default$;\n" @@ -488,7 +486,7 @@ GenerateParsingCode(io::Printer* printer) const { } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n" + "$type$ value = $type$.forNumber(rawValue);\n" "if (value == null) {\n"); if (PreserveUnknownFields(descriptor_->containing_type())) { printer->Print(variables_, @@ -602,7 +600,7 @@ GenerateMembers(io::Printer* printer) const { " new com.google.protobuf.Internal.ListAdapter.Converter<\n" " java.lang.Integer, $type$>() {\n" " public $type$ convert(java.lang.Integer from) {\n" - " $type$ result = $type$.valueOf(from);\n" + " $type$ result = $type$.forNumber(from);\n" " return result == null ? $unknown$ : result;\n" " }\n" " };\n"); @@ -638,7 +636,7 @@ GenerateMembers(io::Printer* printer) const { } if (descriptor_->options().packed() && - HasGeneratedMethods(descriptor_->containing_type())) { + context_->HasGeneratedMethods(descriptor_->containing_type())) { printer->Print(variables_, "private int $name$MemoizedSerializedSize;\n"); } @@ -821,7 +819,6 @@ GenerateMergingCode(io::Printer* printer) const { " ensure$capitalized_name$IsMutable();\n" " $name$_.addAll(other.$name$_);\n" " }\n" - " $on_changed$\n" "}\n"); } @@ -844,7 +841,7 @@ GenerateParsingCode(io::Printer* printer) const { } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n" + "$type$ value = $type$.forNumber(rawValue);\n" "if (value == null) {\n"); if (PreserveUnknownFields(descriptor_->containing_type())) { printer->Print(variables_, diff --git a/src/google/protobuf/compiler/java/java_enum_lite.cc b/src/google/protobuf/compiler/java/java_enum_lite.cc index ed415eee..c22da8d7 100644 --- a/src/google/protobuf/compiler/java/java_enum_lite.cc +++ b/src/google/protobuf/compiler/java/java_enum_lite.cc @@ -141,7 +141,15 @@ void EnumLiteGenerator::Generate(io::Printer* printer) { " return value;\n" "}\n" "\n" + "/**\n" + " * @deprecated Use {@link #forNumber(int)} instead.\n" + " */\n" + "@java.lang.Deprecated\n" "public static $classname$ valueOf(int value) {\n" + " return forNumber(value);\n" + "}\n" + "\n" + "public static $classname$ forNumber(int value) {\n" " switch (value) {\n", "classname", descriptor_->name()); printer->Indent(); @@ -169,7 +177,7 @@ void EnumLiteGenerator::Generate(io::Printer* printer) { " $classname$> internalValueMap =\n" " new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n" " public $classname$ findValueByNumber(int number) {\n" - " return $classname$.valueOf(number);\n" + " return $classname$.forNumber(number);\n" " }\n" " };\n" "\n", diff --git a/src/google/protobuf/compiler/java/java_extension.cc b/src/google/protobuf/compiler/java/java_extension.cc index 4db7085e..46b5faaa 100644 --- a/src/google/protobuf/compiler/java/java_extension.cc +++ b/src/google/protobuf/compiler/java/java_extension.cc @@ -118,75 +118,38 @@ void ImmutableExtensionGenerator::Generate(io::Printer* printer) { "public static final int $constant_name$ = $number$;\n"); WriteFieldDocComment(printer, descriptor_); - if (HasDescriptorMethods(descriptor_->file())) { - // Non-lite extensions - if (descriptor_->extension_scope() == NULL) { - // Non-nested - printer->Print( - vars, - "public static final\n" - " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" - " $containing_type$,\n" - " $type$> $name$ = com.google.protobuf.GeneratedMessage\n" - " .newFileScopedGeneratedExtension(\n" - " $singular_type$.class,\n" - " $prototype$);\n"); - } else { - // Nested - printer->Print( - vars, - "public static final\n" - " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" - " $containing_type$,\n" - " $type$> $name$ = com.google.protobuf.GeneratedMessage\n" - " .newMessageScopedGeneratedExtension(\n" - " $scope$.getDefaultInstance(),\n" - " $index$,\n" - " $singular_type$.class,\n" - " $prototype$);\n"); - } + if (descriptor_->extension_scope() == NULL) { + // Non-nested + printer->Print( + vars, + "public static final\n" + " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ = com.google.protobuf.GeneratedMessage\n" + " .newFileScopedGeneratedExtension(\n" + " $singular_type$.class,\n" + " $prototype$);\n"); } else { - // Lite extensions - if (descriptor_->is_repeated()) { - printer->Print( - vars, - "public static final\n" - " com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n" - " $containing_type$,\n" - " $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n" - " .newRepeatedGeneratedExtension(\n" - " $containing_type$.getDefaultInstance(),\n" - " $prototype$,\n" - " $enum_map$,\n" - " $number$,\n" - " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n" - " $packed$,\n" - " $singular_type$.class);\n"); - } else { - printer->Print( - vars, - "public static final\n" - " com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n" - " $containing_type$,\n" - " $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n" - " .newSingularGeneratedExtension(\n" - " $containing_type$.getDefaultInstance(),\n" - " $default$,\n" - " $prototype$,\n" - " $enum_map$,\n" - " $number$,\n" - " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n" - " $singular_type$.class);\n"); - } + // Nested + printer->Print( + vars, + "public static final\n" + " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ = com.google.protobuf.GeneratedMessage\n" + " .newMessageScopedGeneratedExtension(\n" + " $scope$.getDefaultInstance(),\n" + " $index$,\n" + " $singular_type$.class,\n" + " $prototype$);\n"); } } int ImmutableExtensionGenerator::GenerateNonNestedInitializationCode( io::Printer* printer) { int bytecode_estimate = 0; - if (descriptor_->extension_scope() == NULL && - HasDescriptorMethods(descriptor_->file())) { - // Only applies to non-nested, non-lite extensions. + if (descriptor_->extension_scope() == NULL) { + // Only applies to non-nested extensions. printer->Print( "$name$.internalInit(descriptor.getExtensions().get($index$));\n", "name", UnderscoresToCamelCase(descriptor_), diff --git a/src/google/protobuf/compiler/java/java_extension_lite.cc b/src/google/protobuf/compiler/java/java_extension_lite.cc index e69de29b..23261bac 100644 --- a/src/google/protobuf/compiler/java/java_extension_lite.cc +++ b/src/google/protobuf/compiler/java/java_extension_lite.cc @@ -0,0 +1,118 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <google/protobuf/compiler/java/java_extension_lite.h> + +#include <google/protobuf/compiler/java/java_context.h> +#include <google/protobuf/compiler/java/java_doc_comment.h> +#include <google/protobuf/compiler/java/java_helpers.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +ImmutableExtensionLiteGenerator::ImmutableExtensionLiteGenerator( + const FieldDescriptor* descriptor, Context* context) + : descriptor_(descriptor), context_(context), + name_resolver_(context->GetNameResolver()) { + if (descriptor_->extension_scope() != NULL) { + scope_ = name_resolver_->GetImmutableClassName( + descriptor_->extension_scope()); + } else { + scope_ = name_resolver_->GetImmutableClassName(descriptor_->file()); + } +} + +ImmutableExtensionLiteGenerator::~ImmutableExtensionLiteGenerator() {} + +void ImmutableExtensionLiteGenerator::Generate(io::Printer* printer) { + map<string, string> vars; + const bool kUseImmutableNames = true; + InitTemplateVars(descriptor_, scope_, kUseImmutableNames, name_resolver_, + &vars); + printer->Print(vars, + "public static final int $constant_name$ = $number$;\n"); + + WriteFieldDocComment(printer, descriptor_); + if (descriptor_->is_repeated()) { + printer->Print( + vars, + "public static final\n" + " com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n" + " .newRepeatedGeneratedExtension(\n" + " $containing_type$.getDefaultInstance(),\n" + " $prototype$,\n" + " $enum_map$,\n" + " $number$,\n" + " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n" + " $packed$,\n" + " $singular_type$.class);\n"); + } else { + printer->Print( + vars, + "public static final\n" + " com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n" + " .newSingularGeneratedExtension(\n" + " $containing_type$.getDefaultInstance(),\n" + " $default$,\n" + " $prototype$,\n" + " $enum_map$,\n" + " $number$,\n" + " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n" + " $singular_type$.class);\n"); + } +} + +int ImmutableExtensionLiteGenerator::GenerateNonNestedInitializationCode( + io::Printer* printer) { + return 0; +} + +int ImmutableExtensionLiteGenerator::GenerateRegistrationCode( + io::Printer* printer) { + printer->Print( + "registry.add($scope$.$name$);\n", + "scope", scope_, + "name", UnderscoresToCamelCase(descriptor_)); + return 7; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_extension_lite.h b/src/google/protobuf/compiler/java/java_extension_lite.h index e69de29b..4cd49bda 100644 --- a/src/google/protobuf/compiler/java/java_extension_lite.h +++ b/src/google/protobuf/compiler/java/java_extension_lite.h @@ -0,0 +1,76 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_LITE_H__ + +#include <map> +#include <string> + +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/java/java_extension.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +// Generates code for a lite extension, which may be within the scope of some +// message or may be at file scope. This is much simpler than FieldGenerator +// since extensions are just simple identifiers with interesting types. +class ImmutableExtensionLiteGenerator : public ExtensionGenerator { + public: + explicit ImmutableExtensionLiteGenerator(const FieldDescriptor* descriptor, + Context* context); + virtual ~ImmutableExtensionLiteGenerator(); + + virtual void Generate(io::Printer* printer); + + // Returns an estimate of the number of bytes the printed code will compile to + virtual int GenerateNonNestedInitializationCode(io::Printer* printer); + + // Returns an estimate of the number of bytes the printed code will compile to + virtual int GenerateRegistrationCode(io::Printer* printer); + + private: + const FieldDescriptor* descriptor_; + Context* context_; + ClassNameResolver* name_resolver_; + string scope_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableExtensionLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc index c5434767..3c7bc5c4 100644 --- a/src/google/protobuf/compiler/java/java_field.cc +++ b/src/google/protobuf/compiler/java/java_field.cc @@ -77,7 +77,7 @@ ImmutableFieldGenerator* MakeImmutableGenerator( return new ImmutableMapFieldGenerator( field, messageBitIndex, builderBitIndex, context); } else { - if (IsLazy(field)) { + if (IsLazy(field, context->EnforceLite())) { return new RepeatedImmutableLazyMessageFieldGenerator( field, messageBitIndex, builderBitIndex, context); } else { @@ -99,7 +99,7 @@ ImmutableFieldGenerator* MakeImmutableGenerator( if (field->containing_oneof()) { switch (GetJavaType(field)) { case JAVATYPE_MESSAGE: - if (IsLazy(field)) { + if (IsLazy(field, context->EnforceLite())) { return new ImmutableLazyMessageOneofFieldGenerator( field, messageBitIndex, builderBitIndex, context); } else { @@ -119,7 +119,7 @@ ImmutableFieldGenerator* MakeImmutableGenerator( } else { switch (GetJavaType(field)) { case JAVATYPE_MESSAGE: - if (IsLazy(field)) { + if (IsLazy(field, context->EnforceLite())) { return new ImmutableLazyMessageFieldGenerator( field, messageBitIndex, builderBitIndex, context); } else { @@ -150,7 +150,7 @@ ImmutableFieldLiteGenerator* MakeImmutableLiteGenerator( return new ImmutableMapFieldLiteGenerator( field, messageBitIndex, builderBitIndex, context); } else { - if (IsLazy(field)) { + if (IsLazy(field, context->EnforceLite())) { return new RepeatedImmutableLazyMessageFieldLiteGenerator( field, messageBitIndex, builderBitIndex, context); } else { @@ -172,7 +172,7 @@ ImmutableFieldLiteGenerator* MakeImmutableLiteGenerator( if (field->containing_oneof()) { switch (GetJavaType(field)) { case JAVATYPE_MESSAGE: - if (IsLazy(field)) { + if (IsLazy(field, context->EnforceLite())) { return new ImmutableLazyMessageOneofFieldLiteGenerator( field, messageBitIndex, builderBitIndex, context); } else { @@ -192,7 +192,7 @@ ImmutableFieldLiteGenerator* MakeImmutableLiteGenerator( } else { switch (GetJavaType(field)) { case JAVATYPE_MESSAGE: - if (IsLazy(field)) { + if (IsLazy(field, context->EnforceLite())) { return new ImmutableLazyMessageFieldLiteGenerator( field, messageBitIndex, builderBitIndex, context); } else { diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc index c8172330..c53aae6b 100644 --- a/src/google/protobuf/compiler/java/java_file.cc +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -177,7 +177,7 @@ void MaybeRestartJavaMethod(io::Printer* printer, // since otherwise we hit a hardcoded limit in the jvm and javac will // then fail with the error "code too large". This limit lets our // estimates be off by a factor of two and still we're okay. - static const int bytesPerMethod = 1<<15; // aka 32K + static const int bytesPerMethod = kMaxStaticSize; if ((*bytecode_estimate) > bytesPerMethod) { ++(*method_num); @@ -193,7 +193,8 @@ void MaybeRestartJavaMethod(io::Printer* printer, } // namespace -FileGenerator::FileGenerator(const FileDescriptor* file, bool immutable_api) +FileGenerator::FileGenerator(const FileDescriptor* file, bool immutable_api, + bool enforce_lite) : file_(file), java_package_(FileJavaPackage(file, immutable_api)), message_generators_( @@ -204,6 +205,7 @@ FileGenerator::FileGenerator(const FileDescriptor* file, bool immutable_api) name_resolver_(context_->GetNameResolver()), immutable_api_(immutable_api) { classname_ = name_resolver_->GetFileClassName(file, immutable_api); + context_->SetEnforceLite(enforce_lite); generator_factory_.reset( new ImmutableGeneratorFactory(context_.get())); for (int i = 0; i < file_->message_type_count(); ++i) { @@ -262,7 +264,8 @@ void FileGenerator::Generate(io::Printer* printer) { printer->Print( "public static void registerAllExtensions(\n" " com.google.protobuf.ExtensionRegistry$lite$ registry) {\n", - "lite", HasDescriptorMethods(file_) ? "" : "Lite"); + "lite", + HasDescriptorMethods(file_, context_->EnforceLite()) ? "" : "Lite"); printer->Indent(); @@ -282,7 +285,7 @@ void FileGenerator::Generate(io::Printer* printer) { if (!MultipleJavaFiles(file_, immutable_api_)) { for (int i = 0; i < file_->enum_type_count(); i++) { - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, context_->EnforceLite())) { EnumGenerator(file_->enum_type(i), immutable_api_, context_.get()) .Generate(printer); } else { @@ -294,7 +297,7 @@ void FileGenerator::Generate(io::Printer* printer) { message_generators_[i]->GenerateInterface(printer); message_generators_[i]->Generate(printer); } - if (HasGenericServices(file_)) { + if (HasGenericServices(file_, context_->EnforceLite())) { for (int i = 0; i < file_->service_count(); i++) { google::protobuf::scoped_ptr<ServiceGenerator> generator( generator_factory_->NewServiceGenerator(file_->service(i))); @@ -309,14 +312,18 @@ void FileGenerator::Generate(io::Printer* printer) { extension_generators_[i]->Generate(printer); } - // Static variables. + // Static variables. We'd like them to be final if possible, but due to + // the JVM's 64k size limit on static blocks, we have to initialize some + // of them in methods; thus they cannot be final. + int static_block_bytecode_estimate = 0; for (int i = 0; i < file_->message_type_count(); i++) { - message_generators_[i]->GenerateStaticVariables(printer); + message_generators_[i]->GenerateStaticVariables( + printer, &static_block_bytecode_estimate); } printer->Print("\n"); - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, context_->EnforceLite())) { if (immutable_api_) { GenerateDescriptorInitializationCodeForImmutable(printer); } else { @@ -358,9 +365,11 @@ void FileGenerator::GenerateDescriptorInitializationCodeForImmutable( " getDescriptor() {\n" " return descriptor;\n" "}\n" - "private static com.google.protobuf.Descriptors.FileDescriptor\n" + "private static $final$ com.google.protobuf.Descriptors.FileDescriptor\n" " descriptor;\n" - "static {\n"); + "static {\n", + // TODO(dweis): Mark this as final. + "final", ""); printer->Indent(); SharedCodeGenerator shared_code_generator(file_); @@ -453,7 +462,7 @@ void FileGenerator::GenerateDescriptorInitializationCodeForMutable(io::Printer* " getDescriptor() {\n" " return descriptor;\n" "}\n" - "private static com.google.protobuf.Descriptors.FileDescriptor\n" + "private static final com.google.protobuf.Descriptors.FileDescriptor\n" " descriptor;\n" "static {\n"); printer->Indent(); @@ -549,7 +558,7 @@ void FileGenerator::GenerateSiblings(const string& package_dir, vector<string>* file_list) { if (MultipleJavaFiles(file_, immutable_api_)) { for (int i = 0; i < file_->enum_type_count(); i++) { - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, context_->EnforceLite())) { EnumGenerator generator(file_->enum_type(i), immutable_api_, context_.get()); GenerateSibling<EnumGenerator>(package_dir, java_package_, @@ -582,7 +591,7 @@ void FileGenerator::GenerateSiblings(const string& package_dir, message_generators_[i].get(), &MessageGenerator::Generate); } - if (HasGenericServices(file_)) { + if (HasGenericServices(file_, context_->EnforceLite())) { for (int i = 0; i < file_->service_count(); i++) { google::protobuf::scoped_ptr<ServiceGenerator> generator( generator_factory_->NewServiceGenerator(file_->service(i))); diff --git a/src/google/protobuf/compiler/java/java_file.h b/src/google/protobuf/compiler/java/java_file.h index 080b3424..7dbeb94e 100644 --- a/src/google/protobuf/compiler/java/java_file.h +++ b/src/google/protobuf/compiler/java/java_file.h @@ -67,7 +67,8 @@ namespace java { class FileGenerator { public: - FileGenerator(const FileDescriptor* file, bool immutable_api = true); + FileGenerator(const FileDescriptor* file, bool immutable_api = true, + bool enforce_lite = false); ~FileGenerator(); // Checks for problems that would otherwise lead to cryptic compile errors. diff --git a/src/google/protobuf/compiler/java/java_generator.cc b/src/google/protobuf/compiler/java/java_generator.cc index 6c6f7286..a46c7fc4 100644 --- a/src/google/protobuf/compiler/java/java_generator.cc +++ b/src/google/protobuf/compiler/java/java_generator.cc @@ -75,6 +75,7 @@ bool JavaGenerator::Generate(const FileDescriptor* file, bool generate_immutable_code = false; bool generate_mutable_code = false; bool generate_shared_code = false; + bool enforce_lite = false; for (int i = 0; i < options.size(); i++) { if (options[i].first == "output_list_file") { output_list_file = options[i].second; @@ -84,12 +85,21 @@ bool JavaGenerator::Generate(const FileDescriptor* file, generate_mutable_code = true; } else if (options[i].first == "shared") { generate_shared_code = true; + } else if (options[i].first == "lite") { + // When set, the protoc will generate the current files and all the + // transitive dependencies as lite runtime. + enforce_lite = true; } else { *error = "Unknown generator option: " + options[i].first; return false; } } + if (enforce_lite && generate_mutable_code) { + *error = "lite runtime generator option cannot be used with mutable API."; + return false; + } + // By default we generate immutable code and shared code for immutable API. if (!generate_immutable_code && !generate_mutable_code && !generate_shared_code) { @@ -105,10 +115,12 @@ bool JavaGenerator::Generate(const FileDescriptor* file, vector<FileGenerator*> file_generators; if (generate_immutable_code) { - file_generators.push_back(new FileGenerator(file, /* immutable = */ true)); + file_generators.push_back( + new FileGenerator(file, /* immutable = */ true, enforce_lite)); } if (generate_mutable_code) { - file_generators.push_back(new FileGenerator(file, /* mutable = */ false)); + file_generators.push_back( + new FileGenerator(file, /* mutable = */ false, enforce_lite)); } for (int i = 0; i < file_generators.size(); ++i) { if (!file_generators[i]->Validate(error)) { diff --git a/src/google/protobuf/compiler/java/java_generator_factory.cc b/src/google/protobuf/compiler/java/java_generator_factory.cc index 92ef851b..3218b410 100644 --- a/src/google/protobuf/compiler/java/java_generator_factory.cc +++ b/src/google/protobuf/compiler/java/java_generator_factory.cc @@ -35,6 +35,7 @@ #include <google/protobuf/compiler/java/java_context.h> #include <google/protobuf/compiler/java/java_enum_field.h> #include <google/protobuf/compiler/java/java_extension.h> +#include <google/protobuf/compiler/java/java_extension_lite.h> #include <google/protobuf/compiler/java/java_field.h> #include <google/protobuf/compiler/java/java_helpers.h> #include <google/protobuf/compiler/java/java_message.h> @@ -58,17 +59,20 @@ ImmutableGeneratorFactory::~ImmutableGeneratorFactory() {} MessageGenerator* ImmutableGeneratorFactory::NewMessageGenerator( const Descriptor* descriptor) const { - if (descriptor->file()->options().optimize_for() == - FileOptions::LITE_RUNTIME) { - return new ImmutableMessageLiteGenerator(descriptor, context_); - } else { + if (HasDescriptorMethods(descriptor, context_->EnforceLite())) { return new ImmutableMessageGenerator(descriptor, context_); + } else { + return new ImmutableMessageLiteGenerator(descriptor, context_); } } ExtensionGenerator* ImmutableGeneratorFactory::NewExtensionGenerator( const FieldDescriptor* descriptor) const { - return new ImmutableExtensionGenerator(descriptor, context_); + if (HasDescriptorMethods(descriptor->file(), context_->EnforceLite())) { + return new ImmutableExtensionGenerator(descriptor, context_); + } else { + return new ImmutableExtensionLiteGenerator(descriptor, context_); + } } ServiceGenerator* ImmutableGeneratorFactory::NewServiceGenerator( diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h index 5392d6d7..c850423e 100644 --- a/src/google/protobuf/compiler/java/java_helpers.h +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -170,44 +170,41 @@ inline string ImmutableDefaultValue(const FieldDescriptor* field, } bool IsDefaultValueJavaDefault(const FieldDescriptor* field); -// Does this message class have generated parsing, serialization, and other -// standard methods for which reflection-based fallback implementations exist? -inline bool HasGeneratedMethods(const Descriptor* descriptor) { - return descriptor->file()->options().optimize_for() != - FileOptions::CODE_SIZE; -} - // Does this message have specialized equals() and hashCode() methods? inline bool HasEqualsAndHashCode(const Descriptor* descriptor) { return descriptor->file()->options().java_generate_equals_and_hash(); } // Does this message class have descriptor and reflection methods? -inline bool HasDescriptorMethods(const Descriptor* descriptor) { - return descriptor->file()->options().optimize_for() != - FileOptions::LITE_RUNTIME; +inline bool HasDescriptorMethods(const Descriptor* descriptor, + bool enforce_lite) { + return !enforce_lite && + descriptor->file()->options().optimize_for() != + FileOptions::LITE_RUNTIME; } -inline bool HasDescriptorMethods(const EnumDescriptor* descriptor) { - return descriptor->file()->options().optimize_for() != - FileOptions::LITE_RUNTIME; +inline bool HasDescriptorMethods(const EnumDescriptor* descriptor, + bool enforce_lite) { + return !enforce_lite && + descriptor->file()->options().optimize_for() != + FileOptions::LITE_RUNTIME; } -inline bool HasDescriptorMethods(const FileDescriptor* descriptor) { - return descriptor->options().optimize_for() != - FileOptions::LITE_RUNTIME; +inline bool HasDescriptorMethods(const FileDescriptor* descriptor, + bool enforce_lite) { + return !enforce_lite && + descriptor->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, bool enforce_lite) { return file->service_count() > 0 && - file->options().optimize_for() != FileOptions::LITE_RUNTIME && + HasDescriptorMethods(file, enforce_lite) && file->options().java_generic_services(); } -inline bool IsLazy(const FieldDescriptor* descriptor) { +inline bool IsLazy(const FieldDescriptor* descriptor, bool enforce_lite) { // Currently, the proto-lite version suports lazy field. // TODO(niwasaki): Support lazy fields also for other proto runtimes. - if (descriptor->file()->options().optimize_for() != - FileOptions::LITE_RUNTIME) { + if (HasDescriptorMethods(descriptor->file(), enforce_lite)) { return false; } return descriptor->options().lazy(); diff --git a/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc index 283ba1d3..a648f1c2 100644 --- a/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc @@ -193,7 +193,7 @@ GenerateMergingCode(io::Printer* printer) const { void ImmutableLazyMessageFieldLiteGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, - "$name$_.setByteString(input.readBytes(), extensionRegistry);\n"); + "$name$_.mergeFrom(input, extensionRegistry);\n"); printer->Print(variables_, "$set_has_field_bit_message$;\n"); } @@ -378,8 +378,7 @@ GenerateParsingCode(io::Printer* printer) const { "if (!($has_oneof_case_message$)) {\n" " $oneof_name$_ = new $lazy_type$();\n" "}\n" - "(($lazy_type$) $oneof_name$_).setByteString(\n" - " input.readBytes(), extensionRegistry);\n" + "(($lazy_type$) $oneof_name$_).mergeFrom(input, extensionRegistry);\n" "$set_oneof_case_message$;\n"); } diff --git a/src/google/protobuf/compiler/java/java_map_field.cc b/src/google/protobuf/compiler/java/java_map_field.cc index 3e035c89..17c3646f 100644 --- a/src/google/protobuf/compiler/java/java_map_field.cc +++ b/src/google/protobuf/compiler/java/java_map_field.cc @@ -79,9 +79,10 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, int builderBitIndex, const FieldGeneratorInfo* info, - ClassNameResolver* name_resolver, + Context* context, map<string, string>* variables) { SetCommonFieldVariables(descriptor, info, variables); + ClassNameResolver* name_resolver = context->GetNameResolver(); (*variables)["type"] = name_resolver->GetImmutableClassName(descriptor->message_type()); @@ -123,8 +124,7 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + (*variables)["on_changed"] = "onChanged();"; // For repeated fields, one bit is used for whether the array is immutable // in the parsing constructor. @@ -135,18 +135,12 @@ void SetMessageVariables(const FieldDescriptor* descriptor, (*variables)["default_entry"] = (*variables)["capitalized_name"] + "DefaultEntryHolder.defaultEntry"; - if (HasDescriptorMethods(descriptor->file())) { - (*variables)["lite"] = ""; - (*variables)["map_field_parameter"] = (*variables)["default_entry"]; - (*variables)["descriptor"] = - name_resolver->GetImmutableClassName(descriptor->file()) + - ".internal_" + UniqueFileScopeIdentifier(descriptor->message_type()) + - "_descriptor, "; - } else { - (*variables)["lite"] = "Lite"; - (*variables)["map_field_parameter"] = ""; - (*variables)["descriptor"] = ""; - } + (*variables)["lite"] = ""; + (*variables)["map_field_parameter"] = (*variables)["default_entry"]; + (*variables)["descriptor"] = + name_resolver->GetImmutableClassName(descriptor->file()) + + ".internal_" + UniqueFileScopeIdentifier(descriptor->message_type()) + + "_descriptor, "; } } // namespace @@ -159,7 +153,7 @@ ImmutableMapFieldGenerator(const FieldDescriptor* descriptor, : descriptor_(descriptor), name_resolver_(context->GetNameResolver()) { SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, context->GetFieldGeneratorInfo(descriptor), - name_resolver_, &variables_); + context, &variables_); } ImmutableMapFieldGenerator:: @@ -424,7 +418,7 @@ GenerateParsingCode(io::Printer* printer) const { "$name$ = $default_entry$.getParserForType().parseFrom(bytes);\n"); printer->Print( variables_, - "if ($value_enum_type$.valueOf($name$.getValue()) == null) {\n" + "if ($value_enum_type$.forNumber($name$.getValue()) == null) {\n" " unknownFields.mergeLengthDelimitedField($number$, bytes);\n" "} else {\n" " $name$_.getMutableMap().put($name$.getKey(), $name$.getValue());\n" diff --git a/src/google/protobuf/compiler/java/java_map_field_lite.cc b/src/google/protobuf/compiler/java/java_map_field_lite.cc index d2039403..6bdebb0d 100644 --- a/src/google/protobuf/compiler/java/java_map_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_map_field_lite.cc @@ -79,10 +79,11 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, int builderBitIndex, const FieldGeneratorInfo* info, - ClassNameResolver* name_resolver, + Context* context, map<string, string>* variables) { SetCommonFieldVariables(descriptor, info, variables); + ClassNameResolver* name_resolver = context->GetNameResolver(); (*variables)["type"] = name_resolver->GetImmutableClassName(descriptor->message_type()); const FieldDescriptor* key = KeyField(descriptor); @@ -123,8 +124,6 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; (*variables)["default_entry"] = (*variables)["capitalized_name"] + "DefaultEntryHolder.defaultEntry"; @@ -142,7 +141,7 @@ ImmutableMapFieldLiteGenerator(const FieldDescriptor* descriptor, : descriptor_(descriptor), name_resolver_(context->GetNameResolver()) { SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, context->GetFieldGeneratorInfo(descriptor), - name_resolver_, &variables_); + context, &variables_); } ImmutableMapFieldLiteGenerator:: @@ -404,7 +403,7 @@ GenerateParsingCode(io::Printer* printer) const { "$name$ = $default_entry$.getParserForType().parseFrom(bytes);\n"); printer->Print( variables_, - "if ($value_enum_type$.valueOf($name$.getValue()) == null) {\n" + "if ($value_enum_type$.forNumber($name$.getValue()) == null) {\n" " super.mergeLengthDelimitedField($number$, bytes);\n" "} else {\n" " $name$_.getMutableMap().put($name$.getKey(), $name$.getValue());\n" diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc index 22a70c32..dfd8ad08 100644 --- a/src/google/protobuf/compiler/java/java_message.cc +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -95,13 +95,15 @@ ImmutableMessageGenerator::ImmutableMessageGenerator( : MessageGenerator(descriptor), context_(context), name_resolver_(context->GetNameResolver()), field_generators_(descriptor, context_) { - GOOGLE_CHECK_NE( - FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); + GOOGLE_CHECK(HasDescriptorMethods(descriptor->file(), context->EnforceLite())) + << "Generator factory error: A non-lite message generator is used to " + "generate lite messages."; } ImmutableMessageGenerator::~ImmutableMessageGenerator() {} -void ImmutableMessageGenerator::GenerateStaticVariables(io::Printer* printer) { +void ImmutableMessageGenerator::GenerateStaticVariables( + io::Printer* printer, int* bytecode_estimate) { // Because descriptor.proto (com.google.protobuf.DescriptorProtos) is // used in the construction of descriptors, we have a tricky bootstrapping // problem. To help control static initialization order, we make sure all @@ -124,23 +126,29 @@ void ImmutableMessageGenerator::GenerateStaticVariables(io::Printer* printer) { } else { vars["private"] = "private "; } + if (*bytecode_estimate <= kMaxStaticSize) { + vars["final"] = "final "; + } else { + vars["final"] = ""; + } // The descriptor for this type. printer->Print(vars, // TODO(teboring): final needs to be added back. The way to fix it is to // generate methods that can construct the types, and then still declare the // types, and then init them in clinit with the new method calls. - "$private$static com.google.protobuf.Descriptors.Descriptor\n" + "$private$static $final$com.google.protobuf.Descriptors.Descriptor\n" " internal_$identifier$_descriptor;\n"); + *bytecode_estimate += 30; // And the FieldAccessorTable. - GenerateFieldAccessorTable(printer); + GenerateFieldAccessorTable(printer, bytecode_estimate); // Generate static members for all nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { // TODO(kenton): Reuse MessageGenerator objects? ImmutableMessageGenerator(descriptor_->nested_type(i), context_) - .GenerateStaticVariables(printer); + .GenerateStaticVariables(printer, bytecode_estimate); } } @@ -183,7 +191,7 @@ int ImmutableMessageGenerator::GenerateStaticVariableInitializers( } void ImmutableMessageGenerator:: -GenerateFieldAccessorTable(io::Printer* printer) { +GenerateFieldAccessorTable(io::Printer* printer, int* bytecode_estimate) { map<string, string> vars; vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); if (MultipleJavaFiles(descriptor_->file(), /* immutable = */ true)) { @@ -193,10 +201,19 @@ GenerateFieldAccessorTable(io::Printer* printer) { } else { vars["private"] = "private "; } + if (*bytecode_estimate <= kMaxStaticSize) { + vars["final"] = "final "; + } else { + vars["final"] = ""; + } printer->Print(vars, - "$private$static\n" + "$private$static $final$\n" " com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" " internal_$identifier$_fieldAccessorTable;\n"); + + // 6 bytes per field and oneof + *bytecode_estimate += 10 + 6 * descriptor_->field_count() + + 6 * descriptor_->oneof_decl_count(); } int ImmutableMessageGenerator:: @@ -342,7 +359,7 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { printer->Print( "}\n"); - if (HasGeneratedMethods(descriptor_)) { + if (context_->HasGeneratedMethods(descriptor_)) { GenerateParsingConstructor(printer); } @@ -408,12 +425,20 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { "cap_oneof_name", ToUpper(vars["oneof_name"])); printer->Print(vars, - "private int value = 0;\n" + "private final int value;\n" "private $oneof_capitalized_name$Case(int value) {\n" " this.value = value;\n" "}\n"); printer->Print(vars, + "/**\n" + " * @deprecated Use {@link #forNumber(int)} instead.\n" + " */\n" + "@java.lang.Deprecated\n" "public static $oneof_capitalized_name$Case valueOf(int value) {\n" + " return forNumber(value);\n" + "}\n" + "\n" + "public static $oneof_capitalized_name$Case forNumber(int value) {\n" " switch (value) {\n"); for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); @@ -426,8 +451,7 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { } printer->Print( " case 0: return $cap_oneof_name$_NOT_SET;\n" - " default: throw new java.lang.IllegalArgumentException(\n" - " \"Value is undefined for this oneof enum.\");\n" + " default: return null;\n" " }\n" "}\n" "public int getNumber() {\n" @@ -440,7 +464,7 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { printer->Print(vars, "public $oneof_capitalized_name$Case\n" "get$oneof_capitalized_name$Case() {\n" - " return $oneof_capitalized_name$Case.valueOf(\n" + " return $oneof_capitalized_name$Case.forNumber(\n" " $oneof_name$Case_);\n" "}\n" "\n"); @@ -459,7 +483,7 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { printer->Print("\n"); } - if (HasGeneratedMethods(descriptor_)) { + if (context_->HasGeneratedMethods(descriptor_)) { GenerateIsInitialized(printer); GenerateMessageSerializationMethods(printer); } @@ -664,34 +688,40 @@ GenerateParseFromMethods(io::Printer* printer) { "}\n" "public static $classname$ parseFrom(java.io.InputStream input)\n" " throws java.io.IOException {\n" - " return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input);" + " return com.google.protobuf.GeneratedMessage\n" + " .parseWithIOException(PARSER, 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 com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input, extensionRegistry);" + " return com.google.protobuf.GeneratedMessage\n" + " .parseWithIOException(PARSER, input, extensionRegistry);\n" "}\n" "public static $classname$ parseDelimitedFrom(java.io.InputStream input)\n" " throws java.io.IOException {\n" - " return com.google.protobuf.GeneratedMessage.parseDelimitedWithIOException(PARSER, input);" + " return com.google.protobuf.GeneratedMessage\n" + " .parseDelimitedWithIOException(PARSER, input);\n" "}\n" "public static $classname$ parseDelimitedFrom(\n" " java.io.InputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return com.google.protobuf.GeneratedMessage.parseDelimitedWithIOException(PARSER, input, extensionRegistry);" + " return com.google.protobuf.GeneratedMessage\n" + " .parseDelimitedWithIOException(PARSER, input, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.CodedInputStream input)\n" " throws java.io.IOException {\n" - " return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input);" + " return com.google.protobuf.GeneratedMessage\n" + " .parseWithIOException(PARSER, 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 com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input, extensionRegistry);" + " return com.google.protobuf.GeneratedMessage\n" + " .parseWithIOException(PARSER, input, extensionRegistry);\n" "}\n" "\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); @@ -1049,22 +1079,52 @@ GenerateEqualsAndHashCode(io::Printer* printer) { printer->Print("hash = (19 * hash) + getDescriptorForType().hashCode();\n"); + // hashCode non-oneofs. for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); - const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); - bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); - if (check_has_bits) { + if (field->containing_oneof() == NULL) { + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); + if (check_has_bits) { + printer->Print( + "if (has$name$()) {\n", + "name", info->capitalized_name); + printer->Indent(); + } + field_generators_.get(field).GenerateHashCode(printer); + if (check_has_bits) { + printer->Outdent(); + printer->Print("}\n"); + } + } + } + + // hashCode oneofs. + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "switch ($oneof_name$Case_) {\n", + "oneof_name", + context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); printer->Print( - "if (has$name$()) {\n", - "name", info->capitalized_name); + "case $field_number$:\n", + "field_number", + SimpleItoa(field->number())); printer->Indent(); - } - field_generators_.get(field).GenerateHashCode(printer); - if (check_has_bits) { + field_generators_.get(field).GenerateHashCode(printer); + printer->Print("break;\n"); printer->Outdent(); - printer->Print("}\n"); } + printer->Print( + "case 0:\n" + "default:\n"); + printer->Outdent(); + printer->Print("}\n"); } + if (descriptor_->extension_range_count() > 0) { printer->Print( "hash = hashFields(hash, getExtensionFields());\n"); @@ -1105,7 +1165,8 @@ GenerateParsingConstructor(io::Printer* printer) { printer->Print( "private $classname$(\n" " com.google.protobuf.CodedInputStream input,\n" - " com.google.protobuf.ExtensionRegistryLite extensionRegistry) {\n", + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n", "classname", descriptor_->name()); printer->Indent(); @@ -1215,10 +1276,10 @@ GenerateParsingConstructor(io::Printer* printer) { printer->Outdent(); printer->Print( "} catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" - " throw new RuntimeException(e.setUnfinishedMessage(this));\n" + " throw e.setUnfinishedMessage(this);\n" "} catch (java.io.IOException e) {\n" - " throw new RuntimeException(new com.google.protobuf.InvalidProtocolBufferException(e)\n" - " .setUnfinishedMessage(this));\n" + " throw new com.google.protobuf.InvalidProtocolBufferException(\n" + " e).setUnfinishedMessage(this);\n" "} finally {\n"); printer->Indent(); @@ -1260,20 +1321,9 @@ void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) { " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n", "classname", descriptor_->name()); - if (HasGeneratedMethods(descriptor_)) { - // The parsing constructor throws an InvalidProtocolBufferException via a - // RuntimeException to aid in method pruning. We unwrap it here. + if (context_->HasGeneratedMethods(descriptor_)) { printer->Print( - " try {\n" - " return new $classname$(input, extensionRegistry);\n" - " } catch (RuntimeException e) {\n" - " if (e.getCause() instanceof\n" - " com.google.protobuf.InvalidProtocolBufferException) {\n" - " throw (com.google.protobuf.InvalidProtocolBufferException)\n" - " e.getCause();\n" - " }\n" - " throw e;\n" - " }\n", + " return new $classname$(input, extensionRegistry);\n", "classname", descriptor_->name()); } else { // When parsing constructor isn't generated, use builder to parse @@ -1328,14 +1378,39 @@ void ImmutableMessageGenerator::GenerateInitializers(io::Printer* printer) { void ImmutableMessageGenerator::GenerateAnyMethods(io::Printer* printer) { printer->Print( "private static String getTypeUrl(\n" + " java.lang.String typeUrlPrefix,\n" " com.google.protobuf.Descriptors.Descriptor descriptor) {\n" - " return \"type.googleapis.com/\" + descriptor.getFullName();\n" + " return typeUrlPrefix.endsWith(\"/\")\n" + " ? typeUrlPrefix + descriptor.getFullName()\n" + " : typeUrlPrefix + \"/\" + descriptor.getFullName();\n" + "}\n" + "\n" + "private static String getTypeNameFromTypeUrl(\n" + " java.lang.String typeUrl) {\n" + " int pos = typeUrl.lastIndexOf('/');\n" + " return pos == -1 ? \"\" : typeUrl.substring(pos + 1);\n" "}\n" "\n" "public static <T extends com.google.protobuf.Message> Any pack(\n" " T message) {\n" " return Any.newBuilder()\n" - " .setTypeUrl(getTypeUrl(message.getDescriptorForType()))\n" + " .setTypeUrl(getTypeUrl(\"type.googleapis.com\",\n" + " message.getDescriptorForType()))\n" + " .setValue(message.toByteString())\n" + " .build();\n" + "}\n" + "\n" + "/**\n" + " * Packs a message uisng the given type URL prefix. The type URL will\n" + " * be constructed by concatenating the message type's full name to the\n" + " * prefix with an optional \"/\" separator if the prefix doesn't end\n" + " * with \"/\" already.\n" + " */\n" + "public static <T extends com.google.protobuf.Message> Any pack(\n" + " T message, java.lang.String typeUrlPrefix) {\n" + " return Any.newBuilder()\n" + " .setTypeUrl(getTypeUrl(typeUrlPrefix,\n" + " message.getDescriptorForType()))\n" " .setValue(message.toByteString())\n" " .build();\n" "}\n" @@ -1344,8 +1419,8 @@ void ImmutableMessageGenerator::GenerateAnyMethods(io::Printer* printer) { " java.lang.Class<T> clazz) {\n" " T defaultInstance =\n" " com.google.protobuf.Internal.getDefaultInstance(clazz);\n" - " return getTypeUrl().equals(\n" - " getTypeUrl(defaultInstance.getDescriptorForType()));\n" + " return getTypeNameFromTypeUrl(getTypeUrl()).equals(\n" + " defaultInstance.getDescriptorForType().getFullName());\n" "}\n" "\n" "private volatile com.google.protobuf.Message cachedUnpackValue;\n" diff --git a/src/google/protobuf/compiler/java/java_message.h b/src/google/protobuf/compiler/java/java_message.h index be5bfb07..e9fc57c2 100644 --- a/src/google/protobuf/compiler/java/java_message.h +++ b/src/google/protobuf/compiler/java/java_message.h @@ -56,6 +56,8 @@ namespace protobuf { namespace compiler { namespace java { +static const int kMaxStaticSize = 1 << 15; // aka 32k + class MessageGenerator { public: explicit MessageGenerator(const Descriptor* descriptor); @@ -64,7 +66,8 @@ class MessageGenerator { // All static variables have to be declared at the top-level of the file // so that we can control initialization order, which is important for // DescriptorProto bootstrapping to work. - virtual void GenerateStaticVariables(io::Printer* printer) = 0; + virtual void GenerateStaticVariables( + io::Printer* printer, int* bytecode_estimate) = 0; // Output code which initializes the static variables generated by // GenerateStaticVariables(). Returns an estimate of bytecode size. @@ -96,14 +99,15 @@ class ImmutableMessageGenerator : public MessageGenerator { virtual void Generate(io::Printer* printer); virtual void GenerateInterface(io::Printer* printer); virtual void GenerateExtensionRegistrationCode(io::Printer* printer); - virtual void GenerateStaticVariables(io::Printer* printer); + virtual void GenerateStaticVariables( + io::Printer* printer, int* bytecode_estimate); // Returns an estimate of the number of bytes the printed code will compile to virtual int GenerateStaticVariableInitializers(io::Printer* printer); private: - void GenerateFieldAccessorTable(io::Printer* printer); + void GenerateFieldAccessorTable(io::Printer* printer, int* bytecode_estimate); // Returns an estimate of the number of bytes the printed code will compile to int GenerateFieldAccessorTableInitializer(io::Printer* printer); diff --git a/src/google/protobuf/compiler/java/java_message_builder.cc b/src/google/protobuf/compiler/java/java_message_builder.cc index 5d535034..b3e9e986 100644 --- a/src/google/protobuf/compiler/java/java_message_builder.cc +++ b/src/google/protobuf/compiler/java/java_message_builder.cc @@ -81,8 +81,9 @@ MessageBuilderGenerator::MessageBuilderGenerator( : descriptor_(descriptor), context_(context), name_resolver_(context->GetNameResolver()), field_generators_(descriptor, context_) { - GOOGLE_CHECK_NE( - FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); + GOOGLE_CHECK(HasDescriptorMethods(descriptor->file(), context->EnforceLite())) + << "Generator factory error: A non-lite message generator is used to " + "generate lite messages."; } MessageBuilderGenerator::~MessageBuilderGenerator() {} @@ -113,7 +114,7 @@ Generate(io::Printer* printer) { GenerateDescriptorMethods(printer); GenerateCommonBuilderMethods(printer); - if (HasGeneratedMethods(descriptor_)) { + if (context_->HasGeneratedMethods(descriptor_)) { GenerateIsInitialized(printer); GenerateBuilderParsingMethods(printer); } @@ -134,7 +135,7 @@ Generate(io::Printer* printer) { printer->Print(vars, "public $oneof_capitalized_name$Case\n" " get$oneof_capitalized_name$Case() {\n" - " return $oneof_capitalized_name$Case.valueOf(\n" + " return $oneof_capitalized_name$Case.forNumber(\n" " $oneof_name$Case_);\n" "}\n" "\n" @@ -439,7 +440,7 @@ GenerateCommonBuilderMethods(io::Printer* printer) { // ----------------------------------------------------------------- - if (HasGeneratedMethods(descriptor_)) { + if (context_->HasGeneratedMethods(descriptor_)) { printer->Print( "public Builder mergeFrom(com.google.protobuf.Message other) {\n" " if (other instanceof $classname$) {\n" diff --git a/src/google/protobuf/compiler/java/java_message_builder_lite.cc b/src/google/protobuf/compiler/java/java_message_builder_lite.cc index 8719d00d..dd429dc9 100644 --- a/src/google/protobuf/compiler/java/java_message_builder_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_builder_lite.cc @@ -81,8 +81,9 @@ MessageBuilderLiteGenerator::MessageBuilderLiteGenerator( : descriptor_(descriptor), context_(context), name_resolver_(context->GetNameResolver()), field_generators_(descriptor, context_) { - GOOGLE_CHECK_EQ( - FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); + GOOGLE_CHECK(!HasDescriptorMethods(descriptor->file(), context->EnforceLite())) + << "Generator factory error: A lite message generator is used to " + "generate non-lite messages."; } MessageBuilderLiteGenerator::~MessageBuilderLiteGenerator() {} @@ -148,20 +149,6 @@ Generate(io::Printer* printer) { .GenerateBuilderMembers(printer); } - if (!PreserveUnknownFields(descriptor_)) { - printer->Print( - "public final Builder setUnknownFields(\n" - " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" - " return this;\n" - "}\n" - "\n" - "public final Builder mergeUnknownFields(\n" - " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" - " return this;\n" - "}\n" - "\n"); - } - printer->Print( "\n" "// @@protoc_insertion_point(builder_scope:$full_name$)\n", diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc index b5f8e626..455516f6 100644 --- a/src/google/protobuf/compiler/java/java_message_field.cc +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -70,8 +70,7 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + (*variables)["on_changed"] = "onChanged();"; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. diff --git a/src/google/protobuf/compiler/java/java_message_field_lite.cc b/src/google/protobuf/compiler/java/java_message_field_lite.cc index 356520ec..049679df 100644 --- a/src/google/protobuf/compiler/java/java_message_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_field_lite.cc @@ -70,8 +70,6 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -867,7 +865,6 @@ GenerateMergingCode(io::Printer* printer) const { " ensure$capitalized_name$IsMutable();\n" " $name$_.addAll(other.$name$_);\n" " }\n" - " $on_changed$\n" "}\n"); } diff --git a/src/google/protobuf/compiler/java/java_message_lite.cc b/src/google/protobuf/compiler/java/java_message_lite.cc index 94ed2c39..14cc908b 100644 --- a/src/google/protobuf/compiler/java/java_message_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_lite.cc @@ -46,7 +46,7 @@ #include <google/protobuf/compiler/java/java_context.h> #include <google/protobuf/compiler/java/java_doc_comment.h> #include <google/protobuf/compiler/java/java_enum_lite.h> -#include <google/protobuf/compiler/java/java_extension.h> +#include <google/protobuf/compiler/java/java_extension_lite.h> #include <google/protobuf/compiler/java/java_generator_factory.h> #include <google/protobuf/compiler/java/java_helpers.h> #include <google/protobuf/compiler/java/java_message_builder.h> @@ -87,19 +87,20 @@ ImmutableMessageLiteGenerator::ImmutableMessageLiteGenerator( : MessageGenerator(descriptor), context_(context), name_resolver_(context->GetNameResolver()), field_generators_(descriptor, context_) { - GOOGLE_CHECK_EQ( - FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); + GOOGLE_CHECK(!HasDescriptorMethods(descriptor->file(), context->EnforceLite())) + << "Generator factory error: A lite message generator is used to " + "generate non-lite messages."; } ImmutableMessageLiteGenerator::~ImmutableMessageLiteGenerator() {} void ImmutableMessageLiteGenerator::GenerateStaticVariables( - io::Printer* printer) { + io::Printer* printer, int* bytecode_estimate) { // Generate static members for all nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { // TODO(kenton): Reuse MessageGenerator objects? ImmutableMessageLiteGenerator(descriptor_->nested_type(i), context_) - .GenerateStaticVariables(printer); + .GenerateStaticVariables(printer, bytecode_estimate); } } @@ -197,6 +198,7 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { } printer->Indent(); + GenerateParsingConstructor(printer); // Nested types @@ -259,12 +261,20 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { "cap_oneof_name", ToUpper(vars["oneof_name"])); printer->Print(vars, - "private int value = 0;\n" + "private final int value;\n" "private $oneof_capitalized_name$Case(int value) {\n" " this.value = value;\n" "}\n"); printer->Print(vars, + "/**\n" + " * @deprecated Use {@link #forNumber(int)} instead.\n" + " */\n" + "@java.lang.Deprecated\n" "public static $oneof_capitalized_name$Case valueOf(int value) {\n" + " return forNumber(value);\n" + "}\n" + "\n" + "public static $oneof_capitalized_name$Case forNumber(int value) {\n" " switch (value) {\n"); for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); @@ -277,8 +287,7 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { } printer->Print( " case 0: return $cap_oneof_name$_NOT_SET;\n" - " default: throw new java.lang.IllegalArgumentException(\n" - " \"Value is undefined for this oneof enum.\");\n" + " default: return null;\n" " }\n" "}\n" "public int getNumber() {\n" @@ -291,7 +300,7 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { printer->Print(vars, "public $oneof_capitalized_name$Case\n" "get$oneof_capitalized_name$Case() {\n" - " return $oneof_capitalized_name$Case.valueOf(\n" + " return $oneof_capitalized_name$Case.forNumber(\n" " $oneof_name$Case_);\n" "}\n" "\n" @@ -317,7 +326,6 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { GenerateEqualsAndHashCode(printer); } - GenerateParseFromMethods(printer); GenerateBuilder(printer); @@ -446,7 +454,7 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { // because the DEFAULT_INSTANCE is used by the extension to lazily retrieve // the outer class's FileDescriptor. for (int i = 0; i < descriptor_->extension_count(); i++) { - ImmutableExtensionGenerator(descriptor_->extension(i), context_) + ImmutableExtensionLiteGenerator(descriptor_->extension(i), context_) .Generate(printer); } @@ -557,9 +565,6 @@ GenerateMessageSerializationMethods(io::Printer* printer) { " return size;\n" "}\n" "\n"); - - printer->Print( - "private static final long serialVersionUID = 0L;\n"); } void ImmutableMessageLiteGenerator:: @@ -571,54 +576,62 @@ GenerateParseFromMethods(io::Printer* printer) { "public static $classname$ parseFrom(\n" " com.google.protobuf.ByteString data)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return parser().parseFrom(data);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, data);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.ByteString data,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return parser().parseFrom(data, extensionRegistry);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, data, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(byte[] data)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return parser().parseFrom(data);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, data);\n" "}\n" "public static $classname$ parseFrom(\n" " byte[] data,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return parser().parseFrom(data, extensionRegistry);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, data, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(java.io.InputStream input)\n" " throws java.io.IOException {\n" - " return parser().parseFrom(input);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, input);\n" "}\n" "public static $classname$ parseFrom(\n" " java.io.InputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return parser().parseFrom(input, extensionRegistry);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, input, extensionRegistry);\n" "}\n" "public static $classname$ parseDelimitedFrom(java.io.InputStream input)\n" " throws java.io.IOException {\n" - " return parser().parseDelimitedFrom(input);\n" + " return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n" "}\n" "public static $classname$ parseDelimitedFrom(\n" " java.io.InputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return parser().parseDelimitedFrom(input, extensionRegistry);\n" + " return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.CodedInputStream input)\n" " throws java.io.IOException {\n" - " return parser().parseFrom(input);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, input);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.CodedInputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return parser().parseFrom(input, extensionRegistry);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, input, extensionRegistry);\n" "}\n" "\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); @@ -906,24 +919,61 @@ GenerateEqualsAndHashCode(io::Printer* printer) { "classname", name_resolver_->GetImmutableClassName(descriptor_)); printer->Print("boolean result = true;\n"); + // Compare non-oneofs. for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); - const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); - bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); - if (check_has_bits) { + if (field->containing_oneof() == NULL) { + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); + if (check_has_bits) { + printer->Print( + "result = result && (has$name$() == other.has$name$());\n" + "if (has$name$()) {\n", + "name", info->capitalized_name); + printer->Indent(); + } + field_generators_.get(field).GenerateEqualsCode(printer); + if (check_has_bits) { + printer->Outdent(); + printer->Print( + "}\n"); + } + } + } + + // Compare oneofs. + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "result = result && get$oneof_capitalized_name$Case().equals(\n" + " other.get$oneof_capitalized_name$Case());\n", + "oneof_capitalized_name", + context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->capitalized_name); + printer->Print( + "if (!result) return false;\n" + "switch ($oneof_name$Case_) {\n", + "oneof_name", + context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); printer->Print( - "result = result && (has$name$() == other.has$name$());\n" - "if (has$name$()) {\n", - "name", info->capitalized_name); + "case $field_number$:\n", + "field_number", + SimpleItoa(field->number())); printer->Indent(); - } - field_generators_.get(field).GenerateEqualsCode(printer); - if (check_has_bits) { + field_generators_.get(field).GenerateEqualsCode(printer); + printer->Print("break;\n"); printer->Outdent(); - printer->Print( - "}\n"); } + printer->Print( + "case 0:\n" + "default:\n"); + printer->Outdent(); + printer->Print("}\n"); } + if (PreserveUnknownFields(descriptor_)) { // Always consider unknown fields for equality. This will sometimes return // false for non-canonical ordering when running in LITE_RUNTIME but it's @@ -957,21 +1007,50 @@ GenerateEqualsAndHashCode(io::Printer* printer) { printer->Print("hash = (19 * hash) + $classname$.class.hashCode();\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); + // hashCode non-oneofs. for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); - const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); - bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); - if (check_has_bits) { + if (field->containing_oneof() == NULL) { + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); + if (check_has_bits) { + printer->Print( + "if (has$name$()) {\n", + "name", info->capitalized_name); + printer->Indent(); + } + field_generators_.get(field).GenerateHashCode(printer); + if (check_has_bits) { + printer->Outdent(); + printer->Print("}\n"); + } + } + } + + // hashCode oneofs. + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "switch ($oneof_name$Case_) {\n", + "oneof_name", + context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); printer->Print( - "if (has$name$()) {\n", - "name", info->capitalized_name); + "case $field_number$:\n", + "field_number", + SimpleItoa(field->number())); printer->Indent(); - } - field_generators_.get(field).GenerateHashCode(printer); - if (check_has_bits) { + field_generators_.get(field).GenerateHashCode(printer); + printer->Print("break;\n"); printer->Outdent(); - printer->Print("}\n"); } + printer->Print( + "case 0:\n" + "default:\n"); + printer->Outdent(); + printer->Print("}\n"); } printer->Print( @@ -990,7 +1069,7 @@ GenerateEqualsAndHashCode(io::Printer* printer) { void ImmutableMessageLiteGenerator:: GenerateExtensionRegistrationCode(io::Printer* printer) { for (int i = 0; i < descriptor_->extension_count(); i++) { - ImmutableExtensionGenerator(descriptor_->extension(i), context_) + ImmutableExtensionLiteGenerator(descriptor_->extension(i), context_) .GenerateRegistrationCode(printer); } @@ -1167,7 +1246,6 @@ void ImmutableMessageLiteGenerator::GenerateInitializers(io::Printer* printer) { } } - } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_message_lite.h b/src/google/protobuf/compiler/java/java_message_lite.h index 2bd3cdd4..c8ee99bd 100644 --- a/src/google/protobuf/compiler/java/java_message_lite.h +++ b/src/google/protobuf/compiler/java/java_message_lite.h @@ -54,7 +54,8 @@ class ImmutableMessageLiteGenerator : public MessageGenerator { virtual void Generate(io::Printer* printer); virtual void GenerateInterface(io::Printer* printer); virtual void GenerateExtensionRegistrationCode(io::Printer* printer); - virtual void GenerateStaticVariables(io::Printer* printer); + virtual void GenerateStaticVariables( + io::Printer* printer, int* bytecode_estimate); virtual int GenerateStaticVariableInitializers(io::Printer* printer); private: diff --git a/src/google/protobuf/compiler/java/java_name_resolver.cc b/src/google/protobuf/compiler/java/java_name_resolver.cc index 0c363f9f..bffe4f16 100644 --- a/src/google/protobuf/compiler/java/java_name_resolver.cc +++ b/src/google/protobuf/compiler/java/java_name_resolver.cc @@ -259,6 +259,13 @@ string ClassNameResolver::GetJavaImmutableClassName( descriptor->file(), true); } +string ClassNameResolver::GetJavaImmutableClassName( + const EnumDescriptor* descriptor) { + return GetJavaClassFullName( + ClassNameWithoutPackage(descriptor, true), + descriptor->file(), true); +} + } // namespace java } // namespace compiler diff --git a/src/google/protobuf/compiler/java/java_name_resolver.h b/src/google/protobuf/compiler/java/java_name_resolver.h index ab60b0a0..570d8d8f 100644 --- a/src/google/protobuf/compiler/java/java_name_resolver.h +++ b/src/google/protobuf/compiler/java/java_name_resolver.h @@ -98,6 +98,7 @@ class ClassNameResolver { // For example: // com.package.OuterClass$OuterMessage$InnerMessage string GetJavaImmutableClassName(const Descriptor* descriptor); + string GetJavaImmutableClassName(const EnumDescriptor* descriptor); private: // Get the full name of a Java class by prepending the Java package name // or outer class name. diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc index 178bbe19..e42ec280 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -75,12 +75,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, "" : ("= " + ImmutableDefaultValue(descriptor, name_resolver)); (*variables)["capitalized_type"] = GetCapitalizedType(descriptor, /* immutable = */ true); - if (descriptor->is_packed()) { - (*variables)["tag"] = SimpleItoa(WireFormatLite::MakeTag( - descriptor->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED)); - } else { - (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); - } + (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); (*variables)["tag_size"] = SimpleItoa( WireFormat::TagSize(descriptor->number(), GetType(descriptor))); if (IsReferenceType(GetJavaType(descriptor))) { @@ -99,8 +94,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, if (fixed_size != -1) { (*variables)["fixed_size"] = SimpleItoa(fixed_size); } - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + (*variables)["on_changed"] = "onChanged();"; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -606,7 +600,7 @@ GenerateMembers(io::Printer* printer) const { "}\n"); if (descriptor_->is_packed() && - HasGeneratedMethods(descriptor_->containing_type())) { + context_->HasGeneratedMethods(descriptor_->containing_type())) { printer->Print(variables_, "private int $name$MemoizedSerializedSize = -1;\n"); } diff --git a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc index 5a7bf82d..d277e4f3 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc @@ -129,8 +129,6 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, if (fixed_size != -1) { (*variables)["fixed_size"] = SimpleItoa(fixed_size); } - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -635,7 +633,7 @@ GenerateMembers(io::Printer* printer) const { "}\n"); if (descriptor_->options().packed() && - HasGeneratedMethods(descriptor_->containing_type())) { + context_->HasGeneratedMethods(descriptor_->containing_type())) { printer->Print(variables_, "private int $name$MemoizedSerializedSize = -1;\n"); } @@ -758,7 +756,6 @@ GenerateMergingCode(io::Printer* printer) const { " ensure$capitalized_name$IsMutable();\n" " $name$_.addAll(other.$name$_);\n" " }\n" - " $on_changed$\n" "}\n"); } diff --git a/src/google/protobuf/compiler/java/java_shared_code_generator.cc b/src/google/protobuf/compiler/java/java_shared_code_generator.cc index 70177367..74253c3f 100644 --- a/src/google/protobuf/compiler/java/java_shared_code_generator.cc +++ b/src/google/protobuf/compiler/java/java_shared_code_generator.cc @@ -52,8 +52,9 @@ namespace compiler { namespace java { SharedCodeGenerator::SharedCodeGenerator(const FileDescriptor* file) - : name_resolver_(new ClassNameResolver), file_(file) { -} + : name_resolver_(new ClassNameResolver), + enforce_lite_(false), + file_(file) {} SharedCodeGenerator::~SharedCodeGenerator() { } @@ -63,7 +64,7 @@ void SharedCodeGenerator::Generate(GeneratorContext* context, string java_package = FileJavaPackage(file_); string package_dir = JavaPackageToDir(java_package); - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, enforce_lite_)) { // Generate descriptors. string classname = name_resolver_->GetDescriptorClassName(file_); string filename = package_dir + classname + ".java"; diff --git a/src/google/protobuf/compiler/java/java_shared_code_generator.h b/src/google/protobuf/compiler/java/java_shared_code_generator.h index 38a32fc2..3b573c07 100644 --- a/src/google/protobuf/compiler/java/java_shared_code_generator.h +++ b/src/google/protobuf/compiler/java/java_shared_code_generator.h @@ -72,6 +72,10 @@ class SharedCodeGenerator { void Generate(GeneratorContext* generator_context, vector<string>* file_list); + void SetEnforceLite(bool value) { + enforce_lite_ = value; + } + void GenerateDescriptors(io::Printer* printer); private: @@ -81,6 +85,7 @@ class SharedCodeGenerator { bool ShouldIncludeDependency(const FileDescriptor* descriptor); google::protobuf::scoped_ptr<ClassNameResolver> name_resolver_; + bool enforce_lite_; const FileDescriptor* file_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SharedCodeGenerator); }; diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc index 7f757e47..b67eeb53 100644 --- a/src/google/protobuf/compiler/java/java_string_field.cc +++ b/src/google/protobuf/compiler/java/java_string_field.cc @@ -87,8 +87,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + (*variables)["on_changed"] = "onChanged();"; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -408,15 +407,6 @@ GenerateParsingCode(io::Printer* printer) const { "java.lang.String s = input.readStringRequireUtf8();\n" "$set_has_field_bit_message$\n" "$name$_ = s;\n"); - } else if (!HasDescriptorMethods(descriptor_->file())) { - // Lite runtime should attempt to reduce allocations by attempting to - // construct the string directly from the input stream buffer. This avoids - // spurious intermediary ByteString allocations, cutting overall allocations - // in half. - printer->Print(variables_, - "java.lang.String s = input.readString();\n" - "$set_has_field_bit_message$\n" - "$name$_ = s;\n"); } else { printer->Print(variables_, "com.google.protobuf.ByteString bs = input.readBytes();\n" @@ -668,15 +658,6 @@ GenerateParsingCode(io::Printer* printer) const { "java.lang.String s = input.readStringRequireUtf8();\n" "$set_oneof_case_message$;\n" "$oneof_name$_ = s;\n"); - } else if (!HasDescriptorMethods(descriptor_->file())) { - // Lite runtime should attempt to reduce allocations by attempting to - // construct the string directly from the input stream buffer. This avoids - // spurious intermediary ByteString allocations, cutting overall allocations - // in half. - printer->Print(variables_, - "java.lang.String s = input.readString();\n" - "$set_oneof_case_message$;\n" - "$oneof_name$_ = s;\n"); } else { printer->Print(variables_, "com.google.protobuf.ByteString bs = input.readBytes();\n" @@ -934,13 +915,6 @@ GenerateParsingCode(io::Printer* printer) const { if (CheckUtf8(descriptor_)) { printer->Print(variables_, "java.lang.String s = input.readStringRequireUtf8();\n"); - } else if (!HasDescriptorMethods(descriptor_->file())) { - // Lite runtime should attempt to reduce allocations by attempting to - // construct the string directly from the input stream buffer. This avoids - // spurious intermediary ByteString allocations, cutting overall allocations - // in half. - printer->Print(variables_, - "java.lang.String s = input.readString();\n"); } else { printer->Print(variables_, "com.google.protobuf.ByteString bs = input.readBytes();\n"); @@ -950,7 +924,7 @@ GenerateParsingCode(io::Printer* printer) const { " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" " $set_mutable_bit_parser$;\n" "}\n"); - if (CheckUtf8(descriptor_) || !HasDescriptorMethods(descriptor_->file())) { + if (CheckUtf8(descriptor_)) { printer->Print(variables_, "$name$_.add(s);\n"); } else { diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.cc b/src/google/protobuf/compiler/java/java_string_field_lite.cc index eb5964bd..9012ab5e 100644 --- a/src/google/protobuf/compiler/java/java_string_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_string_field_lite.cc @@ -84,8 +84,6 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -103,7 +101,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, (*variables)["clear_has_field_bit_message"] = ""; (*variables)["is_field_present_message"] = - "!get" + (*variables)["capitalized_name"] + ".isEmpty()"; + "!" + (*variables)["name"] + "_.isEmpty()"; } // For repeated builders, the underlying list tracks mutability state. @@ -309,13 +307,11 @@ GenerateMergingCode(io::Printer* printer) const { "if (other.has$capitalized_name$()) {\n" " $set_has_field_bit_message$\n" " $name$_ = other.$name$_;\n" - " $on_changed$\n" "}\n"); } else { printer->Print(variables_, "if (!other.get$capitalized_name$().isEmpty()) {\n" " $name$_ = other.$name$_;\n" - " $on_changed$\n" "}\n"); } } @@ -528,8 +524,7 @@ GenerateMergingCode(io::Printer* printer) const { // all string fields to Strings when copying fields from a Message. printer->Print(variables_, "$set_oneof_case_message$;\n" - "$oneof_name$_ = other.$oneof_name$_;\n" - "$on_changed$\n"); + "$oneof_name$_ = other.$oneof_name$_;\n"); } void ImmutableStringOneofFieldLiteGenerator:: @@ -792,7 +787,6 @@ GenerateMergingCode(io::Printer* printer) const { " ensure$capitalized_name$IsMutable();\n" " $name$_.addAll(other.$name$_);\n" " }\n" - " $on_changed$\n" "}\n"); } @@ -819,13 +813,8 @@ GenerateParsingCode(io::Printer* printer) const { "if (!$is_mutable$) {\n" " $name$_ = com.google.protobuf.GeneratedMessageLite.newProtobufList();\n" "}\n"); - if (CheckUtf8(descriptor_) || !HasDescriptorMethods(descriptor_->file())) { - printer->Print(variables_, - "$name$_.add(s);\n"); - } else { - printer->Print(variables_, - "$name$_.add(bs);\n"); - } + printer->Print(variables_, + "$name$_.add(s);\n"); } void RepeatedImmutableStringFieldLiteGenerator:: diff --git a/src/google/protobuf/compiler/javanano/javanano_generator.cc b/src/google/protobuf/compiler/javanano/javanano_generator.cc index a33eba1b..a342e6bb 100644 --- a/src/google/protobuf/compiler/javanano/javanano_generator.cc +++ b/src/google/protobuf/compiler/javanano/javanano_generator.cc @@ -68,12 +68,7 @@ void UpdateParamsRecursively(Params& params, } if (file->options().has_java_package()) { string result = file->options().java_package(); - if (!file->options().javanano_use_deprecated_package()) { - if (!result.empty()) { - result += "."; - } - result += "nano"; - } + result += "nano"; params.set_java_package( file->name(), result); } diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.cc b/src/google/protobuf/compiler/javanano/javanano_helpers.cc index 5465655f..02811a24 100644 --- a/src/google/protobuf/compiler/javanano/javanano_helpers.cc +++ b/src/google/protobuf/compiler/javanano/javanano_helpers.cc @@ -201,12 +201,10 @@ string FileJavaPackage(const Params& params, const FileDescriptor* file) { result += file->package(); } - if (!file->options().javanano_use_deprecated_package()) { - if (!result.empty()) { - result += "."; - } - result += "nano"; + if (!result.empty()) { + result += "."; } + result += "nano"; return result; } diff --git a/src/google/protobuf/compiler/js/js_generator.cc b/src/google/protobuf/compiler/js/js_generator.cc index 0de7e2c6..534a5b5b 100755 --- a/src/google/protobuf/compiler/js/js_generator.cc +++ b/src/google/protobuf/compiler/js/js_generator.cc @@ -28,7 +28,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include <google/protobuf/compiler/js/js_generator.h> +#include "google/protobuf/compiler/js/js_generator.h" #include <assert.h> #include <algorithm> @@ -123,6 +123,16 @@ static const int kNumKeyword = sizeof(kKeyword) / sizeof(char*); namespace { +// The mode of operation for bytes fields. Historically JSPB always carried +// bytes as JS {string}, containing base64 content by convention. With binary +// and proto3 serialization the new convention is to represent it as binary +// data in Uint8Array. See b/26173701 for background on the migration. +enum BytesMode { + BYTES_DEFAULT, // Default type for getBytesField to return. + BYTES_B64, // Explicitly coerce to base64 string where needed. + BYTES_U8, // Explicitly coerce to Uint8Array where needed. +}; + bool IsReserved(const string& ident) { for (int i = 0; i < kNumKeyword; i++) { if (ident == kKeyword[i]) { @@ -134,7 +144,7 @@ bool IsReserved(const string& ident) { // Returns a copy of |filename| with any trailing ".protodevel" or ".proto // suffix stripped. -// TODO(robinson): Unify with copy in compiler/cpp/internal/helpers.cc. +// TODO(haberman): Unify with copy in compiler/cpp/internal/helpers.cc. string StripProto(const string& filename) { const char* suffix = HasSuffixString(filename, ".protodevel") ? ".protodevel" : ".proto"; @@ -144,9 +154,7 @@ string StripProto(const string& filename) { // Given a filename like foo/bar/baz.proto, returns the correspoding JavaScript // file foo/bar/baz.js. string GetJSFilename(const string& filename) { - const char* suffix = HasSuffixString(filename, ".protodevel") - ? ".protodevel" : ".proto"; - return StripSuffixString(filename, suffix) + "_pb.js"; + return StripProto(filename) + "_pb.js"; } // Given a filename like foo/bar/baz.proto, returns the root directory @@ -385,15 +393,47 @@ string ToFileName(const string& input) { return result; } +// When we're generating one output file per type name, this is the filename +// that top-level extensions should go in. +string GetExtensionFileName(const GeneratorOptions& options, + const FileDescriptor* file) { + return options.output_dir + "/" + ToFileName(GetPath(options, file)) + ".js"; +} + +// When we're generating one output file per type name, this is the filename +// that a top-level message should go in. +string GetMessageFileName(const GeneratorOptions& options, + const Descriptor* desc) { + return options.output_dir + "/" + ToFileName(desc->name()) + ".js"; +} + +// When we're generating one output file per type name, this is the filename +// that a top-level message should go in. +string GetEnumFileName(const GeneratorOptions& options, + const EnumDescriptor* desc) { + return options.output_dir + "/" + ToFileName(desc->name()) + ".js"; +} + // Returns the message/response ID, if set. string GetMessageId(const Descriptor* desc) { return string(); } +bool IgnoreExtensionField(const FieldDescriptor* field) { + // Exclude descriptor extensions from output "to avoid clutter" (from original + // codegen). + return field->is_extension() && + field->containing_type()->file()->name() == + "google/protobuf/descriptor.proto"; +} + // Used inside Google only -- do not remove. bool IsResponse(const Descriptor* desc) { return false; } -bool IgnoreField(const FieldDescriptor* field) { return false; } + +bool IgnoreField(const FieldDescriptor* field) { + return IgnoreExtensionField(field); +} // Does JSPB ignore this entire oneof? True only if all fields are ignored. @@ -438,12 +478,32 @@ string JSObjectFieldName(const FieldDescriptor* field) { return name; } +string JSByteGetterSuffix(BytesMode bytes_mode) { + switch (bytes_mode) { + case BYTES_DEFAULT: + return ""; + case BYTES_B64: + return "B64"; + case BYTES_U8: + return "U8"; + default: + assert(false); + } +} + // Returns the field name as a capitalized portion of a getter/setter method // name, e.g. MyField for .getMyField(). -string JSGetterName(const FieldDescriptor* field) { +string JSGetterName(const FieldDescriptor* field, + BytesMode bytes_mode = BYTES_DEFAULT) { string name = JSIdent(field, /* is_upper_camel = */ true, /* is_map = */ false); + if (field->type() == FieldDescriptor::TYPE_BYTES) { + string suffix = JSByteGetterSuffix(bytes_mode); + if (!suffix.empty()) { + name += "_as" + suffix; + } + } if (name == "Extension" || name == "JsPbMessageId") { // Avoid conflicts with base-class names. name += "$"; @@ -504,8 +564,7 @@ string JSOneofIndex(const OneofDescriptor* oneof) { return SimpleItoa(index); } -// Decodes a codepoint in \x0000 -- \xFFFF. Since JS strings are UTF-16, we only -// need to handle the BMP (16-bit range) here. +// Decodes a codepoint in \x0000 -- \xFFFF. uint16 DecodeUTF8Codepoint(uint8* bytes, size_t* length) { if (*length == 0) { return 0; @@ -542,80 +601,56 @@ uint16 DecodeUTF8Codepoint(uint8* bytes, size_t* length) { } // Escapes the contents of a string to be included within double-quotes ("") in -// JavaScript. |is_utf8| determines whether the input data (in a C++ string of -// chars) is UTF-8 encoded (in which case codepoints become JavaScript string -// characters, escaped with 16-bit hex escapes where necessary) or raw binary -// (in which case bytes become JavaScript string characters 0 -- 255). -string EscapeJSString(const string& in, bool is_utf8) { - string result; +// JavaScript. The input data should be a UTF-8 encoded C++ string of chars. +// Returns false if |out| was truncated because |in| contained invalid UTF-8 or +// codepoints outside the BMP. +// TODO(lukestebbing): Support codepoints outside the BMP. +bool EscapeJSString(const string& in, string* out) { size_t decoded = 0; for (size_t i = 0; i < in.size(); i += decoded) { uint16 codepoint = 0; - if (is_utf8) { - // Decode the next UTF-8 codepoint. - size_t have_bytes = in.size() - i; - uint8 bytes[3] = { + // Decode the next UTF-8 codepoint. + size_t have_bytes = in.size() - i; + uint8 bytes[3] = { static_cast<uint8>(in[i]), static_cast<uint8>(((i + 1) < in.size()) ? in[i + 1] : 0), static_cast<uint8>(((i + 2) < in.size()) ? in[i + 2] : 0), - }; - codepoint = DecodeUTF8Codepoint(bytes, &have_bytes); - if (have_bytes == 0) { - break; - } - decoded = have_bytes; - } else { - codepoint = static_cast<uint16>(static_cast<uint8>(in[i])); - decoded = 1; + }; + codepoint = DecodeUTF8Codepoint(bytes, &have_bytes); + if (have_bytes == 0) { + return false; } - - // Next byte -- used for minimal octal escapes below. - char next_byte = (i + decoded) < in.size() ? - in[i + decoded] : 0; - bool pad_octal = (next_byte >= '0' && next_byte <= '7'); + decoded = have_bytes; switch (codepoint) { - case '\0': result += pad_octal ? "\\000" : "\\0"; break; - case '\b': result += "\\\b"; break; - case '\t': result += "\\\t"; break; - case '\n': result += "\\\n"; break; - case '\r': result += "\\\r"; break; - case '\f': result += "\\\f"; break; - case '\\': result += "\\\\"; break; - case '"': result += pad_octal ? "\\042" : "\\42"; break; - case '&': result += pad_octal ? "\\046" : "\\46"; break; - case '\'': result += pad_octal ? "\\047" : "\\47"; break; - case '<': result += pad_octal ? "\\074" : "\\74"; break; - case '=': result += pad_octal ? "\\075" : "\\75"; break; - case '>': result += pad_octal ? "\\076" : "\\76"; break; + case '\'': *out += "\\x27"; break; + case '"': *out += "\\x22"; break; + case '<': *out += "\\x3c"; break; + case '=': *out += "\\x3d"; break; + case '>': *out += "\\x3e"; break; + case '&': *out += "\\x26"; break; + case '\b': *out += "\\b"; break; + case '\t': *out += "\\t"; break; + case '\n': *out += "\\n"; break; + case '\f': *out += "\\f"; break; + case '\r': *out += "\\r"; break; + case '\\': *out += "\\\\"; break; default: - // All other non-ASCII codepoints are escaped. - // Original codegen uses hex for >= 0x100 and octal for others. + // TODO(lukestebbing): Once we're supporting codepoints outside the BMP, + // use a single Unicode codepoint escape if the output language is + // ECMAScript 2015 or above. Otherwise, use a surrogate pair. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#String_literals if (codepoint >= 0x20 && codepoint <= 0x7e) { - result += static_cast<char>(codepoint); + *out += static_cast<char>(codepoint); + } else if (codepoint >= 0x100) { + *out += StringPrintf("\\u%04x", codepoint); } else { - if (codepoint >= 0x100) { - result += StringPrintf("\\u%04x", codepoint); - } else { - if (pad_octal || codepoint >= 0100) { - result += "\\"; - result += ('0' + ((codepoint >> 6) & 07)); - result += ('0' + ((codepoint >> 3) & 07)); - result += ('0' + ((codepoint >> 0) & 07)); - } else if (codepoint >= 010) { - result += "\\"; - result += ('0' + ((codepoint >> 3) & 07)); - result += ('0' + ((codepoint >> 0) & 07)); - } else { - result += "\\"; - result += ('0' + ((codepoint >> 0) & 07)); - } - } + *out += StringPrintf("\\x%02x", codepoint); } break; } } - return result; + return true; } string EscapeBase64(const string& in) { @@ -740,11 +775,17 @@ string JSFieldDefault(const FieldDescriptor* field) { return DoubleToString(field->default_value_double()); case FieldDescriptor::CPPTYPE_STRING: if (field->type() == FieldDescriptor::TYPE_STRING) { - return "\"" + EscapeJSString(field->default_value_string(), true) + - "\""; - } else { - return "\"" + EscapeBase64(field->default_value_string()) + - "\""; + string out; + bool is_valid = EscapeJSString(field->default_value_string(), &out); + if (!is_valid) { + // TODO(lukestebbing): Decide whether this should be a hard error. + GOOGLE_LOG(WARNING) << "The default value for field " << field->full_name() + << " was truncated since it contained invalid UTF-8 or" + " codepoints outside the basic multilingual plane."; + } + return "\"" + out + "\""; + } else { // Bytes + return "\"" + EscapeBase64(field->default_value_string()) + "\""; } case FieldDescriptor::CPPTYPE_MESSAGE: return "null"; @@ -801,8 +842,27 @@ string JSIntegerTypeName(const FieldDescriptor* field) { return "number"; } +string JSStringTypeName(const GeneratorOptions& options, + const FieldDescriptor* field, + BytesMode bytes_mode) { + if (field->type() == FieldDescriptor::TYPE_BYTES) { + switch (bytes_mode) { + case BYTES_DEFAULT: + return "(string|Uint8Array)"; + case BYTES_B64: + return "string"; + case BYTES_U8: + return "Uint8Array"; + default: + assert(false); + } + } + return "string"; +} + string JSTypeName(const GeneratorOptions& options, - const FieldDescriptor* field) { + const FieldDescriptor* field, + BytesMode bytes_mode) { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_BOOL: return "boolean"; @@ -819,7 +879,7 @@ string JSTypeName(const GeneratorOptions& options, case FieldDescriptor::CPPTYPE_DOUBLE: return "number"; case FieldDescriptor::CPPTYPE_STRING: - return "string"; + return JSStringTypeName(options, field, bytes_mode); case FieldDescriptor::CPPTYPE_ENUM: return GetPath(options, field->enum_type()); case FieldDescriptor::CPPTYPE_MESSAGE: @@ -836,20 +896,26 @@ string JSFieldTypeAnnotation(const GeneratorOptions& options, bool force_optional, bool force_present, bool singular_if_not_packed, - bool always_singular) { + BytesMode bytes_mode = BYTES_DEFAULT) { bool is_primitive = (field->cpp_type() != FieldDescriptor::CPPTYPE_ENUM && - field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE); + field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE && + (field->type() != FieldDescriptor::TYPE_BYTES || + bytes_mode == BYTES_B64)); - string jstype = JSTypeName(options, field); + string jstype = JSTypeName(options, field, bytes_mode); if (field->is_repeated() && - !always_singular && (field->is_packed() || !singular_if_not_packed)) { - if (!is_primitive) { - jstype = "!" + jstype; + if (field->type() == FieldDescriptor::TYPE_BYTES && + bytes_mode == BYTES_DEFAULT) { + jstype = "(Array<!Uint8Array>|Array<string>)"; + } else { + if (!is_primitive) { + jstype = "!" + jstype; + } + jstype = "Array.<" + jstype + ">"; } - jstype = "Array.<" + jstype + ">"; if (!force_optional) { jstype = "!" + jstype; } @@ -884,10 +950,6 @@ string JSBinaryReaderMethodType(const FieldDescriptor* field) { string JSBinaryReadWriteMethodName(const FieldDescriptor* field, bool is_writer) { string name = JSBinaryReaderMethodType(field); - if (is_writer && field->type() == FieldDescriptor::TYPE_BYTES) { - // Override for `bytes` fields: treat string as raw bytes, not base64. - name = "BytesRawString"; - } if (field->is_packed()) { name = "Packed" + name; } else if (is_writer && field->is_repeated()) { @@ -1044,7 +1106,7 @@ string FieldDefinition(const GeneratorOptions& options, field->number()); } -string FieldComments(const FieldDescriptor* field) { +string FieldComments(const FieldDescriptor* field, BytesMode bytes_mode) { string comments; if (field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL) { comments += @@ -1060,6 +1122,11 @@ string FieldComments(const FieldDescriptor* field) { " * replace the array itself, then you must call the setter to " "update it.\n"; } + if (field->type() == FieldDescriptor::TYPE_BYTES && bytes_mode == BYTES_U8) { + comments += + " * Note that Uint8Array is not supported on all browsers.\n" + " * @see http://caniuse.com/Uint8Array\n"; + } return comments; } @@ -1070,8 +1137,10 @@ bool ShouldGenerateExtension(const FieldDescriptor* field) { } bool HasExtensions(const Descriptor* desc) { - if (desc->extension_count() > 0) { - return true; + for (int i = 0; i < desc->extension_count(); i++) { + if (ShouldGenerateExtension(desc->extension(i))) { + return true; + } } for (int i = 0; i < desc->nested_type_count(); i++) { if (HasExtensions(desc->nested_type(i))) { @@ -1123,7 +1192,7 @@ string GetPivot(const Descriptor* desc) { } // Returns true for fields that represent "null" as distinct from the default -// value. See https://go/proto3#heading=h.kozewqqcqhuz for more information. +// value. See http://go/proto3#heading=h.kozewqqcqhuz for more information. bool HasFieldPresence(const FieldDescriptor* field) { return (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) || @@ -1132,7 +1201,7 @@ bool HasFieldPresence(const FieldDescriptor* field) { } // For proto3 fields without presence, returns a string representing the default -// value in JavaScript. See https://go/proto3#heading=h.kozewqqcqhuz for more +// value in JavaScript. See http://go/proto3#heading=h.kozewqqcqhuz for more // information. string Proto3PrimitiveFieldDefault(const FieldDescriptor* field) { switch (field->cpp_type()) { @@ -1151,16 +1220,153 @@ string Proto3PrimitiveFieldDefault(const FieldDescriptor* field) { case FieldDescriptor::CPPTYPE_BOOL: return "false"; - case FieldDescriptor::CPPTYPE_STRING: + case FieldDescriptor::CPPTYPE_STRING: // includes BYTES return "\"\""; default: - // BYTES and MESSAGE are handled separately. + // MESSAGE is handled separately. assert(false); return ""; } } +// We use this to implement the semantics that same file can be generated +// multiple times, but the last one wins. We never actually write the files, +// but we keep a set of which descriptors were the final one for a given +// filename. +class FileDeduplicator { + public: + explicit FileDeduplicator(const GeneratorOptions& options) + : error_on_conflict_(options.error_on_name_conflict) {} + + bool AddFile(const string& filename, const void* desc, string* error) { + if (descs_by_filename_.find(filename) != descs_by_filename_.end()) { + if (error_on_conflict_) { + *error = "Name conflict: file name " + filename + + " would be generated by two descriptors"; + return false; + } + allowed_descs_.erase(descs_by_filename_[filename]); + } + + descs_by_filename_[filename] = desc; + allowed_descs_.insert(desc); + return true; + } + + void GetAllowedSet(set<const void*>* allowed_set) { + *allowed_set = allowed_descs_; + } + + private: + bool error_on_conflict_; + map<string, const void*> descs_by_filename_; + set<const void*> allowed_descs_; +}; + +void DepthFirstSearch(const FileDescriptor* file, + vector<const FileDescriptor*>* list, + set<const FileDescriptor*>* seen) { + if (!seen->insert(file).second) { + return; + } + + // Add all dependencies. + for (int i = 0; i < file->dependency_count(); i++) { + DepthFirstSearch(file->dependency(i), list, seen); + } + + // Add this file. + list->push_back(file); +} + +// A functor for the predicate to remove_if() below. Returns true if a given +// FileDescriptor is not in the given set. +class NotInSet { + public: + explicit NotInSet(const set<const FileDescriptor*>& file_set) + : file_set_(file_set) {} + + bool operator()(const FileDescriptor* file) { + return file_set_.count(file) == 0; + } + + private: + const set<const FileDescriptor*>& file_set_; +}; + +// This function generates an ordering of the input FileDescriptors that matches +// the logic of the old code generator. The order is significant because two +// different input files can generate the same output file, and the last one +// needs to win. +void GenerateJspbFileOrder(const vector<const FileDescriptor*>& input, + vector<const FileDescriptor*>* ordered) { + // First generate an ordering of all reachable files (including dependencies) + // with depth-first search. This mimics the behavior of --include_imports, + // which is what the old codegen used. + ordered->clear(); + set<const FileDescriptor*> seen; + set<const FileDescriptor*> input_set; + for (int i = 0; i < input.size(); i++) { + DepthFirstSearch(input[i], ordered, &seen); + input_set.insert(input[i]); + } + + // Now remove the entries that are not actually in our input list. + ordered->erase( + std::remove_if(ordered->begin(), ordered->end(), NotInSet(input_set)), + ordered->end()); +} + +// If we're generating code in file-per-type mode, avoid overwriting files +// by choosing the last descriptor that writes each filename and permitting +// only those to generate code. + +bool GenerateJspbAllowedSet(const GeneratorOptions& options, + const vector<const FileDescriptor*>& files, + set<const void*>* allowed_set, + string* error) { + vector<const FileDescriptor*> files_ordered; + GenerateJspbFileOrder(files, &files_ordered); + + // Choose the last descriptor for each filename. + FileDeduplicator dedup(options); + for (int i = 0; i < files_ordered.size(); i++) { + for (int j = 0; j < files_ordered[i]->message_type_count(); j++) { + const Descriptor* desc = files_ordered[i]->message_type(j); + if (!dedup.AddFile(GetMessageFileName(options, desc), desc, error)) { + return false; + } + } + for (int j = 0; j < files_ordered[i]->enum_type_count(); j++) { + const EnumDescriptor* desc = files_ordered[i]->enum_type(j); + if (!dedup.AddFile(GetEnumFileName(options, desc), desc, error)) { + return false; + } + } + + // Pull out all free-floating extensions and generate files for those too. + bool has_extension = false; + + for (int j = 0; j < files_ordered[i]->extension_count(); j++) { + if (ShouldGenerateExtension(files_ordered[i]->extension(j))) { + has_extension = true; + } + } + + if (has_extension) { + if (!dedup.AddFile(GetExtensionFileName(options, files_ordered[i]), + files_ordered[i], error)) { + return false; + } + } + } + + dedup.GetAllowedSet(allowed_set); + + return true; +} + } // anonymous namespace void Generator::GenerateHeader(const GeneratorOptions& options, @@ -1251,10 +1457,10 @@ void Generator::GenerateProvides(const GeneratorOptions& options, } } -void Generator::GenerateRequires(const GeneratorOptions& options, - io::Printer* printer, - const Descriptor* desc, - std::set<string>* provided) const { +void Generator::GenerateRequiresForMessage(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc, + std::set<string>* provided) const { std::set<string> required; std::set<string> forwards; bool have_message = false; @@ -1266,55 +1472,50 @@ void Generator::GenerateRequires(const GeneratorOptions& options, /* require_extension = */ HasExtensions(desc)); } -void Generator::GenerateRequires(const GeneratorOptions& options, - io::Printer* printer, - const vector<const FileDescriptor*>& files, - std::set<string>* provided) const { - if (options.import_style == GeneratorOptions::IMPORT_BROWSER) { - return; - } else if (options.import_style == GeneratorOptions::IMPORT_CLOSURE) { - // For Closure imports we need to import every message type individually. - std::set<string> required; - std::set<string> forwards; - bool have_extensions = false; - bool have_message = false; +void Generator::GenerateRequiresForLibrary( + const GeneratorOptions& options, io::Printer* printer, + const vector<const FileDescriptor*>& files, + std::set<string>* provided) const { + GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::IMPORT_CLOSURE); + // For Closure imports we need to import every message type individually. + std::set<string> required; + std::set<string> forwards; + bool have_extensions = false; + bool have_message = false; - for (int i = 0; i < files.size(); i++) { - for (int j = 0; j < files[i]->message_type_count(); j++) { - FindRequiresForMessage(options, - files[i]->message_type(j), - &required, &forwards, &have_message); - } - if (!have_extensions && HasExtensions(files[i])) { - have_extensions = true; - } + for (int i = 0; i < files.size(); i++) { + for (int j = 0; j < files[i]->message_type_count(); j++) { + FindRequiresForMessage(options, + files[i]->message_type(j), + &required, &forwards, &have_message); + } + if (!have_extensions && HasExtensions(files[i])) { + have_extensions = true; + } - for (int j = 0; j < files[i]->extension_count(); j++) { - const FieldDescriptor* extension = files[i]->extension(j); - if (IgnoreField(extension)) { - continue; - } - if (extension->containing_type()->full_name() != - "google.protobuf.bridge.MessageSet") { - required.insert(GetPath(options, extension->containing_type())); - } - FindRequiresForField(options, extension, &required, &forwards); - have_extensions = true; + for (int j = 0; j < files[i]->extension_count(); j++) { + const FieldDescriptor* extension = files[i]->extension(j); + if (IgnoreField(extension)) { + continue; + } + if (extension->containing_type()->full_name() != + "google.protobuf.bridge.MessageSet") { + required.insert(GetPath(options, extension->containing_type())); } + FindRequiresForField(options, extension, &required, &forwards); + have_extensions = true; } - - GenerateRequiresImpl(options, printer, &required, &forwards, provided, - /* require_jspb = */ have_message, - /* require_extension = */ have_extensions); - } else if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) { - // CommonJS imports are based on files } + + GenerateRequiresImpl(options, printer, &required, &forwards, provided, + /* require_jspb = */ have_message, + /* require_extension = */ have_extensions); } -void Generator::GenerateRequires(const GeneratorOptions& options, - io::Printer* printer, - const vector<const FieldDescriptor*>& fields, - std::set<string>* provided) const { +void Generator::GenerateRequiresForExtensions( + const GeneratorOptions& options, io::Printer* printer, + const vector<const FieldDescriptor*>& fields, + std::set<string>* provided) const { std::set<string> required; std::set<string> forwards; for (int i = 0; i < fields.size(); i++) { @@ -1741,17 +1942,34 @@ void Generator::GenerateClassFieldToObject(const GeneratorOptions& options, } } else { // Simple field (singular or repeated). - if (!HasFieldPresence(field) && !field->is_repeated()) { + if ((!HasFieldPresence(field) && !field->is_repeated()) || + field->type() == FieldDescriptor::TYPE_BYTES) { // Delegate to the generated get<field>() method in order not to duplicate - // the proto3-field-default-value logic here. + // the proto3-field-default-value or byte-coercion logic here. printer->Print("msg.get$getter$()", - "getter", JSGetterName(field)); + "getter", JSGetterName(field, BYTES_B64)); } else { if (field->has_default_value()) { - printer->Print("jspb.Message.getField(msg, $index$) != null ? " - "jspb.Message.getField(msg, $index$) : $defaultValue$", + printer->Print("jspb.Message.getField(msg, $index$) == null ? " + "$defaultValue$ : ", "index", JSFieldIndex(field), "defaultValue", JSFieldDefault(field)); + } + if (field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT || + field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE) { + if (field->is_repeated()) { + printer->Print("jspb.Message.getRepeatedFloatingPointField(" + "msg, $index$)", + "index", JSFieldIndex(field)); + } else if (field->is_optional() && !field->has_default_value()) { + printer->Print("jspb.Message.getOptionalFloatingPointField(" + "msg, $index$)", + "index", JSFieldIndex(field)); + } else { + // Convert "NaN" to NaN. + printer->Print("+jspb.Message.getField(msg, $index$)", + "index", JSFieldIndex(field)); + } } else { printer->Print("jspb.Message.getField(msg, $index$)", "index", JSFieldIndex(field)); @@ -1860,6 +2078,40 @@ void Generator::GenerateClassFields(const GeneratorOptions& options, } } +void GenerateBytesWrapper(const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field, + BytesMode bytes_mode) { + string type = + JSFieldTypeAnnotation(options, field, + /* force_optional = */ false, + /* force_present = */ !HasFieldPresence(field), + /* singular_if_not_packed = */ false, + bytes_mode); + printer->Print( + "/**\n" + " * $fielddef$\n" + "$comment$" + " * This is a type-conversion wrapper around `get$defname$()`\n" + " * @return {$type$}\n" + " */\n" + "$class$.prototype.get$name$ = function() {\n" + " return /** @type {$type$} */ (jspb.Message.bytes$list$As$suffix$(\n" + " this.get$defname$()));\n" + "};\n" + "\n" + "\n", + "fielddef", FieldDefinition(options, field), + "comment", FieldComments(field, bytes_mode), + "type", type, + "class", GetPath(options, field->containing_type()), + "name", JSGetterName(field, bytes_mode), + "list", field->is_repeated() ? "List" : "", + "suffix", JSByteGetterSuffix(bytes_mode), + "defname", JSGetterName(field, BYTES_DEFAULT)); +} + + void Generator::GenerateClassField(const GeneratorOptions& options, io::Printer* printer, const FieldDescriptor* field) const { @@ -1871,12 +2123,11 @@ void Generator::GenerateClassField(const GeneratorOptions& options, " * @return {$type$}\n" " */\n", "fielddef", FieldDefinition(options, field), - "comment", FieldComments(field), + "comment", FieldComments(field, BYTES_DEFAULT), "type", JSFieldTypeAnnotation(options, field, /* force_optional = */ false, /* force_present = */ false, - /* singular_if_not_packed = */ false, - /* always_singular = */ false)); + /* singular_if_not_packed = */ false)); printer->Print( "$class$.prototype.get$name$ = function() {\n" " return /** @type{$type$} */ (\n" @@ -1890,8 +2141,7 @@ void Generator::GenerateClassField(const GeneratorOptions& options, "type", JSFieldTypeAnnotation(options, field, /* force_optional = */ false, /* force_present = */ false, - /* singular_if_not_packed = */ false, - /* always_singular = */ false), + /* singular_if_not_packed = */ false), "rpt", (field->is_repeated() ? "Repeated" : ""), "index", JSFieldIndex(field), "wrapperclass", SubmessageTypeRef(options, field), @@ -1905,8 +2155,7 @@ void Generator::GenerateClassField(const GeneratorOptions& options, JSFieldTypeAnnotation(options, field, /* force_optional = */ true, /* force_present = */ false, - /* singular_if_not_packed = */ false, - /* always_singular = */ false), + /* singular_if_not_packed = */ false), "returndoc", JSReturnDoc(options, field), "class", GetPath(options, field->containing_type()), "name", JSGetterName(field), @@ -1935,15 +2184,29 @@ void Generator::GenerateClassField(const GeneratorOptions& options, "returnvalue", JSReturnClause(field)); } else { - string typed_annotation; + bool untyped = + false; // Simple (primitive) field, either singular or repeated. - { - typed_annotation = JSFieldTypeAnnotation(options, field, + + // TODO(b/26173701): Always use BYTES_DEFAULT for the getter return type; + // at this point we "lie" to non-binary users and tell the the return + // type is always base64 string, pending a LSC to migrate to typed getters. + BytesMode bytes_mode = + field->type() == FieldDescriptor::TYPE_BYTES && !options.binary ? + BYTES_B64 : BYTES_DEFAULT; + string typed_annotation = + JSFieldTypeAnnotation(options, field, /* force_optional = */ false, /* force_present = */ !HasFieldPresence(field), /* singular_if_not_packed = */ false, - /* always_singular = */ false), + /* bytes_mode = */ bytes_mode); + if (untyped) { + printer->Print( + "/**\n" + " * @return {?} Raw field, untyped.\n" + " */\n"); + } else { printer->Print( "/**\n" " * $fielddef$\n" @@ -1951,7 +2214,7 @@ void Generator::GenerateClassField(const GeneratorOptions& options, " * @return {$type$}\n" " */\n", "fielddef", FieldDefinition(options, field), - "comment", FieldComments(field), + "comment", FieldComments(field, bytes_mode), "type", typed_annotation); } @@ -1960,7 +2223,10 @@ void Generator::GenerateClassField(const GeneratorOptions& options, "class", GetPath(options, field->containing_type()), "name", JSGetterName(field)); - { + if (untyped) { + printer->Print( + " return "); + } else { printer->Print( " return /** @type {$type$} */ (", "type", typed_annotation); @@ -1975,17 +2241,39 @@ void Generator::GenerateClassField(const GeneratorOptions& options, "default", Proto3PrimitiveFieldDefault(field)); } else { if (field->has_default_value()) { - printer->Print("jspb.Message.getField(this, $index$) != null ? " - "jspb.Message.getField(this, $index$) : $defaultValue$", + printer->Print("jspb.Message.getField(this, $index$) == null ? " + "$defaultValue$ : ", "index", JSFieldIndex(field), "defaultValue", JSFieldDefault(field)); + } + if (field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT || + field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE) { + if (field->is_repeated()) { + printer->Print("jspb.Message.getRepeatedFloatingPointField(" + "this, $index$)", + "index", JSFieldIndex(field)); + } else if (field->is_optional() && !field->has_default_value()) { + printer->Print("jspb.Message.getOptionalFloatingPointField(" + "this, $index$)", + "index", JSFieldIndex(field)); + } else { + // Convert "NaN" to NaN. + printer->Print("+jspb.Message.getField(this, $index$)", + "index", JSFieldIndex(field)); + } } else { printer->Print("jspb.Message.getField(this, $index$)", "index", JSFieldIndex(field)); } } - { + if (untyped) { + printer->Print( + ";\n" + "};\n" + "\n" + "\n"); + } else { printer->Print( ");\n" "};\n" @@ -1993,18 +2281,27 @@ void Generator::GenerateClassField(const GeneratorOptions& options, "\n"); } - { + if (field->type() == FieldDescriptor::TYPE_BYTES && !untyped) { + GenerateBytesWrapper(options, printer, field, BYTES_B64); + GenerateBytesWrapper(options, printer, field, BYTES_U8); + } + + if (untyped) { + printer->Print( + "/**\n" + " * @param {*} value $returndoc$\n" + " */\n", + "returndoc", JSReturnDoc(options, field)); + } else { printer->Print( "/** @param {$optionaltype$} value $returndoc$ */\n", "optionaltype", JSFieldTypeAnnotation(options, field, /* force_optional = */ true, /* force_present = */ !HasFieldPresence(field), - /* singular_if_not_packed = */ false, - /* always_singular = */ false), + /* singular_if_not_packed = */ false), "returndoc", JSReturnDoc(options, field)); } - printer->Print( "$class$.prototype.set$name$ = function(value) {\n" " jspb.Message.set$oneoftag$Field(this, $index$", @@ -2017,14 +2314,22 @@ void Generator::GenerateClassField(const GeneratorOptions& options, "};\n" "\n" "\n", - "type", "", - "typeclose", "", + "type", + untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "", + "typeclose", untyped ? ")" : "", "oneofgroup", (field->containing_oneof() ? (", " + JSOneofArray(options, field)) : ""), "returnvalue", JSReturnClause(field), "rptvalueinit", (field->is_repeated() ? " || []" : "")); + if (untyped) { + printer->Print( + "/**\n" + " * Clears the value. $returndoc$\n" + " */\n", + "returndoc", JSReturnDoc(options, field)); + } if (HasFieldPresence(field)) { printer->Print( @@ -2162,16 +2467,18 @@ void Generator::GenerateClassDeserializeBinaryField( " var value = /** @type {$fieldtype$} */ (reader.$reader$());\n", "fieldtype", JSFieldTypeAnnotation(options, field, false, true, /* singular_if_not_packed = */ true, - /* always_singular = */ false), + BYTES_U8), "reader", JSBinaryReaderMethodName(field)); } if (field->is_repeated() && !field->is_packed()) { // Repeated fields receive a |value| one at at a time; append to array - // returned by get$name$(). - printer->Print( - " msg.get$name$().push(value);\n", - "name", JSGetterName(field)); + // returned by get$name$(). Annoyingly, we have to call 'set' after + // changing the array. + printer->Print(" msg.get$name$().push(value);\n", "name", + JSGetterName(field)); + printer->Print(" msg.set$name$(msg.get$name$());\n", "name", + JSGetterName(field)); } else { // Singular fields, and packed repeated fields, receive a |value| either as // the field's value or as the array of all the field's values; set this as @@ -2244,7 +2551,7 @@ void Generator::GenerateClassSerializeBinaryField( const FieldDescriptor* field) const { printer->Print( " f = this.get$name$();\n", - "name", JSGetterName(field)); + "name", JSGetterName(field, BYTES_U8)); if (field->is_repeated()) { printer->Print( @@ -2294,7 +2601,6 @@ void Generator::GenerateClassSerializeBinaryField( " $index$,\n" " f", "writer", JSBinaryWriterMethodName(field), - "name", JSGetterName(field), "index", SimpleItoa(field->number())); if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { @@ -2356,8 +2662,7 @@ void Generator::GenerateExtension(const GeneratorOptions& options, options, field, /* force_optional = */ false, /* force_present = */ true, - /* singular_if_not_packed = */ false, - /* always_singular = */ false)); + /* singular_if_not_packed = */ false)); printer->Print( " $index$,\n" " {$name$: 0},\n" @@ -2528,7 +2833,7 @@ void Generator::GenerateFile(const GeneratorOptions& options, printer->Print("var global = Function('return this')();\n\n"); for (int i = 0; i < file->dependency_count(); i++) { - const std::string& name = file->dependency(i)->name(); + const string& name = file->dependency(i)->name(); printer->Print( "var $alias$ = require('$file$');\n", "alias", ModuleAlias(name), @@ -2543,7 +2848,7 @@ void Generator::GenerateFile(const GeneratorOptions& options, // // // Later generated code expects foo.bar = {} to exist: // foo.bar.Baz = function() { /* ... */ } - std::set<std::string> provided; + set<string> provided; // Cover the case where this file declares extensions but no messages. // This will ensure that the file-level object will be declared to hold @@ -2615,7 +2920,7 @@ bool Generator::GenerateAll(const vector<const FileDescriptor*>& files, FindProvidesForFields(options, &printer, extensions, &provided); GenerateProvides(options, &printer, &provided); GenerateTestOnly(options, &printer); - GenerateRequires(options, &printer, files, &provided); + GenerateRequiresForLibrary(options, &printer, files, &provided); GenerateFilesInDepOrder(options, &printer, files); @@ -2629,66 +2934,20 @@ bool Generator::GenerateAll(const vector<const FileDescriptor*>& files, return false; } } else if (options.import_style == GeneratorOptions::IMPORT_CLOSURE) { - // Collect all types, and print each type to a separate file. Pull out - // free-floating extensions while we make this pass. - map< string, vector<const FieldDescriptor*> > extensions_by_namespace; - - // If we're generating code in file-per-type mode, avoid overwriting files - // by choosing the last descriptor that writes each filename and permitting - // only those to generate code. - - // Current descriptor that will generate each filename, indexed by filename. - map<string, const void*> desc_by_filename; - // Set of descriptors allowed to generate files. - set<const void*> allowed_descs; - - for (int i = 0; i < files.size(); i++) { - // Collect all (descriptor, filename) pairs. - map<const void*, string> descs_in_file; - for (int j = 0; j < files[i]->message_type_count(); j++) { - const Descriptor* desc = files[i]->message_type(j); - string filename = - options.output_dir + "/" + ToFileName(desc->name()) + ".js"; - descs_in_file[desc] = filename; - } - for (int j = 0; j < files[i]->enum_type_count(); j++) { - const EnumDescriptor* desc = files[i]->enum_type(j); - string filename = - options.output_dir + "/" + ToFileName(desc->name()) + ".js"; - descs_in_file[desc] = filename; - } - - // For each (descriptor, filename) pair, update the - // descriptors-by-filename map, and if a previous descriptor was already - // writing the filename, remove it from the allowed-descriptors set. - map<const void*, string>::iterator it; - for (it = descs_in_file.begin(); it != descs_in_file.end(); ++it) { - const void* desc = it->first; - const string& filename = it->second; - if (desc_by_filename.find(filename) != desc_by_filename.end()) { - if (options.error_on_name_conflict) { - *error = "Name conflict: file name " + filename + - " would be generated by two descriptors"; - return false; - } - allowed_descs.erase(desc_by_filename[filename]); - } - desc_by_filename[filename] = desc; - allowed_descs.insert(desc); - } + set<const void*> allowed_set; + if (!GenerateJspbAllowedSet(options, files, &allowed_set, error)) { + return false; } - // Generate code. for (int i = 0; i < files.size(); i++) { const FileDescriptor* file = files[i]; for (int j = 0; j < file->message_type_count(); j++) { const Descriptor* desc = file->message_type(j); - if (allowed_descs.find(desc) == allowed_descs.end()) { + if (allowed_set.count(desc) == 0) { continue; } - string filename = options.output_dir + "/" + - ToFileName(desc->name()) + ".js"; + string filename = GetMessageFileName(options, desc); google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( context->Open(filename)); GOOGLE_CHECK(output.get()); @@ -2700,7 +2959,7 @@ bool Generator::GenerateAll(const vector<const FileDescriptor*>& files, FindProvidesForMessage(options, &printer, desc, &provided); GenerateProvides(options, &printer, &provided); GenerateTestOnly(options, &printer); - GenerateRequires(options, &printer, desc, &provided); + GenerateRequiresForMessage(options, &printer, desc, &provided); GenerateClass(options, &printer, desc); @@ -2710,13 +2969,11 @@ bool Generator::GenerateAll(const vector<const FileDescriptor*>& files, } for (int j = 0; j < file->enum_type_count(); j++) { const EnumDescriptor* enumdesc = file->enum_type(j); - if (allowed_descs.find(enumdesc) == allowed_descs.end()) { + if (allowed_set.count(enumdesc) == 0) { continue; } - string filename = options.output_dir + "/" + - ToFileName(enumdesc->name()) + ".js"; - + string filename = GetEnumFileName(options, enumdesc); google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( context->Open(filename)); GOOGLE_CHECK(output.get()); @@ -2735,38 +2992,36 @@ bool Generator::GenerateAll(const vector<const FileDescriptor*>& files, return false; } } - // Pull out all free-floating extensions and generate files for those too. - for (int j = 0; j < file->extension_count(); j++) { - const FieldDescriptor* extension = file->extension(j); - extensions_by_namespace[GetPath(options, files[i])] - .push_back(extension); - } - } + // File-level extensions (message-level extensions are generated under + // the enclosing message). + if (allowed_set.count(file) == 1) { + string filename = GetExtensionFileName(options, file); - // Generate extensions in separate files. - map< string, vector<const FieldDescriptor*> >::iterator it; - for (it = extensions_by_namespace.begin(); - it != extensions_by_namespace.end(); - ++it) { - string filename = options.output_dir + "/" + - ToFileName(it->first) + ".js"; + google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( + context->Open(filename)); + GOOGLE_CHECK(output.get()); + io::Printer printer(output.get(), '$'); - google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( - context->Open(filename)); - GOOGLE_CHECK(output.get()); - io::Printer printer(output.get(), '$'); + GenerateHeader(options, &printer); + + std::set<string> provided; + vector<const FieldDescriptor*> fields; - GenerateHeader(options, &printer); + for (int j = 0; j < files[i]->extension_count(); j++) { + if (ShouldGenerateExtension(files[i]->extension(j))) { + fields.push_back(files[i]->extension(j)); + } + } - std::set<string> provided; - FindProvidesForFields(options, &printer, it->second, &provided); - GenerateProvides(options, &printer, &provided); - GenerateTestOnly(options, &printer); - GenerateRequires(options, &printer, it->second, &provided); + FindProvidesForFields(options, &printer, fields, &provided); + GenerateProvides(options, &printer, &provided); + GenerateTestOnly(options, &printer); + GenerateRequiresForExtensions(options, &printer, fields, &provided); - for (int j = 0; j < it->second.size(); j++) { - if (ShouldGenerateExtension(it->second[j])) { - GenerateExtension(options, &printer, it->second[j]); + for (int j = 0; j < files[i]->extension_count(); j++) { + if (ShouldGenerateExtension(files[i]->extension(j))) { + GenerateExtension(options, &printer, files[i]->extension(j)); + } } } } @@ -2777,7 +3032,7 @@ bool Generator::GenerateAll(const vector<const FileDescriptor*>& files, const google::protobuf::FileDescriptor* file = files[i]; string filename = options.output_dir + "/" + GetJSFilename(file->name()); - google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( + scoped_ptr<io::ZeroCopyOutputStream> output( context->Open(filename)); GOOGLE_CHECK(output.get()); io::Printer printer(output.get(), '$'); diff --git a/src/google/protobuf/compiler/js/js_generator.h b/src/google/protobuf/compiler/js/js_generator.h index db9178d3..6fd7ca50 100755 --- a/src/google/protobuf/compiler/js/js_generator.h +++ b/src/google/protobuf/compiler/js/js_generator.h @@ -146,19 +146,19 @@ class LIBPROTOC_EXPORT Generator : public CodeGenerator { io::Printer* printer) const; // Generate goog.requires() calls. - void GenerateRequires(const GeneratorOptions& options, - io::Printer* printer, - const vector<const FileDescriptor*>& file, - std::set<string>* provided) const; - void GenerateRequires(const GeneratorOptions& options, + void GenerateRequiresForLibrary(const GeneratorOptions& options, + io::Printer* printer, + const vector<const FileDescriptor*>& files, + std::set<string>* provided) const; + void GenerateRequiresForMessage(const GeneratorOptions& options, io::Printer* printer, const Descriptor* desc, std::set<string>* provided) const; // For extension fields at file scope. - void GenerateRequires(const GeneratorOptions& options, - io::Printer* printer, - const vector<const FieldDescriptor*>& fields, - std::set<string>* provided) const; + void GenerateRequiresForExtensions( + const GeneratorOptions& options, io::Printer* printer, + const vector<const FieldDescriptor*>& fields, + std::set<string>* provided) const; void GenerateRequiresImpl(const GeneratorOptions& options, io::Printer* printer, std::set<string>* required, diff --git a/src/google/protobuf/compiler/mock_code_generator.cc b/src/google/protobuf/compiler/mock_code_generator.cc index 121d917b..82bb3427 100644 --- a/src/google/protobuf/compiler/mock_code_generator.cc +++ b/src/google/protobuf/compiler/mock_code_generator.cc @@ -32,20 +32,26 @@ #include <google/protobuf/compiler/mock_code_generator.h> +#include <stdlib.h> +#include <iostream> #include <memory> #ifndef _SHARED_PTR_H #include <google/protobuf/stubs/shared_ptr.h> #endif +#include <vector> +#include <google/protobuf/stubs/logging.h> +#include <google/protobuf/stubs/common.h> #include <google/protobuf/testing/file.h> +#include <google/protobuf/testing/file.h> +#include <google/protobuf/testing/file.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/zero_copy_stream.h> #include <google/protobuf/descriptor.pb.h> #include <google/protobuf/descriptor.h> -#include <google/protobuf/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.h> namespace google { namespace protobuf { diff --git a/src/google/protobuf/compiler/mock_code_generator.h b/src/google/protobuf/compiler/mock_code_generator.h index 8c8348d8..e1665f88 100644 --- a/src/google/protobuf/compiler/mock_code_generator.h +++ b/src/google/protobuf/compiler/mock_code_generator.h @@ -34,10 +34,15 @@ #define GOOGLE_PROTOBUF_COMPILER_MOCK_CODE_GENERATOR_H__ #include <string> + #include <google/protobuf/compiler/code_generator.h> namespace google { namespace protobuf { +class FileDescriptor; +} // namespace protobuf + +namespace protobuf { namespace compiler { // A mock CodeGenerator, used by command_line_interface_unittest. This is in diff --git a/src/google/protobuf/compiler/plugin.cc b/src/google/protobuf/compiler/plugin.cc index 2bebf1f3..2ff50f61 100644 --- a/src/google/protobuf/compiler/plugin.cc +++ b/src/google/protobuf/compiler/plugin.cc @@ -93,31 +93,15 @@ class GeneratorResponseContext : public GeneratorContext { const vector<const FileDescriptor*>& parsed_files_; }; -int PluginMain(int argc, char* argv[], const CodeGenerator* generator) { - - if (argc > 1) { - std::cerr << argv[0] << ": Unknown option: " << argv[1] << std::endl; - return 1; - } - -#ifdef _WIN32 - _setmode(STDIN_FILENO, _O_BINARY); - _setmode(STDOUT_FILENO, _O_BINARY); -#endif - - CodeGeneratorRequest request; - if (!request.ParseFromFileDescriptor(STDIN_FILENO)) { - std::cerr << argv[0] << ": protoc sent unparseable request to plugin." - << std::endl; - return 1; - } - +bool GenerateCode(const CodeGeneratorRequest& request, + const CodeGenerator& generator, CodeGeneratorResponse* response, + string* error_msg) { DescriptorPool pool; for (int i = 0; i < request.proto_file_size(); i++) { const FileDescriptor* file = pool.BuildFile(request.proto_file(i)); if (file == NULL) { // BuildFile() already wrote an error message. - return 1; + return false; } } @@ -125,19 +109,18 @@ int PluginMain(int argc, char* argv[], const CodeGenerator* generator) { for (int i = 0; i < request.file_to_generate_size(); i++) { parsed_files.push_back(pool.FindFileByName(request.file_to_generate(i))); if (parsed_files.back() == NULL) { - std::cerr << argv[0] << ": protoc asked plugin to generate a file but " - "did not provide a descriptor for the file: " - << request.file_to_generate(i) << std::endl; - return 1; + *error_msg = "protoc asked plugin to generate a file but " + "did not provide a descriptor for the file: " + + request.file_to_generate(i); + return false; } } - CodeGeneratorResponse response; - GeneratorResponseContext context(&response, parsed_files); + GeneratorResponseContext context(response, parsed_files); - if (generator->HasGenerateAll()) { + if (generator.HasGenerateAll()) { string error; - bool succeeded = generator->GenerateAll( + bool succeeded = generator.GenerateAll( parsed_files, request.parameter(), &context, &error); if (!succeeded && error.empty()) { @@ -145,14 +128,14 @@ int PluginMain(int argc, char* argv[], const CodeGenerator* generator) { "description."; } if (!error.empty()) { - response.set_error(error); + response->set_error(error); } } else { for (int i = 0; i < parsed_files.size(); i++) { const FileDescriptor* file = parsed_files[i]; string error; - bool succeeded = generator->Generate( + bool succeeded = generator.Generate( file, request.parameter(), &context, &error); if (!succeeded && error.empty()) { @@ -160,14 +143,46 @@ int PluginMain(int argc, char* argv[], const CodeGenerator* generator) { "description."; } if (!error.empty()) { - response.set_error(file->name() + ": " + error); + response->set_error(file->name() + ": " + error); break; } } } - if (!response.SerializeToFileDescriptor(STDOUT_FILENO)) { - std::cerr << argv[0] << ": Error writing to stdout." << std::endl; + return true; +} + +int PluginMain(int argc, char* argv[], const CodeGenerator* generator) { + + if (argc > 1) { + std::cerr << argv[0] << ": Unknown option: " << argv[1] << std::endl; + return 1; + } + +#ifdef _WIN32 + _setmode(STDIN_FILENO, _O_BINARY); + _setmode(STDOUT_FILENO, _O_BINARY); +#endif + + CodeGeneratorRequest request; + if (!request.ParseFromFileDescriptor(STDIN_FILENO)) { + std::cerr << argv[0] << ": protoc sent unparseable request to plugin." + << std::endl; + return 1; + } + + string error_msg; + CodeGeneratorResponse response; + + if (GenerateCode(request, *generator, &response, &error_msg)) { + if (!response.SerializeToFileDescriptor(STDOUT_FILENO)) { + std::cerr << argv[0] << ": Error writing to stdout." << std::endl; + return 1; + } + } else { + if (!error_msg.empty()) { + std::cerr << argv[0] << ": " << error_msg << std::endl; + } return 1; } diff --git a/src/google/protobuf/compiler/plugin.h b/src/google/protobuf/compiler/plugin.h index 679f9bdb..d2793a9f 100644 --- a/src/google/protobuf/compiler/plugin.h +++ b/src/google/protobuf/compiler/plugin.h @@ -40,6 +40,13 @@ // } // You must link your plugin against libprotobuf and libprotoc. // +// The core part of PluginMain is to invoke the given CodeGenerator on a +// CodeGeneratorRequest to generate a CodeGeneratorResponse. This part is +// abstracted out and made into function GenerateCode so that it can be reused, +// for example, to implement a variant of PluginMain that does some +// preprocessing on the input CodeGeneratorRequest before feeding the request +// to the given code generator. +// // To get protoc to use the plugin, do one of the following: // * Place the plugin binary somewhere in the PATH and give it the name // "protoc-gen-NAME" (replacing "NAME" with the name of your plugin). If you @@ -55,16 +62,27 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_PLUGIN_H__ #define GOOGLE_PROTOBUF_COMPILER_PLUGIN_H__ +#include <string> + #include <google/protobuf/stubs/common.h> namespace google { namespace protobuf { namespace compiler { class CodeGenerator; // code_generator.h +class CodeGeneratorRequest; +class CodeGeneratorResponse; // Implements main() for a protoc plugin exposing the given code generator. LIBPROTOC_EXPORT int PluginMain(int argc, char* argv[], const CodeGenerator* generator); +// Generates code using the given code generator. Returns true if the code +// generation is successful. If the code geneartion fails, error_msg may be +// populated to describe the failure cause. +bool GenerateCode(const CodeGeneratorRequest& request, + const CodeGenerator& generator, CodeGeneratorResponse* response, + string* error_msg); + } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/python/python_generator.cc b/src/google/protobuf/compiler/python/python_generator.cc index 4d500f90..0553dd0d 100644 --- a/src/google/protobuf/compiler/python/python_generator.cc +++ b/src/google/protobuf/compiler/python/python_generator.cc @@ -235,7 +235,7 @@ string StringifyDefaultValue(const FieldDescriptor& field) { // infinity * 0 = nan return "(1e10000 * 0)"; } else { - return SimpleDtoa(value); + return "float(" + SimpleDtoa(value) + ")"; } } case FieldDescriptor::CPPTYPE_FLOAT: { @@ -251,7 +251,7 @@ string StringifyDefaultValue(const FieldDescriptor& field) { // infinity - infinity = nan return "(1e10000 * 0)"; } else { - return SimpleFtoa(value); + return "float(" + SimpleFtoa(value) + ")"; } } case FieldDescriptor::CPPTYPE_BOOL: diff --git a/src/google/protobuf/compiler/subprocess.cc b/src/google/protobuf/compiler/subprocess.cc index a30ac305..6e258664 100644 --- a/src/google/protobuf/compiler/subprocess.cc +++ b/src/google/protobuf/compiler/subprocess.cc @@ -361,7 +361,7 @@ bool Subprocess::Communicate(const Message& input, Message* output, string output_data; int input_pos = 0; - int max_fd = max(child_stdin_, child_stdout_); + int max_fd = std::max(child_stdin_, child_stdout_); while (child_stdout_ != -1) { fd_set read_fds; |