aboutsummaryrefslogtreecommitdiffstats
path: root/src/google/protobuf/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/google/protobuf/compiler')
-rw-r--r--src/google/protobuf/compiler/code_generator.cc21
-rw-r--r--src/google/protobuf/compiler/code_generator.h29
-rw-r--r--src/google/protobuf/compiler/command_line_interface.cc512
-rw-r--r--src/google/protobuf/compiler/command_line_interface.h84
-rw-r--r--src/google/protobuf/compiler/command_line_interface_unittest.cc333
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc44
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_enum.cc60
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_enum.h10
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_enum_field.cc100
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_enum_field.h27
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_extension.cc10
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_extension.h9
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_field.cc59
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_field.h28
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_file.cc153
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_file.h9
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_generator.cc17
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_generator.h4
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_helpers.cc169
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_helpers.h64
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_message.cc1329
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_message.h13
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_message_field.cc127
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_message_field.h27
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_options.h58
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc42
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_primitive_field.cc87
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_primitive_field.h28
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_service.cc8
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_service.h6
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_string_field.cc294
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_string_field.h31
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto25
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_unittest.cc878
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_unittest.h51
-rw-r--r--src/google/protobuf/compiler/importer.cc48
-rw-r--r--src/google/protobuf/compiler/importer.h22
-rw-r--r--src/google/protobuf/compiler/importer_unittest.cc57
-rw-r--r--src/google/protobuf/compiler/java/java_context.cc195
-rw-r--r--src/google/protobuf/compiler/java/java_context.h95
-rw-r--r--src/google/protobuf/compiler/java/java_doc_comment.cc233
-rw-r--r--src/google/protobuf/compiler/java/java_doc_comment.h69
-rw-r--r--src/google/protobuf/compiler/java/java_doc_comment_unittest.cc67
-rw-r--r--src/google/protobuf/compiler/java/java_enum.cc161
-rw-r--r--src/google/protobuf/compiler/java/java_enum.h19
-rw-r--r--src/google/protobuf/compiler/java/java_enum_field.cc661
-rw-r--r--src/google/protobuf/compiler/java/java_enum_field.h83
-rw-r--r--src/google/protobuf/compiler/java/java_extension.cc250
-rw-r--r--src/google/protobuf/compiler/java/java_extension.h48
-rw-r--r--src/google/protobuf/compiler/java/java_field.cc199
-rw-r--r--src/google/protobuf/compiler/java/java_field.h90
-rw-r--r--src/google/protobuf/compiler/java/java_file.cc434
-rw-r--r--src/google/protobuf/compiler/java/java_file.h38
-rw-r--r--src/google/protobuf/compiler/java/java_generator.cc84
-rw-r--r--src/google/protobuf/compiler/java/java_generator.h4
-rw-r--r--src/google/protobuf/compiler/java/java_generator_factory.cc77
-rw-r--r--src/google/protobuf/compiler/java/java_generator_factory.h101
-rw-r--r--src/google/protobuf/compiler/java/java_helpers.cc472
-rw-r--r--src/google/protobuf/compiler/java/java_helpers.h205
-rw-r--r--src/google/protobuf/compiler/java/java_lazy_message_field.cc826
-rw-r--r--src/google/protobuf/compiler/java/java_lazy_message_field.h121
-rw-r--r--src/google/protobuf/compiler/java/java_message.cc1442
-rw-r--r--src/google/protobuf/compiler/java/java_message.h64
-rw-r--r--src/google/protobuf/compiler/java/java_message_field.cc1334
-rw-r--r--src/google/protobuf/compiler/java/java_message_field.h101
-rw-r--r--src/google/protobuf/compiler/java/java_name_resolver.cc266
-rw-r--r--src/google/protobuf/compiler/java/java_name_resolver.h124
-rw-r--r--src/google/protobuf/compiler/java/java_plugin_unittest.cc43
-rw-r--r--src/google/protobuf/compiler/java/java_primitive_field.cc804
-rw-r--r--src/google/protobuf/compiler/java/java_primitive_field.h85
-rw-r--r--src/google/protobuf/compiler/java/java_service.cc87
-rw-r--r--src/google/protobuf/compiler/java/java_service.h40
-rw-r--r--src/google/protobuf/compiler/java/java_shared_code_generator.cc201
-rw-r--r--src/google/protobuf/compiler/java/java_shared_code_generator.h90
-rw-r--r--src/google/protobuf/compiler/java/java_string_field.cc1056
-rw-r--r--src/google/protobuf/compiler/java/java_string_field.h160
-rw-r--r--src/google/protobuf/compiler/javamicro/javamicro_file.h4
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_enum.cc40
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_enum_field.cc39
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_enum_field.h1
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_field.h1
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_file.h4
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_generator.cc14
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_helpers.cc12
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_message.cc99
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_message.h2
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_message_field.cc21
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_message_field.h2
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_params.h38
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_primitive_field.cc8
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_primitive_field.h1
-rw-r--r--src/google/protobuf/compiler/main.cc4
-rw-r--r--src/google/protobuf/compiler/mock_code_generator.cc85
-rw-r--r--src/google/protobuf/compiler/mock_code_generator.h15
-rw-r--r--src/google/protobuf/compiler/package_info.h2
-rw-r--r--src/google/protobuf/compiler/parser.cc1158
-rw-r--r--src/google/protobuf/compiler/parser.h262
-rw-r--r--src/google/protobuf/compiler/parser_unittest.cc1372
-rw-r--r--src/google/protobuf/compiler/plugin.cc37
-rw-r--r--src/google/protobuf/compiler/plugin.h3
-rw-r--r--src/google/protobuf/compiler/plugin.pb.cc418
-rw-r--r--src/google/protobuf/compiler/plugin.pb.h482
-rw-r--r--src/google/protobuf/compiler/plugin.proto6
-rw-r--r--src/google/protobuf/compiler/python/python_generator.cc382
-rw-r--r--src/google/protobuf/compiler/python/python_generator.h19
-rw-r--r--src/google/protobuf/compiler/python/python_plugin_unittest.cc32
-rw-r--r--src/google/protobuf/compiler/subprocess.cc41
-rw-r--r--src/google/protobuf/compiler/subprocess.h9
-rw-r--r--src/google/protobuf/compiler/test_plugin.cc2
-rwxr-xr-xsrc/google/protobuf/compiler/zip_output_unittest.sh38
-rw-r--r--src/google/protobuf/compiler/zip_writer.cc36
-rw-r--r--src/google/protobuf/compiler/zip_writer.h36
112 files changed, 16963 insertions, 3064 deletions
diff --git a/src/google/protobuf/compiler/code_generator.cc b/src/google/protobuf/compiler/code_generator.cc
index 3413a36..0039b00 100644
--- a/src/google/protobuf/compiler/code_generator.cc
+++ b/src/google/protobuf/compiler/code_generator.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -42,19 +42,28 @@ namespace protobuf {
namespace compiler {
CodeGenerator::~CodeGenerator() {}
-OutputDirectory::~OutputDirectory() {}
+GeneratorContext::~GeneratorContext() {}
-io::ZeroCopyOutputStream* OutputDirectory::OpenForInsert(
+io::ZeroCopyOutputStream*
+GeneratorContext::OpenForAppend(const string& filename) {
+ return NULL;
+}
+
+io::ZeroCopyOutputStream* GeneratorContext::OpenForInsert(
const string& filename, const string& insertion_point) {
- GOOGLE_LOG(FATAL) << "This OutputDirectory does not support insertion.";
+ GOOGLE_LOG(FATAL) << "This GeneratorContext does not support insertion.";
return NULL; // make compiler happy
}
+void GeneratorContext::ListParsedFiles(
+ vector<const FileDescriptor*>* output) {
+ GOOGLE_LOG(FATAL) << "This GeneratorContext does not support ListParsedFiles";
+}
+
// Parses a set of comma-delimited name/value pairs.
void ParseGeneratorParameter(const string& text,
vector<pair<string, string> >* output) {
- vector<string> parts;
- SplitStringUsing(text, ",", &parts);
+ vector<string> parts = Split(text, ",", true);
for (int i = 0; i < parts.size(); i++) {
string::size_type equals_pos = parts[i].find_first_of('=');
diff --git a/src/google/protobuf/compiler/code_generator.h b/src/google/protobuf/compiler/code_generator.h
index ea094cd..321a8cc 100644
--- a/src/google/protobuf/compiler/code_generator.h
+++ b/src/google/protobuf/compiler/code_generator.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -53,7 +53,7 @@ namespace compiler {
// Defined in this file.
class CodeGenerator;
-class OutputDirectory;
+class GeneratorContext;
// The abstract interface to a class which generates code implementing a
// particular proto file in a particular language. A number of these may
@@ -76,7 +76,7 @@ class LIBPROTOC_EXPORT CodeGenerator {
// the problem (e.g. "invalid parameter") and returns false.
virtual bool Generate(const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* generator_context,
string* error) const = 0;
private:
@@ -85,11 +85,12 @@ class LIBPROTOC_EXPORT CodeGenerator {
// CodeGenerators generate one or more files in a given directory. This
// abstract interface represents the directory to which the CodeGenerator is
-// to write.
-class LIBPROTOC_EXPORT OutputDirectory {
+// to write and other information about the context in which the Generator
+// runs.
+class LIBPROTOC_EXPORT GeneratorContext {
public:
- inline OutputDirectory() {}
- virtual ~OutputDirectory();
+ inline GeneratorContext() {}
+ virtual ~GeneratorContext();
// Opens the given file, truncating it if it exists, and returns a
// ZeroCopyOutputStream that writes to the file. The caller takes ownership
@@ -103,6 +104,9 @@ class LIBPROTOC_EXPORT OutputDirectory {
// contain "." or ".." components.
virtual io::ZeroCopyOutputStream* Open(const string& filename) = 0;
+ // Similar to Open() but the output will be appended to the file if exists
+ virtual io::ZeroCopyOutputStream* OpenForAppend(const string& filename);
+
// Creates a ZeroCopyOutputStream which will insert code into the given file
// at the given insertion point. See plugin.proto (plugin.pb.h) for more
// information on insertion points. The default implementation
@@ -112,10 +116,19 @@ class LIBPROTOC_EXPORT OutputDirectory {
virtual io::ZeroCopyOutputStream* OpenForInsert(
const string& filename, const string& insertion_point);
+ // Returns a vector of FileDescriptors for all the files being compiled
+ // in this run. Useful for languages, such as Go, that treat files
+ // differently when compiled as a set rather than individually.
+ virtual void ListParsedFiles(vector<const FileDescriptor*>* output);
+
private:
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OutputDirectory);
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GeneratorContext);
};
+// The type GeneratorContext was once called OutputDirectory. This typedef
+// provides backward compatibility.
+typedef GeneratorContext OutputDirectory;
+
// Several code generators treat the parameter argument as holding a
// list of options separated by commas. This helper function parses
// a set of comma-delimited name/value pairs: e.g.,
diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc
index d3495ca..f9f27bd 100644
--- a/src/google/protobuf/compiler/command_line_interface.cc
+++ b/src/google/protobuf/compiler/command_line_interface.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -48,6 +48,11 @@
#include <iostream>
#include <ctype.h>
+#include <google/protobuf/stubs/hash.h>
+#include <memory>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringprintf.h>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/plugin.pb.h>
@@ -56,14 +61,13 @@
#include <google/protobuf/descriptor.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/printer.h>
-#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
-#include <google/protobuf/stubs/map-util.h>
-#include <google/protobuf/stubs/stl_util-inl.h>
-#include <google/protobuf/stubs/hash.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/stl_util.h>
namespace google {
@@ -144,7 +148,7 @@ void AddTrailingSlash(string* path) {
bool VerifyDirectoryExists(const string& path) {
if (path.empty()) return true;
- if (access(path.c_str(), W_OK) == -1) {
+ if (access(path.c_str(), F_OK) == -1) {
cerr << path << ": " << strerror(errno) << endl;
return false;
} else {
@@ -158,8 +162,7 @@ bool VerifyDirectoryExists(const string& path) {
// directories listed in |filename|.
bool TryCreateParentDirectory(const string& prefix, const string& filename) {
// Recursively create parent directories to the output file.
- vector<string> parts;
- SplitStringUsing(filename, "/", &parts);
+ vector<string> parts = Split(filename, "/", true);
string path_so_far = prefix;
for (int i = 0; i < parts.size() - 1; i++) {
path_so_far += parts[i];
@@ -182,14 +185,23 @@ bool TryCreateParentDirectory(const string& prefix, const string& filename) {
class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector,
public io::ErrorCollector {
public:
- ErrorPrinter(ErrorFormat format) : format_(format) {}
+ ErrorPrinter(ErrorFormat format, DiskSourceTree *tree = NULL)
+ : format_(format), tree_(tree) {}
~ErrorPrinter() {}
// implements MultiFileErrorCollector ------------------------------
void AddError(const string& filename, int line, int column,
const string& message) {
- cerr << filename;
+ // Print full path when running under MSVS
+ string dfile;
+ if (format_ == CommandLineInterface::ERROR_FORMAT_MSVS &&
+ tree_ != NULL &&
+ tree_->VirtualFileToDiskFile(filename, &dfile)) {
+ cerr << dfile;
+ } else {
+ cerr << filename;
+ }
// Users typically expect 1-based line/column numbers, so we add 1
// to each here.
@@ -215,16 +227,17 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector,
private:
const ErrorFormat format_;
+ DiskSourceTree *tree_;
};
// -------------------------------------------------------------------
-// An OutputDirectory implementation that buffers files in memory, then dumps
+// A GeneratorContext implementation that buffers files in memory, then dumps
// them all to disk on demand.
-class CommandLineInterface::MemoryOutputDirectory : public OutputDirectory {
+class CommandLineInterface::GeneratorContextImpl : public GeneratorContext {
public:
- MemoryOutputDirectory();
- ~MemoryOutputDirectory();
+ GeneratorContextImpl(const vector<const FileDescriptor*>& parsed_files);
+ ~GeneratorContextImpl();
// Write all files in the directory to disk at the given output location,
// which must end in a '/'.
@@ -238,10 +251,14 @@ class CommandLineInterface::MemoryOutputDirectory : public OutputDirectory {
// format, unless one has already been written.
void AddJarManifest();
- // implements OutputDirectory --------------------------------------
+ // implements GeneratorContext --------------------------------------
io::ZeroCopyOutputStream* Open(const string& filename);
+ io::ZeroCopyOutputStream* OpenForAppend(const string& filename);
io::ZeroCopyOutputStream* OpenForInsert(
const string& filename, const string& insertion_point);
+ void ListParsedFiles(vector<const FileDescriptor*>* output) {
+ *output = parsed_files_;
+ }
private:
friend class MemoryOutputStream;
@@ -249,14 +266,16 @@ class CommandLineInterface::MemoryOutputDirectory : public OutputDirectory {
// map instead of hash_map so that files are written in order (good when
// writing zips).
map<string, string*> files_;
+ const vector<const FileDescriptor*>& parsed_files_;
bool had_error_;
};
class CommandLineInterface::MemoryOutputStream
: public io::ZeroCopyOutputStream {
public:
- MemoryOutputStream(MemoryOutputDirectory* directory, const string& filename);
- MemoryOutputStream(MemoryOutputDirectory* directory, const string& filename,
+ MemoryOutputStream(GeneratorContextImpl* directory, const string& filename,
+ bool append_mode);
+ MemoryOutputStream(GeneratorContextImpl* directory, const string& filename,
const string& insertion_point);
virtual ~MemoryOutputStream();
@@ -267,27 +286,33 @@ class CommandLineInterface::MemoryOutputStream
private:
// Where to insert the string when it's done.
- MemoryOutputDirectory* directory_;
+ GeneratorContextImpl* directory_;
string filename_;
string insertion_point_;
// The string we're building.
string data_;
+ // Whether we should append the output stream to the existing file.
+ bool append_mode_;
+
// StringOutputStream writing to data_.
scoped_ptr<io::StringOutputStream> inner_;
};
// -------------------------------------------------------------------
-CommandLineInterface::MemoryOutputDirectory::MemoryOutputDirectory()
- : had_error_(false) {}
+CommandLineInterface::GeneratorContextImpl::GeneratorContextImpl(
+ const vector<const FileDescriptor*>& parsed_files)
+ : parsed_files_(parsed_files),
+ had_error_(false) {
+}
-CommandLineInterface::MemoryOutputDirectory::~MemoryOutputDirectory() {
+CommandLineInterface::GeneratorContextImpl::~GeneratorContextImpl() {
STLDeleteValues(&files_);
}
-bool CommandLineInterface::MemoryOutputDirectory::WriteAllToDisk(
+bool CommandLineInterface::GeneratorContextImpl::WriteAllToDisk(
const string& prefix) {
if (had_error_) {
return false;
@@ -362,7 +387,7 @@ bool CommandLineInterface::MemoryOutputDirectory::WriteAllToDisk(
return true;
}
-bool CommandLineInterface::MemoryOutputDirectory::WriteAllToZip(
+bool CommandLineInterface::GeneratorContextImpl::WriteAllToZip(
const string& filename) {
if (had_error_) {
return false;
@@ -403,7 +428,7 @@ bool CommandLineInterface::MemoryOutputDirectory::WriteAllToZip(
return true;
}
-void CommandLineInterface::MemoryOutputDirectory::AddJarManifest() {
+void CommandLineInterface::GeneratorContextImpl::AddJarManifest() {
string** map_slot = &files_["META-INF/MANIFEST.MF"];
if (*map_slot == NULL) {
*map_slot = new string(
@@ -413,13 +438,19 @@ void CommandLineInterface::MemoryOutputDirectory::AddJarManifest() {
}
}
-io::ZeroCopyOutputStream* CommandLineInterface::MemoryOutputDirectory::Open(
+io::ZeroCopyOutputStream* CommandLineInterface::GeneratorContextImpl::Open(
const string& filename) {
- return new MemoryOutputStream(this, filename);
+ return new MemoryOutputStream(this, filename, false);
}
io::ZeroCopyOutputStream*
-CommandLineInterface::MemoryOutputDirectory::OpenForInsert(
+CommandLineInterface::GeneratorContextImpl::OpenForAppend(
+ const string& filename) {
+ return new MemoryOutputStream(this, filename, true);
+}
+
+io::ZeroCopyOutputStream*
+CommandLineInterface::GeneratorContextImpl::OpenForInsert(
const string& filename, const string& insertion_point) {
return new MemoryOutputStream(this, filename, insertion_point);
}
@@ -427,14 +458,15 @@ CommandLineInterface::MemoryOutputDirectory::OpenForInsert(
// -------------------------------------------------------------------
CommandLineInterface::MemoryOutputStream::MemoryOutputStream(
- MemoryOutputDirectory* directory, const string& filename)
+ GeneratorContextImpl* directory, const string& filename, bool append_mode)
: directory_(directory),
filename_(filename),
+ append_mode_(append_mode),
inner_(new io::StringOutputStream(&data_)) {
}
CommandLineInterface::MemoryOutputStream::MemoryOutputStream(
- MemoryOutputDirectory* directory, const string& filename,
+ GeneratorContextImpl* directory, const string& filename,
const string& insertion_point)
: directory_(directory),
filename_(filename),
@@ -452,8 +484,12 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() {
if (insertion_point_.empty()) {
// This was just a regular Open().
if (*map_slot != NULL) {
- cerr << filename_ << ": Tried to write the same file twice." << endl;
- directory_->had_error_ = true;
+ if (append_mode_) {
+ (*map_slot)->append(data_);
+ } else {
+ cerr << filename_ << ": Tried to write the same file twice." << endl;
+ directory_->had_error_ = true;
+ }
return;
}
@@ -546,8 +582,10 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() {
CommandLineInterface::CommandLineInterface()
: mode_(MODE_COMPILE),
+ print_mode_(PRINT_NONE),
error_format_(ERROR_FORMAT_GCC),
imports_in_descriptor_set_(false),
+ source_info_in_descriptor_set_(false),
disallow_services_(false),
inputs_are_proto_path_relative_(false) {}
CommandLineInterface::~CommandLineInterface() {}
@@ -556,9 +594,23 @@ void CommandLineInterface::RegisterGenerator(const string& flag_name,
CodeGenerator* generator,
const string& help_text) {
GeneratorInfo info;
+ info.flag_name = flag_name;
+ info.generator = generator;
+ info.help_text = help_text;
+ generators_by_flag_name_[flag_name] = info;
+}
+
+void CommandLineInterface::RegisterGenerator(const string& flag_name,
+ const string& option_flag_name,
+ CodeGenerator* generator,
+ const string& help_text) {
+ GeneratorInfo info;
+ info.flag_name = flag_name;
+ info.option_flag_name = option_flag_name;
info.generator = generator;
info.help_text = help_text;
- generators_[flag_name] = info;
+ generators_by_flag_name_[flag_name] = info;
+ generators_by_option_name_[option_flag_name] = info;
}
void CommandLineInterface::AllowPlugins(const string& exe_name_prefix) {
@@ -567,7 +619,14 @@ void CommandLineInterface::AllowPlugins(const string& exe_name_prefix) {
int CommandLineInterface::Run(int argc, const char* const argv[]) {
Clear();
- if (!ParseArguments(argc, argv)) return 1;
+ switch (ParseArguments(argc, argv)) {
+ case PARSE_ARGUMENT_DONE_AND_EXIT:
+ return 0;
+ case PARSE_ARGUMENT_FAIL:
+ return 1;
+ case PARSE_ARGUMENT_DONE_AND_CONTINUE:
+ break;
+ }
// Set up the source tree.
DiskSourceTree source_tree;
@@ -583,7 +642,7 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
}
// Allocate the Importer.
- ErrorPrinter error_collector(error_format_);
+ ErrorPrinter error_collector(error_format_, &source_tree);
Importer importer(&source_tree, &error_collector);
vector<const FileDescriptor*> parsed_files;
@@ -591,7 +650,9 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
// Parse each file.
for (int i = 0; i < input_files_.size(); i++) {
// Import the file.
+ importer.AddUnusedImportTrackFile(input_files_[i]);
const FileDescriptor* parsed_file = importer.Import(input_files_[i]);
+ importer.ClearUnusedImportTrackFiles();
if (parsed_file == NULL) return 1;
parsed_files.push_back(parsed_file);
@@ -603,11 +664,11 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
}
}
- // We construct a separate OutputDirectory for each output location. Note
+ // We construct a separate GeneratorContext for each output location. Note
// that two code generators may output to the same location, in which case
- // they should share a single OutputDirectory (so that OpenForInsert() works).
- typedef hash_map<string, MemoryOutputDirectory*> OutputDirectoryMap;
- OutputDirectoryMap output_directories;
+ // they should share a single GeneratorContext so that OpenForInsert() works.
+ typedef hash_map<string, GeneratorContextImpl*> GeneratorContextMap;
+ GeneratorContextMap output_directories;
// Generate output.
if (mode_ == MODE_COMPILE) {
@@ -617,11 +678,11 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
!HasSuffixString(output_location, ".jar")) {
AddTrailingSlash(&output_location);
}
- MemoryOutputDirectory** map_slot = &output_directories[output_location];
+ GeneratorContextImpl** map_slot = &output_directories[output_location];
if (*map_slot == NULL) {
// First time we've seen this output location.
- *map_slot = new MemoryOutputDirectory;
+ *map_slot = new GeneratorContextImpl(parsed_files);
}
if (!GenerateOutput(parsed_files, output_directives_[i], *map_slot)) {
@@ -632,10 +693,10 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
}
// Write all output to disk.
- for (OutputDirectoryMap::iterator iter = output_directories.begin();
+ for (GeneratorContextMap::iterator iter = output_directories.begin();
iter != output_directories.end(); ++iter) {
const string& location = iter->first;
- MemoryOutputDirectory* directory = iter->second;
+ GeneratorContextImpl* directory = iter->second;
if (HasSuffixString(location, "/")) {
if (!directory->WriteAllToDisk(location)) {
STLDeleteValues(&output_directories);
@@ -680,6 +741,25 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
}
}
+ if (mode_ == MODE_PRINT) {
+ switch (print_mode_) {
+ case PRINT_FREE_FIELDS:
+ for (int i = 0; i < parsed_files.size(); ++i) {
+ const FileDescriptor* fd = parsed_files[i];
+ for (int j = 0; j < fd->message_type_count(); ++j) {
+ PrintFreeFieldNumbers(fd->message_type(j));
+ }
+ }
+ break;
+ case PRINT_NONE:
+ GOOGLE_LOG(ERROR) << "If the code reaches here, it usually means a bug of "
+ "flag parsing in the CommonadLineInterface.";
+ return 1;
+
+ // Do not add a default case.
+ }
+ }
+
return 0;
}
@@ -694,7 +774,9 @@ void CommandLineInterface::Clear() {
descriptor_set_name_.clear();
mode_ = MODE_COMPILE;
+ print_mode_ = PRINT_NONE;
imports_in_descriptor_set_ = false;
+ source_info_in_descriptor_set_ = false;
disallow_services_ = false;
}
@@ -737,7 +819,8 @@ bool CommandLineInterface::MakeInputsBeProtoPathRelative(
return true;
}
-bool CommandLineInterface::ParseArguments(int argc, const char* const argv[]) {
+CommandLineInterface::ParseArgumentStatus
+CommandLineInterface::ParseArguments(int argc, const char* const argv[]) {
executable_name_ = argv[0];
// Iterate through all arguments and parse them.
@@ -751,41 +834,50 @@ bool CommandLineInterface::ParseArguments(int argc, const char* const argv[]) {
if (name == "--decode") {
cerr << "To decode an unknown message, use --decode_raw." << endl;
}
- return false;
+ return PARSE_ARGUMENT_FAIL;
} else {
++i;
value = argv[i];
}
}
- if (!InterpretArgument(name, value)) return false;
+ ParseArgumentStatus status = InterpretArgument(name, value);
+ if (status != PARSE_ARGUMENT_DONE_AND_CONTINUE)
+ return status;
}
// If no --proto_path was given, use the current working directory.
if (proto_path_.empty()) {
- proto_path_.push_back(make_pair("", "."));
+ // Don't use make_pair as the old/default standard library on Solaris
+ // doesn't support it without explicit template parameters, which are
+ // incompatible with C++0x's make_pair.
+ proto_path_.push_back(pair<string, string>("", "."));
}
// Check some errror cases.
bool decoding_raw = (mode_ == MODE_DECODE) && codec_type_.empty();
if (decoding_raw && !input_files_.empty()) {
cerr << "When using --decode_raw, no input files should be given." << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
} else if (!decoding_raw && input_files_.empty()) {
cerr << "Missing input file." << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
if (mode_ == MODE_COMPILE && output_directives_.empty() &&
descriptor_set_name_.empty()) {
cerr << "Missing output directives." << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
if (imports_in_descriptor_set_ && descriptor_set_name_.empty()) {
cerr << "--include_imports only makes sense when combined with "
"--descriptor_set_out." << endl;
}
+ if (source_info_in_descriptor_set_ && descriptor_set_name_.empty()) {
+ cerr << "--include_source_info only makes sense when combined with "
+ "--descriptor_set_out." << endl;
+ }
- return true;
+ return PARSE_ARGUMENT_DONE_AND_CONTINUE;
}
bool CommandLineInterface::ParseArgument(const char* arg,
@@ -835,8 +927,10 @@ bool CommandLineInterface::ParseArgument(const char* arg,
if (*name == "-h" || *name == "--help" ||
*name == "--disallow_services" ||
*name == "--include_imports" ||
+ *name == "--include_source_info" ||
*name == "--version" ||
- *name == "--decode_raw") {
+ *name == "--decode_raw" ||
+ *name == "--print_free_field_numbers") {
// HACK: These are the only flags that don't take a value.
// They probably should not be hard-coded like this but for now it's
// not worth doing better.
@@ -847,8 +941,9 @@ bool CommandLineInterface::ParseArgument(const char* arg,
return true;
}
-bool CommandLineInterface::InterpretArgument(const string& name,
- const string& value) {
+CommandLineInterface::ParseArgumentStatus
+CommandLineInterface::InterpretArgument(const string& name,
+ const string& value) {
if (name.empty()) {
// Not a flag. Just a filename.
if (value.empty()) {
@@ -856,7 +951,7 @@ bool CommandLineInterface::InterpretArgument(const string& name,
"arguments to " << executable_name_ << ". This is actually "
"sort of hard to do. Congrats. Unfortunately it is not valid "
"input so the program is going to die now." << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
input_files_.push_back(value);
@@ -865,14 +960,14 @@ bool CommandLineInterface::InterpretArgument(const string& name,
// Java's -classpath (and some other languages) delimits path components
// with colons. Let's accept that syntax too just to make things more
// intuitive.
- vector<string> parts;
- SplitStringUsing(value, kPathSeparator, &parts);
+ vector<string> parts = Split(
+ value, kPathSeparator, true);
for (int i = 0; i < parts.size(); i++) {
string virtual_path;
string disk_path;
- int equals_pos = parts[i].find_first_of('=');
+ string::size_type equals_pos = parts[i].find_first_of('=');
if (equals_pos == string::npos) {
virtual_path = "";
disk_path = parts[i];
@@ -884,7 +979,7 @@ bool CommandLineInterface::InterpretArgument(const string& name,
if (disk_path.empty()) {
cerr << "--proto_path passed empty directory name. (Use \".\" for "
"current directory.)" << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
// Make sure disk path exists, warn otherwise.
@@ -892,35 +987,45 @@ bool CommandLineInterface::InterpretArgument(const string& name,
cerr << disk_path << ": warning: directory does not exist." << endl;
}
- proto_path_.push_back(make_pair(virtual_path, disk_path));
+ // Don't use make_pair as the old/default standard library on Solaris
+ // doesn't support it without explicit template parameters, which are
+ // incompatible with C++0x's make_pair.
+ proto_path_.push_back(pair<string, string>(virtual_path, disk_path));
}
} else if (name == "-o" || name == "--descriptor_set_out") {
if (!descriptor_set_name_.empty()) {
cerr << name << " may only be passed once." << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
if (value.empty()) {
cerr << name << " requires a non-empty value." << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
if (mode_ != MODE_COMPILE) {
cerr << "Cannot use --encode or --decode and generate descriptors at the "
"same time." << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
descriptor_set_name_ = value;
} else if (name == "--include_imports") {
if (imports_in_descriptor_set_) {
cerr << name << " may only be passed once." << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
imports_in_descriptor_set_ = true;
+ } else if (name == "--include_source_info") {
+ if (source_info_in_descriptor_set_) {
+ cerr << name << " may only be passed once." << endl;
+ return PARSE_ARGUMENT_FAIL;
+ }
+ source_info_in_descriptor_set_ = true;
+
} else if (name == "-h" || name == "--help") {
PrintHelpText();
- return false; // Exit without running compiler.
+ return PARSE_ARGUMENT_DONE_AND_EXIT; // Exit without running compiler.
} else if (name == "--version") {
if (!version_info_.empty()) {
@@ -929,7 +1034,7 @@ bool CommandLineInterface::InterpretArgument(const string& name,
cout << "libprotoc "
<< protobuf::internal::VersionString(GOOGLE_PROTOBUF_VERSION)
<< endl;
- return false; // Exit without running compiler.
+ return PARSE_ARGUMENT_DONE_AND_EXIT; // Exit without running compiler.
} else if (name == "--disallow_services") {
disallow_services_ = true;
@@ -938,12 +1043,12 @@ bool CommandLineInterface::InterpretArgument(const string& name,
name == "--decode_raw") {
if (mode_ != MODE_COMPILE) {
cerr << "Only one of --encode and --decode can be specified." << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
if (!output_directives_.empty() || !descriptor_set_name_.empty()) {
cerr << "Cannot use " << name
<< " and generate code or descriptors at the same time." << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
mode_ = (name == "--encode") ? MODE_ENCODE : MODE_DECODE;
@@ -953,10 +1058,10 @@ bool CommandLineInterface::InterpretArgument(const string& name,
if (name == "--decode") {
cerr << "To decode an unknown message, use --decode_raw." << endl;
}
- return false;
+ return PARSE_ARGUMENT_FAIL;
} else if (!value.empty() && name == "--decode_raw") {
cerr << "--decode_raw does not take a parameter." << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
codec_type_ = value;
@@ -968,16 +1073,16 @@ bool CommandLineInterface::InterpretArgument(const string& name,
error_format_ = ERROR_FORMAT_MSVS;
} else {
cerr << "Unknown error format: " << value << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
} else if (name == "--plugin") {
if (plugin_prefix_.empty()) {
cerr << "This compiler does not support plugins." << endl;
- return false;
+ return PARSE_ARGUMENT_FAIL;
}
- string name;
+ string plugin_name;
string path;
string::size_type equals_pos = value.find_first_of('=');
@@ -985,57 +1090,81 @@ bool CommandLineInterface::InterpretArgument(const string& name,
// Use the basename of the file.
string::size_type slash_pos = value.find_last_of('/');
if (slash_pos == string::npos) {
- name = value;
+ plugin_name = value;
} else {
- name = value.substr(slash_pos + 1);
+ plugin_name = value.substr(slash_pos + 1);
}
path = value;
} else {
- name = value.substr(0, equals_pos);
+ plugin_name = value.substr(0, equals_pos);
path = value.substr(equals_pos + 1);
}
- plugins_[name] = path;
+ plugins_[plugin_name] = path;
+ } else if (name == "--print_free_field_numbers") {
+ if (mode_ != MODE_COMPILE) {
+ cerr << "Cannot use " << name << " and use --encode, --decode or print "
+ << "other info at the same time." << endl;
+ return PARSE_ARGUMENT_FAIL;
+ }
+ if (!output_directives_.empty() || !descriptor_set_name_.empty()) {
+ cerr << "Cannot use " << name
+ << " and generate code or descriptors at the same time." << endl;
+ return PARSE_ARGUMENT_FAIL;
+ }
+ mode_ = MODE_PRINT;
+ print_mode_ = PRINT_FREE_FIELDS;
} else {
// Some other flag. Look it up in the generators list.
- const GeneratorInfo* generator_info = FindOrNull(generators_, name);
+ const GeneratorInfo* generator_info =
+ FindOrNull(generators_by_flag_name_, name);
if (generator_info == NULL &&
(plugin_prefix_.empty() || !HasSuffixString(name, "_out"))) {
- cerr << "Unknown flag: " << name << endl;
- return false;
- }
+ // Check if it's a generator option flag.
+ generator_info = FindOrNull(generators_by_option_name_, name);
+ if (generator_info == NULL) {
+ cerr << "Unknown flag: " << name << endl;
+ return PARSE_ARGUMENT_FAIL;
+ } else {
+ string* parameters = &generator_parameters_[generator_info->flag_name];
+ if (!parameters->empty()) {
+ parameters->append(",");
+ }
+ parameters->append(value);
+ }
+ } else {
+ // It's an output flag. Add it to the output directives.
+ if (mode_ != MODE_COMPILE) {
+ cerr << "Cannot use --encode, --decode or print .proto info and "
+ "generate code at the same time." << endl;
+ return PARSE_ARGUMENT_FAIL;
+ }
- // It's an output flag. Add it to the output directives.
- if (mode_ != MODE_COMPILE) {
- cerr << "Cannot use --encode or --decode and generate code at the "
- "same time." << endl;
- return false;
- }
+ OutputDirective directive;
+ directive.name = name;
+ if (generator_info == NULL) {
+ directive.generator = NULL;
+ } else {
+ directive.generator = generator_info->generator;
+ }
- OutputDirective directive;
- directive.name = name;
- if (generator_info == NULL) {
- directive.generator = NULL;
- } else {
- directive.generator = generator_info->generator;
- }
+ // Split value at ':' to separate the generator parameter from the
+ // filename. However, avoid doing this if the colon is part of a valid
+ // Windows-style absolute path.
+ string::size_type colon_pos = value.find_first_of(':');
+ if (colon_pos == string::npos || IsWindowsAbsolutePath(value)) {
+ directive.output_location = value;
+ } else {
+ directive.parameter = value.substr(0, colon_pos);
+ directive.output_location = value.substr(colon_pos + 1);
+ }
- // Split value at ':' to separate the generator parameter from the
- // filename. However, avoid doing this if the colon is part of a valid
- // Windows-style absolute path.
- string::size_type colon_pos = value.find_first_of(':');
- if (colon_pos == string::npos || IsWindowsAbsolutePath(value)) {
- directive.output_location = value;
- } else {
- directive.parameter = value.substr(0, colon_pos);
- directive.output_location = value.substr(colon_pos + 1);
+ output_directives_.push_back(directive);
}
-
- output_directives_.push_back(directive);
}
- return true;
+ return PARSE_ARGUMENT_DONE_AND_CONTINUE;
}
void CommandLineInterface::PrintHelpText() {
@@ -1068,9 +1197,20 @@ void CommandLineInterface::PrintHelpText() {
" --include_imports When using --descriptor_set_out, also include\n"
" all dependencies of the input files in the\n"
" set, so that the set is self-contained.\n"
+" --include_source_info When using --descriptor_set_out, do not strip\n"
+" SourceCodeInfo from the FileDescriptorProto.\n"
+" This results in vastly larger descriptors that\n"
+" include information about the original\n"
+" location of each decl in the source file as\n"
+" well as surrounding comments.\n"
" --error_format=FORMAT Set the format in which to print errors.\n"
" FORMAT may be 'gcc' (the default) or 'msvs'\n"
-" (Microsoft Visual Studio format)." << endl;
+" (Microsoft Visual Studio format).\n"
+" --print_free_field_numbers Print the free field numbers of the messages\n"
+" defined in the given proto files. Groups share\n"
+" the same field number space with the parent \n"
+" message. Extension ranges are counted as \n"
+" occupied fields numbers." << endl;
if (!plugin_prefix_.empty()) {
cerr <<
" --plugin=EXECUTABLE Specifies a plugin executable to use.\n"
@@ -1083,8 +1223,8 @@ void CommandLineInterface::PrintHelpText() {
" the executable's own name differs." << endl;
}
- for (GeneratorMap::iterator iter = generators_.begin();
- iter != generators_.end(); ++iter) {
+ for (GeneratorMap::iterator iter = generators_by_flag_name_.begin();
+ iter != generators_by_flag_name_.end(); ++iter) {
// FIXME(kenton): If the text is long enough it will wrap, which is ugly,
// but fixing this nicely (e.g. splitting on spaces) is probably more
// trouble than it's worth.
@@ -1097,7 +1237,7 @@ void CommandLineInterface::PrintHelpText() {
bool CommandLineInterface::GenerateOutput(
const vector<const FileDescriptor*>& parsed_files,
const OutputDirective& output_directive,
- OutputDirectory* output_directory) {
+ GeneratorContext* generator_context) {
// Call the generator.
string error;
if (output_directive.generator == NULL) {
@@ -1112,16 +1252,22 @@ bool CommandLineInterface::GenerateOutput(
if (!GeneratePluginOutput(parsed_files, plugin_name,
output_directive.parameter,
- output_directory, &error)) {
+ generator_context, &error)) {
cerr << output_directive.name << ": " << error << endl;
return false;
}
} else {
// Regular generator.
+ string parameters = output_directive.parameter;
+ if (!generator_parameters_[output_directive.name].empty()) {
+ if (!parameters.empty()) {
+ parameters.append(",");
+ }
+ parameters.append(generator_parameters_[output_directive.name]);
+ }
for (int i = 0; i < parsed_files.size(); i++) {
- if (!output_directive.generator->Generate(
- parsed_files[i], output_directive.parameter,
- output_directory, &error)) {
+ if (!output_directive.generator->Generate(parsed_files[i], parameters,
+ generator_context, &error)) {
// Generator returned an error.
cerr << output_directive.name << ": " << parsed_files[i]->name() << ": "
<< error << endl;
@@ -1137,7 +1283,7 @@ bool CommandLineInterface::GeneratePluginOutput(
const vector<const FileDescriptor*>& parsed_files,
const string& plugin_name,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* generator_context,
string* error) {
CodeGeneratorRequest request;
CodeGeneratorResponse response;
@@ -1150,8 +1296,9 @@ bool CommandLineInterface::GeneratePluginOutput(
set<const FileDescriptor*> already_seen;
for (int i = 0; i < parsed_files.size(); i++) {
request.add_file_to_generate(parsed_files[i]->name());
- GetTransitiveDependencies(parsed_files[i], &already_seen,
- request.mutable_proto_file());
+ GetTransitiveDependencies(parsed_files[i],
+ true, // Include source code info.
+ &already_seen, request.mutable_proto_file());
}
// Invoke the plugin.
@@ -1180,14 +1327,14 @@ bool CommandLineInterface::GeneratePluginOutput(
// We reset current_output to NULL first so that the old file is closed
// before the new one is opened.
current_output.reset();
- current_output.reset(output_directory->OpenForInsert(
+ current_output.reset(generator_context->OpenForInsert(
output_file.name(), output_file.insertion_point()));
} else if (!output_file.name().empty()) {
// Starting a new file. Open it.
// We reset current_output to NULL first so that the old file is closed
// before the new one is opened.
current_output.reset();
- current_output.reset(output_directory->Open(output_file.name()));
+ current_output.reset(generator_context->Open(output_file.name()));
} else if (current_output == NULL) {
*error = strings::Substitute(
"$0: First file chunk returned by plugin did not specify a file name.",
@@ -1281,12 +1428,17 @@ bool CommandLineInterface::WriteDescriptorSet(
if (imports_in_descriptor_set_) {
set<const FileDescriptor*> already_seen;
for (int i = 0; i < parsed_files.size(); i++) {
- GetTransitiveDependencies(
- parsed_files[i], &already_seen, file_set.mutable_file());
+ GetTransitiveDependencies(parsed_files[i],
+ source_info_in_descriptor_set_,
+ &already_seen, file_set.mutable_file());
}
} else {
for (int i = 0; i < parsed_files.size(); i++) {
- parsed_files[i]->CopyTo(file_set.add_file());
+ FileDescriptorProto* file_proto = file_set.add_file();
+ parsed_files[i]->CopyTo(file_proto);
+ if (source_info_in_descriptor_set_) {
+ parsed_files[i]->CopySourceCodeInfoTo(file_proto);
+ }
}
}
@@ -1316,7 +1468,7 @@ bool CommandLineInterface::WriteDescriptorSet(
}
void CommandLineInterface::GetTransitiveDependencies(
- const FileDescriptor* file,
+ const FileDescriptor* file, bool include_source_code_info,
set<const FileDescriptor*>* already_seen,
RepeatedPtrField<FileDescriptorProto>* output) {
if (!already_seen->insert(file).second) {
@@ -1326,13 +1478,125 @@ void CommandLineInterface::GetTransitiveDependencies(
// Add all dependencies.
for (int i = 0; i < file->dependency_count(); i++) {
- GetTransitiveDependencies(file->dependency(i), already_seen, output);
+ GetTransitiveDependencies(file->dependency(i), include_source_code_info,
+ already_seen, output);
}
// Add this file.
- file->CopyTo(output->Add());
+ FileDescriptorProto* new_descriptor = output->Add();
+ file->CopyTo(new_descriptor);
+ if (include_source_code_info) {
+ file->CopySourceCodeInfoTo(new_descriptor);
+ }
+}
+
+namespace {
+
+// Utility function for PrintFreeFieldNumbers.
+// Stores occupied ranges into the ranges parameter, and next level of sub
+// message types into the nested_messages parameter. The FieldRange is left
+// inclusive, right exclusive. i.e. [a, b).
+//
+// Nested Messages:
+// Note that it only stores the nested message type, iff the nested type is
+// either a direct child of the given descriptor, or the nested type is a
+// decendent of the given descriptor and all the nodes between the
+// nested type and the given descriptor are group types. e.g.
+//
+// message Foo {
+// message Bar {
+// message NestedBar {}
+// }
+// group Baz = 1 {
+// group NestedBazGroup = 2 {
+// message Quz {
+// message NestedQuz {}
+// }
+// }
+// message NestedBaz {}
+// }
+// }
+//
+// In this case, Bar, Quz and NestedBaz will be added into the nested types.
+// Since free field numbers of group types will not be printed, this makes sure
+// the nested message types in groups will not be dropped. The nested_messages
+// parameter will contain the direct children (when groups are ignored in the
+// tree) of the given descriptor for the caller to traverse. The declaration
+// order of the nested messages is also preserved.
+typedef pair<int, int> FieldRange;
+void GatherOccupiedFieldRanges(const Descriptor* descriptor,
+ set<FieldRange>* ranges,
+ vector<const Descriptor*>* nested_messages) {
+ set<const Descriptor*> groups;
+ for (int i = 0; i < descriptor->field_count(); ++i) {
+ const FieldDescriptor* fd = descriptor->field(i);
+ ranges->insert(FieldRange(fd->number(), fd->number() + 1));
+ if (fd->type() == FieldDescriptor::TYPE_GROUP) {
+ groups.insert(fd->message_type());
+ }
+ }
+ for (int i = 0; i < descriptor->extension_range_count(); ++i) {
+ ranges->insert(FieldRange(descriptor->extension_range(i)->start,
+ descriptor->extension_range(i)->end));
+ }
+ // Handle the nested messages/groups in declaration order to make it
+ // post-order strict.
+ for (int i = 0; i < descriptor->nested_type_count(); ++i) {
+ const Descriptor* nested_desc = descriptor->nested_type(i);
+ if (groups.find(nested_desc) != groups.end()) {
+ GatherOccupiedFieldRanges(nested_desc, ranges, nested_messages);
+ } else {
+ nested_messages->push_back(nested_desc);
+ }
+ }
}
+// Utility function for PrintFreeFieldNumbers.
+// Actually prints the formatted free field numbers for given message name and
+// occupied ranges.
+void FormatFreeFieldNumbers(const string& name,
+ const set<FieldRange>& ranges) {
+ string output;
+ StringAppendF(&output, "%-35s free:", name.c_str());
+ int next_free_number = 1;
+ for (set<FieldRange>::const_iterator i = ranges.begin();
+ i != ranges.end(); ++i) {
+ // This happens when groups re-use parent field numbers, in which
+ // case we skip the FieldRange entirely.
+ if (next_free_number >= i->second) continue;
+
+ if (next_free_number < i->first) {
+ if (next_free_number + 1 == i->first) {
+ // Singleton
+ StringAppendF(&output, " %d", next_free_number);
+ } else {
+ // Range
+ StringAppendF(&output, " %d-%d", next_free_number, i->first - 1);
+ }
+ }
+ next_free_number = i->second;
+ }
+ if (next_free_number <= FieldDescriptor::kMaxNumber) {
+ StringAppendF(&output, " %d-INF", next_free_number);
+ }
+ cout << output << endl;
+}
+
+} // namespace
+
+void CommandLineInterface::PrintFreeFieldNumbers(
+ const Descriptor* descriptor) {
+ set<FieldRange> ranges;
+ vector<const Descriptor*> nested_messages;
+ GatherOccupiedFieldRanges(descriptor, &ranges, &nested_messages);
+
+ for (int i = 0; i < nested_messages.size(); ++i) {
+ PrintFreeFieldNumbers(nested_messages[i]);
+ }
+ FormatFreeFieldNumbers(descriptor->full_name(), ranges);
+}
+
+
} // namespace compiler
} // namespace protobuf
diff --git a/src/google/protobuf/compiler/command_line_interface.h b/src/google/protobuf/compiler/command_line_interface.h
index d25a50e..47f2891 100644
--- a/src/google/protobuf/compiler/command_line_interface.h
+++ b/src/google/protobuf/compiler/command_line_interface.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -48,15 +48,16 @@
namespace google {
namespace protobuf {
-class FileDescriptor; // descriptor.h
+class Descriptor; // descriptor.h
class DescriptorPool; // descriptor.h
+class FileDescriptor; // descriptor.h
class FileDescriptorProto; // descriptor.pb.h
template<typename T> class RepeatedPtrField; // repeated_field.h
namespace compiler {
class CodeGenerator; // code_generator.h
-class OutputDirectory; // code_generator.h
+class GeneratorContext; // code_generator.h
class DiskSourceTree; // importer.h
// This class implements the command-line interface to the protocol compiler.
@@ -112,6 +113,19 @@ class LIBPROTOC_EXPORT CommandLineInterface {
CodeGenerator* generator,
const string& help_text);
+ // Register a code generator for a language.
+ // Besides flag_name you can specify another option_flag_name that could be
+ // used to pass extra parameters to the registered code generator.
+ // Suppose you have registered a generator by calling:
+ // command_line_interface.RegisterGenerator("--foo_out", "--foo_opt", ...)
+ // Then you could invoke the compiler with a command like:
+ // protoc --foo_out=enable_bar:outdir --foo_opt=enable_baz
+ // This will pass "enable_bar,enable_baz" as the parameter to the generator.
+ void RegisterGenerator(const string& flag_name,
+ const string& option_flag_name,
+ CodeGenerator* generator,
+ const string& help_text);
+
// Enables "plugins". In this mode, if a command-line flag ends with "_out"
// but does not match any registered generator, the compiler will attempt to
// find a "plugin" to implement the generator. Plugins are just executables.
@@ -174,7 +188,7 @@ class LIBPROTOC_EXPORT CommandLineInterface {
// -----------------------------------------------------------------
class ErrorPrinter;
- class MemoryOutputDirectory;
+ class GeneratorContextImpl;
class MemoryOutputStream;
// Clear state from previous Run().
@@ -186,8 +200,15 @@ class LIBPROTOC_EXPORT CommandLineInterface {
bool MakeInputsBeProtoPathRelative(
DiskSourceTree* source_tree);
+ // Return status for ParseArguments() and InterpretArgument().
+ enum ParseArgumentStatus {
+ PARSE_ARGUMENT_DONE_AND_CONTINUE,
+ PARSE_ARGUMENT_DONE_AND_EXIT,
+ PARSE_ARGUMENT_FAIL
+ };
+
// Parse all command-line arguments.
- bool ParseArguments(int argc, const char* const argv[]);
+ ParseArgumentStatus ParseArguments(int argc, const char* const argv[]);
// Parses a command-line argument into a name/value pair. Returns
// true if the next argument in the argv should be used as the value,
@@ -203,7 +224,8 @@ class LIBPROTOC_EXPORT CommandLineInterface {
bool ParseArgument(const char* arg, string* name, string* value);
// Interprets arguments parsed with ParseArgument.
- bool InterpretArgument(const string& name, const string& value);
+ ParseArgumentStatus InterpretArgument(const string& name,
+ const string& value);
// Print the --help text to stderr.
void PrintHelpText();
@@ -212,11 +234,11 @@ class LIBPROTOC_EXPORT CommandLineInterface {
struct OutputDirective; // see below
bool GenerateOutput(const vector<const FileDescriptor*>& parsed_files,
const OutputDirective& output_directive,
- OutputDirectory* output_directory);
+ GeneratorContext* generator_context);
bool GeneratePluginOutput(const vector<const FileDescriptor*>& parsed_files,
const string& plugin_name,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* generator_context,
string* error);
// Implements --encode and --decode.
@@ -230,12 +252,30 @@ class LIBPROTOC_EXPORT CommandLineInterface {
// protos will be ordered such that every file is listed before any file that
// depends on it, so that you can call DescriptorPool::BuildFile() on them
// in order. Any files in *already_seen will not be added, and each file
- // added will be inserted into *already_seen.
+ // added will be inserted into *already_seen. If include_source_code_info is
+ // true then include the source code information in the FileDescriptorProtos.
static void GetTransitiveDependencies(
const FileDescriptor* file,
+ bool include_source_code_info,
set<const FileDescriptor*>* already_seen,
RepeatedPtrField<FileDescriptorProto>* output);
+ // Implements the --print_free_field_numbers. This function prints free field
+ // numbers into stdout for the message and it's nested message types in
+ // post-order, i.e. nested types first. Printed range are left-right
+ // inclusive, i.e. [a, b].
+ //
+ // Groups:
+ // For historical reasons, groups are considered to share the same
+ // field number space with the parent message, thus it will not print free
+ // field numbers for groups. The field numbers used in the groups are
+ // excluded in the free field numbers of the parent message.
+ //
+ // Extension Ranges:
+ // Extension ranges are considered ocuppied field numbers and they will not be
+ // listed as free numbers in the output.
+ void PrintFreeFieldNumbers(const Descriptor* descriptor);
+
// -----------------------------------------------------------------
// The name of the executable as invoked (i.e. argv[0]).
@@ -244,13 +284,21 @@ class LIBPROTOC_EXPORT CommandLineInterface {
// Version info set with SetVersionInfo().
string version_info_;
- // Map from flag names to registered generators.
+ // Registered generators.
struct GeneratorInfo {
+ string flag_name;
+ string option_flag_name;
CodeGenerator* generator;
string help_text;
};
typedef map<string, GeneratorInfo> GeneratorMap;
- GeneratorMap generators_;
+ GeneratorMap generators_by_flag_name_;
+ GeneratorMap generators_by_option_name_;
+ // A map from generator names to the parameters specified using the option
+ // flag. For example, if the user invokes the compiler with:
+ // protoc --foo_out=outputdir --foo_opt=enable_bar ...
+ // Then there will be an entry ("--foo_out", "enable_bar") in this map.
+ map<string, string> generator_parameters_;
// See AllowPlugins(). If this is empty, plugins aren't allowed.
string plugin_prefix_;
@@ -264,11 +312,19 @@ class LIBPROTOC_EXPORT CommandLineInterface {
enum Mode {
MODE_COMPILE, // Normal mode: parse .proto files and compile them.
MODE_ENCODE, // --encode: read text from stdin, write binary to stdout.
- MODE_DECODE // --decode: read binary from stdin, write text to stdout.
+ MODE_DECODE, // --decode: read binary from stdin, write text to stdout.
+ MODE_PRINT, // Print mode: print info of the given .proto files and exit.
};
Mode mode_;
+ enum PrintMode {
+ PRINT_NONE, // Not in MODE_PRINT
+ PRINT_FREE_FIELDS, // --print_free_fields
+ };
+
+ PrintMode print_mode_;
+
enum ErrorFormat {
ERROR_FORMAT_GCC, // GCC error output format (default).
ERROR_FORMAT_MSVS // Visual Studio output (--error_format=msvs).
@@ -302,6 +358,10 @@ class LIBPROTOC_EXPORT CommandLineInterface {
// the .proto files listed on the command-line are added.
bool imports_in_descriptor_set_;
+ // True if --include_source_info was given, meaning that we should not strip
+ // SourceCodeInfo from the DescriptorSet.
+ bool source_info_in_descriptor_set_;
+
// Was the --disallow_services flag used?
bool disallow_services_;
diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc
index 9129ebf..6bf7357 100644
--- a/src/google/protobuf/compiler/command_line_interface_unittest.cc
+++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -40,6 +40,7 @@
#else
#include <unistd.h>
#endif
+#include <memory>
#include <vector>
#include <google/protobuf/descriptor.pb.h>
@@ -48,6 +49,7 @@
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/mock_code_generator.h>
+#include <google/protobuf/compiler/subprocess.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/unittest.pb.h>
#include <google/protobuf/testing/file.h>
@@ -121,6 +123,13 @@ class CommandLineInterfaceTest : public testing::Test {
// substring.
void ExpectErrorSubstring(const string& expected_substring);
+ // Like ExpectErrorSubstring, but checks that Run() returned zero.
+ void ExpectErrorSubstringWithZeroReturnCode(
+ const string& expected_substring);
+
+ // Checks that the captured stdout is the same as the expected_text.
+ void ExpectCapturedStdout(const string& expected_text);
+
// Returns true if ExpectErrorSubstring(expected_substring) would pass, but
// does not fail otherwise.
bool HasAlternateErrorSubstring(const string& expected_substring);
@@ -143,6 +152,10 @@ class CommandLineInterfaceTest : public testing::Test {
const string& proto_name,
const string& message_name,
const string& output_directory);
+ void ExpectGeneratedWithMultipleInputs(const string& generator_name,
+ const string& all_proto_names,
+ const string& proto_name,
+ const string& message_name);
void ExpectGeneratedWithInsertions(const string& generator_name,
const string& parameter,
const string& insertions,
@@ -173,6 +186,9 @@ class CommandLineInterfaceTest : public testing::Test {
// The captured stderr output.
string error_text_;
+ // The captured stdout.
+ string captured_stdout_;
+
// Pointers which need to be deleted later.
vector<CodeGenerator*> mock_generators_to_delete_;
@@ -190,7 +206,7 @@ class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator {
// implements CodeGenerator ----------------------------------------
bool Generate(const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* context,
string* error) const {
called_ = true;
parameter_ = parameter;
@@ -215,12 +231,12 @@ void CommandLineInterfaceTest::SetUp() {
}
// Create the temp directory.
- GOOGLE_CHECK(File::CreateDir(temp_directory_.c_str(), DEFAULT_FILE_MODE));
+ GOOGLE_CHECK_OK(File::CreateDir(temp_directory_, 0777));
// Register generators.
CodeGenerator* generator = new MockCodeGenerator("test_generator");
mock_generators_to_delete_.push_back(generator);
- cli_.RegisterGenerator("--test_out", generator, "Test output.");
+ cli_.RegisterGenerator("--test_out", "--test_opt", generator, "Test output.");
cli_.RegisterGenerator("-t", generator, "Test output.");
generator = new MockCodeGenerator("alt_generator");
@@ -246,12 +262,10 @@ void CommandLineInterfaceTest::TearDown() {
}
void CommandLineInterfaceTest::Run(const string& command) {
- vector<string> args;
- SplitStringUsing(command, " ", &args);
+ vector<string> args = Split(command, " ", true);
if (!disallow_plugins_) {
cli_.AllowPlugins("prefix-");
-
const char* possible_paths[] = {
// When building with shared libraries, libtool hides the real executable
// in .libs and puts a fake wrapper in the current directory.
@@ -287,18 +301,27 @@ void CommandLineInterfaceTest::Run(const string& command) {
}
}
- scoped_array<const char*> argv(new const char*[args.size()]);
+ scoped_array<const char*> argv(new const char* [args.size()]);
for (int i = 0; i < args.size(); i++) {
args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true);
argv[i] = args[i].c_str();
}
+ // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
+ // stdout at the same time. Need to figure out why and add this capture back
+ // for Cygwin.
+#if !defined(__CYGWIN__)
+ CaptureTestStdout();
+#endif
CaptureTestStderr();
return_code_ = cli_.Run(args.size(), argv.get());
error_text_ = GetCapturedTestStderr();
+#if !defined(__CYGWIN__)
+ captured_stdout_ = GetCapturedTestStdout();
+#endif
}
// -------------------------------------------------------------------
@@ -310,16 +333,20 @@ void CommandLineInterfaceTest::CreateTempFile(
string::size_type slash_pos = name.find_last_of('/');
if (slash_pos != string::npos) {
string dir = name.substr(0, slash_pos);
- File::RecursivelyCreateDir(temp_directory_ + "/" + dir, 0777);
+ if (!File::Exists(temp_directory_ + "/" + dir)) {
+ GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + dir,
+ 0777));
+ }
}
// Write file.
string full_name = temp_directory_ + "/" + name;
- File::WriteStringToFileOrDie(contents, full_name);
+ GOOGLE_CHECK_OK(File::SetContents(full_name, contents, true));
}
void CommandLineInterfaceTest::CreateTempDir(const string& name) {
- File::RecursivelyCreateDir(temp_directory_ + "/" + name, 0777);
+ GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + name,
+ 0777));
}
// -------------------------------------------------------------------
@@ -341,6 +368,12 @@ void CommandLineInterfaceTest::ExpectErrorSubstring(
EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
}
+void CommandLineInterfaceTest::ExpectErrorSubstringWithZeroReturnCode(
+ const string& expected_substring) {
+ EXPECT_EQ(0, return_code_);
+ EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
+}
+
bool CommandLineInterfaceTest::HasAlternateErrorSubstring(
const string& expected_substring) {
EXPECT_NE(0, return_code_);
@@ -353,7 +386,8 @@ void CommandLineInterfaceTest::ExpectGenerated(
const string& proto_name,
const string& message_name) {
MockCodeGenerator::ExpectGenerated(
- generator_name, parameter, "", proto_name, message_name, temp_directory_);
+ generator_name, parameter, "", proto_name, message_name, proto_name,
+ temp_directory_);
}
void CommandLineInterfaceTest::ExpectGenerated(
@@ -363,10 +397,21 @@ void CommandLineInterfaceTest::ExpectGenerated(
const string& message_name,
const string& output_directory) {
MockCodeGenerator::ExpectGenerated(
- generator_name, parameter, "", proto_name, message_name,
+ generator_name, parameter, "", proto_name, message_name, proto_name,
temp_directory_ + "/" + output_directory);
}
+void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs(
+ const string& generator_name,
+ const string& all_proto_names,
+ const string& proto_name,
+ const string& message_name) {
+ MockCodeGenerator::ExpectGenerated(
+ generator_name, "", "", proto_name, message_name,
+ all_proto_names,
+ temp_directory_);
+}
+
void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
const string& generator_name,
const string& parameter,
@@ -375,7 +420,7 @@ void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
const string& message_name) {
MockCodeGenerator::ExpectGenerated(
generator_name, parameter, insertions, proto_name, message_name,
- temp_directory_);
+ proto_name, temp_directory_);
}
void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled(
@@ -388,14 +433,18 @@ void CommandLineInterfaceTest::ReadDescriptorSet(
const string& filename, FileDescriptorSet* descriptor_set) {
string path = temp_directory_ + "/" + filename;
string file_contents;
- if (!File::ReadFileToString(path, &file_contents)) {
- FAIL() << "File not found: " << path;
- }
+ GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
+
if (!descriptor_set->ParseFromString(file_contents)) {
FAIL() << "Could not parse file contents: " << path;
}
}
+void CommandLineInterfaceTest::ExpectCapturedStdout(
+ const string& expected_text) {
+ EXPECT_EQ(expected_text, captured_stdout_);
+}
+
// ===================================================================
TEST_F(CommandLineInterfaceTest, BasicOutput) {
@@ -455,8 +504,44 @@ TEST_F(CommandLineInterfaceTest, MultipleInputs) {
"--proto_path=$tmpdir foo.proto bar.proto");
ExpectNoErrors();
- ExpectGenerated("test_generator", "", "foo.proto", "Foo");
- ExpectGenerated("test_generator", "", "bar.proto", "Bar");
+ ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
+ "foo.proto", "Foo");
+ ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
+ "bar.proto", "Bar");
+ ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
+ "foo.proto", "Foo");
+ ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
+ "bar.proto", "Bar");
+}
+
+TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) {
+ // Test parsing multiple input files with an import of a separate file.
+
+ CreateTempFile("foo.proto",
+ "syntax = \"proto2\";\n"
+ "message Foo {}\n");
+ CreateTempFile("bar.proto",
+ "syntax = \"proto2\";\n"
+ "import \"baz.proto\";\n"
+ "message Bar {\n"
+ " optional Baz a = 1;\n"
+ "}\n");
+ CreateTempFile("baz.proto",
+ "syntax = \"proto2\";\n"
+ "message Baz {}\n");
+
+ Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
+ "--proto_path=$tmpdir foo.proto bar.proto");
+
+ ExpectNoErrors();
+ ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
+ "foo.proto", "Foo");
+ ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
+ "bar.proto", "Bar");
+ ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
+ "foo.proto", "Foo");
+ ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
+ "bar.proto", "Bar");
}
TEST_F(CommandLineInterfaceTest, CreateDirectory) {
@@ -492,6 +577,32 @@ TEST_F(CommandLineInterfaceTest, GeneratorParameters) {
ExpectGenerated("test_plugin", "TestPluginParameter", "foo.proto", "Foo");
}
+TEST_F(CommandLineInterfaceTest, ExtraGeneratorParameters) {
+ // Test that generator parameters specified with the option flag are
+ // correctly passed to the code generator.
+
+ CreateTempFile("foo.proto",
+ "syntax = \"proto2\";\n"
+ "message Foo {}\n");
+ // Create the "a" and "b" sub-directories.
+ CreateTempDir("a");
+ CreateTempDir("b");
+
+ Run("protocol_compiler "
+ "--test_opt=foo1 "
+ "--test_out=bar:$tmpdir/a "
+ "--test_opt=foo2 "
+ "--test_out=baz:$tmpdir/b "
+ "--test_opt=foo3 "
+ "--proto_path=$tmpdir foo.proto");
+
+ ExpectNoErrors();
+ ExpectGenerated(
+ "test_generator", "bar,foo1,foo2,foo3", "foo.proto", "Foo", "a");
+ ExpectGenerated(
+ "test_generator", "baz,foo1,foo2,foo3", "foo.proto", "Foo", "b");
+}
+
TEST_F(CommandLineInterfaceTest, Insert) {
// Test running a generator that inserts code into another's output.
@@ -515,7 +626,7 @@ TEST_F(CommandLineInterfaceTest, Insert) {
"foo.proto", "Foo");
}
-#if defined(_WIN32) || defined(__CYGWIN__)
+#if defined(_WIN32)
TEST_F(CommandLineInterfaceTest, WindowsOutputPath) {
// Test that the output path can be a Windows-style path.
@@ -725,8 +836,35 @@ TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
FileDescriptorSet descriptor_set;
ReadDescriptorSet("descriptor_set", &descriptor_set);
if (HasFatalFailure()) return;
- ASSERT_EQ(1, descriptor_set.file_size());
+ EXPECT_EQ(1, descriptor_set.file_size());
EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
+ // Descriptor set should not have source code info.
+ EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
+}
+
+TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
+ CreateTempFile("foo.proto",
+ "syntax = \"proto2\";\n"
+ "message Foo {}\n");
+ CreateTempFile("bar.proto",
+ "syntax = \"proto2\";\n"
+ "import \"foo.proto\";\n"
+ "message Bar {\n"
+ " optional Foo foo = 1;\n"
+ "}\n");
+
+ Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
+ "--include_source_info --proto_path=$tmpdir bar.proto");
+
+ ExpectNoErrors();
+
+ FileDescriptorSet descriptor_set;
+ ReadDescriptorSet("descriptor_set", &descriptor_set);
+ if (HasFatalFailure()) return;
+ EXPECT_EQ(1, descriptor_set.file_size());
+ EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
+ // Source code info included.
+ EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
}
TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
@@ -748,13 +886,47 @@ TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
FileDescriptorSet descriptor_set;
ReadDescriptorSet("descriptor_set", &descriptor_set);
if (HasFatalFailure()) return;
- ASSERT_EQ(2, descriptor_set.file_size());
+ EXPECT_EQ(2, descriptor_set.file_size());
if (descriptor_set.file(0).name() == "bar.proto") {
std::swap(descriptor_set.mutable_file()->mutable_data()[0],
descriptor_set.mutable_file()->mutable_data()[1]);
}
EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
+ // Descriptor set should not have source code info.
+ EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
+ EXPECT_FALSE(descriptor_set.file(1).has_source_code_info());
+}
+
+TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) {
+ CreateTempFile("foo.proto",
+ "syntax = \"proto2\";\n"
+ "message Foo {}\n");
+ CreateTempFile("bar.proto",
+ "syntax = \"proto2\";\n"
+ "import \"foo.proto\";\n"
+ "message Bar {\n"
+ " optional Foo foo = 1;\n"
+ "}\n");
+
+ Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
+ "--include_imports --include_source_info --proto_path=$tmpdir bar.proto");
+
+ ExpectNoErrors();
+
+ FileDescriptorSet descriptor_set;
+ ReadDescriptorSet("descriptor_set", &descriptor_set);
+ if (HasFatalFailure()) return;
+ EXPECT_EQ(2, descriptor_set.file_size());
+ if (descriptor_set.file(0).name() == "bar.proto") {
+ std::swap(descriptor_set.mutable_file()->mutable_data()[0],
+ descriptor_set.mutable_file()->mutable_data()[1]);
+ }
+ EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
+ EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
+ // Source code info included.
+ EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
+ EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
}
// -------------------------------------------------------------------
@@ -1077,6 +1249,17 @@ TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
#endif
}
+TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) {
+ CreateTempFile("foo.proto",
+ "syntax = \"proto2\";\n"
+ "message MockCodeGenerator_HasSourceCodeInfo {}\n");
+
+ Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
+
+ ExpectErrorSubstring(
+ "Saw message type MockCodeGenerator_HasSourceCodeInfo: 1.");
+}
+
TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
// Test what happens if the plugin isn't found.
@@ -1089,9 +1272,8 @@ TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
"--proto_path=$tmpdir error.proto");
#ifdef _WIN32
- ExpectErrorSubstring(
- "--badplug_out: prefix-gen-badplug: The system cannot find the file "
- "specified.");
+ ExpectErrorSubstring("--badplug_out: prefix-gen-badplug: " +
+ Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
#else
// Error written to stdout by child process after exec() fails.
ExpectErrorSubstring(
@@ -1120,11 +1302,11 @@ TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
TEST_F(CommandLineInterfaceTest, HelpText) {
Run("test_exec_name --help");
- ExpectErrorSubstring("Usage: test_exec_name ");
- ExpectErrorSubstring("--test_out=OUT_DIR");
- ExpectErrorSubstring("Test output.");
- ExpectErrorSubstring("--alt_out=OUT_DIR");
- ExpectErrorSubstring("Alt output.");
+ ExpectErrorSubstringWithZeroReturnCode("Usage: test_exec_name ");
+ ExpectErrorSubstringWithZeroReturnCode("--test_out=OUT_DIR");
+ ExpectErrorSubstringWithZeroReturnCode("Test output.");
+ ExpectErrorSubstringWithZeroReturnCode("--alt_out=OUT_DIR");
+ ExpectErrorSubstringWithZeroReturnCode("Alt output.");
}
TEST_F(CommandLineInterfaceTest, GccFormatErrors) {
@@ -1153,7 +1335,7 @@ TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) {
"--proto_path=$tmpdir --error_format=msvs foo.proto");
ExpectErrorText(
- "foo.proto(2) : error in column=1: Expected top-level statement "
+ "$tmpdir/foo.proto(2) : error in column=1: Expected top-level statement "
"(e.g. \"message\").\n");
}
@@ -1234,6 +1416,76 @@ TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
ExpectErrorText("Missing value for flag: --test_out\n");
}
+TEST_F(CommandLineInterfaceTest, PrintFreeFieldNumbers) {
+ CreateTempFile(
+ "foo.proto",
+ "syntax = \"proto2\";\n"
+ "package foo;\n"
+ "message Foo {\n"
+ " optional int32 a = 2;\n"
+ " optional string b = 4;\n"
+ " optional string c = 5;\n"
+ " optional int64 d = 8;\n"
+ " optional double e = 10;\n"
+ "}\n");
+ CreateTempFile(
+ "bar.proto",
+ "syntax = \"proto2\";\n"
+ "message Bar {\n"
+ " optional int32 a = 2;\n"
+ " extensions 4 to 5;\n"
+ " optional int64 d = 8;\n"
+ " extensions 10;\n"
+ "}\n");
+ CreateTempFile(
+ "baz.proto",
+ "syntax = \"proto2\";\n"
+ "message Baz {\n"
+ " optional int32 a = 2;\n"
+ " optional int64 d = 8;\n"
+ " extensions 15 to max;\n" // unordered.
+ " extensions 13;\n"
+ " extensions 10 to 12;\n"
+ " extensions 5;\n"
+ " extensions 4;\n"
+ "}\n");
+ CreateTempFile(
+ "quz.proto",
+ "syntax = \"proto2\";\n"
+ "message Quz {\n"
+ " message Foo {}\n" // nested message
+ " optional int32 a = 2;\n"
+ " optional group C = 4 {\n"
+ " optional int32 d = 5;\n"
+ " }\n"
+ " extensions 8 to 10;\n"
+ " optional group E = 11 {\n"
+ " optional int32 f = 9;\n" // explicitly reuse extension range 8-10
+ " optional group G = 15 {\n" // nested group
+ " message Foo {}\n" // nested message inside nested group
+ " }\n"
+ " }\n"
+ "}\n");
+
+ Run("protocol_compiler --print_free_field_numbers --proto_path=$tmpdir "
+ "foo.proto bar.proto baz.proto quz.proto");
+
+ ExpectNoErrors();
+
+ // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
+ // stdout at the same time. Need to figure out why and add this test back
+ // for Cygwin.
+#if !defined(__CYGWIN__)
+ ExpectCapturedStdout(
+ "foo.Foo free: 1 3 6-7 9 11-INF\n"
+ "Bar free: 1 3 6-7 9 11-INF\n"
+ "Baz free: 1 3 6-7 9 14\n"
+ "Quz.Foo free: 1-INF\n"
+ "Quz.E.G.Foo free: 1-INF\n"
+ "Quz free: 1 3 6-7 12-14 16-INF\n");
+#endif
+}
+
// ===================================================================
// Test for --encode and --decode. Note that it would be easier to do this
@@ -1253,7 +1505,7 @@ class EncodeDecodeTest : public testing::Test {
void RedirectStdinFromText(const string& input) {
string filename = TestTempDir() + "/test_stdin";
- File::WriteStringToFileOrDie(input, filename);
+ GOOGLE_CHECK_OK(File::SetContents(filename, input, true));
GOOGLE_CHECK(RedirectStdinFromFile(filename));
}
@@ -1287,7 +1539,7 @@ class EncodeDecodeTest : public testing::Test {
SplitStringUsing(command, " ", &args);
args.push_back("--proto_path=" + TestSourceDir());
- scoped_array<const char*> argv(new const char*[args.size()]);
+ scoped_array<const char*> argv(new const char* [args.size()]);
for (int i = 0; i < args.size(); i++) {
argv[i] = args[i].c_str();
}
@@ -1308,7 +1560,7 @@ class EncodeDecodeTest : public testing::Test {
void ExpectStdoutMatchesBinaryFile(const string& filename) {
string expected_output;
- ASSERT_TRUE(File::ReadFileToString(filename, &expected_output));
+ GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
// Don't use EXPECT_EQ because we don't want to print raw binary data to
// stdout on failure.
@@ -1317,7 +1569,7 @@ class EncodeDecodeTest : public testing::Test {
void ExpectStdoutMatchesTextFile(const string& filename) {
string expected_output;
- ASSERT_TRUE(File::ReadFileToString(filename, &expected_output));
+ GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
ExpectStdoutMatchesText(expected_output);
}
@@ -1337,22 +1589,23 @@ class EncodeDecodeTest : public testing::Test {
};
TEST_F(EncodeDecodeTest, Encode) {
- RedirectStdinFromFile(TestSourceDir() +
- "/google/protobuf/testdata/text_format_unittest_data.txt");
+ RedirectStdinFromFile(TestSourceDir() + "/google/protobuf/"
+ "testdata/text_format_unittest_data_oneof_implemented.txt");
EXPECT_TRUE(Run("google/protobuf/unittest.proto "
"--encode=protobuf_unittest.TestAllTypes"));
ExpectStdoutMatchesBinaryFile(TestSourceDir() +
- "/google/protobuf/testdata/golden_message");
+ "/google/protobuf/testdata/golden_message_oneof_implemented");
ExpectStderrMatchesText("");
}
TEST_F(EncodeDecodeTest, Decode) {
RedirectStdinFromFile(TestSourceDir() +
- "/google/protobuf/testdata/golden_message");
+ "/google/protobuf/testdata/golden_message_oneof_implemented");
EXPECT_TRUE(Run("google/protobuf/unittest.proto "
"--decode=protobuf_unittest.TestAllTypes"));
ExpectStdoutMatchesTextFile(TestSourceDir() +
- "/google/protobuf/testdata/text_format_unittest_data.txt");
+ "/google/protobuf/"
+ "testdata/text_format_unittest_data_oneof_implemented.txt");
ExpectStderrMatchesText("");
}
diff --git a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc
index 30b1d2b..4878819 100644
--- a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -48,8 +48,8 @@
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <google/protobuf/stubs/stl_util-inl.h>
-#include <google/protobuf/stubs/map-util.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/stl_util.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
@@ -79,10 +79,10 @@ class MockErrorCollector : public MultiFileErrorCollector {
}
};
-class MockOutputDirectory : public OutputDirectory {
+class MockGeneratorContext : public GeneratorContext {
public:
- MockOutputDirectory() {}
- ~MockOutputDirectory() {
+ MockGeneratorContext() {}
+ ~MockGeneratorContext() {
STLDeleteValues(&files_);
}
@@ -93,16 +93,16 @@ class MockOutputDirectory : public OutputDirectory {
<< "Generator failed to generate file: " << virtual_filename;
string actual_contents;
- File::ReadFileToStringOrDie(
- TestSourceDir() + "/" + physical_filename,
- &actual_contents);
+ GOOGLE_CHECK_OK(
+ File::GetContents(TestSourceDir() + "/" + physical_filename,
+ &actual_contents, true));
EXPECT_TRUE(actual_contents == *expected_contents)
<< physical_filename << " needs to be regenerated. Please run "
"generate_descriptor_proto.sh and add this file "
"to your CL.";
}
- // implements OutputDirectory --------------------------------------
+ // implements GeneratorContext --------------------------------------
virtual io::ZeroCopyOutputStream* Open(const string& filename) {
string** map_slot = &files_[filename];
@@ -130,24 +130,24 @@ TEST(BootstrapTest, GeneratedDescriptorMatches) {
ASSERT_TRUE(plugin_proto_file != NULL);
CppGenerator generator;
- MockOutputDirectory output_directory;
+ MockGeneratorContext context;
string error;
string parameter;
parameter = "dllexport_decl=LIBPROTOBUF_EXPORT";
ASSERT_TRUE(generator.Generate(proto_file, parameter,
- &output_directory, &error));
+ &context, &error));
parameter = "dllexport_decl=LIBPROTOC_EXPORT";
ASSERT_TRUE(generator.Generate(plugin_proto_file, parameter,
- &output_directory, &error));
-
- output_directory.ExpectFileMatches("google/protobuf/descriptor.pb.h",
- "google/protobuf/descriptor.pb.h");
- output_directory.ExpectFileMatches("google/protobuf/descriptor.pb.cc",
- "google/protobuf/descriptor.pb.cc");
- output_directory.ExpectFileMatches("google/protobuf/compiler/plugin.pb.h",
- "google/protobuf/compiler/plugin.pb.h");
- output_directory.ExpectFileMatches("google/protobuf/compiler/plugin.pb.cc",
- "google/protobuf/compiler/plugin.pb.cc");
+ &context, &error));
+
+ context.ExpectFileMatches("google/protobuf/descriptor.pb.h",
+ "google/protobuf/descriptor.pb.h");
+ context.ExpectFileMatches("google/protobuf/descriptor.pb.cc",
+ "google/protobuf/descriptor.pb.cc");
+ context.ExpectFileMatches("google/protobuf/compiler/plugin.pb.h",
+ "google/protobuf/compiler/plugin.pb.h");
+ context.ExpectFileMatches("google/protobuf/compiler/plugin.pb.cc",
+ "google/protobuf/compiler/plugin.pb.cc");
}
} // namespace
diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc
index 76d2b79..c31cb5b 100644
--- a/src/google/protobuf/compiler/cpp/cpp_enum.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -45,11 +45,27 @@ namespace protobuf {
namespace compiler {
namespace cpp {
+namespace {
+// The GOOGLE_ARRAYSIZE constant is the max enum value plus 1. If the max enum value
+// is kint32max, GOOGLE_ARRAYSIZE will overflow. In such cases we should omit the
+// generation of the GOOGLE_ARRAYSIZE constant.
+bool ShouldGenerateArraySize(const EnumDescriptor* descriptor) {
+ int32 max_value = descriptor->value(0)->number();
+ for (int i = 0; i < descriptor->value_count(); i++) {
+ if (descriptor->value(i)->number() > max_value) {
+ max_value = descriptor->value(i)->number();
+ }
+ }
+ return max_value != kint32max;
+}
+} // namespace
+
EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
- const string& dllexport_decl)
+ const Options& options)
: descriptor_(descriptor),
classname_(ClassName(descriptor, false)),
- dllexport_decl_(dllexport_decl) {
+ options_(options),
+ generate_array_size_(ShouldGenerateArraySize(descriptor)) {
}
EnumGenerator::~EnumGenerator() {}
@@ -67,7 +83,10 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) {
for (int i = 0; i < descriptor_->value_count(); i++) {
vars["name"] = descriptor_->value(i)->name();
- vars["number"] = SimpleItoa(descriptor_->value(i)->number());
+ // In C++, an value of -2147483648 gets interpreted as the negative of
+ // 2147483648, and since 2147483648 can't fit in an integer, this produces a
+ // compiler warning. This works around that issue.
+ vars["number"] = Int32ToString(descriptor_->value(i)->number());
vars["prefix"] = (descriptor_->containing_type() == NULL) ?
"" : classname_ + "_";
@@ -88,18 +107,22 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) {
vars["min_name"] = min_value->name();
vars["max_name"] = max_value->name();
- if (dllexport_decl_.empty()) {
+ if (options_.dllexport_decl.empty()) {
vars["dllexport"] = "";
} else {
- vars["dllexport"] = dllexport_decl_ + " ";
+ vars["dllexport"] = options_.dllexport_decl + " ";
}
printer->Print(vars,
"$dllexport$bool $classname$_IsValid(int value);\n"
"const $classname$ $prefix$$short_name$_MIN = $prefix$$min_name$;\n"
- "const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n"
- "const int $prefix$$short_name$_ARRAYSIZE = $prefix$$short_name$_MAX + 1;\n"
- "\n");
+ "const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n");
+
+ if (generate_array_size_) {
+ printer->Print(vars,
+ "const int $prefix$$short_name$_ARRAYSIZE = "
+ "$prefix$$short_name$_MAX + 1;\n\n");
+ }
if (HasDescriptorMethods(descriptor_->file())) {
printer->Print(vars,
@@ -123,6 +146,7 @@ void EnumGenerator::
GenerateGetEnumDescriptorSpecializations(io::Printer* printer) {
if (HasDescriptorMethods(descriptor_->file())) {
printer->Print(
+ "template <> struct is_proto_enum< $classname$> : ::google::protobuf::internal::true_type {};\n"
"template <>\n"
"inline const EnumDescriptor* GetEnumDescriptor< $classname$>() {\n"
" return $classname$_descriptor();\n"
@@ -150,9 +174,12 @@ void EnumGenerator::GenerateSymbolImports(io::Printer* printer) {
"static const $nested_name$ $nested_name$_MIN =\n"
" $classname$_$nested_name$_MIN;\n"
"static const $nested_name$ $nested_name$_MAX =\n"
- " $classname$_$nested_name$_MAX;\n"
- "static const int $nested_name$_ARRAYSIZE =\n"
- " $classname$_$nested_name$_ARRAYSIZE;\n");
+ " $classname$_$nested_name$_MAX;\n");
+ if (generate_array_size_) {
+ printer->Print(vars,
+ "static const int $nested_name$_ARRAYSIZE =\n"
+ " $classname$_$nested_name$_ARRAYSIZE;\n");
+ }
if (HasDescriptorMethods(descriptor_->file())) {
printer->Print(vars,
@@ -218,7 +245,7 @@ void EnumGenerator::GenerateMethods(io::Printer* printer) {
iter != numbers.end(); ++iter) {
printer->Print(
" case $number$:\n",
- "number", SimpleItoa(*iter));
+ "number", Int32ToString(*iter));
}
printer->Print(vars,
@@ -245,8 +272,11 @@ void EnumGenerator::GenerateMethods(io::Printer* printer) {
}
printer->Print(vars,
"const $classname$ $parent$::$nested_name$_MIN;\n"
- "const $classname$ $parent$::$nested_name$_MAX;\n"
- "const int $parent$::$nested_name$_ARRAYSIZE;\n");
+ "const $classname$ $parent$::$nested_name$_MAX;\n");
+ if (generate_array_size_) {
+ printer->Print(vars,
+ "const int $parent$::$nested_name$_ARRAYSIZE;\n");
+ }
printer->Print("#endif // _MSC_VER\n");
}
diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.h b/src/google/protobuf/compiler/cpp/cpp_enum.h
index 58f7721..1ebd7cf 100644
--- a/src/google/protobuf/compiler/cpp/cpp_enum.h
+++ b/src/google/protobuf/compiler/cpp/cpp_enum.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -36,8 +36,10 @@
#define GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__
#include <string>
+#include <google/protobuf/compiler/cpp/cpp_options.h>
#include <google/protobuf/descriptor.h>
+
namespace google {
namespace protobuf {
namespace io {
@@ -53,7 +55,7 @@ class EnumGenerator {
public:
// See generator.cc for the meaning of dllexport_decl.
explicit EnumGenerator(const EnumDescriptor* descriptor,
- const string& dllexport_decl);
+ const Options& options);
~EnumGenerator();
// Header stuff.
@@ -86,7 +88,9 @@ class EnumGenerator {
private:
const EnumDescriptor* descriptor_;
string classname_;
- string dllexport_decl_;
+ Options options_;
+ // whether to generate the *_ARRAYSIZE constant.
+ bool generate_array_size_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator);
};
diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc
index 91ce493..20b18ad 100644
--- a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -46,11 +46,13 @@ namespace cpp {
namespace {
void SetEnumVariables(const FieldDescriptor* descriptor,
- map<string, string>* variables) {
- SetCommonFieldVariables(descriptor, variables);
+ map<string, string>* variables,
+ const Options& options) {
+ SetCommonFieldVariables(descriptor, variables, options);
const EnumValueDescriptor* default_value = descriptor->default_value_enum();
(*variables)["type"] = ClassName(descriptor->enum_type(), true);
- (*variables)["default"] = SimpleItoa(default_value->number());
+ (*variables)["default"] = Int32ToString(default_value->number());
+ (*variables)["full_name"] = descriptor->full_name();
}
} // namespace
@@ -58,9 +60,10 @@ void SetEnumVariables(const FieldDescriptor* descriptor,
// ===================================================================
EnumFieldGenerator::
-EnumFieldGenerator(const FieldDescriptor* descriptor)
+EnumFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options)
: descriptor_(descriptor) {
- SetEnumVariables(descriptor, &variables_);
+ SetEnumVariables(descriptor, &variables_, options);
}
EnumFieldGenerator::~EnumFieldGenerator() {}
@@ -81,12 +84,14 @@ void EnumFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline $type$ $classname$::$name$() const {\n"
+ " // @@protoc_insertion_point(field_get:$full_name$)\n"
" return static_cast< $type$ >($name$_);\n"
"}\n"
"inline void $classname$::set_$name$($type$ value) {\n"
- " GOOGLE_DCHECK($type$_IsValid(value));\n"
- " _set_bit($index$);\n"
+ " assert($type$_IsValid(value));\n"
+ " set_has_$name$();\n"
" $name$_ = value;\n"
+ " // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n");
}
@@ -119,10 +124,15 @@ GenerateMergeFromCodedStream(io::Printer* printer) const {
" input, &value)));\n"
"if ($type$_IsValid(value)) {\n"
" set_$name$(static_cast< $type$ >(value));\n");
- if (HasUnknownFields(descriptor_->file())) {
+ if (UseUnknownFieldSet(descriptor_->file())) {
printer->Print(variables_,
"} else {\n"
" mutable_unknown_fields()->AddVarint($number$, value);\n");
+ } else {
+ printer->Print(
+ "} else {\n"
+ " unknown_fields_stream.WriteVarint32(tag);\n"
+ " unknown_fields_stream.WriteVarint32(value);\n");
}
printer->Print(variables_,
"}\n");
@@ -151,10 +161,57 @@ GenerateByteSize(io::Printer* printer) const {
// ===================================================================
+EnumOneofFieldGenerator::
+EnumOneofFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options)
+ : EnumFieldGenerator(descriptor, options) {
+ SetCommonOneofFieldVariables(descriptor, &variables_);
+}
+
+EnumOneofFieldGenerator::~EnumOneofFieldGenerator() {}
+
+void EnumOneofFieldGenerator::
+GenerateInlineAccessorDefinitions(io::Printer* printer) const {
+ printer->Print(variables_,
+ "inline $type$ $classname$::$name$() const {\n"
+ " if (has_$name$()) {\n"
+ " return static_cast< $type$ >($oneof_prefix$$name$_);\n"
+ " }\n"
+ " return static_cast< $type$ >($default$);\n"
+ "}\n"
+ "inline void $classname$::set_$name$($type$ value) {\n"
+ " assert($type$_IsValid(value));\n"
+ " if (!has_$name$()) {\n"
+ " clear_$oneof_name$();\n"
+ " set_has_$name$();\n"
+ " }\n"
+ " $oneof_prefix$$name$_ = value;\n"
+ "}\n");
+}
+
+void EnumOneofFieldGenerator::
+GenerateClearingCode(io::Printer* printer) const {
+ printer->Print(variables_, "$oneof_prefix$$name$_ = $default$;\n");
+}
+
+void EnumOneofFieldGenerator::
+GenerateSwappingCode(io::Printer* printer) const {
+ // Don't print any swapping code. Swapping the union will swap this field.
+}
+
+void EnumOneofFieldGenerator::
+GenerateConstructorCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ " $classname$_default_oneof_instance_->$name$_ = $default$;\n");
+}
+
+// ===================================================================
+
RepeatedEnumFieldGenerator::
-RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor)
+RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options)
: descriptor_(descriptor) {
- SetEnumVariables(descriptor, &variables_);
+ SetEnumVariables(descriptor, &variables_, options);
}
RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {}
@@ -163,7 +220,8 @@ void RepeatedEnumFieldGenerator::
GeneratePrivateMembers(io::Printer* printer) const {
printer->Print(variables_,
"::google::protobuf::RepeatedField<int> $name$_;\n");
- if (descriptor_->options().packed() && HasGeneratedMethods(descriptor_->file())) {
+ if (descriptor_->options().packed()
+ && HasGeneratedMethods(descriptor_->file())) {
printer->Print(variables_,
"mutable int _$name$_cached_byte_size_;\n");
}
@@ -184,23 +242,28 @@ void RepeatedEnumFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline $type$ $classname$::$name$(int index) const {\n"
+ " // @@protoc_insertion_point(field_get:$full_name$)\n"
" return static_cast< $type$ >($name$_.Get(index));\n"
"}\n"
"inline void $classname$::set_$name$(int index, $type$ value) {\n"
- " GOOGLE_DCHECK($type$_IsValid(value));\n"
+ " assert($type$_IsValid(value));\n"
" $name$_.Set(index, value);\n"
+ " // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n"
"inline void $classname$::add_$name$($type$ value) {\n"
- " GOOGLE_DCHECK($type$_IsValid(value));\n"
+ " assert($type$_IsValid(value));\n"
" $name$_.Add(value);\n"
+ " // @@protoc_insertion_point(field_add:$full_name$)\n"
"}\n");
printer->Print(variables_,
"inline const ::google::protobuf::RepeatedField<int>&\n"
"$classname$::$name$() const {\n"
+ " // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $name$_;\n"
"}\n"
"inline ::google::protobuf::RepeatedField<int>*\n"
"$classname$::mutable_$name$() {\n"
+ " // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return &$name$_;\n"
"}\n");
}
@@ -235,10 +298,15 @@ GenerateMergeFromCodedStream(io::Printer* printer) const {
" input, &value)));\n"
"if ($type$_IsValid(value)) {\n"
" add_$name$(static_cast< $type$ >(value));\n");
- if (HasUnknownFields(descriptor_->file())) {
+ if (UseUnknownFieldSet(descriptor_->file())) {
printer->Print(variables_,
"} else {\n"
" mutable_unknown_fields()->AddVarint($number$, value);\n");
+ } else {
+ printer->Print(
+ "} else {\n"
+ " unknown_fields_stream.WriteVarint32(tag);\n"
+ " unknown_fields_stream.WriteVarint32(value);\n");
}
printer->Print("}\n");
}
@@ -345,7 +413,9 @@ GenerateByteSize(io::Printer* printer) const {
" total_size += $tag_size$ +\n"
" ::google::protobuf::internal::WireFormatLite::Int32Size(data_size);\n"
"}\n"
+ "GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();\n"
"_$name$_cached_byte_size_ = data_size;\n"
+ "GOOGLE_SAFE_CONCURRENT_WRITES_END();\n"
"total_size += data_size;\n");
} else {
printer->Print(variables_,
diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.h b/src/google/protobuf/compiler/cpp/cpp_enum_field.h
index 0793430..def2b23 100644
--- a/src/google/protobuf/compiler/cpp/cpp_enum_field.h
+++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -46,7 +46,8 @@ namespace cpp {
class EnumFieldGenerator : public FieldGenerator {
public:
- explicit EnumFieldGenerator(const FieldDescriptor* descriptor);
+ explicit EnumFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options);
~EnumFieldGenerator();
// implements FieldGenerator ---------------------------------------
@@ -62,16 +63,34 @@ class EnumFieldGenerator : public FieldGenerator {
void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const;
void GenerateByteSize(io::Printer* printer) const;
- private:
+ protected:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator);
};
+class EnumOneofFieldGenerator : public EnumFieldGenerator {
+ public:
+ explicit EnumOneofFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options);
+ ~EnumOneofFieldGenerator();
+
+ // implements FieldGenerator ---------------------------------------
+ void GenerateInlineAccessorDefinitions(io::Printer* printer) const;
+ void GenerateClearingCode(io::Printer* printer) const;
+ void GenerateSwappingCode(io::Printer* printer) const;
+ void GenerateConstructorCode(io::Printer* printer) const;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumOneofFieldGenerator);
+};
+
class RepeatedEnumFieldGenerator : public FieldGenerator {
public:
- explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor);
+ explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options);
~RepeatedEnumFieldGenerator();
// implements FieldGenerator ---------------------------------------
diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.cc b/src/google/protobuf/compiler/cpp/cpp_extension.cc
index 658a707..468ca48 100644
--- a/src/google/protobuf/compiler/cpp/cpp_extension.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_extension.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -57,9 +57,9 @@ string ExtendeeClassName(const FieldDescriptor* descriptor) {
} // anonymous namespace
ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor,
- const string& dllexport_decl)
+ const Options& options)
: descriptor_(descriptor),
- dllexport_decl_(dllexport_decl) {
+ options_(options) {
// Construct type_traits_.
if (descriptor_->is_repeated()) {
type_traits_ = "Repeated";
@@ -106,8 +106,8 @@ void ExtensionGenerator::GenerateDeclaration(io::Printer* printer) {
// export/import specifier.
if (descriptor_->extension_scope() == NULL) {
vars["qualifier"] = "extern";
- if (!dllexport_decl_.empty()) {
- vars["qualifier"] = dllexport_decl_ + " " + vars["qualifier"];
+ if (!options_.dllexport_decl.empty()) {
+ vars["qualifier"] = options_.dllexport_decl + " " + vars["qualifier"];
}
} else {
vars["qualifier"] = "static";
diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.h b/src/google/protobuf/compiler/cpp/cpp_extension.h
index 3068b09..d354c16 100644
--- a/src/google/protobuf/compiler/cpp/cpp_extension.h
+++ b/src/google/protobuf/compiler/cpp/cpp_extension.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -37,6 +37,7 @@
#include <string>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/compiler/cpp/cpp_options.h>
namespace google {
namespace protobuf {
@@ -56,8 +57,8 @@ namespace cpp {
class ExtensionGenerator {
public:
// See generator.cc for the meaning of dllexport_decl.
- explicit ExtensionGenerator(const FieldDescriptor* descriptor,
- const string& dllexport_decl);
+ explicit ExtensionGenerator(const FieldDescriptor* desycriptor,
+ const Options& options);
~ExtensionGenerator();
// Header stuff.
@@ -72,7 +73,7 @@ class ExtensionGenerator {
private:
const FieldDescriptor* descriptor_;
string type_traits_;
- string dllexport_decl_;
+ Options options_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator);
};
diff --git a/src/google/protobuf/compiler/cpp/cpp_field.cc b/src/google/protobuf/compiler/cpp/cpp_field.cc
index 103cac4..1e9a40a 100644
--- a/src/google/protobuf/compiler/cpp/cpp_field.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_field.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -33,6 +33,8 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/cpp_field.h>
+#include <memory>
+
#include <google/protobuf/compiler/cpp/cpp_helpers.h>
#include <google/protobuf/compiler/cpp/cpp_primitive_field.h>
#include <google/protobuf/compiler/cpp/cpp_string_field.h>
@@ -52,7 +54,8 @@ namespace cpp {
using internal::WireFormat;
void SetCommonFieldVariables(const FieldDescriptor* descriptor,
- map<string, string>* variables) {
+ map<string, string>* variables,
+ const Options& options) {
(*variables)["name"] = FieldName(descriptor);
(*variables)["index"] = SimpleItoa(descriptor->index());
(*variables)["number"] = SimpleItoa(descriptor->number());
@@ -64,6 +67,13 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor,
(*variables)["deprecation"] = descriptor->options().deprecated()
? " PROTOBUF_DEPRECATED" : "";
+ (*variables)["cppget"] = "Get";
+}
+
+void SetCommonOneofFieldVariables(const FieldDescriptor* descriptor,
+ map<string, string>* variables) {
+ (*variables)["oneof_prefix"] = descriptor->containing_oneof()->name() + "_.";
+ (*variables)["oneof_name"] = descriptor->containing_oneof()->name();
}
FieldGenerator::~FieldGenerator() {}
@@ -80,46 +90,63 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const {
}
-FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor)
- : descriptor_(descriptor),
- field_generators_(
- new scoped_ptr<FieldGenerator>[descriptor->field_count()]) {
+FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor,
+ const Options& options)
+ : descriptor_(descriptor),
+ field_generators_(
+ new scoped_ptr<FieldGenerator>[descriptor->field_count()]) {
// Construct all the FieldGenerators.
for (int i = 0; i < descriptor->field_count(); i++) {
- field_generators_[i].reset(MakeGenerator(descriptor->field(i)));
+ field_generators_[i].reset(MakeGenerator(descriptor->field(i), options));
}
}
-FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field) {
+FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field,
+ const Options& options) {
if (field->is_repeated()) {
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE:
- return new RepeatedMessageFieldGenerator(field);
+ return new RepeatedMessageFieldGenerator(field, options);
case FieldDescriptor::CPPTYPE_STRING:
switch (field->options().ctype()) {
default: // RepeatedStringFieldGenerator handles unknown ctypes.
case FieldOptions::STRING:
- return new RepeatedStringFieldGenerator(field);
+ return new RepeatedStringFieldGenerator(field, options);
+ }
+ case FieldDescriptor::CPPTYPE_ENUM:
+ return new RepeatedEnumFieldGenerator(field, options);
+ default:
+ return new RepeatedPrimitiveFieldGenerator(field, options);
+ }
+ } else if (field->containing_oneof()) {
+ switch (field->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_MESSAGE:
+ return new MessageOneofFieldGenerator(field, options);
+ case FieldDescriptor::CPPTYPE_STRING:
+ switch (field->options().ctype()) {
+ default: // StringOneofFieldGenerator handles unknown ctypes.
+ case FieldOptions::STRING:
+ return new StringOneofFieldGenerator(field, options);
}
case FieldDescriptor::CPPTYPE_ENUM:
- return new RepeatedEnumFieldGenerator(field);
+ return new EnumOneofFieldGenerator(field, options);
default:
- return new RepeatedPrimitiveFieldGenerator(field);
+ return new PrimitiveOneofFieldGenerator(field, options);
}
} else {
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE:
- return new MessageFieldGenerator(field);
+ return new MessageFieldGenerator(field, options);
case FieldDescriptor::CPPTYPE_STRING:
switch (field->options().ctype()) {
default: // StringFieldGenerator handles unknown ctypes.
case FieldOptions::STRING:
- return new StringFieldGenerator(field);
+ return new StringFieldGenerator(field, options);
}
case FieldDescriptor::CPPTYPE_ENUM:
- return new EnumFieldGenerator(field);
+ return new EnumFieldGenerator(field, options);
default:
- return new PrimitiveFieldGenerator(field);
+ return new PrimitiveFieldGenerator(field, options);
}
}
}
diff --git a/src/google/protobuf/compiler/cpp/cpp_field.h b/src/google/protobuf/compiler/cpp/cpp_field.h
index c303a33..96c2963 100644
--- a/src/google/protobuf/compiler/cpp/cpp_field.h
+++ b/src/google/protobuf/compiler/cpp/cpp_field.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -36,10 +36,11 @@
#define GOOGLE_PROTOBUF_COMPILER_CPP_FIELD_H__
#include <map>
+#include <memory>
#include <string>
-#include <google/protobuf/stubs/common.h>
#include <google/protobuf/descriptor.h>
+#include <google/protobuf/compiler/cpp/cpp_options.h>
namespace google {
namespace protobuf {
@@ -57,7 +58,11 @@ namespace cpp {
// ['name', 'index', 'number', 'classname', 'declared_type', 'tag_size',
// 'deprecation'].
void SetCommonFieldVariables(const FieldDescriptor* descriptor,
- map<string, string>* variables);
+ map<string, string>* variables,
+ const Options& options);
+
+void SetCommonOneofFieldVariables(const FieldDescriptor* descriptor,
+ map<string, string>* variables);
class FieldGenerator {
public:
@@ -69,6 +74,11 @@ class FieldGenerator {
// class.
virtual void GeneratePrivateMembers(io::Printer* printer) const = 0;
+ // Generate static default variable for this field. These are placed inside
+ // the message class. Most field types don't need this, so the default
+ // implementation is empty.
+ virtual void GenerateStaticMembers(io::Printer* printer) const {}
+
// Generate prototypes for all of the accessor functions related to this
// field. These are placed inside the class definition.
virtual void GenerateAccessorDeclarations(io::Printer* printer) const = 0;
@@ -114,6 +124,13 @@ class FieldGenerator {
// Most field types don't need this, so the default implementation is empty.
virtual void GenerateDestructorCode(io::Printer* printer) const {}
+ // Generate code that allocates the fields's default instance.
+ virtual void GenerateDefaultInstanceAllocator(io::Printer* printer) const {}
+
+ // Generate code that should be run when ShutdownProtobufLibrary() is called,
+ // to delete all dynamically-allocated objects.
+ virtual void GenerateShutdownCode(io::Printer* printer) const {}
+
// Generate lines to decode this field, which will be placed inside the
// message's MergeFromCodedStream() method.
virtual void GenerateMergeFromCodedStream(io::Printer* printer) const = 0;
@@ -144,7 +161,7 @@ class FieldGenerator {
// Convenience class which constructs FieldGenerators for a Descriptor.
class FieldGeneratorMap {
public:
- explicit FieldGeneratorMap(const Descriptor* descriptor);
+ explicit FieldGeneratorMap(const Descriptor* descriptor, const Options& options);
~FieldGeneratorMap();
const FieldGenerator& get(const FieldDescriptor* field) const;
@@ -153,7 +170,8 @@ class FieldGeneratorMap {
const Descriptor* descriptor_;
scoped_array<scoped_ptr<FieldGenerator> > field_generators_;
- static FieldGenerator* MakeGenerator(const FieldDescriptor* field);
+ static FieldGenerator* MakeGenerator(const FieldDescriptor* field,
+ const Options& options);
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap);
};
diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc
index 80da7e4..fa19427 100644
--- a/src/google/protobuf/compiler/cpp/cpp_file.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_file.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -33,6 +33,9 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/cpp_file.h>
+#include <memory>
+#include <set>
+
#include <google/protobuf/compiler/cpp/cpp_enum.h>
#include <google/protobuf/compiler/cpp/cpp_service.h>
#include <google/protobuf/compiler/cpp/cpp_extension.h>
@@ -50,37 +53,36 @@ namespace cpp {
// ===================================================================
-FileGenerator::FileGenerator(const FileDescriptor* file,
- const string& dllexport_decl)
- : file_(file),
- message_generators_(
- new scoped_ptr<MessageGenerator>[file->message_type_count()]),
- enum_generators_(
- new scoped_ptr<EnumGenerator>[file->enum_type_count()]),
- service_generators_(
- new scoped_ptr<ServiceGenerator>[file->service_count()]),
- extension_generators_(
- new scoped_ptr<ExtensionGenerator>[file->extension_count()]),
- dllexport_decl_(dllexport_decl) {
+FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options)
+ : file_(file),
+ message_generators_(
+ new scoped_ptr<MessageGenerator>[file->message_type_count()]),
+ enum_generators_(
+ new scoped_ptr<EnumGenerator>[file->enum_type_count()]),
+ service_generators_(
+ new scoped_ptr<ServiceGenerator>[file->service_count()]),
+ extension_generators_(
+ new scoped_ptr<ExtensionGenerator>[file->extension_count()]),
+ options_(options) {
for (int i = 0; i < file->message_type_count(); i++) {
message_generators_[i].reset(
- new MessageGenerator(file->message_type(i), dllexport_decl));
+ new MessageGenerator(file->message_type(i), options));
}
for (int i = 0; i < file->enum_type_count(); i++) {
enum_generators_[i].reset(
- new EnumGenerator(file->enum_type(i), dllexport_decl));
+ new EnumGenerator(file->enum_type(i), options));
}
for (int i = 0; i < file->service_count(); i++) {
service_generators_[i].reset(
- new ServiceGenerator(file->service(i), dllexport_decl));
+ new ServiceGenerator(file->service(i), options));
}
for (int i = 0; i < file->extension_count(); i++) {
extension_generators_[i].reset(
- new ExtensionGenerator(file->extension(i), dllexport_decl));
+ new ExtensionGenerator(file->extension(i), options));
}
SplitStringUsing(file_->package(), ".", &package_parts_);
@@ -104,6 +106,7 @@ void FileGenerator::GenerateHeader(io::Printer* printer) {
"filename", file_->name(),
"filename_identifier", filename_identifier);
+
printer->Print(
"#include <google/protobuf/stubs/common.h>\n"
"\n");
@@ -128,13 +131,23 @@ void FileGenerator::GenerateHeader(io::Printer* printer) {
// OK, it's now safe to #include other files.
printer->Print(
- "#include <google/protobuf/generated_message_util.h>\n"
+ "#include <google/protobuf/generated_message_util.h>\n");
+ if (file_->message_type_count() > 0) {
+ if (HasDescriptorMethods(file_)) {
+ printer->Print(
+ "#include <google/protobuf/message.h>\n");
+ } else {
+ printer->Print(
+ "#include <google/protobuf/message_lite.h>\n");
+ }
+ }
+ printer->Print(
"#include <google/protobuf/repeated_field.h>\n"
"#include <google/protobuf/extension_set.h>\n");
- if (HasDescriptorMethods(file_)) {
+ if (HasDescriptorMethods(file_) && HasEnumDefinitions(file_)) {
printer->Print(
- "#include <google/protobuf/generated_message_reflection.h>\n");
+ "#include <google/protobuf/generated_enum_reflection.h>\n");
}
if (HasGenericServices(file_)) {
@@ -142,16 +155,32 @@ void FileGenerator::GenerateHeader(io::Printer* printer) {
"#include <google/protobuf/service.h>\n");
}
+ if (UseUnknownFieldSet(file_) && file_->message_type_count() > 0) {
+ printer->Print(
+ "#include <google/protobuf/unknown_field_set.h>\n");
+ }
+
+
+ set<string> public_import_names;
+ for (int i = 0; i < file_->public_dependency_count(); i++) {
+ public_import_names.insert(file_->public_dependency(i)->name());
+ }
for (int i = 0; i < file_->dependency_count(); i++) {
+ const string& name = file_->dependency(i)->name();
+ bool public_import = (public_import_names.count(name) != 0);
+
+
printer->Print(
- "#include \"$dependency$.pb.h\"\n",
- "dependency", StripProto(file_->dependency(i)->name()));
+ "#include \"$dependency$.pb.h\"$iwyu$\n",
+ "dependency", StripProto(name),
+ "iwyu", (public_import) ? " // IWYU pragma: export" : "");
}
printer->Print(
"// @@protoc_insertion_point(includes)\n");
+
// Open namespace.
GenerateNamespaceOpeners(printer);
@@ -162,7 +191,7 @@ void FileGenerator::GenerateHeader(io::Printer* printer) {
"// Internal implementation detail -- do not call these.\n"
"void $dllexport_decl$ $adddescriptorsname$();\n",
"adddescriptorsname", GlobalAddDescriptorsName(file_->name()),
- "dllexport_decl", dllexport_decl_);
+ "dllexport_decl", options_.dllexport_decl);
printer->Print(
// Note that we don't put dllexport_decl on these because they are only
@@ -230,6 +259,7 @@ void FileGenerator::GenerateHeader(io::Printer* printer) {
printer->Print(kThickSeparator);
printer->Print("\n");
+
// Generate class inline methods.
for (int i = 0; i < file_->message_type_count(); i++) {
if (i > 0) {
@@ -282,20 +312,33 @@ void FileGenerator::GenerateHeader(io::Printer* printer) {
void FileGenerator::GenerateSource(io::Printer* printer) {
printer->Print(
"// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
+ "// source: $filename$\n"
"\n"
+
// The generated code calls accessors that might be deprecated. We don't
// want the compiler to warn in generated code.
"#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION\n"
"#include \"$basename$.pb.h\"\n"
-
+ "\n"
+ "#include <algorithm>\n" // for swap()
+ "\n"
+ "#include <google/protobuf/stubs/common.h>\n"
"#include <google/protobuf/stubs/once.h>\n"
"#include <google/protobuf/io/coded_stream.h>\n"
"#include <google/protobuf/wire_format_lite_inl.h>\n",
+ "filename", file_->name(),
"basename", StripProto(file_->name()));
+ // Unknown fields implementation in lite mode uses StringOutputStream
+ if (!UseUnknownFieldSet(file_) && file_->message_type_count() > 0) {
+ printer->Print(
+ "#include <google/protobuf/io/zero_copy_stream_impl_lite.h>\n");
+ }
+
if (HasDescriptorMethods(file_)) {
printer->Print(
"#include <google/protobuf/descriptor.h>\n"
+ "#include <google/protobuf/generated_message_reflection.h>\n"
"#include <google/protobuf/reflection_ops.h>\n"
"#include <google/protobuf/wire_format.h>\n");
}
@@ -378,11 +421,12 @@ void FileGenerator::GenerateSource(io::Printer* printer) {
void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) {
// AddDescriptors() is a file-level procedure which adds the encoded
- // FileDescriptorProto for this .proto file to the global DescriptorPool
- // for generated files (DescriptorPool::generated_pool()). It always runs
- // at static initialization time, so all files will be registered before
- // main() starts. This procedure also constructs default instances and
- // registers extensions.
+ // FileDescriptorProto for this .proto file to the global DescriptorPool for
+ // generated files (DescriptorPool::generated_pool()). It either runs at
+ // static initialization time (by default) or when default_instance() is
+ // called for the first time (in LITE_RUNTIME mode with
+ // GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER flag enabled). This procedure also
+ // constructs default instances and registers extensions.
//
// Its sibling, AssignDescriptors(), actually pulls the compiled
// FileDescriptor from the DescriptorPool and uses it to populate all of
@@ -486,22 +530,29 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) {
printer->Outdent();
printer->Print(
- "}\n");
+ "}\n\n");
// -----------------------------------------------------------------
// Now generate the AddDescriptors() function.
- printer->Print(
- "\n"
+ PrintHandlingOptionalStaticInitializers(
+ file_, printer,
+ // With static initializers.
+ // Note that we don't need any special synchronization in the following code
+ // because it is called at static init time before any threads exist.
"void $adddescriptorsname$() {\n"
- // We don't need any special synchronization here because this code is
- // called at static init time before any threads exist.
" static bool already_here = false;\n"
" if (already_here) return;\n"
" already_here = true;\n"
" GOOGLE_PROTOBUF_VERIFY_VERSION;\n"
"\n",
+ // Without.
+ "void $adddescriptorsname$_impl() {\n"
+ " GOOGLE_PROTOBUF_VERIFY_VERSION;\n"
+ "\n",
+ // Vars.
"adddescriptorsname", GlobalAddDescriptorsName(file_->name()));
+
printer->Indent();
// Call the AddDescriptors() methods for all of our dependencies, to make
@@ -509,17 +560,12 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) {
for (int i = 0; i < file_->dependency_count(); i++) {
const FileDescriptor* dependency = file_->dependency(i);
// Print the namespace prefix for the dependency.
- vector<string> dependency_package_parts;
- SplitStringUsing(dependency->package(), ".", &dependency_package_parts);
- printer->Print("::");
- for (int i = 0; i < dependency_package_parts.size(); i++) {
- printer->Print("$name$::",
- "name", dependency_package_parts[i]);
- }
+ string add_desc_name = QualifiedFileLevelSymbol(
+ dependency->package(), GlobalAddDescriptorsName(dependency->name()));
// Call its AddDescriptors function.
printer->Print(
"$name$();\n",
- "name", GlobalAddDescriptorsName(dependency->name()));
+ "name", add_desc_name);
}
if (HasDescriptorMethods(file_)) {
@@ -538,10 +584,12 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) {
static const int kBytesPerLine = 40;
for (int i = 0; i < file_data.size(); i += kBytesPerLine) {
printer->Print("\n \"$data$\"",
- "data", CEscape(file_data.substr(i, kBytesPerLine)));
+ "data",
+ EscapeTrigraphs(
+ CEscape(file_data.substr(i, kBytesPerLine))));
}
printer->Print(
- ", $size$);\n",
+ ", $size$);\n",
"size", SimpleItoa(file_data.size()));
// Call MessageFactory::InternalRegisterGeneratedFile().
@@ -569,17 +617,26 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) {
"shutdownfilename", GlobalShutdownFileName(file_->name()));
printer->Outdent();
-
printer->Print(
"}\n"
- "\n"
+ "\n");
+
+ PrintHandlingOptionalStaticInitializers(
+ file_, printer,
+ // With static initializers.
"// Force AddDescriptors() to be called at static initialization time.\n"
"struct StaticDescriptorInitializer_$filename$ {\n"
" StaticDescriptorInitializer_$filename$() {\n"
" $adddescriptorsname$();\n"
" }\n"
- "} static_descriptor_initializer_$filename$_;\n"
- "\n",
+ "} static_descriptor_initializer_$filename$_;\n",
+ // Without.
+ "GOOGLE_PROTOBUF_DECLARE_ONCE($adddescriptorsname$_once_);\n"
+ "void $adddescriptorsname$() {\n"
+ " ::google::protobuf::GoogleOnceInit(&$adddescriptorsname$_once_,\n"
+ " &$adddescriptorsname$_impl);\n"
+ "}\n",
+ // Vars.
"adddescriptorsname", GlobalAddDescriptorsName(file_->name()),
"filename", FilenameIdentifier(file_->name()));
}
diff --git a/src/google/protobuf/compiler/cpp/cpp_file.h b/src/google/protobuf/compiler/cpp/cpp_file.h
index b4e0128..4e4d8b6 100644
--- a/src/google/protobuf/compiler/cpp/cpp_file.h
+++ b/src/google/protobuf/compiler/cpp/cpp_file.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -35,10 +35,12 @@
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__
+#include <memory>
#include <string>
#include <vector>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/cpp/cpp_field.h>
+#include <google/protobuf/compiler/cpp/cpp_options.h>
namespace google {
namespace protobuf {
@@ -61,7 +63,7 @@ class FileGenerator {
public:
// See generator.cc for the meaning of dllexport_decl.
explicit FileGenerator(const FileDescriptor* file,
- const string& dllexport_decl);
+ const Options& options);
~FileGenerator();
void GenerateHeader(io::Printer* printer);
@@ -84,8 +86,7 @@ class FileGenerator {
// E.g. if the package is foo.bar, package_parts_ is {"foo", "bar"}.
vector<string> package_parts_;
-
- string dllexport_decl_;
+ const Options options_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator);
};
diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.cc b/src/google/protobuf/compiler/cpp/cpp_generator.cc
index d67d350..75d558e 100644
--- a/src/google/protobuf/compiler/cpp/cpp_generator.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -35,6 +35,7 @@
#include <google/protobuf/compiler/cpp/cpp_generator.h>
#include <vector>
+#include <memory>
#include <utility>
#include <google/protobuf/compiler/cpp/cpp_file.h>
@@ -53,7 +54,7 @@ CppGenerator::~CppGenerator() {}
bool CppGenerator::Generate(const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* generator_context,
string* error) const {
vector<pair<string, string> > options;
ParseGeneratorParameter(parameter, &options);
@@ -78,11 +79,13 @@ bool CppGenerator::Generate(const FileDescriptor* file,
// }
// FOO_EXPORT is a macro which should expand to __declspec(dllexport) or
// __declspec(dllimport) depending on what is being compiled.
- string dllexport_decl;
+ Options file_options;
for (int i = 0; i < options.size(); i++) {
if (options[i].first == "dllexport_decl") {
- dllexport_decl = options[i].second;
+ file_options.dllexport_decl = options[i].second;
+ } else if (options[i].first == "safe_boundary_check") {
+ file_options.safe_boundary_check = true;
} else {
*error = "Unknown generator option: " + options[i].first;
return false;
@@ -95,12 +98,12 @@ bool CppGenerator::Generate(const FileDescriptor* file,
string basename = StripProto(file->name());
basename.append(".pb");
- FileGenerator file_generator(file, dllexport_decl);
+ FileGenerator file_generator(file, file_options);
// Generate header.
{
scoped_ptr<io::ZeroCopyOutputStream> output(
- output_directory->Open(basename + ".h"));
+ generator_context->Open(basename + ".h"));
io::Printer printer(output.get(), '$');
file_generator.GenerateHeader(&printer);
}
@@ -108,7 +111,7 @@ bool CppGenerator::Generate(const FileDescriptor* file,
// Generate cc file.
{
scoped_ptr<io::ZeroCopyOutputStream> output(
- output_directory->Open(basename + ".cc"));
+ generator_context->Open(basename + ".cc"));
io::Printer printer(output.get(), '$');
file_generator.GenerateSource(&printer);
}
diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.h b/src/google/protobuf/compiler/cpp/cpp_generator.h
index f52e886..3d517cf 100644
--- a/src/google/protobuf/compiler/cpp/cpp_generator.h
+++ b/src/google/protobuf/compiler/cpp/cpp_generator.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -57,7 +57,7 @@ class LIBPROTOC_EXPORT CppGenerator : public CodeGenerator {
// implements CodeGenerator ----------------------------------------
bool Generate(const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* generator_context,
string* error) const;
private:
diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc
index e3df88b..b7a47ac 100644
--- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -33,10 +33,12 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <limits>
+#include <map>
#include <vector>
#include <google/protobuf/stubs/hash.h>
#include <google/protobuf/compiler/cpp/cpp_helpers.h>
+#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
@@ -80,6 +82,22 @@ hash_set<string> MakeKeywordsMap() {
hash_set<string> kKeywords = MakeKeywordsMap();
+// Returns whether the provided descriptor has an extension. This includes its
+// nested types.
+bool HasExtension(const Descriptor* descriptor) {
+ if (descriptor->extension_count() > 0) {
+ return true;
+ }
+ for (int i = 0; i < descriptor->nested_type_count(); ++i) {
+ if (HasExtension(descriptor->nested_type(i))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
string UnderscoresToCamelCase(const string& input, bool cap_next_letter) {
string result;
// Note: I distrust ctype.h due to locales.
@@ -105,8 +123,6 @@ string UnderscoresToCamelCase(const string& input, bool cap_next_letter) {
return result;
}
-} // namespace
-
const char kThickSeparator[] =
"// ===================================================================\n";
const char kThinSeparator[] =
@@ -132,7 +148,7 @@ string ClassName(const Descriptor* descriptor, bool qualified) {
string ClassName(const EnumDescriptor* enum_descriptor, bool qualified) {
if (enum_descriptor->containing_type() == NULL) {
if (qualified) {
- return DotsToColons(enum_descriptor->full_name());
+ return "::" + DotsToColons(enum_descriptor->full_name());
} else {
return enum_descriptor->name();
}
@@ -240,14 +256,35 @@ const char* DeclaredTypeMethodName(FieldDescriptor::Type type) {
return "";
}
+string Int32ToString(int number) {
+ // gcc rejects the decimal form of kint32min.
+ if (number == kint32min) {
+ GOOGLE_COMPILE_ASSERT(kint32min == (~0x7fffffff), kint32min_value_error);
+ return "(~0x7fffffff)";
+ } else {
+ return SimpleItoa(number);
+ }
+}
+
+string Int64ToString(int64 number) {
+ // gcc rejects the decimal form of kint64min
+ if (number == kint64min) {
+ // Make sure we are in a 2's complement system.
+ GOOGLE_COMPILE_ASSERT(kint64min == GOOGLE_LONGLONG(~0x7fffffffffffffff),
+ kint64min_value_error);
+ return "GOOGLE_LONGLONG(~0x7fffffffffffffff)";
+ }
+ return "GOOGLE_LONGLONG(" + SimpleItoa(number) + ")";
+}
+
string DefaultValue(const FieldDescriptor* field) {
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_INT32:
- return SimpleItoa(field->default_value_int32());
+ return Int32ToString(field->default_value_int32());
case FieldDescriptor::CPPTYPE_UINT32:
return SimpleItoa(field->default_value_uint32()) + "u";
case FieldDescriptor::CPPTYPE_INT64:
- return "GOOGLE_LONGLONG(" + SimpleItoa(field->default_value_int64()) + ")";
+ return Int64ToString(field->default_value_int64());
case FieldDescriptor::CPPTYPE_UINT64:
return "GOOGLE_ULONGLONG(" + SimpleItoa(field->default_value_uint64())+ ")";
case FieldDescriptor::CPPTYPE_DOUBLE: {
@@ -290,9 +327,11 @@ string DefaultValue(const FieldDescriptor* field) {
return strings::Substitute(
"static_cast< $0 >($1)",
ClassName(field->enum_type(), true),
- field->default_value_enum()->number());
+ Int32ToString(field->default_value_enum()->number()));
case FieldDescriptor::CPPTYPE_STRING:
- return "\"" + CEscape(field->default_value_string()) + "\"";
+ return "\"" + EscapeTrigraphs(
+ CEscape(field->default_value_string())) +
+ "\"";
case FieldDescriptor::CPPTYPE_MESSAGE:
return FieldMessageTypeName(field) + "::default_instance()";
}
@@ -335,6 +374,120 @@ string GlobalShutdownFileName(const string& filename) {
return "protobuf_ShutdownFile_" + FilenameIdentifier(filename);
}
+// Return the qualified C++ name for a file level symbol.
+string QualifiedFileLevelSymbol(const string& package, const string& name) {
+ if (package.empty()) {
+ return StrCat("::", name);
+ }
+ return StrCat("::", DotsToColons(package), "::", name);
+}
+
+// Escape C++ trigraphs by escaping question marks to \?
+string EscapeTrigraphs(const string& to_escape) {
+ return StringReplace(to_escape, "?", "\\?", true);
+}
+
+// Escaped function name to eliminate naming conflict.
+string SafeFunctionName(const Descriptor* descriptor,
+ const FieldDescriptor* field,
+ const string& prefix) {
+ // Do not use FieldName() since it will escape keywords.
+ string name = field->name();
+ LowerString(&name);
+ string function_name = prefix + name;
+ if (descriptor->FindFieldByName(function_name)) {
+ // Single underscore will also make it conflicting with the private data
+ // member. We use double underscore to escape function names.
+ function_name.append("__");
+ } else if (kKeywords.count(name) > 0) {
+ // If the field name is a keyword, we append the underscore back to keep it
+ // consistent with other function names.
+ function_name.append("_");
+ }
+ return function_name;
+}
+
+bool StaticInitializersForced(const FileDescriptor* file) {
+ if (HasDescriptorMethods(file) || file->extension_count() > 0) {
+ return true;
+ }
+ for (int i = 0; i < file->message_type_count(); ++i) {
+ if (HasExtension(file->message_type(i))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void PrintHandlingOptionalStaticInitializers(
+ const FileDescriptor* file, io::Printer* printer,
+ const char* with_static_init, const char* without_static_init,
+ const char* var1, const string& val1,
+ const char* var2, const string& val2) {
+ map<string, string> vars;
+ if (var1) {
+ vars[var1] = val1;
+ }
+ if (var2) {
+ vars[var2] = val2;
+ }
+ PrintHandlingOptionalStaticInitializers(
+ vars, file, printer, with_static_init, without_static_init);
+}
+
+void PrintHandlingOptionalStaticInitializers(
+ const map<string, string>& vars, const FileDescriptor* file,
+ io::Printer* printer, const char* with_static_init,
+ const char* without_static_init) {
+ if (StaticInitializersForced(file)) {
+ printer->Print(vars, with_static_init);
+ } else {
+ printer->Print(vars, (string(
+ "#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER\n") +
+ without_static_init +
+ "#else\n" +
+ with_static_init +
+ "#endif\n").c_str());
+ }
+}
+
+
+static bool HasEnumDefinitions(const Descriptor* message_type) {
+ if (message_type->enum_type_count() > 0) return true;
+ for (int i = 0; i < message_type->nested_type_count(); ++i) {
+ if (HasEnumDefinitions(message_type->nested_type(i))) return true;
+ }
+ return false;
+}
+
+bool HasEnumDefinitions(const FileDescriptor* file) {
+ if (file->enum_type_count() > 0) return true;
+ for (int i = 0; i < file->message_type_count(); ++i) {
+ if (HasEnumDefinitions(file->message_type(i))) return true;
+ }
+ return false;
+}
+
+bool IsStringOrMessage(const FieldDescriptor* field) {
+ switch (field->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32:
+ case FieldDescriptor::CPPTYPE_INT64:
+ case FieldDescriptor::CPPTYPE_UINT32:
+ case FieldDescriptor::CPPTYPE_UINT64:
+ case FieldDescriptor::CPPTYPE_DOUBLE:
+ case FieldDescriptor::CPPTYPE_FLOAT:
+ case FieldDescriptor::CPPTYPE_BOOL:
+ case FieldDescriptor::CPPTYPE_ENUM:
+ return false;
+ case FieldDescriptor::CPPTYPE_STRING:
+ case FieldDescriptor::CPPTYPE_MESSAGE:
+ return true;
+ }
+
+ GOOGLE_LOG(FATAL) << "Can't get here.";
+ return false;
+}
+
} // namespace cpp
} // namespace compiler
} // namespace protobuf
diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h
index f99b5fe..5d30240 100644
--- a/src/google/protobuf/compiler/cpp/cpp_helpers.h
+++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -35,12 +35,18 @@
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_HELPERS_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_HELPERS_H__
+#include <map>
#include <string>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
namespace google {
namespace protobuf {
+
+namespace io {
+class Printer;
+}
+
namespace compiler {
namespace cpp {
@@ -97,6 +103,12 @@ const char* PrimitiveTypeName(FieldDescriptor::CppType type);
// methods of WireFormat. For example, TYPE_INT32 becomes "Int32".
const char* DeclaredTypeMethodName(FieldDescriptor::Type type);
+// Return the code that evaluates to the number when compiled.
+string Int32ToString(int number);
+
+// Return the code that evaluates to the number when compiled.
+string Int64ToString(int64 number);
+
// Get code that evaluates to the field's default value.
string DefaultValue(const FieldDescriptor* field);
@@ -109,27 +121,43 @@ string GlobalAddDescriptorsName(const string& filename);
// Return the name of the AssignDescriptors() function for a given file.
string GlobalAssignDescriptorsName(const string& filename);
+// Return the qualified C++ name for a file level symbol.
+string QualifiedFileLevelSymbol(const string& package, const string& name);
+
// Return the name of the ShutdownFile() function for a given file.
string GlobalShutdownFileName(const string& filename);
-// Do message classes in this file keep track of unknown fields?
-inline bool HasUnknownFields(const FileDescriptor *file) {
+// Escape C++ trigraphs by escaping question marks to \?
+string EscapeTrigraphs(const string& to_escape);
+
+// Escaped function name to eliminate naming conflict.
+string SafeFunctionName(const Descriptor* descriptor,
+ const FieldDescriptor* field,
+ const string& prefix);
+
+// Do message classes in this file use UnknownFieldSet?
+// Otherwise, messages will store unknown fields in a string
+inline bool UseUnknownFieldSet(const FileDescriptor* file) {
return file->options().optimize_for() != FileOptions::LITE_RUNTIME;
}
+
+// Does this file have any enum type definitions?
+bool HasEnumDefinitions(const FileDescriptor* file);
+
// Does this file have generated parsing, serialization, and other
// standard methods for which reflection-based fallback implementations exist?
-inline bool HasGeneratedMethods(const FileDescriptor *file) {
+inline bool HasGeneratedMethods(const FileDescriptor* file) {
return file->options().optimize_for() != FileOptions::CODE_SIZE;
}
-// Do message classes in this file have descriptor and refelction methods?
-inline bool HasDescriptorMethods(const FileDescriptor *file) {
+// Do message classes in this file have descriptor and reflection methods?
+inline bool HasDescriptorMethods(const FileDescriptor* file) {
return file->options().optimize_for() != FileOptions::LITE_RUNTIME;
}
// Should we generate generic services for this file?
-inline bool HasGenericServices(const FileDescriptor *file) {
+inline bool HasGenericServices(const FileDescriptor* file) {
return file->service_count() > 0 &&
file->options().optimize_for() != FileOptions::LITE_RUNTIME &&
file->options().cc_generic_services();
@@ -147,6 +175,28 @@ inline bool HasFastArraySerialization(const FileDescriptor* file) {
return file->options().optimize_for() == FileOptions::SPEED;
}
+// Returns whether we have to generate code with static initializers.
+bool StaticInitializersForced(const FileDescriptor* file);
+
+// Prints 'with_static_init' if static initializers have to be used for the
+// provided file. Otherwise emits both 'with_static_init' and
+// 'without_static_init' using #ifdef.
+void PrintHandlingOptionalStaticInitializers(
+ const FileDescriptor* file, io::Printer* printer,
+ const char* with_static_init, const char* without_static_init,
+ const char* var1 = NULL, const string& val1 = "",
+ const char* var2 = NULL, const string& val2 = "");
+
+void PrintHandlingOptionalStaticInitializers(
+ const map<string, string>& vars, const FileDescriptor* file,
+ io::Printer* printer, const char* with_static_init,
+ const char* without_static_init);
+
+
+// Returns true if the field's CPPTYPE is string or message.
+bool IsStringOrMessage(const FieldDescriptor* field);
+
+string UnderscoresToCamelCase(const string& input, bool cap_next_letter);
} // namespace cpp
} // namespace compiler
diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc
index cbdcce8..3a9d263 100644
--- a/src/google/protobuf/compiler/cpp/cpp_message.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_message.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -35,6 +35,9 @@
#include <algorithm>
#include <google/protobuf/stubs/hash.h>
#include <map>
+#include <memory>
+#include <set>
+#include <utility>
#include <vector>
#include <google/protobuf/compiler/cpp/cpp_message.h>
#include <google/protobuf/compiler/cpp/cpp_field.h>
@@ -47,6 +50,7 @@
#include <google/protobuf/wire_format.h>
#include <google/protobuf/descriptor.pb.h>
+
namespace google {
namespace protobuf {
namespace compiler {
@@ -72,15 +76,6 @@ struct FieldOrderingByNumber {
}
};
-const char* kWireTypeNames[] = {
- "VARINT",
- "FIXED64",
- "LENGTH_DELIMITED",
- "START_GROUP",
- "END_GROUP",
- "FIXED32",
-};
-
// Sort the fields of the given Descriptor by number into a new[]'d array
// and return it.
const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) {
@@ -102,6 +97,13 @@ struct ExtensionRangeSorter {
}
};
+// Returns true if the "required" restriction check should be ignored for the
+// given field.
+inline static bool ShouldIgnoreRequiredFieldCheck(
+ const FieldDescriptor* field) {
+ return false;
+}
+
// Returns true if the message type has any required fields. If it doesn't,
// we can optimize out calls to its IsInitialized() method.
//
@@ -128,7 +130,8 @@ static bool HasRequiredFields(
if (field->is_required()) {
return true;
}
- if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
+ !ShouldIgnoreRequiredFieldCheck(field)) {
if (HasRequiredFields(field->message_type(), already_seen)) {
return true;
}
@@ -143,36 +146,171 @@ static bool HasRequiredFields(const Descriptor* type) {
return HasRequiredFields(type, &already_seen);
}
+// This returns an estimate of the compiler's alignment for the field. This
+// can't guarantee to be correct because the generated code could be compiled on
+// different systems with different alignment rules. The estimates below assume
+// 64-bit pointers.
+int EstimateAlignmentSize(const FieldDescriptor* field) {
+ if (field == NULL) return 0;
+ if (field->is_repeated()) return 8;
+ switch (field->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_BOOL:
+ return 1;
+
+ case FieldDescriptor::CPPTYPE_INT32:
+ case FieldDescriptor::CPPTYPE_UINT32:
+ case FieldDescriptor::CPPTYPE_ENUM:
+ case FieldDescriptor::CPPTYPE_FLOAT:
+ return 4;
+
+ case FieldDescriptor::CPPTYPE_INT64:
+ case FieldDescriptor::CPPTYPE_UINT64:
+ case FieldDescriptor::CPPTYPE_DOUBLE:
+ case FieldDescriptor::CPPTYPE_STRING:
+ case FieldDescriptor::CPPTYPE_MESSAGE:
+ return 8;
+ }
+ GOOGLE_LOG(FATAL) << "Can't get here.";
+ return -1; // Make compiler happy.
+}
+
+// FieldGroup is just a helper for OptimizePadding below. It holds a vector of
+// fields that are grouped together because they have compatible alignment, and
+// a preferred location in the final field ordering.
+class FieldGroup {
+ public:
+ FieldGroup()
+ : preferred_location_(0) {}
+
+ // A group with a single field.
+ FieldGroup(float preferred_location, const FieldDescriptor* field)
+ : preferred_location_(preferred_location),
+ fields_(1, field) {}
+
+ // Append the fields in 'other' to this group.
+ void Append(const FieldGroup& other) {
+ if (other.fields_.empty()) {
+ return;
+ }
+ // Preferred location is the average among all the fields, so we weight by
+ // the number of fields on each FieldGroup object.
+ preferred_location_ =
+ (preferred_location_ * fields_.size() +
+ (other.preferred_location_ * other.fields_.size())) /
+ (fields_.size() + other.fields_.size());
+ fields_.insert(fields_.end(), other.fields_.begin(), other.fields_.end());
+ }
+
+ void SetPreferredLocation(float location) { preferred_location_ = location; }
+ const vector<const FieldDescriptor*>& fields() const { return fields_; }
+
+ // FieldGroup objects sort by their preferred location.
+ bool operator<(const FieldGroup& other) const {
+ return preferred_location_ < other.preferred_location_;
+ }
+
+ private:
+ // "preferred_location_" is an estimate of where this group should go in the
+ // final list of fields. We compute this by taking the average index of each
+ // field in this group in the original ordering of fields. This is very
+ // approximate, but should put this group close to where its member fields
+ // originally went.
+ float preferred_location_;
+ vector<const FieldDescriptor*> fields_;
+ // We rely on the default copy constructor and operator= so this type can be
+ // used in a vector.
+};
+
+// Reorder 'fields' so that if the fields are output into a c++ class in the new
+// order, the alignment padding is minimized. We try to do this while keeping
+// each field as close as possible to its original position so that we don't
+// reduce cache locality much for function that access each field in order.
+void OptimizePadding(vector<const FieldDescriptor*>* fields) {
+ // First divide fields into those that align to 1 byte, 4 bytes or 8 bytes.
+ vector<FieldGroup> aligned_to_1, aligned_to_4, aligned_to_8;
+ for (int i = 0; i < fields->size(); ++i) {
+ switch (EstimateAlignmentSize((*fields)[i])) {
+ case 1: aligned_to_1.push_back(FieldGroup(i, (*fields)[i])); break;
+ case 4: aligned_to_4.push_back(FieldGroup(i, (*fields)[i])); break;
+ case 8: aligned_to_8.push_back(FieldGroup(i, (*fields)[i])); break;
+ default:
+ GOOGLE_LOG(FATAL) << "Unknown alignment size.";
+ }
+ }
+
+ // Now group fields aligned to 1 byte into sets of 4, and treat those like a
+ // single field aligned to 4 bytes.
+ for (int i = 0; i < aligned_to_1.size(); i += 4) {
+ FieldGroup field_group;
+ for (int j = i; j < aligned_to_1.size() && j < i + 4; ++j) {
+ field_group.Append(aligned_to_1[j]);
+ }
+ aligned_to_4.push_back(field_group);
+ }
+ // Sort by preferred location to keep fields as close to their original
+ // location as possible. Using stable_sort ensures that the output is
+ // consistent across runs.
+ stable_sort(aligned_to_4.begin(), aligned_to_4.end());
+
+ // Now group fields aligned to 4 bytes (or the 4-field groups created above)
+ // into pairs, and treat those like a single field aligned to 8 bytes.
+ for (int i = 0; i < aligned_to_4.size(); i += 2) {
+ FieldGroup field_group;
+ for (int j = i; j < aligned_to_4.size() && j < i + 2; ++j) {
+ field_group.Append(aligned_to_4[j]);
+ }
+ if (i == aligned_to_4.size() - 1) {
+ // Move incomplete 4-byte block to the end.
+ field_group.SetPreferredLocation(fields->size() + 1);
+ }
+ aligned_to_8.push_back(field_group);
+ }
+ // Sort by preferred location.
+ stable_sort(aligned_to_8.begin(), aligned_to_8.end());
+
+ // Now pull out all the FieldDescriptors in order.
+ fields->clear();
+ for (int i = 0; i < aligned_to_8.size(); ++i) {
+ fields->insert(fields->end(),
+ aligned_to_8[i].fields().begin(),
+ aligned_to_8[i].fields().end());
+ }
+}
+
+string MessageTypeProtoName(const FieldDescriptor* field) {
+ return field->message_type()->full_name();
+}
+
}
// ===================================================================
MessageGenerator::MessageGenerator(const Descriptor* descriptor,
- const string& dllexport_decl)
- : descriptor_(descriptor),
- classname_(ClassName(descriptor, false)),
- dllexport_decl_(dllexport_decl),
- field_generators_(descriptor),
- nested_generators_(new scoped_ptr<MessageGenerator>[
- descriptor->nested_type_count()]),
- enum_generators_(new scoped_ptr<EnumGenerator>[
- descriptor->enum_type_count()]),
- extension_generators_(new scoped_ptr<ExtensionGenerator>[
- descriptor->extension_count()]) {
+ const Options& options)
+ : descriptor_(descriptor),
+ classname_(ClassName(descriptor, false)),
+ options_(options),
+ field_generators_(descriptor, options),
+ nested_generators_(new scoped_ptr<
+ MessageGenerator>[descriptor->nested_type_count()]),
+ enum_generators_(
+ new scoped_ptr<EnumGenerator>[descriptor->enum_type_count()]),
+ extension_generators_(new scoped_ptr<
+ ExtensionGenerator>[descriptor->extension_count()]) {
for (int i = 0; i < descriptor->nested_type_count(); i++) {
nested_generators_[i].reset(
- new MessageGenerator(descriptor->nested_type(i), dllexport_decl));
+ new MessageGenerator(descriptor->nested_type(i), options));
}
for (int i = 0; i < descriptor->enum_type_count(); i++) {
enum_generators_[i].reset(
- new EnumGenerator(descriptor->enum_type(i), dllexport_decl));
+ new EnumGenerator(descriptor->enum_type(i), options));
}
for (int i = 0; i < descriptor->extension_count(); i++) {
extension_generators_[i].reset(
- new ExtensionGenerator(descriptor->extension(i), dllexport_decl));
+ new ExtensionGenerator(descriptor->extension(i), options));
}
}
@@ -217,7 +355,7 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) {
PrintFieldComment(printer, field);
map<string, string> vars;
- SetCommonFieldVariables(field, &vars);
+ SetCommonFieldVariables(field, &vars, options_);
vars["constant_name"] = FieldConstantName(field);
if (field->is_repeated()) {
@@ -242,6 +380,14 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) {
"GOOGLE_PROTOBUF_EXTENSION_ACCESSORS($classname$)\n",
"classname", classname_);
}
+
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ printer->Print(
+ "inline $camel_oneof_name$Case $oneof_name$_case() const;\n",
+ "camel_oneof_name",
+ UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true),
+ "oneof_name", descriptor_->oneof_decl(i)->name());
+ }
}
void MessageGenerator::
@@ -254,7 +400,7 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) {
PrintFieldComment(printer, field);
map<string, string> vars;
- SetCommonFieldVariables(field, &vars);
+ SetCommonFieldVariables(field, &vars, options_);
// Generate has_$name$() or $name$_size().
if (field->is_repeated()) {
@@ -262,12 +408,34 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) {
"inline int $classname$::$name$_size() const {\n"
" return $name$_.size();\n"
"}\n");
+ } else if (field->containing_oneof()) {
+ // Singular field in a oneof
+ vars["field_name"] = UnderscoresToCamelCase(field->name(), true);
+ vars["oneof_name"] = field->containing_oneof()->name();
+ vars["oneof_index"] = SimpleItoa(field->containing_oneof()->index());
+ printer->Print(vars,
+ "inline bool $classname$::has_$name$() const {\n"
+ " return $oneof_name$_case() == k$field_name$;\n"
+ "}\n"
+ "inline void $classname$::set_has_$name$() {\n"
+ " _oneof_case_[$oneof_index$] = k$field_name$;\n"
+ "}\n");
} else {
// Singular field.
+ char buffer[kFastToBufferSize];
+ vars["has_array_index"] = SimpleItoa(field->index() / 32);
+ vars["has_mask"] = FastHex32ToBuffer(1u << (field->index() % 32), buffer);
printer->Print(vars,
"inline bool $classname$::has_$name$() const {\n"
- " return _has_bit($index$);\n"
- "}\n");
+ " return (_has_bits_[$has_array_index$] & 0x$has_mask$u) != 0;\n"
+ "}\n"
+ "inline void $classname$::set_has_$name$() {\n"
+ " _has_bits_[$has_array_index$] |= 0x$has_mask$u;\n"
+ "}\n"
+ "inline void $classname$::clear_has_$name$() {\n"
+ " _has_bits_[$has_array_index$] &= ~0x$has_mask$u;\n"
+ "}\n"
+ );
}
// Generate clear_$name$()
@@ -275,13 +443,27 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) {
"inline void $classname$::clear_$name$() {\n");
printer->Indent();
- field_generators_.get(field).GenerateClearingCode(printer);
- printer->Outdent();
- if (!field->is_repeated()) {
- printer->Print(vars, " _clear_bit($index$);\n");
+ if (field->containing_oneof()) {
+ // Clear this field only if it is the active field in this oneof,
+ // otherwise ignore
+ printer->Print(vars,
+ "if (has_$name$()) {\n");
+ printer->Indent();
+ field_generators_.get(field).GenerateClearingCode(printer);
+ printer->Print(vars,
+ "clear_has_$oneof_name$();\n");
+ printer->Outdent();
+ printer->Print("}\n");
+ } else {
+ field_generators_.get(field).GenerateClearingCode(printer);
+ if (!field->is_repeated()) {
+ printer->Print(vars,
+ "clear_has_$name$();\n");
+ }
}
+ printer->Outdent();
printer->Print("}\n");
// Generate type-specific accessors.
@@ -289,6 +471,49 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) {
printer->Print("\n");
}
+
+ // Generate has_$name$() and clear_has_$name$() functions for oneofs
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ map<string, string> vars;
+ vars["oneof_name"] = descriptor_->oneof_decl(i)->name();
+ vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index());
+ vars["cap_oneof_name"] =
+ ToUpper(descriptor_->oneof_decl(i)->name());
+ vars["classname"] = classname_;
+ printer->Print(
+ vars,
+ "inline bool $classname$::has_$oneof_name$() {\n"
+ " return $oneof_name$_case() != $cap_oneof_name$_NOT_SET;\n"
+ "}\n"
+ "inline void $classname$::clear_has_$oneof_name$() {\n"
+ " _oneof_case_[$oneof_index$] = $cap_oneof_name$_NOT_SET;\n"
+ "}\n");
+ }
+}
+
+// Helper for the code that emits the Clear() method.
+static bool CanClearByZeroing(const FieldDescriptor* field) {
+ if (field->is_repeated() || field->is_extension()) return false;
+ switch (field->cpp_type()) {
+ case internal::WireFormatLite::CPPTYPE_ENUM:
+ return field->default_value_enum()->number() == 0;
+ case internal::WireFormatLite::CPPTYPE_INT32:
+ return field->default_value_int32() == 0;
+ case internal::WireFormatLite::CPPTYPE_INT64:
+ return field->default_value_int64() == 0;
+ case internal::WireFormatLite::CPPTYPE_UINT32:
+ return field->default_value_uint32() == 0;
+ case internal::WireFormatLite::CPPTYPE_UINT64:
+ return field->default_value_uint64() == 0;
+ case internal::WireFormatLite::CPPTYPE_FLOAT:
+ return field->default_value_float() == 0;
+ case internal::WireFormatLite::CPPTYPE_DOUBLE:
+ return field->default_value_double() == 0;
+ case internal::WireFormatLite::CPPTYPE_BOOL:
+ return field->default_value_bool() == false;
+ default:
+ return false;
+ }
}
void MessageGenerator::
@@ -303,10 +528,11 @@ GenerateClassDefinition(io::Printer* printer) {
map<string, string> vars;
vars["classname"] = classname_;
vars["field_count"] = SimpleItoa(descriptor_->field_count());
- if (dllexport_decl_.empty()) {
+ vars["oneof_decl_count"] = SimpleItoa(descriptor_->oneof_decl_count());
+ if (options_.dllexport_decl.empty()) {
vars["dllexport"] = "";
} else {
- vars["dllexport"] = dllexport_decl_ + " ";
+ vars["dllexport"] = options_.dllexport_decl + " ";
}
vars["superclass"] = SuperClassName(descriptor_);
@@ -327,7 +553,7 @@ GenerateClassDefinition(io::Printer* printer) {
"}\n"
"\n");
- if (HasUnknownFields(descriptor_->file())) {
+ if (UseUnknownFieldSet(descriptor_->file())) {
printer->Print(
"inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {\n"
" return _unknown_fields_;\n"
@@ -337,6 +563,16 @@ GenerateClassDefinition(io::Printer* printer) {
" return &_unknown_fields_;\n"
"}\n"
"\n");
+ } else {
+ printer->Print(
+ "inline const ::std::string& unknown_fields() const {\n"
+ " return _unknown_fields_;\n"
+ "}\n"
+ "\n"
+ "inline ::std::string* mutable_unknown_fields() {\n"
+ " return &_unknown_fields_;\n"
+ "}\n"
+ "\n");
}
// Only generate this member if it's not disabled.
@@ -350,6 +586,47 @@ GenerateClassDefinition(io::Printer* printer) {
"static const $classname$& default_instance();\n"
"\n");
+ // Generate enum values for every field in oneofs. One list is generated for
+ // each oneof with an additional *_NOT_SET value.
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ printer->Print(
+ "enum $camel_oneof_name$Case {\n",
+ "camel_oneof_name",
+ UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true));
+ printer->Indent();
+ for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+ printer->Print(
+ "k$field_name$ = $field_number$,\n",
+ "field_name",
+ UnderscoresToCamelCase(
+ descriptor_->oneof_decl(i)->field(j)->name(), true),
+ "field_number",
+ SimpleItoa(descriptor_->oneof_decl(i)->field(j)->number()));
+ }
+ printer->Print(
+ "$cap_oneof_name$_NOT_SET = 0,\n",
+ "cap_oneof_name",
+ ToUpper(descriptor_->oneof_decl(i)->name()));
+ printer->Outdent();
+ printer->Print(
+ "};\n"
+ "\n");
+ }
+
+ if (!StaticInitializersForced(descriptor_->file())) {
+ printer->Print(vars,
+ "#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER\n"
+ "// Returns the internal default instance pointer. This function can\n"
+ "// return NULL thus should not be used by the user. This is intended\n"
+ "// for Protobuf internal code. Please use default_instance() declared\n"
+ "// above instead.\n"
+ "static inline const $classname$* internal_default_instance() {\n"
+ " return default_instance_;\n"
+ "}\n"
+ "#endif\n"
+ "\n");
+ }
+
printer->Print(vars,
"void Swap($classname$* other);\n"
@@ -379,20 +656,50 @@ GenerateClassDefinition(io::Printer* printer) {
" ::google::protobuf::io::CodedInputStream* input);\n"
"void SerializeWithCachedSizes(\n"
" ::google::protobuf::io::CodedOutputStream* output) const;\n");
+ // DiscardUnknownFields() is implemented in message.cc using reflections. We
+ // need to implement this function in generated code for messages.
+ if (!UseUnknownFieldSet(descriptor_->file())) {
+ printer->Print(
+ "void DiscardUnknownFields();\n");
+ }
if (HasFastArraySerialization(descriptor_->file())) {
printer->Print(
"::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;\n");
}
}
- printer->Print(vars,
+ // Check all FieldDescriptors including those in oneofs to estimate
+ // whether ::std::string is likely to be used, and depending on that
+ // estimate, set uses_string_ to true or false. That contols
+ // whether to force initialization of empty_string_ in SharedCtor().
+ // It's often advantageous to do so to keep "is empty_string_
+ // inited?" code from appearing all over the place.
+ vector<const FieldDescriptor*> descriptors;
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ descriptors.push_back(descriptor_->field(i));
+ }
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+ descriptors.push_back(descriptor_->oneof_decl(i)->field(j));
+ }
+ }
+ uses_string_ = false;
+ for (int i = 0; i < descriptors.size(); i++) {
+ const FieldDescriptor* field = descriptors[i];
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
+ switch (field->options().ctype()) {
+ default: uses_string_ = true; break;
+ }
+ }
+ }
+
+ printer->Print(
"int GetCachedSize() const { return _cached_size_; }\n"
"private:\n"
"void SharedCtor();\n"
"void SharedDtor();\n"
"void SetCachedSize(int size) const;\n"
- "public:\n"
- "\n");
+ "public:\n");
if (HasDescriptorMethods(descriptor_->file())) {
printer->Print(
@@ -444,38 +751,172 @@ GenerateClassDefinition(io::Printer* printer) {
"// @@protoc_insertion_point(class_scope:$full_name$)\n",
"full_name", descriptor_->full_name());
- // Generate private members for fields.
+ // Generate private members.
printer->Outdent();
printer->Print(" private:\n");
printer->Indent();
+
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ if (!descriptor_->field(i)->is_repeated()) {
+ printer->Print(
+ "inline void set_has_$name$();\n",
+ "name", FieldName(descriptor_->field(i)));
+ if (!descriptor_->field(i)->containing_oneof()) {
+ printer->Print(
+ "inline void clear_has_$name$();\n",
+ "name", FieldName(descriptor_->field(i)));
+ }
+ }
+ }
+ printer->Print("\n");
+
+ // Generate oneof function declarations
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ printer->Print(
+ "inline bool has_$oneof_name$();\n"
+ "void clear_$oneof_name$();\n"
+ "inline void clear_has_$oneof_name$();\n\n",
+ "oneof_name", descriptor_->oneof_decl(i)->name());
+ }
+
+ // Prepare decls for _cached_size_ and _has_bits_. Their position in the
+ // output will be determined later.
+
+ bool need_to_emit_cached_size = true;
+ // TODO(kenton): Make _cached_size_ an atomic<int> when C++ supports it.
+ const string cached_size_decl = "mutable int _cached_size_;\n";
+
+ // TODO(jieluo) - Optimize _has_bits_ for repeated and oneof fields.
+ size_t sizeof_has_bits = (descriptor_->field_count() + 31) / 32 * 4;
+ if (descriptor_->field_count() == 0) {
+ // Zero-size arrays aren't technically allowed, and MSVC in particular
+ // doesn't like them. We still need to declare these arrays to make
+ // other code compile. Since this is an uncommon case, we'll just declare
+ // them with size 1 and waste some space. Oh well.
+ sizeof_has_bits = 4;
+ }
+ const string has_bits_decl = sizeof_has_bits == 0 ? "" :
+ "::google::protobuf::uint32 _has_bits_[" + SimpleItoa(sizeof_has_bits / 4) + "];\n";
+
+
+ // To minimize padding, data members are divided into three sections:
+ // (1) members assumed to align to 8 bytes
+ // (2) members corresponding to message fields, re-ordered to optimize
+ // alignment.
+ // (3) members assumed to align to 4 bytes.
+
+ // Members assumed to align to 8 bytes:
+
if (descriptor_->extension_range_count() > 0) {
printer->Print(
- "::google::protobuf::internal::ExtensionSet _extensions_;\n");
+ "::google::protobuf::internal::ExtensionSet _extensions_;\n"
+ "\n");
}
- if (HasUnknownFields(descriptor_->file())) {
+ if (UseUnknownFieldSet(descriptor_->file())) {
printer->Print(
- "::google::protobuf::UnknownFieldSet _unknown_fields_;\n");
+ "::google::protobuf::UnknownFieldSet _unknown_fields_;\n"
+ "\n");
+ } else {
+ printer->Print(
+ "::std::string _unknown_fields_;\n"
+ "\n");
}
- // TODO(kenton): Make _cached_size_ an atomic<int> when C++ supports it.
- printer->Print(
- "mutable int _cached_size_;\n"
- "\n");
+ // _has_bits_ is frequently accessed, so to reduce code size and improve
+ // speed, it should be close to the start of the object. But, try not to
+ // waste space:_has_bits_ by itself always makes sense if its size is a
+ // multiple of 8, but, otherwise, maybe _has_bits_ and cached_size_ together
+ // will work well.
+ printer->Print(has_bits_decl.c_str());
+ if ((sizeof_has_bits % 8) != 0) {
+ printer->Print(cached_size_decl.c_str());
+ need_to_emit_cached_size = false;
+ }
+
+ // Field members:
+
+ // List fields which doesn't belong to any oneof
+ vector<const FieldDescriptor*> fields;
+ hash_map<string, int> fieldname_to_chunk;
for (int i = 0; i < descriptor_->field_count(); i++) {
- field_generators_.get(descriptor_->field(i))
- .GeneratePrivateMembers(printer);
+ if (!descriptor_->field(i)->containing_oneof()) {
+ const FieldDescriptor* field = descriptor_->field(i);
+ fields.push_back(field);
+ fieldname_to_chunk[FieldName(field)] = i / 8;
+ }
+ }
+ OptimizePadding(&fields);
+ // Emit some private and static members
+ runs_of_fields_ = vector< vector<string> >(1);
+ for (int i = 0; i < fields.size(); ++i) {
+ const FieldDescriptor* field = fields[i];
+ const FieldGenerator& generator = field_generators_.get(field);
+ generator.GenerateStaticMembers(printer);
+ generator.GeneratePrivateMembers(printer);
+ if (CanClearByZeroing(field)) {
+ const string& fieldname = FieldName(field);
+ if (!runs_of_fields_.back().empty() &&
+ (fieldname_to_chunk[runs_of_fields_.back().back()] !=
+ fieldname_to_chunk[fieldname])) {
+ runs_of_fields_.push_back(vector<string>());
+ }
+ runs_of_fields_.back().push_back(fieldname);
+ } else if (!runs_of_fields_.back().empty()) {
+ runs_of_fields_.push_back(vector<string>());
+ }
+ }
+
+ // For each oneof generate a union
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ printer->Print(
+ "union $camel_oneof_name$Union {\n",
+ "camel_oneof_name",
+ UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true));
+ printer->Indent();
+ for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+ field_generators_.get(descriptor_->oneof_decl(i)->
+ field(j)).GeneratePrivateMembers(printer);
+ }
+ printer->Outdent();
+ printer->Print(
+ "} $oneof_name$_;\n",
+ "oneof_name", descriptor_->oneof_decl(i)->name());
+ for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+ field_generators_.get(descriptor_->oneof_decl(i)->
+ field(j)).GenerateStaticMembers(printer);
+ }
+ }
+
+ // Members assumed to align to 4 bytes:
+
+ if (need_to_emit_cached_size) {
+ printer->Print(cached_size_decl.c_str());
+ need_to_emit_cached_size = false;
+ }
+
+ // Generate _oneof_case_.
+ if (descriptor_->oneof_decl_count() > 0) {
+ printer->Print(vars,
+ "::google::protobuf::uint32 _oneof_case_[$oneof_decl_count$];\n"
+ "\n");
}
// Declare AddDescriptors(), BuildDescriptors(), and ShutdownFile() as
// friends so that they can access private static variables like
// default_instance_ and reflection_.
- printer->Print(
+ PrintHandlingOptionalStaticInitializers(
+ descriptor_->file(), printer,
+ // With static initializers.
"friend void $dllexport_decl$ $adddescriptorsname$();\n",
- "dllexport_decl", dllexport_decl_,
+ // Without.
+ "friend void $dllexport_decl$ $adddescriptorsname$_impl();\n",
+ // Vars.
+ "dllexport_decl", options_.dllexport_decl,
"adddescriptorsname",
- GlobalAddDescriptorsName(descriptor_->file()->name()));
+ GlobalAddDescriptorsName(descriptor_->file()->name()));
+
printer->Print(
"friend void $assigndescriptorsname$();\n"
"friend void $shutdownfilename$();\n"
@@ -484,38 +925,14 @@ GenerateClassDefinition(io::Printer* printer) {
GlobalAssignDescriptorsName(descriptor_->file()->name()),
"shutdownfilename", GlobalShutdownFileName(descriptor_->file()->name()));
- // Generate offsets and _has_bits_ boilerplate.
- if (descriptor_->field_count() > 0) {
- printer->Print(vars,
- "::google::protobuf::uint32 _has_bits_[($field_count$ + 31) / 32];\n");
- } else {
- // Zero-size arrays aren't technically allowed, and MSVC in particular
- // doesn't like them. We still need to declare these arrays to make
- // other code compile. Since this is an uncommon case, we'll just declare
- // them with size 1 and waste some space. Oh well.
- printer->Print(
- "::google::protobuf::uint32 _has_bits_[1];\n");
- }
-
printer->Print(
- "\n"
- "// WHY DOES & HAVE LOWER PRECEDENCE THAN != !?\n"
- "inline bool _has_bit(int index) const {\n"
- " return (_has_bits_[index / 32] & (1u << (index % 32))) != 0;\n"
- "}\n"
- "inline void _set_bit(int index) {\n"
- " _has_bits_[index / 32] |= (1u << (index % 32));\n"
- "}\n"
- "inline void _clear_bit(int index) {\n"
- " _has_bits_[index / 32] &= ~(1u << (index % 32));\n"
- "}\n"
- "\n"
"void InitAsDefaultInstance();\n"
"static $classname$* default_instance_;\n",
"classname", classname_);
printer->Outdent();
printer->Print(vars, "};");
+ GOOGLE_DCHECK(!need_to_emit_cached_size);
}
void MessageGenerator::
@@ -527,6 +944,23 @@ GenerateInlineMethods(io::Printer* printer) {
}
GenerateFieldAccessorDefinitions(printer);
+
+ // Generate oneof_case() functions.
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ map<string, string> vars;
+ vars["class_name"] = classname_;
+ vars["camel_oneof_name"] = UnderscoresToCamelCase(
+ descriptor_->oneof_decl(i)->name(), true);
+ vars["oneof_name"] = descriptor_->oneof_decl(i)->name();
+ vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index());
+ printer->Print(
+ vars,
+ "inline $class_name$::$camel_oneof_name$Case $class_name$::"
+ "$oneof_name$_case() const {\n"
+ " return $class_name$::$camel_oneof_name$Case("
+ "_oneof_case_[$oneof_index$]);\n"
+ "}\n");
+ }
}
void MessageGenerator::
@@ -537,6 +971,25 @@ GenerateDescriptorDeclarations(io::Printer* printer) {
" $name$_reflection_ = NULL;\n",
"name", classname_);
+ // Generate oneof default instance for reflection usage.
+ if (descriptor_->oneof_decl_count() > 0) {
+ printer->Print("struct $name$OneofInstance {\n",
+ "name", classname_);
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+ const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j);
+ printer->Print(" ");
+ if (IsStringOrMessage(field)) {
+ printer->Print("const ");
+ }
+ field_generators_.get(field).GeneratePrivateMembers(printer);
+ }
+ }
+
+ printer->Print("}* $name$_default_oneof_instance_ = NULL;\n",
+ "name", classname_);
+ }
+
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
nested_generators_[i]->GenerateDescriptorDeclarations(printer);
}
@@ -589,9 +1042,19 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) {
printer->Print(vars,
" -1,\n");
}
+
+ if (descriptor_->oneof_decl_count() > 0) {
+ printer->Print(vars,
+ " $classname$_default_oneof_instance_,\n"
+ " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET("
+ "$classname$, _oneof_case_[0]),\n");
+ }
+
+ printer->Print(
+ " ::google::protobuf::DescriptorPool::generated_pool(),\n");
+ printer->Print(vars,
+ " ::google::protobuf::MessageFactory::generated_factory(),\n");
printer->Print(vars,
- " ::google::protobuf::DescriptorPool::generated_pool(),\n"
- " ::google::protobuf::MessageFactory::generated_factory(),\n"
" sizeof($classname$));\n");
// Handle nested types.
@@ -620,6 +1083,13 @@ GenerateTypeRegistrations(io::Printer* printer) {
void MessageGenerator::
GenerateDefaultInstanceAllocator(io::Printer* printer) {
+ // Construct the default instances of all fields, as they will be used
+ // when creating the default instance of the entire message.
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ field_generators_.get(descriptor_->field(i))
+ .GenerateDefaultInstanceAllocator(printer);
+ }
+
// Construct the default instance. We can't call InitAsDefaultInstance() yet
// because we need to make sure all default instances that this one might
// depend on are constructed first.
@@ -627,6 +1097,13 @@ GenerateDefaultInstanceAllocator(io::Printer* printer) {
"$classname$::default_instance_ = new $classname$();\n",
"classname", classname_);
+ if ((descriptor_->oneof_decl_count() > 0) &&
+ HasDescriptorMethods(descriptor_->file())) {
+ printer->Print(
+ "$classname$_default_oneof_instance_ = new $classname$OneofInstance;\n",
+ "classname", classname_);
+ }
+
// Handle nested types.
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
nested_generators_[i]->GenerateDefaultInstanceAllocator(printer);
@@ -658,11 +1135,22 @@ GenerateShutdownCode(io::Printer* printer) {
"classname", classname_);
if (HasDescriptorMethods(descriptor_->file())) {
+ if (descriptor_->oneof_decl_count() > 0) {
+ printer->Print(
+ "delete $classname$_default_oneof_instance_;\n",
+ "classname", classname_);
+ }
printer->Print(
"delete $classname$_reflection_;\n",
"classname", classname_);
}
+ // Handle default instances of fields.
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ field_generators_.get(descriptor_->field(i))
+ .GenerateShutdownCode(printer);
+ }
+
// Handle nested types.
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
nested_generators_[i]->GenerateShutdownCode(printer);
@@ -709,6 +1197,11 @@ GenerateClassMethods(io::Printer* printer) {
GenerateStructors(printer);
printer->Print("\n");
+ if (descriptor_->oneof_decl_count() > 0) {
+ GenerateOneofClear(printer);
+ printer->Print("\n");
+ }
+
if (HasGeneratedMethods(descriptor_->file())) {
GenerateClear(printer);
printer->Print("\n");
@@ -768,15 +1261,33 @@ GenerateOffsets(io::Printer* printer) {
printer->Print(
"static const int $classname$_offsets_[$field_count$] = {\n",
"classname", classname_,
- "field_count", SimpleItoa(max(1, descriptor_->field_count())));
+ "field_count", SimpleItoa(max(
+ 1, descriptor_->field_count() + descriptor_->oneof_decl_count())));
printer->Indent();
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
+ if (field->containing_oneof()) {
+ printer->Print(
+ "PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET("
+ "$classname$_default_oneof_instance_, $name$_),\n",
+ "classname", classname_,
+ "name", FieldName(field));
+ } else {
+ printer->Print(
+ "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, "
+ "$name$_),\n",
+ "classname", classname_,
+ "name", FieldName(field));
+ }
+ }
+
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ const OneofDescriptor* oneof = descriptor_->oneof_decl(i);
printer->Print(
"GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, $name$_),\n",
"classname", classname_,
- "name", FieldName(field));
+ "name", oneof->name());
}
printer->Outdent();
@@ -790,17 +1301,26 @@ GenerateSharedConstructorCode(io::Printer* printer) {
"classname", classname_);
printer->Indent();
- printer->Print(
- "_cached_size_ = 0;\n");
+ printer->Print(StrCat(
+ uses_string_ ? "::google::protobuf::internal::GetEmptyString();\n" : "",
+ "_cached_size_ = 0;\n").c_str());
for (int i = 0; i < descriptor_->field_count(); i++) {
- field_generators_.get(descriptor_->field(i))
- .GenerateConstructorCode(printer);
+ if (!descriptor_->field(i)->containing_oneof()) {
+ field_generators_.get(descriptor_->field(i))
+ .GenerateConstructorCode(printer);
+ }
}
printer->Print(
"::memset(_has_bits_, 0, sizeof(_has_bits_));\n");
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ printer->Print(
+ "clear_has_$oneof_name$();\n",
+ "oneof_name", descriptor_->oneof_decl(i)->name());
+ }
+
printer->Outdent();
printer->Print("}\n\n");
}
@@ -811,14 +1331,29 @@ GenerateSharedDestructorCode(io::Printer* printer) {
"void $classname$::SharedDtor() {\n",
"classname", classname_);
printer->Indent();
- // Write the destructors for each field.
+ // Write the destructors for each field except oneof members.
for (int i = 0; i < descriptor_->field_count(); i++) {
- field_generators_.get(descriptor_->field(i))
- .GenerateDestructorCode(printer);
+ if (!descriptor_->field(i)->containing_oneof()) {
+ field_generators_.get(descriptor_->field(i))
+ .GenerateDestructorCode(printer);
+ }
}
- printer->Print(
- "if (this != default_instance_) {\n");
+ // Generate code to destruct oneofs. Clearing should do the work.
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ printer->Print(
+ "if (has_$oneof_name$()) {\n"
+ " clear_$oneof_name$();\n"
+ "}\n",
+ "oneof_name", descriptor_->oneof_decl(i)->name());
+ }
+
+ PrintHandlingOptionalStaticInitializers(
+ descriptor_->file(), printer,
+ // With static initializers.
+ "if (this != default_instance_) {\n",
+ // Without.
+ "if (this != &default_instance()) {\n");
// We need to delete all embedded messages.
// TODO(kenton): If we make unset messages point at default instances
@@ -829,8 +1364,12 @@ GenerateSharedDestructorCode(io::Printer* printer) {
if (!field->is_repeated() &&
field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- printer->Print(" delete $name$_;\n",
- "name", FieldName(field));
+ // Skip oneof members
+ if (!field->containing_oneof()) {
+ printer->Print(
+ " delete $name$_;\n",
+ "name", FieldName(field));
+ }
}
}
@@ -850,9 +1389,11 @@ GenerateStructors(io::Printer* printer) {
"$classname$::$classname$()\n"
" : $superclass$() {\n"
" SharedCtor();\n"
+ " // @@protoc_insertion_point(constructor:$full_name$)\n"
"}\n",
"classname", classname_,
- "superclass", superclass);
+ "superclass", superclass,
+ "full_name", descriptor_->full_name());
printer->Print(
"\n"
@@ -869,11 +1410,28 @@ GenerateStructors(io::Printer* printer) {
const FieldDescriptor* field = descriptor_->field(i);
if (!field->is_repeated() &&
- field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- printer->Print(
- " $name$_ = const_cast< $type$*>(&$type$::default_instance());\n",
- "name", FieldName(field),
- "type", FieldMessageTypeName(field));
+ field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
+ (field->containing_oneof() == NULL ||
+ HasDescriptorMethods(descriptor_->file()))) {
+ string name;
+ if (field->containing_oneof()) {
+ name = classname_ + "_default_oneof_instance_->";
+ }
+ name += FieldName(field);
+ PrintHandlingOptionalStaticInitializers(
+ descriptor_->file(), printer,
+ // With static initializers.
+ " $name$_ = const_cast< $type$*>(&$type$::default_instance());\n",
+ // Without.
+ " $name$_ = const_cast< $type$*>(\n"
+ " $type$::internal_default_instance());\n",
+ // Vars.
+ "name", name,
+ "type", FieldMessageTypeName(field));
+ } else if (field->containing_oneof() &&
+ HasDescriptorMethods(descriptor_->file())) {
+ field_generators_.get(descriptor_->field(i))
+ .GenerateConstructorCode(printer);
}
}
printer->Print(
@@ -886,10 +1444,12 @@ GenerateStructors(io::Printer* printer) {
" : $superclass$() {\n"
" SharedCtor();\n"
" MergeFrom(from);\n"
+ " // @@protoc_insertion_point(copy_constructor:$full_name$)\n"
"}\n"
"\n",
"classname", classname_,
- "superclass", superclass);
+ "superclass", superclass,
+ "full_name", descriptor_->full_name());
// Generate the shared constructor code.
GenerateSharedConstructorCode(printer);
@@ -897,10 +1457,12 @@ GenerateStructors(io::Printer* printer) {
// Generate the destructor.
printer->Print(
"$classname$::~$classname$() {\n"
+ " // @@protoc_insertion_point(destructor:$full_name$)\n"
" SharedDtor();\n"
"}\n"
"\n",
- "classname", classname_);
+ "classname", classname_,
+ "full_name", descriptor_->full_name());
// Generate the shared destructor code.
GenerateSharedDestructorCode(printer);
@@ -929,86 +1491,178 @@ GenerateStructors(io::Printer* printer) {
}
printer->Print(
- "const $classname$& $classname$::default_instance() {\n"
- " if (default_instance_ == NULL) $adddescriptorsname$();"
+ "const $classname$& $classname$::default_instance() {\n",
+ "classname", classname_);
+
+ PrintHandlingOptionalStaticInitializers(
+ descriptor_->file(), printer,
+ // With static initializers.
+ " if (default_instance_ == NULL) $adddescriptorsname$();\n",
+ // Without.
+ " $adddescriptorsname$();\n",
+ // Vars.
+ "adddescriptorsname",
+ GlobalAddDescriptorsName(descriptor_->file()->name()));
+
+ printer->Print(
" return *default_instance_;\n"
"}\n"
"\n"
"$classname$* $classname$::default_instance_ = NULL;\n"
- "\n"
+ "\n",
+ "classname", classname_);
+
+ printer->Print(
"$classname$* $classname$::New() const {\n"
" return new $classname$;\n"
"}\n",
- "classname", classname_,
- "adddescriptorsname",
- GlobalAddDescriptorsName(descriptor_->file()->name()));
+ "classname", classname_);
}
+// Return the number of bits set in n, a non-negative integer.
+static int popcnt(uint32 n) {
+ int result = 0;
+ while (n != 0) {
+ result += (n & 1);
+ n = n / 2;
+ }
+ return result;
+}
+
void MessageGenerator::
GenerateClear(io::Printer* printer) {
printer->Print("void $classname$::Clear() {\n",
"classname", classname_);
printer->Indent();
- int last_index = -1;
-
+ // Step 1: Extensions
if (descriptor_->extension_range_count() > 0) {
printer->Print("_extensions_.Clear();\n");
}
+ // Step 2: Everything but extensions, repeateds, unions.
+ // These are handled in chunks of 8. The first chunk is
+ // the non-extensions-non-repeateds-non-unions in
+ // descriptor_->field(0), descriptor_->field(1), ... descriptor_->field(7),
+ // and the second chunk is the same for
+ // descriptor_->field(8), descriptor_->field(9), ... descriptor_->field(15),
+ // etc.
+ set<int> step2_indices;
+ hash_map<string, int> fieldname_to_chunk;
+ hash_map<int, string> memsets_for_chunk;
+ hash_map<int, int> memset_field_count_for_chunk;
+ hash_set<string> handled; // fields that appear anywhere in memsets_for_chunk
+ hash_map<int, uint32> fields_mask_for_chunk;
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
+ if (!field->is_repeated() && !field->containing_oneof()) {
+ step2_indices.insert(i);
+ int chunk = i / 8;
+ fieldname_to_chunk[FieldName(field)] = chunk;
+ fields_mask_for_chunk[chunk] |= static_cast<uint32>(1) << (i % 32);
+ }
+ }
- if (!field->is_repeated()) {
- map<string, string> vars;
- vars["index"] = SimpleItoa(field->index());
-
- // We can use the fact that _has_bits_ is a giant bitfield to our
- // advantage: We can check up to 32 bits at a time for equality to
- // zero, and skip the whole range if so. This can improve the speed
- // of Clear() for messages which contain a very large number of
- // optional fields of which only a few are used at a time. Here,
- // we've chosen to check 8 bits at a time rather than 32.
- if (i / 8 != last_index / 8 || last_index < 0) {
- if (last_index >= 0) {
- printer->Outdent();
- printer->Print("}\n");
- }
- printer->Print(vars,
- "if (_has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n");
- printer->Indent();
+ // Step 2a: Greedily seek runs of fields that can be cleared by memset-to-0.
+ // The generated code uses two macros to help it clear runs of fields:
+ // OFFSET_OF_FIELD_ computes the offset (in bytes) of a field in the Message.
+ // ZR_ zeroes a non-empty range of fields via memset.
+ const char* macros =
+ "#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \\\n"
+ " &reinterpret_cast<$classname$*>(16)->f) - \\\n"
+ " reinterpret_cast<char*>(16))\n\n"
+ "#define ZR_(first, last) do { \\\n"
+ " size_t f = OFFSET_OF_FIELD_(first); \\\n"
+ " size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \\\n"
+ " ::memset(&first, 0, n); \\\n"
+ " } while (0)\n\n";
+ for (int i = 0; i < runs_of_fields_.size(); i++) {
+ const vector<string>& run = runs_of_fields_[i];
+ if (run.size() < 2) continue;
+ const string& first_field_name = run[0];
+ const string& last_field_name = run.back();
+ int chunk = fieldname_to_chunk[run[0]];
+ memsets_for_chunk[chunk].append(
+ "ZR_(" + first_field_name + "_, " + last_field_name + "_);\n");
+ for (int j = 0; j < run.size(); j++) {
+ GOOGLE_DCHECK_EQ(chunk, fieldname_to_chunk[run[j]]);
+ handled.insert(run[j]);
+ }
+ memset_field_count_for_chunk[chunk] += run.size();
+ }
+ const bool macros_are_needed = handled.size() > 0;
+ if (macros_are_needed) {
+ printer->Outdent();
+ printer->Print(macros,
+ "classname", classname_);
+ printer->Indent();
+ }
+ // Step 2b: Finish step 2, ignoring fields handled in step 2a.
+ int last_index = -1;
+ bool chunk_block_in_progress = false;
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ if (step2_indices.count(i) == 0) continue;
+ const FieldDescriptor* field = descriptor_->field(i);
+ const string fieldname = FieldName(field);
+ if (i / 8 != last_index / 8 || last_index < 0) {
+ // End previous chunk, if there was one.
+ if (chunk_block_in_progress) {
+ printer->Outdent();
+ printer->Print("}\n");
+ chunk_block_in_progress = false;
}
- last_index = i;
-
- // It's faster to just overwrite primitive types, but we should
- // only clear strings and messages if they were set.
- // TODO(kenton): Let the CppFieldGenerator decide this somehow.
- bool should_check_bit =
- field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ||
- field->cpp_type() == FieldDescriptor::CPPTYPE_STRING;
-
- if (should_check_bit) {
- printer->Print(vars, "if (_has_bit($index$)) {\n");
+ // Start chunk.
+ const string& memsets = memsets_for_chunk[i / 8];
+ uint32 mask = fields_mask_for_chunk[i / 8];
+ int count = popcnt(mask);
+ if (count == 1 ||
+ (count <= 4 && count == memset_field_count_for_chunk[i / 8])) {
+ // No "if" here because the chunk is trivial.
+ } else {
+ printer->Print(
+ "if (_has_bits_[$index$ / 32] & $mask$) {\n",
+ "index", SimpleItoa(i / 8 * 8),
+ "mask", SimpleItoa(mask));
printer->Indent();
+ chunk_block_in_progress = true;
}
+ printer->Print(memsets.c_str());
+ }
+ last_index = i;
+ if (handled.count(fieldname) > 0) continue;
+
+ // It's faster to just overwrite primitive types, but we should
+ // only clear strings and messages if they were set.
+ // TODO(kenton): Let the CppFieldGenerator decide this somehow.
+ bool should_check_bit =
+ field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ||
+ field->cpp_type() == FieldDescriptor::CPPTYPE_STRING;
+
+ if (should_check_bit) {
+ printer->Print("if (has_$name$()) {\n", "name", fieldname);
+ printer->Indent();
+ }
- field_generators_.get(field).GenerateClearingCode(printer);
+ field_generators_.get(field).GenerateClearingCode(printer);
- if (should_check_bit) {
- printer->Outdent();
- printer->Print("}\n");
- }
+ if (should_check_bit) {
+ printer->Outdent();
+ printer->Print("}\n");
}
}
- if (last_index >= 0) {
+ if (chunk_block_in_progress) {
printer->Outdent();
printer->Print("}\n");
}
+ if (macros_are_needed) {
+ printer->Outdent();
+ printer->Print("\n#undef OFFSET_OF_FIELD_\n#undef ZR_\n\n");
+ printer->Indent();
+ }
- // Repeated fields don't use _has_bits_ so we clear them in a separate
- // pass.
+ // Step 3: Repeated fields don't use _has_bits_; emit code to clear them here.
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
@@ -1017,12 +1671,23 @@ GenerateClear(io::Printer* printer) {
}
}
+ // Step 4: Unions.
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ printer->Print(
+ "clear_$oneof_name$();\n",
+ "oneof_name", descriptor_->oneof_decl(i)->name());
+ }
+
+ // Step 5: Everything else.
printer->Print(
"::memset(_has_bits_, 0, sizeof(_has_bits_));\n");
- if (HasUnknownFields(descriptor_->file())) {
+ if (UseUnknownFieldSet(descriptor_->file())) {
printer->Print(
"mutable_unknown_fields()->Clear();\n");
+ } else {
+ printer->Print(
+ "mutable_unknown_fields()->clear();\n");
}
printer->Outdent();
@@ -1030,6 +1695,58 @@ GenerateClear(io::Printer* printer) {
}
void MessageGenerator::
+GenerateOneofClear(io::Printer* printer) {
+ // Generated function clears the active field and union case (e.g. foo_case_).
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ printer->Print(
+ "void $classname$::clear_$oneofname$() {\n",
+ "classname", classname_,
+ "oneofname", descriptor_->oneof_decl(i)->name());
+ printer->Indent();
+ printer->Print(
+ "switch($oneofname$_case()) {\n",
+ "oneofname", descriptor_->oneof_decl(i)->name());
+ printer->Indent();
+ for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+ const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j);
+ printer->Print(
+ "case k$field_name$: {\n",
+ "field_name", UnderscoresToCamelCase(field->name(), true));
+ printer->Indent();
+ // We clear only allocated objects in oneofs
+ if (!IsStringOrMessage(field)) {
+ printer->Print(
+ "// No need to clear\n");
+ } else {
+ field_generators_.get(field).GenerateClearingCode(printer);
+ }
+ printer->Print(
+ "break;\n");
+ printer->Outdent();
+ printer->Print(
+ "}\n");
+ }
+ printer->Print(
+ "case $cap_oneof_name$_NOT_SET: {\n"
+ " break;\n"
+ "}\n",
+ "cap_oneof_name",
+ ToUpper(descriptor_->oneof_decl(i)->name()));
+ printer->Outdent();
+ printer->Print(
+ "}\n"
+ "_oneof_case_[$oneof_index$] = $cap_oneof_name$_NOT_SET;\n",
+ "oneof_index", SimpleItoa(i),
+ "cap_oneof_name",
+ ToUpper(descriptor_->oneof_decl(i)->name()));
+ printer->Outdent();
+ printer->Print(
+ "}\n"
+ "\n");
+ }
+}
+
+void MessageGenerator::
GenerateSwap(io::Printer* printer) {
// Generate the Swap member function.
printer->Print("void $classname$::Swap($classname$* other) {\n",
@@ -1044,13 +1761,23 @@ GenerateSwap(io::Printer* printer) {
field_generators_.get(field).GenerateSwappingCode(printer);
}
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ printer->Print(
+ "std::swap($oneof_name$_, other->$oneof_name$_);\n"
+ "std::swap(_oneof_case_[$i$], other->_oneof_case_[$i$]);\n",
+ "oneof_name", descriptor_->oneof_decl(i)->name(),
+ "i", SimpleItoa(i));
+ }
+
for (int i = 0; i < (descriptor_->field_count() + 31) / 32; ++i) {
printer->Print("std::swap(_has_bits_[$i$], other->_has_bits_[$i$]);\n",
"i", SimpleItoa(i));
}
- if (HasUnknownFields(descriptor_->file())) {
+ if (UseUnknownFieldSet(descriptor_->file())) {
printer->Print("_unknown_fields_.Swap(&other->_unknown_fields_);\n");
+ } else {
+ printer->Print("_unknown_fields_.swap(other->_unknown_fields_);\n");
}
printer->Print("std::swap(_cached_size_, other->_cached_size_);\n");
if (descriptor_->extension_range_count() > 0) {
@@ -1122,31 +1849,60 @@ GenerateMergeFrom(io::Printer* printer) {
}
}
+ // Merge oneof fields. Oneof field requires oneof case check.
+ for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) {
+ printer->Print(
+ "switch (from.$oneofname$_case()) {\n",
+ "oneofname", descriptor_->oneof_decl(i)->name());
+ printer->Indent();
+ for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+ const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j);
+ printer->Print(
+ "case k$field_name$: {\n",
+ "field_name", UnderscoresToCamelCase(field->name(), true));
+ printer->Indent();
+ field_generators_.get(field).GenerateMergingCode(printer);
+ printer->Print(
+ "break;\n");
+ printer->Outdent();
+ printer->Print(
+ "}\n");
+ }
+ printer->Print(
+ "case $cap_oneof_name$_NOT_SET: {\n"
+ " break;\n"
+ "}\n",
+ "cap_oneof_name",
+ ToUpper(descriptor_->oneof_decl(i)->name()));
+ printer->Outdent();
+ printer->Print(
+ "}\n");
+ }
+
// Merge Optional and Required fields (after a _has_bit check).
int last_index = -1;
for (int i = 0; i < descriptor_->field_count(); ++i) {
const FieldDescriptor* field = descriptor_->field(i);
- if (!field->is_repeated()) {
- map<string, string> vars;
- vars["index"] = SimpleItoa(field->index());
-
+ if (!field->is_repeated() && !field->containing_oneof()) {
// See above in GenerateClear for an explanation of this.
if (i / 8 != last_index / 8 || last_index < 0) {
if (last_index >= 0) {
printer->Outdent();
printer->Print("}\n");
}
- printer->Print(vars,
- "if (from._has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n");
+ printer->Print(
+ "if (from._has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n",
+ "index", SimpleItoa(field->index()));
printer->Indent();
}
last_index = i;
- printer->Print(vars,
- "if (from._has_bit($index$)) {\n");
+ printer->Print(
+ "if (from.has_$name$()) {\n",
+ "name", FieldName(field));
printer->Indent();
field_generators_.get(field).GenerateMergingCode(printer);
@@ -1165,9 +1921,12 @@ GenerateMergeFrom(io::Printer* printer) {
printer->Print("_extensions_.MergeFrom(from._extensions_);\n");
}
- if (HasUnknownFields(descriptor_->file())) {
+ if (UseUnknownFieldSet(descriptor_->file())) {
printer->Print(
"mutable_unknown_fields()->MergeFrom(from.unknown_fields());\n");
+ } else {
+ printer->Print(
+ "mutable_unknown_fields()->append(from.unknown_fields());\n");
}
printer->Outdent();
@@ -1214,25 +1973,61 @@ GenerateMergeFromCodedStream(io::Printer* printer) {
// Special-case MessageSet.
printer->Print(
"bool $classname$::MergePartialFromCodedStream(\n"
- " ::google::protobuf::io::CodedInputStream* input) {\n"
+ " ::google::protobuf::io::CodedInputStream* input) {\n",
+ "classname", classname_);
+
+ PrintHandlingOptionalStaticInitializers(
+ descriptor_->file(), printer,
+ // With static initializers.
" return _extensions_.ParseMessageSet(input, default_instance_,\n"
- " mutable_unknown_fields());\n"
- "}\n",
+ " mutable_unknown_fields());\n",
+ // Without.
+ " return _extensions_.ParseMessageSet(input, &default_instance(),\n"
+ " mutable_unknown_fields());\n",
+ // Vars.
"classname", classname_);
+
+ printer->Print(
+ "}\n");
return;
}
printer->Print(
"bool $classname$::MergePartialFromCodedStream(\n"
" ::google::protobuf::io::CodedInputStream* input) {\n"
- "#define DO_(EXPRESSION) if (!(EXPRESSION)) return false\n"
- " ::google::protobuf::uint32 tag;\n"
- " while ((tag = input->ReadTag()) != 0) {\n",
+ "#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure\n"
+ " ::google::protobuf::uint32 tag;\n",
"classname", classname_);
+ if (!UseUnknownFieldSet(descriptor_->file())) {
+ printer->Print(
+ " ::google::protobuf::io::StringOutputStream unknown_fields_string(\n"
+ " mutable_unknown_fields());\n"
+ " ::google::protobuf::io::CodedOutputStream unknown_fields_stream(\n"
+ " &unknown_fields_string);\n");
+ }
+
+ printer->Print(
+ " // @@protoc_insertion_point(parse_start:$full_name$)\n",
+ "full_name", descriptor_->full_name());
+
printer->Indent();
+ printer->Print("for (;;) {\n");
printer->Indent();
+ scoped_array<const FieldDescriptor*> ordered_fields(
+ SortFieldsByNumber(descriptor_));
+ uint32 maxtag = descriptor_->field_count() == 0 ? 0 :
+ WireFormat::MakeTag(ordered_fields[descriptor_->field_count() - 1]);
+ const int kCutoff0 = 127; // fits in 1-byte varint
+ const int kCutoff1 = (127 << 7) + 127; // fits in 2-byte varint
+ printer->Print("::std::pair< ::google::protobuf::uint32, bool> p = "
+ "input->ReadTagWithCutoff($max$);\n"
+ "tag = p.first;\n"
+ "if (!p.second) goto handle_unusual;\n",
+ "max", SimpleItoa(maxtag <= kCutoff0 ? kCutoff0 :
+ (maxtag <= kCutoff1 ? kCutoff1 :
+ maxtag)));
if (descriptor_->field_count() > 0) {
// We don't even want to print the switch() if we have no fields because
// MSVC dislikes switch() statements that contain only a default value.
@@ -1242,14 +2037,11 @@ GenerateMergeFromCodedStream(io::Printer* printer) {
// of each case. However, this is actually a bit slower in practice as it
// creates a jump table that is 8x larger and sparser, and meanwhile the
// if()s are highly predictable.
- printer->Print(
- "switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {\n");
+ printer->Print("switch (::google::protobuf::internal::WireFormatLite::"
+ "GetTagFieldNumber(tag)) {\n");
printer->Indent();
- scoped_array<const FieldDescriptor*> ordered_fields(
- SortFieldsByNumber(descriptor_));
-
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = ordered_fields[i];
@@ -1262,10 +2054,8 @@ GenerateMergeFromCodedStream(io::Printer* printer) {
const FieldGenerator& field_generator = field_generators_.get(field);
// Emit code to parse the common, expected case.
- printer->Print(
- "if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==\n"
- " ::google::protobuf::internal::WireFormatLite::WIRETYPE_$wiretype$) {\n",
- "wiretype", kWireTypeNames[WireFormat::WireTypeForField(field)]);
+ printer->Print("if (tag == $commontag$) {\n",
+ "commontag", SimpleItoa(WireFormat::MakeTag(field)));
if (i > 0 || (field->is_repeated() && !field->options().packed())) {
printer->Print(
@@ -1283,20 +2073,22 @@ GenerateMergeFromCodedStream(io::Printer* printer) {
// Emit code to parse unexpectedly packed or unpacked values.
if (field->is_packable() && field->options().packed()) {
- printer->Print(
- "} else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)\n"
- " == ::google::protobuf::internal::WireFormatLite::\n"
- " WIRETYPE_$wiretype$) {\n",
- "wiretype",
- kWireTypeNames[WireFormat::WireTypeForFieldType(field->type())]);
+ internal::WireFormatLite::WireType wiretype =
+ WireFormat::WireTypeForFieldType(field->type());
+ printer->Print("} else if (tag == $uncommontag$) {\n",
+ "uncommontag", SimpleItoa(
+ internal::WireFormatLite::MakeTag(
+ field->number(), wiretype)));
printer->Indent();
field_generator.GenerateMergeFromCodedStream(printer);
printer->Outdent();
} else if (field->is_packable() && !field->options().packed()) {
- printer->Print(
- "} else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)\n"
- " == ::google::protobuf::internal::WireFormatLite::\n"
- " WIRETYPE_LENGTH_DELIMITED) {\n");
+ internal::WireFormatLite::WireType wiretype =
+ internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED;
+ printer->Print("} else if (tag == $uncommontag$) {\n",
+ "uncommontag", SimpleItoa(
+ internal::WireFormatLite::MakeTag(
+ field->number(), wiretype)));
printer->Indent();
field_generator.GenerateMergeFromCodedStreamWithPacking(printer);
printer->Outdent();
@@ -1304,7 +2096,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) {
printer->Print(
"} else {\n"
- " goto handle_uninterpreted;\n"
+ " goto handle_unusual;\n"
"}\n");
// switch() is slow since it can't be predicted well. Insert some if()s
@@ -1328,7 +2120,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) {
// Expect EOF.
// TODO(kenton): Expect group end-tag?
printer->Print(
- "if (input->ExpectAtEnd()) return true;\n");
+ "if (input->ExpectAtEnd()) goto success;\n");
}
printer->Print(
@@ -1338,17 +2130,19 @@ GenerateMergeFromCodedStream(io::Printer* printer) {
printer->Print("}\n\n");
}
- printer->Print(
- "default: {\n"
- "handle_uninterpreted:\n");
+ printer->Print("default: {\n");
printer->Indent();
}
- // Is this an end-group tag? If so, this must be the end of the message.
+ printer->Outdent();
+ printer->Print("handle_unusual:\n");
+ printer->Indent();
+ // If tag is 0 or an end-group tag then this must be the end of the message.
printer->Print(
- "if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==\n"
+ "if (tag == 0 ||\n"
+ " ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==\n"
" ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {\n"
- " return true;\n"
+ " goto success;\n"
"}\n");
// Handle extension ranges.
@@ -1377,13 +2171,24 @@ GenerateMergeFromCodedStream(io::Printer* printer) {
}
}
printer->Print(") {\n");
- if (HasUnknownFields(descriptor_->file())) {
- printer->Print(
+ if (UseUnknownFieldSet(descriptor_->file())) {
+ PrintHandlingOptionalStaticInitializers(
+ descriptor_->file(), printer,
+ // With static initializers.
" DO_(_extensions_.ParseField(tag, input, default_instance_,\n"
+ " mutable_unknown_fields()));\n",
+ // Without.
+ " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n"
" mutable_unknown_fields()));\n");
} else {
- printer->Print(
- " DO_(_extensions_.ParseField(tag, input, default_instance_));\n");
+ PrintHandlingOptionalStaticInitializers(
+ descriptor_->file(), printer,
+ // With static initializers.
+ " DO_(_extensions_.ParseField(tag, input, default_instance_,\n"
+ " &unknown_fields_stream));\n",
+ // Without.
+ " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n"
+ " &unknown_fields_stream));\n");
}
printer->Print(
" continue;\n"
@@ -1391,13 +2196,14 @@ GenerateMergeFromCodedStream(io::Printer* printer) {
}
// We really don't recognize this tag. Skip it.
- if (HasUnknownFields(descriptor_->file())) {
+ if (UseUnknownFieldSet(descriptor_->file())) {
printer->Print(
"DO_(::google::protobuf::internal::WireFormat::SkipField(\n"
" input, tag, mutable_unknown_fields()));\n");
} else {
printer->Print(
- "DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));\n");
+ "DO_(::google::protobuf::internal::WireFormatLite::SkipField(\n"
+ " input, tag, &unknown_fields_stream));\n");
}
if (descriptor_->field_count() > 0) {
@@ -1411,10 +2217,15 @@ GenerateMergeFromCodedStream(io::Printer* printer) {
printer->Outdent();
printer->Outdent();
printer->Print(
- " }\n" // while
+ " }\n" // for (;;)
+ "success:\n"
+ " // @@protoc_insertion_point(parse_success:$full_name$)\n"
" return true;\n"
+ "failure:\n"
+ " // @@protoc_insertion_point(parse_failure:$full_name$)\n"
+ " return false;\n"
"#undef DO_\n"
- "}\n");
+ "}\n", "full_name", descriptor_->full_name());
}
void MessageGenerator::GenerateSerializeOneField(
@@ -1423,8 +2234,8 @@ void MessageGenerator::GenerateSerializeOneField(
if (!field->is_repeated()) {
printer->Print(
- "if (_has_bit($index$)) {\n",
- "index", SimpleItoa(field->index()));
+ "if (has_$name$()) {\n",
+ "name", FieldName(field));
printer->Indent();
}
@@ -1470,11 +2281,10 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) {
" ::google::protobuf::io::CodedOutputStream* output) const {\n"
" _extensions_.SerializeMessageSetWithCachedSizes(output);\n",
"classname", classname_);
- if (HasUnknownFields(descriptor_->file())) {
- printer->Print(
- " ::google::protobuf::internal::WireFormat::SerializeUnknownMessageSetItems(\n"
- " unknown_fields(), output);\n");
- }
+ GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file()));
+ printer->Print(
+ " ::google::protobuf::internal::WireFormat::SerializeUnknownMessageSetItems(\n"
+ " unknown_fields(), output);\n");
printer->Print(
"}\n");
return;
@@ -1486,8 +2296,16 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) {
"classname", classname_);
printer->Indent();
+ printer->Print(
+ "// @@protoc_insertion_point(serialize_start:$full_name$)\n",
+ "full_name", descriptor_->full_name());
+
GenerateSerializeWithCachedSizesBody(printer, false);
+ printer->Print(
+ "// @@protoc_insertion_point(serialize_end:$full_name$)\n",
+ "full_name", descriptor_->full_name());
+
printer->Outdent();
printer->Print(
"}\n");
@@ -1503,12 +2321,11 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) {
" target =\n"
" _extensions_.SerializeMessageSetWithCachedSizesToArray(target);\n",
"classname", classname_);
- if (HasUnknownFields(descriptor_->file())) {
- printer->Print(
- " target = ::google::protobuf::internal::WireFormat::\n"
- " SerializeUnknownMessageSetItemsToArray(\n"
- " unknown_fields(), target);\n");
- }
+ GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file()));
+ printer->Print(
+ " target = ::google::protobuf::internal::WireFormat::\n"
+ " SerializeUnknownMessageSetItemsToArray(\n"
+ " unknown_fields(), target);\n");
printer->Print(
" return target;\n"
"}\n");
@@ -1521,8 +2338,16 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) {
"classname", classname_);
printer->Indent();
+ printer->Print(
+ "// @@protoc_insertion_point(serialize_to_array_start:$full_name$)\n",
+ "full_name", descriptor_->full_name());
+
GenerateSerializeWithCachedSizesBody(printer, true);
+ printer->Print(
+ "// @@protoc_insertion_point(serialize_to_array_end:$full_name$)\n",
+ "full_name", descriptor_->full_name());
+
printer->Outdent();
printer->Print(
" return target;\n"
@@ -1532,7 +2357,7 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) {
void MessageGenerator::
GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) {
scoped_array<const FieldDescriptor*> ordered_fields(
- SortFieldsByNumber(descriptor_));
+ SortFieldsByNumber(descriptor_));
vector<const Descriptor::ExtensionRange*> sorted_extensions;
for (int i = 0; i < descriptor_->extension_range_count(); ++i) {
@@ -1561,7 +2386,7 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) {
}
}
- if (HasUnknownFields(descriptor_->file())) {
+ if (UseUnknownFieldSet(descriptor_->file())) {
printer->Print("if (!unknown_fields().empty()) {\n");
printer->Indent();
if (to_array) {
@@ -1578,6 +2403,10 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) {
printer->Print(
"}\n");
+ } else {
+ printer->Print(
+ "output->WriteRaw(unknown_fields().data(),\n"
+ " unknown_fields().size());\n");
}
}
@@ -1589,11 +2418,10 @@ GenerateByteSize(io::Printer* printer) {
"int $classname$::ByteSize() const {\n"
" int total_size = _extensions_.MessageSetByteSize();\n",
"classname", classname_);
- if (HasUnknownFields(descriptor_->file())) {
- printer->Print(
- " total_size += ::google::protobuf::internal::WireFormat::\n"
- " ComputeUnknownMessageSetItemsSize(unknown_fields());\n");
- }
+ GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file()));
+ printer->Print(
+ " total_size += ::google::protobuf::internal::WireFormat::\n"
+ " ComputeUnknownMessageSetItemsSize(unknown_fields());\n");
printer->Print(
" GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();\n"
" _cached_size_ = total_size;\n"
@@ -1616,7 +2444,7 @@ GenerateByteSize(io::Printer* printer) {
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
- if (!field->is_repeated()) {
+ if (!field->is_repeated() && !field->containing_oneof()) {
// See above in GenerateClear for an explanation of this.
// TODO(kenton): Share code? Unclear how to do so without
// over-engineering.
@@ -1666,13 +2494,45 @@ GenerateByteSize(io::Printer* printer) {
}
}
+ // Fields inside a oneof don't use _has_bits_ so we count them in a separate
+ // pass.
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ printer->Print(
+ "switch ($oneofname$_case()) {\n",
+ "oneofname", descriptor_->oneof_decl(i)->name());
+ printer->Indent();
+ for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+ const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j);
+ PrintFieldComment(printer, field);
+ printer->Print(
+ "case k$field_name$: {\n",
+ "field_name", UnderscoresToCamelCase(field->name(), true));
+ printer->Indent();
+ field_generators_.get(field).GenerateByteSize(printer);
+ printer->Print(
+ "break;\n");
+ printer->Outdent();
+ printer->Print(
+ "}\n");
+ }
+ printer->Print(
+ "case $cap_oneof_name$_NOT_SET: {\n"
+ " break;\n"
+ "}\n",
+ "cap_oneof_name",
+ ToUpper(descriptor_->oneof_decl(i)->name()));
+ printer->Outdent();
+ printer->Print(
+ "}\n");
+ }
+
if (descriptor_->extension_range_count() > 0) {
printer->Print(
"total_size += _extensions_.ByteSize();\n"
"\n");
}
- if (HasUnknownFields(descriptor_->file())) {
+ if (UseUnknownFieldSet(descriptor_->file())) {
printer->Print("if (!unknown_fields().empty()) {\n");
printer->Indent();
printer->Print(
@@ -1681,6 +2541,10 @@ GenerateByteSize(io::Printer* printer) {
" unknown_fields());\n");
printer->Outdent();
printer->Print("}\n");
+ } else {
+ printer->Print(
+ "total_size += unknown_fields().size();\n"
+ "\n");
}
// We update _cached_size_ even though this is a const method. In theory,
@@ -1734,19 +2598,30 @@ GenerateIsInitialized(io::Printer* printer) {
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
+ !ShouldIgnoreRequiredFieldCheck(field) &&
HasRequiredFields(field->message_type())) {
if (field->is_repeated()) {
printer->Print(
- "for (int i = 0; i < $name$_size(); i++) {\n"
- " if (!this->$name$(i).IsInitialized()) return false;\n"
- "}\n",
+ "if (!::google::protobuf::internal::AllAreInitialized(this->$name$()))"
+ " return false;\n",
"name", FieldName(field));
} else {
- printer->Print(
- "if (has_$name$()) {\n"
- " if (!this->$name$().IsInitialized()) return false;\n"
- "}\n",
- "name", FieldName(field));
+ if (field->options().weak()) {
+ // For weak fields, use the data member (google::protobuf::Message*) instead
+ // of the getter to avoid a link dependency on the weak message type
+ // which is only forward declared.
+ printer->Print(
+ "if (has_$name$()) {\n"
+ " if (!this->$name$_->IsInitialized()) return false;\n"
+ "}\n",
+ "name", FieldName(field));
+ } else {
+ printer->Print(
+ "if (has_$name$()) {\n"
+ " if (!this->$name$().IsInitialized()) return false;\n"
+ "}\n",
+ "name", FieldName(field));
+ }
}
}
}
diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h
index 04778f6..bfd3cec 100644
--- a/src/google/protobuf/compiler/cpp/cpp_message.h
+++ b/src/google/protobuf/compiler/cpp/cpp_message.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -35,9 +35,11 @@
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__
+#include <memory>
#include <string>
-#include <google/protobuf/stubs/common.h>
+#include <vector>
#include <google/protobuf/compiler/cpp/cpp_field.h>
+#include <google/protobuf/compiler/cpp/cpp_options.h>
namespace google {
namespace protobuf {
@@ -57,7 +59,7 @@ class MessageGenerator {
public:
// See generator.cc for the meaning of dllexport_decl.
explicit MessageGenerator(const Descriptor* descriptor,
- const string& dllexport_decl);
+ const Options& options);
~MessageGenerator();
// Header stuff.
@@ -131,6 +133,7 @@ class MessageGenerator {
// Generate standard Message methods.
void GenerateClear(io::Printer* printer);
+ void GenerateOneofClear(io::Printer* printer);
void GenerateMergeFromCodedStream(io::Printer* printer);
void GenerateSerializeWithCachedSizes(io::Printer* printer);
void GenerateSerializeWithCachedSizesToArray(io::Printer* printer);
@@ -153,11 +156,13 @@ class MessageGenerator {
const Descriptor* descriptor_;
string classname_;
- string dllexport_decl_;
+ Options options_;
FieldGeneratorMap field_generators_;
+ vector< vector<string> > runs_of_fields_; // that might be trivially cleared
scoped_array<scoped_ptr<MessageGenerator> > nested_generators_;
scoped_array<scoped_ptr<EnumGenerator> > enum_generators_;
scoped_array<scoped_ptr<ExtensionGenerator> > extension_generators_;
+ bool uses_string_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator);
};
diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc
index c04bdc6..6ac15a5 100644
--- a/src/google/protobuf/compiler/cpp/cpp_message_field.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -45,13 +45,20 @@ namespace cpp {
namespace {
void SetMessageVariables(const FieldDescriptor* descriptor,
- map<string, string>* variables) {
- SetCommonFieldVariables(descriptor, variables);
+ map<string, string>* variables,
+ const Options& options) {
+ SetCommonFieldVariables(descriptor, variables, options);
(*variables)["type"] = FieldMessageTypeName(descriptor);
(*variables)["stream_writer"] = (*variables)["declared_type"] +
(HasFastArraySerialization(descriptor->message_type()->file()) ?
"MaybeToArray" :
"");
+ // NOTE: Escaped here to unblock proto1->proto2 migration.
+ // TODO(liujisi): Extend this to apply for other conflicting methods.
+ (*variables)["release_name"] =
+ SafeFunctionName(descriptor->containing_type(),
+ descriptor, "release_");
+ (*variables)["full_name"] = descriptor->full_name();
}
} // namespace
@@ -59,9 +66,10 @@ void SetMessageVariables(const FieldDescriptor* descriptor,
// ===================================================================
MessageFieldGenerator::
-MessageFieldGenerator(const FieldDescriptor* descriptor)
+MessageFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options)
: descriptor_(descriptor) {
- SetMessageVariables(descriptor, &variables_);
+ SetMessageVariables(descriptor, &variables_, options);
}
MessageFieldGenerator::~MessageFieldGenerator() {}
@@ -75,19 +83,47 @@ void MessageFieldGenerator::
GenerateAccessorDeclarations(io::Printer* printer) const {
printer->Print(variables_,
"inline const $type$& $name$() const$deprecation$;\n"
- "inline $type$* mutable_$name$()$deprecation$;\n");
+ "inline $type$* mutable_$name$()$deprecation$;\n"
+ "inline $type$* $release_name$()$deprecation$;\n"
+ "inline void set_allocated_$name$($type$* $name$)$deprecation$;\n");
}
void MessageFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline const $type$& $classname$::$name$() const {\n"
- " return $name$_ != NULL ? *$name$_ : *default_instance_->$name$_;\n"
+ " // @@protoc_insertion_point(field_get:$full_name$)\n");
+
+ PrintHandlingOptionalStaticInitializers(
+ variables_, descriptor_->file(), printer,
+ // With static initializers.
+ " return $name$_ != NULL ? *$name$_ : *default_instance_->$name$_;\n",
+ // Without.
+ " return $name$_ != NULL ? *$name$_ : *default_instance().$name$_;\n");
+
+ printer->Print(variables_,
"}\n"
"inline $type$* $classname$::mutable_$name$() {\n"
- " _set_bit($index$);\n"
+ " set_has_$name$();\n"
" if ($name$_ == NULL) $name$_ = new $type$;\n"
+ " // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_;\n"
+ "}\n"
+ "inline $type$* $classname$::$release_name$() {\n"
+ " clear_has_$name$();\n"
+ " $type$* temp = $name$_;\n"
+ " $name$_ = NULL;\n"
+ " return temp;\n"
+ "}\n"
+ "inline void $classname$::set_allocated_$name$($type$* $name$) {\n"
+ " delete $name$_;\n"
+ " $name$_ = $name$;\n"
+ " if ($name$) {\n"
+ " set_has_$name$();\n"
+ " } else {\n"
+ " clear_has_$name$();\n"
+ " }\n"
+ " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n");
}
@@ -151,10 +187,74 @@ GenerateByteSize(io::Printer* printer) const {
// ===================================================================
+MessageOneofFieldGenerator::
+MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options)
+ : MessageFieldGenerator(descriptor, options) {
+ SetCommonOneofFieldVariables(descriptor, &variables_);
+}
+
+MessageOneofFieldGenerator::~MessageOneofFieldGenerator() {}
+
+void MessageOneofFieldGenerator::
+GenerateInlineAccessorDefinitions(io::Printer* printer) const {
+ printer->Print(variables_,
+ "inline const $type$& $classname$::$name$() const {\n"
+ " return has_$name$() ? *$oneof_prefix$$name$_\n"
+ " : $type$::default_instance();\n"
+ "}\n"
+ "inline $type$* $classname$::mutable_$name$() {\n"
+ " if (!has_$name$()) {\n"
+ " clear_$oneof_name$();\n"
+ " set_has_$name$();\n"
+ " $oneof_prefix$$name$_ = new $type$;\n"
+ " }\n"
+ " return $oneof_prefix$$name$_;\n"
+ "}\n"
+ "inline $type$* $classname$::$release_name$() {\n"
+ " if (has_$name$()) {\n"
+ " clear_has_$oneof_name$();\n"
+ " $type$* temp = $oneof_prefix$$name$_;\n"
+ " $oneof_prefix$$name$_ = NULL;\n"
+ " return temp;\n"
+ " } else {\n"
+ " return NULL;\n"
+ " }\n"
+ "}\n"
+ "inline void $classname$::set_allocated_$name$($type$* $name$) {\n"
+ " clear_$oneof_name$();\n"
+ " if ($name$) {\n"
+ " set_has_$name$();\n"
+ " $oneof_prefix$$name$_ = $name$;\n"
+ " }\n"
+ "}\n");
+}
+
+void MessageOneofFieldGenerator::
+GenerateClearingCode(io::Printer* printer) const {
+ // if it is the active field, it cannot be NULL.
+ printer->Print(variables_,
+ "delete $oneof_prefix$$name$_;\n");
+}
+
+void MessageOneofFieldGenerator::
+GenerateSwappingCode(io::Printer* printer) const {
+ // Don't print any swapping code. Swapping the union will swap this field.
+}
+
+void MessageOneofFieldGenerator::
+GenerateConstructorCode(io::Printer* printer) const {
+ // Don't print any constructor code. The field is in a union. We allocate
+ // space only when this field is used.
+}
+
+// ===================================================================
+
RepeatedMessageFieldGenerator::
-RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor)
+RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options)
: descriptor_(descriptor) {
- SetMessageVariables(descriptor, &variables_);
+ SetMessageVariables(descriptor, &variables_, options);
}
RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {}
@@ -182,21 +282,26 @@ void RepeatedMessageFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline const $type$& $classname$::$name$(int index) const {\n"
- " return $name$_.Get(index);\n"
+ " // @@protoc_insertion_point(field_get:$full_name$)\n"
+ " return $name$_.$cppget$(index);\n"
"}\n"
"inline $type$* $classname$::mutable_$name$(int index) {\n"
+ " // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_.Mutable(index);\n"
"}\n"
"inline $type$* $classname$::add_$name$() {\n"
+ " // @@protoc_insertion_point(field_add:$full_name$)\n"
" return $name$_.Add();\n"
"}\n");
printer->Print(variables_,
"inline const ::google::protobuf::RepeatedPtrField< $type$ >&\n"
"$classname$::$name$() const {\n"
+ " // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $name$_;\n"
"}\n"
"inline ::google::protobuf::RepeatedPtrField< $type$ >*\n"
"$classname$::mutable_$name$() {\n"
+ " // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return &$name$_;\n"
"}\n");
}
diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.h b/src/google/protobuf/compiler/cpp/cpp_message_field.h
index f514727..2dff314 100644
--- a/src/google/protobuf/compiler/cpp/cpp_message_field.h
+++ b/src/google/protobuf/compiler/cpp/cpp_message_field.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -46,7 +46,8 @@ namespace cpp {
class MessageFieldGenerator : public FieldGenerator {
public:
- explicit MessageFieldGenerator(const FieldDescriptor* descriptor);
+ explicit MessageFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options);
~MessageFieldGenerator();
// implements FieldGenerator ---------------------------------------
@@ -62,16 +63,34 @@ class MessageFieldGenerator : public FieldGenerator {
void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const;
void GenerateByteSize(io::Printer* printer) const;
- private:
+ protected:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator);
};
+class MessageOneofFieldGenerator : public MessageFieldGenerator {
+ public:
+ explicit MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options);
+ ~MessageOneofFieldGenerator();
+
+ // implements FieldGenerator ---------------------------------------
+ void GenerateInlineAccessorDefinitions(io::Printer* printer) const;
+ void GenerateClearingCode(io::Printer* printer) const;
+ void GenerateSwappingCode(io::Printer* printer) const;
+ void GenerateConstructorCode(io::Printer* printer) const;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageOneofFieldGenerator);
+};
+
class RepeatedMessageFieldGenerator : public FieldGenerator {
public:
- explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor);
+ explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options);
~RepeatedMessageFieldGenerator();
// implements FieldGenerator ---------------------------------------
diff --git a/src/google/protobuf/compiler/cpp/cpp_options.h b/src/google/protobuf/compiler/cpp/cpp_options.h
new file mode 100644
index 0000000..0c99cff
--- /dev/null
+++ b/src/google/protobuf/compiler/cpp/cpp_options.h
@@ -0,0 +1,58 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: rennie@google.com (Jeffrey Rennie)
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_OPTIONS_H__
+#define GOOGLE_PROTOBUF_COMPILER_CPP_OPTIONS_H__
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace cpp {
+
+// Generator options:
+struct Options {
+ Options() : safe_boundary_check(false) {
+ }
+ string dllexport_decl;
+ bool safe_boundary_check;
+};
+
+} // namespace cpp
+} // namespace compiler
+} // namespace protobuf
+
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_COMPILER_CPP_OPTIONS_H__
diff --git a/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc
index 440b716..2dbf14c 100644
--- a/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -34,6 +34,8 @@
// It seemed like parameterizing it would add more complexity than it is
// worth.
+#include <memory>
+
#include <google/protobuf/compiler/cpp/cpp_generator.h>
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/io/zero_copy_stream.h>
@@ -56,24 +58,24 @@ class TestGenerator : public CodeGenerator {
virtual bool Generate(const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* context,
string* error) const {
- TryInsert("test.pb.h", "includes", output_directory);
- TryInsert("test.pb.h", "namespace_scope", output_directory);
- TryInsert("test.pb.h", "global_scope", output_directory);
- TryInsert("test.pb.h", "class_scope:foo.Bar", output_directory);
- TryInsert("test.pb.h", "class_scope:foo.Bar.Baz", output_directory);
-
- TryInsert("test.pb.cc", "includes", output_directory);
- TryInsert("test.pb.cc", "namespace_scope", output_directory);
- TryInsert("test.pb.cc", "global_scope", output_directory);
+ TryInsert("test.pb.h", "includes", context);
+ TryInsert("test.pb.h", "namespace_scope", context);
+ TryInsert("test.pb.h", "global_scope", context);
+ TryInsert("test.pb.h", "class_scope:foo.Bar", context);
+ TryInsert("test.pb.h", "class_scope:foo.Bar.Baz", context);
+
+ TryInsert("test.pb.cc", "includes", context);
+ TryInsert("test.pb.cc", "namespace_scope", context);
+ TryInsert("test.pb.cc", "global_scope", context);
return true;
}
void TryInsert(const string& filename, const string& insertion_point,
- OutputDirectory* output_directory) const {
+ GeneratorContext* context) const {
scoped_ptr<io::ZeroCopyOutputStream> output(
- output_directory->OpenForInsert(filename, insertion_point));
+ context->OpenForInsert(filename, insertion_point));
io::Printer printer(output.get(), '$');
printer.Print("// inserted $name$\n", "name", insertion_point);
}
@@ -83,13 +85,13 @@ class TestGenerator : public CodeGenerator {
// not verify that they are correctly-placed; that would require actually
// compiling the output which is a bit more than I care to do for this test.
TEST(CppPluginTest, PluginTest) {
- File::WriteStringToFileOrDie(
- "syntax = \"proto2\";\n"
- "package foo;\n"
- "message Bar {\n"
- " message Baz {}\n"
- "}\n",
- TestTempDir() + "/test.proto");
+ GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/test.proto",
+ "syntax = \"proto2\";\n"
+ "package foo;\n"
+ "message Bar {\n"
+ " message Baz {}\n"
+ "}\n",
+ true));
google::protobuf::compiler::CommandLineInterface cli;
cli.SetInputsAreProtoPathRelative(true);
diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc
index a69c48b..44290a3 100644
--- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -80,8 +80,9 @@ int FixedSize(FieldDescriptor::Type type) {
}
void SetPrimitiveVariables(const FieldDescriptor* descriptor,
- map<string, string>* variables) {
- SetCommonFieldVariables(descriptor, variables);
+ map<string, string>* variables,
+ const Options& options) {
+ SetCommonFieldVariables(descriptor, variables, options);
(*variables)["type"] = PrimitiveTypeName(descriptor->cpp_type());
(*variables)["default"] = DefaultValue(descriptor);
(*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor));
@@ -92,6 +93,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor,
(*variables)["wire_format_field_type"] =
"::google::protobuf::internal::WireFormatLite::" + FieldDescriptorProto_Type_Name(
static_cast<FieldDescriptorProto_Type>(descriptor->type()));
+ (*variables)["full_name"] = descriptor->full_name();
}
} // namespace
@@ -99,9 +101,10 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor,
// ===================================================================
PrimitiveFieldGenerator::
-PrimitiveFieldGenerator(const FieldDescriptor* descriptor)
+PrimitiveFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options)
: descriptor_(descriptor) {
- SetPrimitiveVariables(descriptor, &variables_);
+ SetPrimitiveVariables(descriptor, &variables_, options);
}
PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {}
@@ -122,11 +125,13 @@ void PrimitiveFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline $type$ $classname$::$name$() const {\n"
+ " // @@protoc_insertion_point(field_get:$full_name$)\n"
" return $name$_;\n"
"}\n"
"inline void $classname$::set_$name$($type$ value) {\n"
- " _set_bit($index$);\n"
+ " set_has_$name$();\n"
" $name$_ = value;\n"
+ " // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n");
}
@@ -156,7 +161,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const {
"DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<\n"
" $type$, $wire_format_field_type$>(\n"
" input, &$name$_)));\n"
- "_set_bit($index$);\n");
+ "set_has_$name$();\n");
}
void PrimitiveFieldGenerator::
@@ -189,10 +194,67 @@ GenerateByteSize(io::Printer* printer) const {
// ===================================================================
+PrimitiveOneofFieldGenerator::
+PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options)
+ : PrimitiveFieldGenerator(descriptor, options) {
+ SetCommonOneofFieldVariables(descriptor, &variables_);
+}
+
+PrimitiveOneofFieldGenerator::~PrimitiveOneofFieldGenerator() {}
+
+void PrimitiveOneofFieldGenerator::
+GenerateInlineAccessorDefinitions(io::Printer* printer) const {
+ printer->Print(variables_,
+ "inline $type$ $classname$::$name$() const {\n"
+ " if (has_$name$()) {\n"
+ " return $oneof_prefix$$name$_;\n"
+ " }\n"
+ " return $default$;\n"
+ "}\n"
+ "inline void $classname$::set_$name$($type$ value) {\n"
+ " if (!has_$name$()) {\n"
+ " clear_$oneof_name$();\n"
+ " set_has_$name$();\n"
+ " }\n"
+ " $oneof_prefix$$name$_ = value;\n"
+ "}\n");
+}
+
+void PrimitiveOneofFieldGenerator::
+GenerateClearingCode(io::Printer* printer) const {
+ printer->Print(variables_, "$oneof_prefix$$name$_ = $default$;\n");
+}
+
+void PrimitiveOneofFieldGenerator::
+GenerateSwappingCode(io::Printer* printer) const {
+ // Don't print any swapping code. Swapping the union will swap this field.
+}
+
+void PrimitiveOneofFieldGenerator::
+GenerateConstructorCode(io::Printer* printer) const {
+ printer->Print(
+ variables_,
+ " $classname$_default_oneof_instance_->$name$_ = $default$;\n");
+}
+
+void PrimitiveOneofFieldGenerator::
+GenerateMergeFromCodedStream(io::Printer* printer) const {
+ printer->Print(variables_,
+ "clear_$oneof_name$();\n"
+ "DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<\n"
+ " $type$, $wire_format_field_type$>(\n"
+ " input, &$oneof_prefix$$name$_)));\n"
+ "set_has_$name$();\n");
+}
+
+// ===================================================================
+
RepeatedPrimitiveFieldGenerator::
-RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor)
+RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options)
: descriptor_(descriptor) {
- SetPrimitiveVariables(descriptor, &variables_);
+ SetPrimitiveVariables(descriptor, &variables_, options);
if (descriptor->options().packed()) {
variables_["packed_reader"] = "ReadPackedPrimitive";
@@ -232,21 +294,26 @@ void RepeatedPrimitiveFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline $type$ $classname$::$name$(int index) const {\n"
+ " // @@protoc_insertion_point(field_get:$full_name$)\n"
" return $name$_.Get(index);\n"
"}\n"
"inline void $classname$::set_$name$(int index, $type$ value) {\n"
" $name$_.Set(index, value);\n"
+ " // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n"
"inline void $classname$::add_$name$($type$ value) {\n"
" $name$_.Add(value);\n"
+ " // @@protoc_insertion_point(field_add:$full_name$)\n"
"}\n");
printer->Print(variables_,
"inline const ::google::protobuf::RepeatedField< $type$ >&\n"
"$classname$::$name$() const {\n"
+ " // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $name$_;\n"
"}\n"
"inline ::google::protobuf::RepeatedField< $type$ >*\n"
"$classname$::mutable_$name$() {\n"
+ " // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return &$name$_;\n"
"}\n");
}
@@ -366,7 +433,9 @@ GenerateByteSize(io::Printer* printer) const {
" total_size += $tag_size$ +\n"
" ::google::protobuf::internal::WireFormatLite::Int32Size(data_size);\n"
"}\n"
+ "GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();\n"
"_$name$_cached_byte_size_ = data_size;\n"
+ "GOOGLE_SAFE_CONCURRENT_WRITES_END();\n"
"total_size += data_size;\n");
} else {
printer->Print(variables_,
diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.h b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h
index 8fcd74a..97b5e86 100644
--- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.h
+++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -46,7 +46,8 @@ namespace cpp {
class PrimitiveFieldGenerator : public FieldGenerator {
public:
- explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor);
+ explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options);
~PrimitiveFieldGenerator();
// implements FieldGenerator ---------------------------------------
@@ -62,16 +63,35 @@ class PrimitiveFieldGenerator : public FieldGenerator {
void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const;
void GenerateByteSize(io::Printer* printer) const;
- private:
+ protected:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator);
};
+class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator {
+ public:
+ explicit PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options);
+ ~PrimitiveOneofFieldGenerator();
+
+ // implements FieldGenerator ---------------------------------------
+ void GenerateInlineAccessorDefinitions(io::Printer* printer) const;
+ void GenerateClearingCode(io::Printer* printer) const;
+ void GenerateSwappingCode(io::Printer* printer) const;
+ void GenerateConstructorCode(io::Printer* printer) const;
+ void GenerateMergeFromCodedStream(io::Printer* printer) const;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveOneofFieldGenerator);
+};
+
class RepeatedPrimitiveFieldGenerator : public FieldGenerator {
public:
- explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor);
+ explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options);
~RepeatedPrimitiveFieldGenerator();
// implements FieldGenerator ---------------------------------------
diff --git a/src/google/protobuf/compiler/cpp/cpp_service.cc b/src/google/protobuf/compiler/cpp/cpp_service.cc
index c282568..a8f303d 100644
--- a/src/google/protobuf/compiler/cpp/cpp_service.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_service.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -43,14 +43,14 @@ namespace compiler {
namespace cpp {
ServiceGenerator::ServiceGenerator(const ServiceDescriptor* descriptor,
- const string& dllexport_decl)
+ const Options& options)
: descriptor_(descriptor) {
vars_["classname"] = descriptor_->name();
vars_["full_name"] = descriptor_->full_name();
- if (dllexport_decl.empty()) {
+ if (options.dllexport_decl.empty()) {
vars_["dllexport"] = "";
} else {
- vars_["dllexport"] = dllexport_decl + " ";
+ vars_["dllexport"] = options.dllexport_decl + " ";
}
}
diff --git a/src/google/protobuf/compiler/cpp/cpp_service.h b/src/google/protobuf/compiler/cpp/cpp_service.h
index 10e9dd3..ede2fd8 100644
--- a/src/google/protobuf/compiler/cpp/cpp_service.h
+++ b/src/google/protobuf/compiler/cpp/cpp_service.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -37,7 +37,7 @@
#include <map>
#include <string>
-#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/compiler/cpp/cpp_options.h>
#include <google/protobuf/descriptor.h>
namespace google {
@@ -55,7 +55,7 @@ class ServiceGenerator {
public:
// See generator.cc for the meaning of dllexport_decl.
explicit ServiceGenerator(const ServiceDescriptor* descriptor,
- const string& dllexport_decl);
+ const Options& options);
~ServiceGenerator();
// Header stuff.
diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc
index ea6809a..180d236 100644
--- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -46,12 +46,23 @@ namespace cpp {
namespace {
void SetStringVariables(const FieldDescriptor* descriptor,
- map<string, string>* variables) {
- SetCommonFieldVariables(descriptor, variables);
- (*variables)["default"] =
- "\"" + CEscape(descriptor->default_value_string()) + "\"";
+ map<string, string>* variables,
+ const Options& options) {
+ SetCommonFieldVariables(descriptor, variables, options);
+ (*variables)["default"] = DefaultValue(descriptor);
+ (*variables)["default_length"] =
+ SimpleItoa(descriptor->default_value_string().length());
+ (*variables)["default_variable"] = descriptor->default_value_string().empty()
+ ? "&::google::protobuf::internal::GetEmptyStringAlreadyInited()"
+ : "_default_" + FieldName(descriptor) + "_";
(*variables)["pointer_type"] =
descriptor->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char";
+ // NOTE: Escaped here to unblock proto1->proto2 migration.
+ // TODO(liujisi): Extend this to apply for other conflicting methods.
+ (*variables)["release_name"] =
+ SafeFunctionName(descriptor->containing_type(),
+ descriptor, "release_");
+ (*variables)["full_name"] = descriptor->full_name();
}
} // namespace
@@ -59,18 +70,24 @@ void SetStringVariables(const FieldDescriptor* descriptor,
// ===================================================================
StringFieldGenerator::
-StringFieldGenerator(const FieldDescriptor* descriptor)
+StringFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options)
: descriptor_(descriptor) {
- SetStringVariables(descriptor, &variables_);
+ SetStringVariables(descriptor, &variables_, options);
}
StringFieldGenerator::~StringFieldGenerator() {}
void StringFieldGenerator::
GeneratePrivateMembers(io::Printer* printer) const {
- printer->Print(variables_,
- "::std::string* $name$_;\n"
- "static const ::std::string _default_$name$_;\n");
+ printer->Print(variables_, "::std::string* $name$_;\n");
+}
+
+void StringFieldGenerator::
+GenerateStaticMembers(io::Printer* printer) const {
+ if (!descriptor_->default_value_string().empty()) {
+ printer->Print(variables_, "static ::std::string* $default_variable$;\n");
+ }
}
void StringFieldGenerator::
@@ -105,7 +122,10 @@ GenerateAccessorDeclarations(io::Printer* printer) const {
"inline void set_$name$(const char* value)$deprecation$;\n"
"inline void set_$name$(const $pointer_type$* value, size_t size)"
"$deprecation$;\n"
- "inline ::std::string* mutable_$name$()$deprecation$;\n");
+ "inline ::std::string* mutable_$name$()$deprecation$;\n"
+ "inline ::std::string* $release_name$()$deprecation$;\n"
+ "inline void set_allocated_$name$(::std::string* $name$)$deprecation$;\n");
+
if (descriptor_->options().ctype() != FieldOptions::STRING) {
printer->Outdent();
@@ -118,54 +138,80 @@ void StringFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline const ::std::string& $classname$::$name$() const {\n"
+ " // @@protoc_insertion_point(field_get:$full_name$)\n"
" return *$name$_;\n"
"}\n"
"inline void $classname$::set_$name$(const ::std::string& value) {\n"
- " _set_bit($index$);\n"
- " if ($name$_ == &_default_$name$_) {\n"
+ " set_has_$name$();\n"
+ " if ($name$_ == $default_variable$) {\n"
" $name$_ = new ::std::string;\n"
" }\n"
" $name$_->assign(value);\n"
+ " // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n"
"inline void $classname$::set_$name$(const char* value) {\n"
- " _set_bit($index$);\n"
- " if ($name$_ == &_default_$name$_) {\n"
+ " set_has_$name$();\n"
+ " if ($name$_ == $default_variable$) {\n"
" $name$_ = new ::std::string;\n"
" }\n"
" $name$_->assign(value);\n"
+ " // @@protoc_insertion_point(field_set_char:$full_name$)\n"
"}\n"
"inline "
"void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n"
- " _set_bit($index$);\n"
- " if ($name$_ == &_default_$name$_) {\n"
+ " set_has_$name$();\n"
+ " if ($name$_ == $default_variable$) {\n"
" $name$_ = new ::std::string;\n"
" }\n"
" $name$_->assign(reinterpret_cast<const char*>(value), size);\n"
+ " // @@protoc_insertion_point(field_set_pointer:$full_name$)\n"
"}\n"
"inline ::std::string* $classname$::mutable_$name$() {\n"
- " _set_bit($index$);\n"
- " if ($name$_ == &_default_$name$_) {\n");
+ " set_has_$name$();\n"
+ " if ($name$_ == $default_variable$) {\n");
if (descriptor_->default_value_string().empty()) {
printer->Print(variables_,
" $name$_ = new ::std::string;\n");
} else {
printer->Print(variables_,
- " $name$_ = new ::std::string(_default_$name$_);\n");
+ " $name$_ = new ::std::string(*$default_variable$);\n");
}
printer->Print(variables_,
" }\n"
+ " // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_;\n"
+ "}\n"
+ "inline ::std::string* $classname$::$release_name$() {\n"
+ " clear_has_$name$();\n"
+ " if ($name$_ == $default_variable$) {\n"
+ " return NULL;\n"
+ " } else {\n"
+ " ::std::string* temp = $name$_;\n"
+ " $name$_ = const_cast< ::std::string*>($default_variable$);\n"
+ " return temp;\n"
+ " }\n"
+ "}\n"
+ "inline void $classname$::set_allocated_$name$(::std::string* $name$) {\n"
+ " if ($name$_ != $default_variable$) {\n"
+ " delete $name$_;\n"
+ " }\n"
+ " if ($name$) {\n"
+ " set_has_$name$();\n"
+ " $name$_ = $name$;\n"
+ " } else {\n"
+ " clear_has_$name$();\n"
+ " $name$_ = const_cast< ::std::string*>($default_variable$);\n"
+ " }\n"
+ " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n");
}
void StringFieldGenerator::
GenerateNonInlineAccessorDefinitions(io::Printer* printer) const {
- if (descriptor_->default_value_string().empty()) {
- printer->Print(variables_,
- "const ::std::string $classname$::_default_$name$_;\n");
- } else {
+ if (!descriptor_->default_value_string().empty()) {
+ // Initialized in GenerateDefaultInstanceAllocator.
printer->Print(variables_,
- "const ::std::string $classname$::_default_$name$_($default$);\n");
+ "::std::string* $classname$::$default_variable$ = NULL;\n");
}
}
@@ -173,13 +219,13 @@ void StringFieldGenerator::
GenerateClearingCode(io::Printer* printer) const {
if (descriptor_->default_value_string().empty()) {
printer->Print(variables_,
- "if ($name$_ != &_default_$name$_) {\n"
+ "if ($name$_ != $default_variable$) {\n"
" $name$_->clear();\n"
"}\n");
} else {
printer->Print(variables_,
- "if ($name$_ != &_default_$name$_) {\n"
- " $name$_->assign(_default_$name$_);\n"
+ "if ($name$_ != $default_variable$) {\n"
+ " $name$_->assign(*$default_variable$);\n"
"}\n");
}
}
@@ -197,18 +243,35 @@ GenerateSwappingCode(io::Printer* printer) const {
void StringFieldGenerator::
GenerateConstructorCode(io::Printer* printer) const {
printer->Print(variables_,
- "$name$_ = const_cast< ::std::string*>(&_default_$name$_);\n");
+ "$name$_ = const_cast< ::std::string*>($default_variable$);\n");
}
void StringFieldGenerator::
GenerateDestructorCode(io::Printer* printer) const {
printer->Print(variables_,
- "if ($name$_ != &_default_$name$_) {\n"
+ "if ($name$_ != $default_variable$) {\n"
" delete $name$_;\n"
"}\n");
}
void StringFieldGenerator::
+GenerateDefaultInstanceAllocator(io::Printer* printer) const {
+ if (!descriptor_->default_value_string().empty()) {
+ printer->Print(variables_,
+ "$classname$::$default_variable$ =\n"
+ " new ::std::string($default$, $default_length$);\n");
+ }
+}
+
+void StringFieldGenerator::
+GenerateShutdownCode(io::Printer* printer) const {
+ if (!descriptor_->default_value_string().empty()) {
+ printer->Print(variables_,
+ "delete $classname$::$default_variable$;\n");
+ }
+}
+
+void StringFieldGenerator::
GenerateMergeFromCodedStream(io::Printer* printer) const {
printer->Print(variables_,
"DO_(::google::protobuf::internal::WireFormatLite::Read$declared_type$(\n"
@@ -216,9 +279,10 @@ GenerateMergeFromCodedStream(io::Printer* printer) const {
if (HasUtf8Verification(descriptor_->file()) &&
descriptor_->type() == FieldDescriptor::TYPE_STRING) {
printer->Print(variables_,
- "::google::protobuf::internal::WireFormat::VerifyUTF8String(\n"
+ "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n"
" this->$name$().data(), this->$name$().length(),\n"
- " ::google::protobuf::internal::WireFormat::PARSE);\n");
+ " ::google::protobuf::internal::WireFormat::PARSE,\n"
+ " \"$name$\");\n");
}
}
@@ -227,12 +291,13 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const {
if (HasUtf8Verification(descriptor_->file()) &&
descriptor_->type() == FieldDescriptor::TYPE_STRING) {
printer->Print(variables_,
- "::google::protobuf::internal::WireFormat::VerifyUTF8String(\n"
+ "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n"
" this->$name$().data(), this->$name$().length(),\n"
- " ::google::protobuf::internal::WireFormat::SERIALIZE);\n");
+ " ::google::protobuf::internal::WireFormat::SERIALIZE,\n"
+ " \"$name$\");\n");
}
printer->Print(variables_,
- "::google::protobuf::internal::WireFormatLite::Write$declared_type$(\n"
+ "::google::protobuf::internal::WireFormatLite::Write$declared_type$MaybeAliased(\n"
" $number$, this->$name$(), output);\n");
}
@@ -241,9 +306,10 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const {
if (HasUtf8Verification(descriptor_->file()) &&
descriptor_->type() == FieldDescriptor::TYPE_STRING) {
printer->Print(variables_,
- "::google::protobuf::internal::WireFormat::VerifyUTF8String(\n"
+ "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n"
" this->$name$().data(), this->$name$().length(),\n"
- " ::google::protobuf::internal::WireFormat::SERIALIZE);\n");
+ " ::google::protobuf::internal::WireFormat::SERIALIZE,\n"
+ " \"$name$\");\n");
}
printer->Print(variables_,
"target =\n"
@@ -261,10 +327,130 @@ GenerateByteSize(io::Printer* printer) const {
// ===================================================================
+StringOneofFieldGenerator::
+StringOneofFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options)
+ : StringFieldGenerator(descriptor, options) {
+ SetCommonOneofFieldVariables(descriptor, &variables_);
+}
+
+StringOneofFieldGenerator::~StringOneofFieldGenerator() {}
+
+void StringOneofFieldGenerator::
+GenerateInlineAccessorDefinitions(io::Printer* printer) const {
+ printer->Print(variables_,
+ "inline const ::std::string& $classname$::$name$() const {\n"
+ " if (has_$name$()) {\n"
+ " return *$oneof_prefix$$name$_;\n"
+ " }\n");
+ if (descriptor_->default_value_string().empty()) {
+ printer->Print(variables_,
+ " return ::google::protobuf::internal::GetEmptyStringAlreadyInited();\n");
+ } else {
+ printer->Print(variables_,
+ " return *$default_variable$;\n");
+ }
+ printer->Print(variables_,
+ "}\n"
+ "inline void $classname$::set_$name$(const ::std::string& value) {\n"
+ " if (!has_$name$()) {\n"
+ " clear_$oneof_name$();\n"
+ " set_has_$name$();\n"
+ " $oneof_prefix$$name$_ = new ::std::string;\n"
+ " }\n"
+ " $oneof_prefix$$name$_->assign(value);\n"
+ "}\n"
+ "inline void $classname$::set_$name$(const char* value) {\n"
+ " if (!has_$name$()) {\n"
+ " clear_$oneof_name$();\n"
+ " set_has_$name$();\n"
+ " $oneof_prefix$$name$_ = new ::std::string;\n"
+ " }\n"
+ " $oneof_prefix$$name$_->assign(value);\n"
+ "}\n"
+ "inline "
+ "void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n"
+ " if (!has_$name$()) {\n"
+ " clear_$oneof_name$();\n"
+ " set_has_$name$();\n"
+ " $oneof_prefix$$name$_ = new ::std::string;\n"
+ " }\n"
+ " $oneof_prefix$$name$_->assign(\n"
+ " reinterpret_cast<const char*>(value), size);\n"
+ "}\n"
+ "inline ::std::string* $classname$::mutable_$name$() {\n"
+ " if (!has_$name$()) {\n"
+ " clear_$oneof_name$();\n"
+ " set_has_$name$();\n");
+ if (descriptor_->default_value_string().empty()) {
+ printer->Print(variables_,
+ " $oneof_prefix$$name$_ = new ::std::string;\n");
+ } else {
+ printer->Print(variables_,
+ " $oneof_prefix$$name$_ = new ::std::string(*$default_variable$);\n");
+ }
+ printer->Print(variables_,
+ " }\n"
+ " return $oneof_prefix$$name$_;\n"
+ "}\n"
+ "inline ::std::string* $classname$::$release_name$() {\n"
+ " if (has_$name$()) {\n"
+ " clear_has_$oneof_name$();\n"
+ " ::std::string* temp = $oneof_prefix$$name$_;\n"
+ " $oneof_prefix$$name$_ = NULL;\n"
+ " return temp;\n"
+ " } else {\n"
+ " return NULL;\n"
+ " }\n"
+ "}\n"
+ "inline void $classname$::set_allocated_$name$(::std::string* $name$) {\n"
+ " clear_$oneof_name$();\n"
+ " if ($name$) {\n"
+ " set_has_$name$();\n"
+ " $oneof_prefix$$name$_ = $name$;\n"
+ " }\n"
+ "}\n");
+}
+
+void StringOneofFieldGenerator::
+GenerateClearingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "delete $oneof_prefix$$name$_;\n");
+}
+
+void StringOneofFieldGenerator::
+GenerateSwappingCode(io::Printer* printer) const {
+ // Don't print any swapping code. Swapping the union will swap this field.
+}
+
+void StringOneofFieldGenerator::
+GenerateConstructorCode(io::Printer* printer) const {
+ if (!descriptor_->default_value_string().empty()) {
+ printer->Print(variables_,
+ " $classname$_default_oneof_instance_->$name$_ = "
+ "$classname$::$default_variable$;\n");
+ } else {
+ printer->Print(variables_,
+ " $classname$_default_oneof_instance_->$name$_ = "
+ "$default_variable$;\n");
+ }
+}
+
+void StringOneofFieldGenerator::
+GenerateDestructorCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (has_$name$()) {\n"
+ " delete $oneof_prefix$$name$_;\n"
+ "}\n");
+}
+
+// ===================================================================
+
RepeatedStringFieldGenerator::
-RepeatedStringFieldGenerator(const FieldDescriptor* descriptor)
+RepeatedStringFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options)
: descriptor_(descriptor) {
- SetStringVariables(descriptor, &variables_);
+ SetStringVariables(descriptor, &variables_, options);
}
RepeatedStringFieldGenerator::~RepeatedStringFieldGenerator() {}
@@ -317,43 +503,53 @@ void RepeatedStringFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline const ::std::string& $classname$::$name$(int index) const {\n"
- " return $name$_.Get(index);\n"
+ " // @@protoc_insertion_point(field_get:$full_name$)\n"
+ " return $name$_.$cppget$(index);\n"
"}\n"
"inline ::std::string* $classname$::mutable_$name$(int index) {\n"
+ " // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_.Mutable(index);\n"
"}\n"
"inline void $classname$::set_$name$(int index, const ::std::string& value) {\n"
+ " // @@protoc_insertion_point(field_set:$full_name$)\n"
" $name$_.Mutable(index)->assign(value);\n"
"}\n"
"inline void $classname$::set_$name$(int index, const char* value) {\n"
" $name$_.Mutable(index)->assign(value);\n"
+ " // @@protoc_insertion_point(field_set_char:$full_name$)\n"
"}\n"
"inline void "
"$classname$::set_$name$"
"(int index, const $pointer_type$* value, size_t size) {\n"
" $name$_.Mutable(index)->assign(\n"
" reinterpret_cast<const char*>(value), size);\n"
+ " // @@protoc_insertion_point(field_set_pointer:$full_name$)\n"
"}\n"
"inline ::std::string* $classname$::add_$name$() {\n"
" return $name$_.Add();\n"
"}\n"
"inline void $classname$::add_$name$(const ::std::string& value) {\n"
" $name$_.Add()->assign(value);\n"
+ " // @@protoc_insertion_point(field_add:$full_name$)\n"
"}\n"
"inline void $classname$::add_$name$(const char* value) {\n"
" $name$_.Add()->assign(value);\n"
+ " // @@protoc_insertion_point(field_add_char:$full_name$)\n"
"}\n"
"inline void "
"$classname$::add_$name$(const $pointer_type$* value, size_t size) {\n"
" $name$_.Add()->assign(reinterpret_cast<const char*>(value), size);\n"
+ " // @@protoc_insertion_point(field_add_pointer:$full_name$)\n"
"}\n");
printer->Print(variables_,
"inline const ::google::protobuf::RepeatedPtrField< ::std::string>&\n"
"$classname$::$name$() const {\n"
+ " // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $name$_;\n"
"}\n"
"inline ::google::protobuf::RepeatedPtrField< ::std::string>*\n"
"$classname$::mutable_$name$() {\n"
+ " // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return &$name$_;\n"
"}\n");
}
@@ -386,9 +582,11 @@ GenerateMergeFromCodedStream(io::Printer* printer) const {
if (HasUtf8Verification(descriptor_->file()) &&
descriptor_->type() == FieldDescriptor::TYPE_STRING) {
printer->Print(variables_,
- "::google::protobuf::internal::WireFormat::VerifyUTF8String(\n"
- " this->$name$(0).data(), this->$name$(0).length(),\n"
- " ::google::protobuf::internal::WireFormat::PARSE);\n");
+ "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n"
+ " this->$name$(this->$name$_size() - 1).data(),\n"
+ " this->$name$(this->$name$_size() - 1).length(),\n"
+ " ::google::protobuf::internal::WireFormat::PARSE,\n"
+ " \"$name$\");\n");
}
}
@@ -399,9 +597,10 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const {
if (HasUtf8Verification(descriptor_->file()) &&
descriptor_->type() == FieldDescriptor::TYPE_STRING) {
printer->Print(variables_,
- "::google::protobuf::internal::WireFormat::VerifyUTF8String(\n"
+ "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n"
" this->$name$(i).data(), this->$name$(i).length(),\n"
- " ::google::protobuf::internal::WireFormat::SERIALIZE);\n");
+ " ::google::protobuf::internal::WireFormat::SERIALIZE,\n"
+ " \"$name$\");\n");
}
printer->Print(variables_,
" ::google::protobuf::internal::WireFormatLite::Write$declared_type$(\n"
@@ -416,9 +615,10 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const {
if (HasUtf8Verification(descriptor_->file()) &&
descriptor_->type() == FieldDescriptor::TYPE_STRING) {
printer->Print(variables_,
- " ::google::protobuf::internal::WireFormat::VerifyUTF8String(\n"
+ " ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n"
" this->$name$(i).data(), this->$name$(i).length(),\n"
- " ::google::protobuf::internal::WireFormat::SERIALIZE);\n");
+ " ::google::protobuf::internal::WireFormat::SERIALIZE,\n"
+ " \"$name$\");\n");
}
printer->Print(variables_,
" target = ::google::protobuf::internal::WireFormatLite::\n"
diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.h b/src/google/protobuf/compiler/cpp/cpp_string_field.h
index 7f45107..86da38f 100644
--- a/src/google/protobuf/compiler/cpp/cpp_string_field.h
+++ b/src/google/protobuf/compiler/cpp/cpp_string_field.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -46,11 +46,13 @@ namespace cpp {
class StringFieldGenerator : public FieldGenerator {
public:
- explicit StringFieldGenerator(const FieldDescriptor* descriptor);
+ explicit StringFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options);
~StringFieldGenerator();
// implements FieldGenerator ---------------------------------------
void GeneratePrivateMembers(io::Printer* printer) const;
+ void GenerateStaticMembers(io::Printer* printer) const;
void GenerateAccessorDeclarations(io::Printer* printer) const;
void GenerateInlineAccessorDefinitions(io::Printer* printer) const;
void GenerateNonInlineAccessorDefinitions(io::Printer* printer) const;
@@ -59,21 +61,42 @@ class StringFieldGenerator : public FieldGenerator {
void GenerateSwappingCode(io::Printer* printer) const;
void GenerateConstructorCode(io::Printer* printer) const;
void GenerateDestructorCode(io::Printer* printer) const;
+ void GenerateDefaultInstanceAllocator(io::Printer* printer) const;
+ void GenerateShutdownCode(io::Printer* printer) const;
void GenerateMergeFromCodedStream(io::Printer* printer) const;
void GenerateSerializeWithCachedSizes(io::Printer* printer) const;
void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const;
void GenerateByteSize(io::Printer* printer) const;
- private:
+ protected:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringFieldGenerator);
};
+class StringOneofFieldGenerator : public StringFieldGenerator {
+ public:
+ explicit StringOneofFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options);
+ ~StringOneofFieldGenerator();
+
+ // implements FieldGenerator ---------------------------------------
+ void GenerateInlineAccessorDefinitions(io::Printer* printer) const;
+ void GenerateClearingCode(io::Printer* printer) const;
+ void GenerateSwappingCode(io::Printer* printer) const;
+ void GenerateConstructorCode(io::Printer* printer) const;
+ void GenerateDestructorCode(io::Printer* printer) const;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringOneofFieldGenerator);
+};
+
class RepeatedStringFieldGenerator : public FieldGenerator {
public:
- explicit RepeatedStringFieldGenerator(const FieldDescriptor* descriptor);
+ explicit RepeatedStringFieldGenerator(const FieldDescriptor* descriptor,
+ const Options& options);
~RepeatedStringFieldGenerator();
// implements FieldGenerator ---------------------------------------
diff --git a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto
index 79971a9..6b7f830 100644
--- a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto
+++ b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -36,6 +36,10 @@
// though the same identifiers are used internally by the C++ code generator.
+// Some generic_services option(s) added automatically.
+// See: http://go/proto2-generic-services-default
+option cc_generic_services = true; // auto-added
+
// We don't put this in a package within proto2 because we need to make sure
// that the generated code doesn't depend on being in the proto2 namespace.
package protobuf_unittest;
@@ -94,14 +98,33 @@ message TestConflictingSymbolNames {
// Some keywords.
optional uint32 int = 30;
optional uint32 friend = 31;
+ optional uint32 class = 37;
// The generator used to #define a macro called "DO" inside the .cc file.
message DO {}
optional DO do = 32;
+ // Some template parameter names for extensions.
+ optional int32 field_type = 33;
+ optional bool is_packed = 34;
+
+ // test conflicting release_$name$. "length" and "do" field in this message
+ // must remain string or message fields to make the test valid.
+ optional string release_length = 35;
+ // A more extreme case, the field name "do" here is a keyword, which will be
+ // escaped to "do_" already. Test there is no conflict even with escaped field
+ // names.
+ optional DO release_do = 36;
+
extensions 1000 to max;
}
+message TestConflictingSymbolNamesExtension {
+ extend TestConflictingSymbolNames {
+ repeated int32 repeated_int32_ext = 20423638 [packed=true];
+ }
+}
+
message DummyMessage {}
service TestConflictingMethodNames {
diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc
index a7e852d..93e1c3f 100644
--- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -44,6 +44,9 @@
// correctly and produces the interfaces we expect, which is why this test
// is written this way.
+#include <google/protobuf/compiler/cpp/cpp_unittest.h>
+
+#include <memory>
#include <vector>
#include <google/protobuf/unittest.pb.h>
@@ -51,6 +54,7 @@
#include <google/protobuf/unittest_embed_optimize_for.pb.h>
#include <google/protobuf/unittest_no_generic_services.pb.h>
#include <google/protobuf/test_util.h>
+#include <google/protobuf/compiler/cpp/cpp_helpers.h>
#include <google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.h>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/io/coded_stream.h>
@@ -64,7 +68,7 @@
#include <google/protobuf/stubs/substitute.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
-#include <google/protobuf/stubs/stl_util-inl.h>
+#include <google/protobuf/stubs/stl_util.h>
namespace google {
namespace protobuf {
@@ -74,6 +78,8 @@ namespace cpp {
// Can't use an anonymous namespace here due to brokenness of Tru64 compiler.
namespace cpp_unittest {
+namespace protobuf_unittest = ::protobuf_unittest;
+
class MockErrorCollector : public MultiFileErrorCollector {
public:
@@ -144,6 +150,19 @@ TEST(GeneratedMessageTest, Defaults) {
&message.optional_import_message());
}
+TEST(GeneratedMessageTest, Int32StringConversion) {
+ EXPECT_EQ("971", Int32ToString(971));
+ EXPECT_EQ("(~0x7fffffff)", Int32ToString(kint32min));
+ EXPECT_EQ("2147483647", Int32ToString(kint32max));
+}
+
+TEST(GeneratedMessageTest, Int64StringConversion) {
+ EXPECT_EQ("GOOGLE_LONGLONG(971)", Int64ToString(971));
+ EXPECT_EQ("GOOGLE_LONGLONG(-2147483648)", Int64ToString(kint32min));
+ EXPECT_EQ("GOOGLE_LONGLONG(~0x7fffffffffffffff)", Int64ToString(kint64min));
+ EXPECT_EQ("GOOGLE_LONGLONG(9223372036854775807)", Int64ToString(kint64max));
+}
+
TEST(GeneratedMessageTest, FloatingPointDefaults) {
const unittest::TestExtremeDefaultValues& extreme_default =
unittest::TestExtremeDefaultValues::default_instance();
@@ -167,6 +186,22 @@ TEST(GeneratedMessageTest, FloatingPointDefaults) {
EXPECT_TRUE(extreme_default.nan_float() != extreme_default.nan_float());
}
+TEST(GeneratedMessageTest, Trigraph) {
+ const unittest::TestExtremeDefaultValues& extreme_default =
+ unittest::TestExtremeDefaultValues::default_instance();
+
+ EXPECT_EQ("? ? ?? ?? ??? ?\?/ ?\?-", extreme_default.cpp_trigraph());
+}
+
+TEST(GeneratedMessageTest, ExtremeSmallIntegerDefault) {
+ const unittest::TestExtremeDefaultValues& extreme_default =
+ unittest::TestExtremeDefaultValues::default_instance();
+ EXPECT_EQ(~0x7fffffff, kint32min);
+ EXPECT_EQ(GOOGLE_LONGLONG(~0x7fffffffffffffff), kint64min);
+ EXPECT_EQ(kint32min, extreme_default.really_small_int32());
+ EXPECT_EQ(kint64min, extreme_default.really_small_int64());
+}
+
TEST(GeneratedMessageTest, Accessors) {
// Set every field to a unique value then go back and check all those
// values.
@@ -195,6 +230,96 @@ TEST(GeneratedMessageTest, MutableStringDefault) {
EXPECT_EQ("hello", *message.mutable_default_string());
}
+TEST(GeneratedMessageTest, StringDefaults) {
+ unittest::TestExtremeDefaultValues message;
+ // Check if '\000' can be used in default string value.
+ EXPECT_EQ(string("hel\000lo", 6), message.string_with_zero());
+ EXPECT_EQ(string("wor\000ld", 6), message.bytes_with_zero());
+}
+
+TEST(GeneratedMessageTest, ReleaseString) {
+ // Check that release_foo() starts out NULL, and gives us a value
+ // that we can delete after it's been set.
+ unittest::TestAllTypes message;
+
+ EXPECT_EQ(NULL, message.release_default_string());
+ EXPECT_FALSE(message.has_default_string());
+ EXPECT_EQ("hello", message.default_string());
+
+ message.set_default_string("blah");
+ EXPECT_TRUE(message.has_default_string());
+ scoped_ptr<string> str(message.release_default_string());
+ EXPECT_FALSE(message.has_default_string());
+ ASSERT_TRUE(str != NULL);
+ EXPECT_EQ("blah", *str);
+
+ EXPECT_EQ(NULL, message.release_default_string());
+ EXPECT_FALSE(message.has_default_string());
+ EXPECT_EQ("hello", message.default_string());
+}
+
+TEST(GeneratedMessageTest, ReleaseMessage) {
+ // Check that release_foo() starts out NULL, and gives us a value
+ // that we can delete after it's been set.
+ unittest::TestAllTypes message;
+
+ EXPECT_EQ(NULL, message.release_optional_nested_message());
+ EXPECT_FALSE(message.has_optional_nested_message());
+
+ message.mutable_optional_nested_message()->set_bb(1);
+ scoped_ptr<unittest::TestAllTypes::NestedMessage> nest(
+ message.release_optional_nested_message());
+ EXPECT_FALSE(message.has_optional_nested_message());
+ ASSERT_TRUE(nest != NULL);
+ EXPECT_EQ(1, nest->bb());
+
+ EXPECT_EQ(NULL, message.release_optional_nested_message());
+ EXPECT_FALSE(message.has_optional_nested_message());
+}
+
+TEST(GeneratedMessageTest, SetAllocatedString) {
+ // Check that set_allocated_foo() works for strings.
+ unittest::TestAllTypes message;
+
+ EXPECT_FALSE(message.has_optional_string());
+ const string kHello("hello");
+ message.set_optional_string(kHello);
+ EXPECT_TRUE(message.has_optional_string());
+
+ message.set_allocated_optional_string(NULL);
+ EXPECT_FALSE(message.has_optional_string());
+ EXPECT_EQ("", message.optional_string());
+
+ message.set_allocated_optional_string(new string(kHello));
+ EXPECT_TRUE(message.has_optional_string());
+ EXPECT_EQ(kHello, message.optional_string());
+}
+
+TEST(GeneratedMessageTest, SetAllocatedMessage) {
+ // Check that set_allocated_foo() can be called in all cases.
+ unittest::TestAllTypes message;
+
+ EXPECT_FALSE(message.has_optional_nested_message());
+
+ message.mutable_optional_nested_message()->set_bb(1);
+ EXPECT_TRUE(message.has_optional_nested_message());
+
+ message.set_allocated_optional_nested_message(NULL);
+ EXPECT_FALSE(message.has_optional_nested_message());
+ EXPECT_EQ(&unittest::TestAllTypes::NestedMessage::default_instance(),
+ &message.optional_nested_message());
+
+ message.mutable_optional_nested_message()->set_bb(1);
+ unittest::TestAllTypes::NestedMessage* nest =
+ message.release_optional_nested_message();
+ ASSERT_TRUE(nest != NULL);
+ EXPECT_FALSE(message.has_optional_nested_message());
+
+ message.set_allocated_optional_nested_message(nest);
+ EXPECT_TRUE(message.has_optional_nested_message());
+ EXPECT_EQ(1, message.optional_nested_message().bb());
+}
+
TEST(GeneratedMessageTest, Clear) {
// Set every field to a unique value, clear the message, then check that
// it is cleared.
@@ -282,6 +407,7 @@ TEST(GeneratedMessageTest, CopyFrom) {
TestUtil::ExpectAllFieldsSet(message2);
}
+
TEST(GeneratedMessageTest, SwapWithEmpty) {
unittest::TestAllTypes message1, message2;
TestUtil::SetAllFields(&message1);
@@ -376,10 +502,12 @@ TEST(GeneratedMessageTest, CopyAssignmentOperator) {
TestUtil::ExpectAllFieldsSet(message2);
// Make sure that self-assignment does something sane.
- message2 = message2;
+ message2.operator=(message2);
TestUtil::ExpectAllFieldsSet(message2);
}
+#if !defined(PROTOBUF_TEST_NO_DESCRIPTORS) || \
+ !defined(GOOGLE_PROTOBUF_NO_RTTI)
TEST(GeneratedMessageTest, UpcastCopyFrom) {
// Test the CopyFrom method that takes in the generic const Message&
// parameter.
@@ -392,6 +520,7 @@ TEST(GeneratedMessageTest, UpcastCopyFrom) {
TestUtil::ExpectAllFieldsSet(message2);
}
+#endif
#ifndef PROTOBUF_TEST_NO_DESCRIPTORS
@@ -443,7 +572,9 @@ TEST(GeneratedMessageTest, NonEmptyMergeFrom) {
TestUtil::ExpectAllFieldsSet(message1);
}
-#ifdef GTEST_HAS_DEATH_TEST
+#if !defined(PROTOBUF_TEST_NO_DESCRIPTORS) || \
+ !defined(GOOGLE_PROTOBUF_NO_RTTI)
+#ifdef PROTOBUF_HAS_DEATH_TEST
TEST(GeneratedMessageTest, MergeFromSelf) {
unittest::TestAllTypes message;
@@ -452,7 +583,8 @@ TEST(GeneratedMessageTest, MergeFromSelf) {
"&from");
}
-#endif // GTEST_HAS_DEATH_TEST
+#endif // PROTOBUF_HAS_DEATH_TEST
+#endif // !PROTOBUF_TEST_NO_DESCRIPTORS || !GOOGLE_PROTOBUF_NO_RTTI
// Test the generated SerializeWithCachedSizesToArray(),
TEST(GeneratedMessageTest, SerializationToArray) {
@@ -645,6 +777,16 @@ TEST(GeneratedMessageTest, TestConflictingSymbolNames) {
message.set_friend_(5);
EXPECT_EQ(5, message.friend_());
+
+ message.set_class_(6);
+ EXPECT_EQ(6, message.class_());
+
+ // Instantiate extension template functions to test conflicting template
+ // parameter names.
+ typedef protobuf_unittest::TestConflictingSymbolNamesExtension ExtensionMessage;
+ message.AddExtension(ExtensionMessage::repeated_int32_ext, 123);
+ EXPECT_EQ(123,
+ message.GetExtension(ExtensionMessage::repeated_int32_ext, 0));
}
#ifndef PROTOBUF_TEST_NO_DESCRIPTORS
@@ -716,8 +858,43 @@ TEST(GeneratedMessageTest, TestSpaceUsed) {
message1.SpaceUsed());
}
+TEST(GeneratedMessageTest, TestOneofSpaceUsed) {
+ unittest::TestOneof2 message1;
+ EXPECT_LE(sizeof(unittest::TestOneof2), message1.SpaceUsed());
+
+ const int empty_message_size = message1.SpaceUsed();
+ // Setting primitive types shouldn't affect the space used.
+ message1.set_foo_int(123);
+ message1.set_bar_int(12345);
+ EXPECT_EQ(empty_message_size, message1.SpaceUsed());
+
+ // Setting a string in oneof to a small value should only increase SpaceUsed()
+ // by the size of a string object.
+ message1.set_foo_string("abc");
+ EXPECT_LE(empty_message_size + sizeof(string), message1.SpaceUsed());
+
+ // Setting a string in oneof to a value larger than the string object itself
+ // should increase SpaceUsed(), because it cannot store the value internally.
+ message1.set_foo_string(string(sizeof(string) + 1, 'x'));
+ int min_expected_increase = message1.foo_string().capacity() +
+ sizeof(string);
+ EXPECT_LE(empty_message_size + min_expected_increase,
+ message1.SpaceUsed());
+
+ // Setting a message in oneof should delete the other fields and increase the
+ // size by the size of the nested message type. NestedMessage is simple enough
+ // that it is equal to sizeof(NestedMessage)
+ message1.mutable_foo_message();
+ ASSERT_EQ(sizeof(unittest::TestOneof2::NestedMessage),
+ message1.foo_message().SpaceUsed());
+ EXPECT_EQ(empty_message_size +
+ sizeof(unittest::TestOneof2::NestedMessage),
+ message1.SpaceUsed());
+}
+
#endif // !PROTOBUF_TEST_NO_DESCRIPTORS
+
TEST(GeneratedMessageTest, FieldConstantValues) {
unittest::TestRequired message;
EXPECT_EQ(unittest::TestAllTypes_NestedMessage::kBbFieldNumber, 1);
@@ -762,6 +939,9 @@ TEST(GeneratedEnumTest, EnumValuesAsSwitchCases) {
case unittest::TestAllTypes::BAZ:
i = 3;
break;
+ case unittest::TestAllTypes::NEG:
+ i = -1;
+ break;
// no default case: We want to make sure the compiler recognizes that
// all cases are covered. (GCC warns if you do not cover all cases of
// an enum in a switch.)
@@ -790,7 +970,7 @@ TEST(GeneratedEnumTest, IsValidValue) {
}
TEST(GeneratedEnumTest, MinAndMax) {
- EXPECT_EQ(unittest::TestAllTypes::FOO,
+ EXPECT_EQ(unittest::TestAllTypes::NEG,
unittest::TestAllTypes::NestedEnum_MIN);
EXPECT_EQ(unittest::TestAllTypes::BAZ,
unittest::TestAllTypes::NestedEnum_MAX);
@@ -809,20 +989,19 @@ TEST(GeneratedEnumTest, MinAndMax) {
EXPECT_EQ(12589235, unittest::TestSparseEnum_ARRAYSIZE);
// Make sure we can take the address of _MIN, _MAX and _ARRAYSIZE.
- void* nullptr = 0; // NULL may be integer-type, not pointer-type.
- EXPECT_NE(nullptr, &unittest::TestAllTypes::NestedEnum_MIN);
- EXPECT_NE(nullptr, &unittest::TestAllTypes::NestedEnum_MAX);
- EXPECT_NE(nullptr, &unittest::TestAllTypes::NestedEnum_ARRAYSIZE);
+ void* null_pointer = 0; // NULL may be integer-type, not pointer-type.
+ EXPECT_NE(null_pointer, &unittest::TestAllTypes::NestedEnum_MIN);
+ EXPECT_NE(null_pointer, &unittest::TestAllTypes::NestedEnum_MAX);
+ EXPECT_NE(null_pointer, &unittest::TestAllTypes::NestedEnum_ARRAYSIZE);
- EXPECT_NE(nullptr, &unittest::ForeignEnum_MIN);
- EXPECT_NE(nullptr, &unittest::ForeignEnum_MAX);
- EXPECT_NE(nullptr, &unittest::ForeignEnum_ARRAYSIZE);
+ EXPECT_NE(null_pointer, &unittest::ForeignEnum_MIN);
+ EXPECT_NE(null_pointer, &unittest::ForeignEnum_MAX);
+ EXPECT_NE(null_pointer, &unittest::ForeignEnum_ARRAYSIZE);
- // Make sure we can use _MIN, _MAX and _ARRAYSIZE as switch cases.
+ // Make sure we can use _MIN and _MAX as switch cases.
switch (unittest::SPARSE_A) {
case unittest::TestSparseEnum_MIN:
case unittest::TestSparseEnum_MAX:
- case unittest::TestSparseEnum_ARRAYSIZE:
break;
default:
break;
@@ -865,6 +1044,20 @@ TEST(GeneratedEnumTest, GetEnumDescriptor) {
GetEnumDescriptor<unittest::TestSparseEnum>());
}
+enum NonProtoEnum {
+ kFoo = 1,
+};
+
+TEST(GeneratedEnumTest, IsProtoEnumTypeTrait) {
+ EXPECT_TRUE(is_proto_enum<unittest::TestAllTypes::NestedEnum>::value);
+ EXPECT_TRUE(is_proto_enum<unittest::ForeignEnum>::value);
+ EXPECT_TRUE(is_proto_enum<unittest::TestEnumWithDupValue>::value);
+ EXPECT_TRUE(is_proto_enum<unittest::TestSparseEnum>::value);
+
+ EXPECT_FALSE(is_proto_enum<int>::value);
+ EXPECT_FALSE(is_proto_enum<NonProtoEnum>::value);
+}
+
#endif // PROTOBUF_TEST_NO_DESCRIPTORS
// ===================================================================
@@ -1079,7 +1272,7 @@ TEST_F(GeneratedServiceTest, CallMethod) {
TEST_F(GeneratedServiceTest, CallMethodTypeFailure) {
// Verify death if we call Foo() with Bar's message types.
-#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet
+#ifdef PROTOBUF_HAS_DEATH_TEST // death tests do not work on Windows yet
EXPECT_DEBUG_DEATH(
mock_service_.CallMethod(foo_, &mock_controller_,
&foo_request_, &bar_response_, done_.get()),
@@ -1090,7 +1283,7 @@ TEST_F(GeneratedServiceTest, CallMethodTypeFailure) {
mock_service_.CallMethod(foo_, &mock_controller_,
&bar_request_, &foo_response_, done_.get()),
"dynamic_cast");
-#endif // GTEST_HAS_DEATH_TEST
+#endif // PROTOBUF_HAS_DEATH_TEST
}
TEST_F(GeneratedServiceTest, GetPrototypes) {
@@ -1164,6 +1357,657 @@ TEST_F(GeneratedServiceTest, NotImplemented) {
EXPECT_TRUE(controller.called_);
}
+// ===================================================================
+
+class OneofTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ }
+
+ void ExpectEnumCasesWork(const unittest::TestOneof2 &message) {
+ switch (message.foo_case()) {
+ case unittest::TestOneof2::kFooInt:
+ EXPECT_TRUE(message.has_foo_int());
+ break;
+ case unittest::TestOneof2::kFooString:
+ EXPECT_TRUE(message.has_foo_string());
+ break;
+ case unittest::TestOneof2::kFooBytes:
+ EXPECT_TRUE(message.has_foo_bytes());
+ break;
+ case unittest::TestOneof2::kFooEnum:
+ EXPECT_TRUE(message.has_foo_enum());
+ break;
+ case unittest::TestOneof2::kFooMessage:
+ EXPECT_TRUE(message.has_foo_message());
+ break;
+ case unittest::TestOneof2::kFoogroup:
+ EXPECT_TRUE(message.has_foogroup());
+ break;
+ case unittest::TestOneof2::FOO_NOT_SET:
+ break;
+ }
+ }
+};
+
+TEST_F(OneofTest, SettingOneFieldClearsOthers) {
+ unittest::TestOneof2 message;
+
+ message.set_foo_int(123);
+ EXPECT_TRUE(message.has_foo_int());
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message);
+
+ message.set_foo_string("foo");
+ EXPECT_TRUE(message.has_foo_string());
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message);
+
+
+ message.set_foo_bytes("qux");
+ EXPECT_TRUE(message.has_foo_bytes());
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message);
+
+ message.set_foo_enum(unittest::TestOneof2::FOO);
+ EXPECT_TRUE(message.has_foo_enum());
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message);
+
+ message.mutable_foo_message()->set_qux_int(234);
+ EXPECT_TRUE(message.has_foo_message());
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message);
+
+ message.mutable_foogroup()->set_a(345);
+ EXPECT_TRUE(message.has_foogroup());
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message);
+
+
+ // we repeat this because we didn't test if this properly clears other fields
+ // at the beginning.
+ message.set_foo_int(123);
+ EXPECT_TRUE(message.has_foo_int());
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message);
+}
+
+TEST_F(OneofTest, EnumCases) {
+ unittest::TestOneof2 message;
+
+ message.set_foo_int(123);
+ ExpectEnumCasesWork(message);
+ message.set_foo_string("foo");
+ ExpectEnumCasesWork(message);
+ message.set_foo_bytes("qux");
+ ExpectEnumCasesWork(message);
+ message.set_foo_enum(unittest::TestOneof2::FOO);
+ ExpectEnumCasesWork(message);
+ message.mutable_foo_message()->set_qux_int(234);
+ ExpectEnumCasesWork(message);
+ message.mutable_foogroup()->set_a(345);
+ ExpectEnumCasesWork(message);
+}
+
+TEST_F(OneofTest, PrimitiveType) {
+ unittest::TestOneof2 message;
+ // Unset field returns default value
+ EXPECT_EQ(message.foo_int(), 0);
+
+ message.set_foo_int(123);
+ EXPECT_TRUE(message.has_foo_int());
+ EXPECT_EQ(message.foo_int(), 123);
+ message.clear_foo_int();
+ EXPECT_FALSE(message.has_foo_int());
+}
+
+TEST_F(OneofTest, EnumType) {
+ unittest::TestOneof2 message;
+ // Unset field returns default value
+ EXPECT_EQ(message.foo_enum(), 1);
+
+ message.set_foo_enum(unittest::TestOneof2::FOO);
+ EXPECT_TRUE(message.has_foo_enum());
+ EXPECT_EQ(message.foo_enum(), unittest::TestOneof2::FOO);
+ message.clear_foo_enum();
+ EXPECT_FALSE(message.has_foo_enum());
+}
+
+TEST_F(OneofTest, SetString) {
+ // Check that setting a string field in various ways works
+ unittest::TestOneof2 message;
+
+ // Unset field returns default value
+ EXPECT_EQ(message.foo_string(), "");
+
+ message.set_foo_string("foo");
+ EXPECT_TRUE(message.has_foo_string());
+ EXPECT_EQ(message.foo_string(), "foo");
+ message.clear_foo_string();
+ EXPECT_FALSE(message.has_foo_string());
+
+ message.set_foo_string(string("bar"));
+ EXPECT_TRUE(message.has_foo_string());
+ EXPECT_EQ(message.foo_string(), "bar");
+ message.clear_foo_string();
+ EXPECT_FALSE(message.has_foo_string());
+
+
+ message.set_foo_string("qux", 3);
+ EXPECT_TRUE(message.has_foo_string());
+ EXPECT_EQ(message.foo_string(), "qux");
+ message.clear_foo_string();
+ EXPECT_FALSE(message.has_foo_string());
+
+ message.mutable_foo_string()->assign("quux");
+ EXPECT_TRUE(message.has_foo_string());
+ EXPECT_EQ(message.foo_string(), "quux");
+ message.clear_foo_string();
+ EXPECT_FALSE(message.has_foo_string());
+
+ message.set_foo_string("corge");
+ EXPECT_TRUE(message.has_foo_string());
+ EXPECT_EQ(message.foo_string(), "corge");
+ message.clear_foo_string();
+ EXPECT_FALSE(message.has_foo_string());
+}
+
+TEST_F(OneofTest, ReleaseString) {
+ // Check that release_foo() starts out NULL, and gives us a value
+ // that we can delete after it's been set.
+ unittest::TestOneof2 message;
+
+ EXPECT_EQ(NULL, message.release_foo_string());
+ EXPECT_FALSE(message.has_foo_string());
+
+ message.set_foo_string("blah");
+ EXPECT_TRUE(message.has_foo_string());
+ scoped_ptr<string> str(message.release_foo_string());
+ EXPECT_FALSE(message.has_foo_string());
+ ASSERT_TRUE(str != NULL);
+ EXPECT_EQ("blah", *str);
+
+ EXPECT_EQ(NULL, message.release_foo_string());
+ EXPECT_FALSE(message.has_foo_string());
+}
+
+TEST_F(OneofTest, SetAllocatedString) {
+ // Check that set_allocated_foo() works for strings.
+ unittest::TestOneof2 message;
+
+ EXPECT_FALSE(message.has_foo_string());
+ const string kHello("hello");
+ message.set_foo_string(kHello);
+ EXPECT_TRUE(message.has_foo_string());
+
+ message.set_allocated_foo_string(NULL);
+ EXPECT_FALSE(message.has_foo_string());
+ EXPECT_EQ("", message.foo_string());
+
+ message.set_allocated_foo_string(new string(kHello));
+ EXPECT_TRUE(message.has_foo_string());
+ EXPECT_EQ(kHello, message.foo_string());
+}
+
+
+TEST_F(OneofTest, SetMessage) {
+ // Check that setting a message field works
+ unittest::TestOneof2 message;
+
+ // Unset field returns default instance
+ EXPECT_EQ(&message.foo_message(),
+ &unittest::TestOneof2_NestedMessage::default_instance());
+ EXPECT_EQ(message.foo_message().qux_int(), 0);
+
+ message.mutable_foo_message()->set_qux_int(234);
+ EXPECT_TRUE(message.has_foo_message());
+ EXPECT_EQ(message.foo_message().qux_int(), 234);
+ message.clear_foo_message();
+ EXPECT_FALSE(message.has_foo_message());
+}
+
+TEST_F(OneofTest, ReleaseMessage) {
+ // Check that release_foo() starts out NULL, and gives us a value
+ // that we can delete after it's been set.
+ unittest::TestOneof2 message;
+
+ EXPECT_EQ(NULL, message.release_foo_message());
+ EXPECT_FALSE(message.has_foo_message());
+
+ message.mutable_foo_message()->set_qux_int(1);
+ EXPECT_TRUE(message.has_foo_message());
+ scoped_ptr<unittest::TestOneof2_NestedMessage> mes(
+ message.release_foo_message());
+ EXPECT_FALSE(message.has_foo_message());
+ ASSERT_TRUE(mes != NULL);
+ EXPECT_EQ(1, mes->qux_int());
+
+ EXPECT_EQ(NULL, message.release_foo_message());
+ EXPECT_FALSE(message.has_foo_message());
+}
+
+TEST_F(OneofTest, SetAllocatedMessage) {
+ // Check that set_allocated_foo() works for messages.
+ unittest::TestOneof2 message;
+
+ EXPECT_FALSE(message.has_foo_message());
+
+ message.mutable_foo_message()->set_qux_int(1);
+ EXPECT_TRUE(message.has_foo_message());
+
+ message.set_allocated_foo_message(NULL);
+ EXPECT_FALSE(message.has_foo_message());
+ EXPECT_EQ(&message.foo_message(),
+ &unittest::TestOneof2_NestedMessage::default_instance());
+
+ message.mutable_foo_message()->set_qux_int(1);
+ unittest::TestOneof2_NestedMessage* mes = message.release_foo_message();
+ ASSERT_TRUE(mes != NULL);
+ EXPECT_FALSE(message.has_foo_message());
+
+ message.set_allocated_foo_message(mes);
+ EXPECT_TRUE(message.has_foo_message());
+ EXPECT_EQ(1, message.foo_message().qux_int());
+}
+
+
+TEST_F(OneofTest, Clear) {
+ unittest::TestOneof2 message;
+
+ message.set_foo_int(1);
+ EXPECT_TRUE(message.has_foo_int());
+ message.clear_foo_int();
+ EXPECT_FALSE(message.has_foo_int());
+}
+
+TEST_F(OneofTest, Defaults) {
+ unittest::TestOneof2 message;
+
+ EXPECT_FALSE(message.has_foo_int());
+ EXPECT_EQ(message.foo_int(), 0);
+
+ EXPECT_FALSE(message.has_foo_string());
+ EXPECT_EQ(message.foo_string(), "");
+
+
+ EXPECT_FALSE(message.has_foo_bytes());
+ EXPECT_EQ(message.foo_bytes(), "");
+
+ EXPECT_FALSE(message.has_foo_enum());
+ EXPECT_EQ(message.foo_enum(), 1);
+
+ EXPECT_FALSE(message.has_foo_message());
+ EXPECT_EQ(message.foo_message().qux_int(), 0);
+
+ EXPECT_FALSE(message.has_foogroup());
+ EXPECT_EQ(message.foogroup().a(), 0);
+
+
+ EXPECT_FALSE(message.has_bar_int());
+ EXPECT_EQ(message.bar_int(), 5);
+
+ EXPECT_FALSE(message.has_bar_string());
+ EXPECT_EQ(message.bar_string(), "STRING");
+
+
+ EXPECT_FALSE(message.has_bar_bytes());
+ EXPECT_EQ(message.bar_bytes(), "BYTES");
+
+ EXPECT_FALSE(message.has_bar_enum());
+ EXPECT_EQ(message.bar_enum(), 2);
+}
+
+TEST_F(OneofTest, SwapWithEmpty) {
+ unittest::TestOneof2 message1, message2;
+ message1.set_foo_string("FOO");
+ EXPECT_TRUE(message1.has_foo_string());
+ message1.Swap(&message2);
+ EXPECT_FALSE(message1.has_foo_string());
+ EXPECT_TRUE(message2.has_foo_string());
+ EXPECT_EQ(message2.foo_string(), "FOO");
+}
+
+TEST_F(OneofTest, SwapWithSelf) {
+ unittest::TestOneof2 message;
+ message.set_foo_string("FOO");
+ EXPECT_TRUE(message.has_foo_string());
+ message.Swap(&message);
+ EXPECT_TRUE(message.has_foo_string());
+ EXPECT_EQ(message.foo_string(), "FOO");
+}
+
+TEST_F(OneofTest, SwapBothHasFields) {
+ unittest::TestOneof2 message1, message2;
+
+ message1.set_foo_string("FOO");
+ EXPECT_TRUE(message1.has_foo_string());
+ message2.mutable_foo_message()->set_qux_int(1);
+ EXPECT_TRUE(message2.has_foo_message());
+
+ message1.Swap(&message2);
+ EXPECT_FALSE(message1.has_foo_string());
+ EXPECT_FALSE(message2.has_foo_message());
+ EXPECT_TRUE(message1.has_foo_message());
+ EXPECT_EQ(message1.foo_message().qux_int(), 1);
+ EXPECT_TRUE(message2.has_foo_string());
+ EXPECT_EQ(message2.foo_string(), "FOO");
+}
+
+TEST_F(OneofTest, CopyContructor) {
+ unittest::TestOneof2 message1;
+ message1.set_foo_bytes("FOO");
+
+ unittest::TestOneof2 message2(message1);
+ EXPECT_TRUE(message2.has_foo_bytes());
+ EXPECT_EQ(message2.foo_bytes(), "FOO");
+}
+
+TEST_F(OneofTest, CopyFrom) {
+ unittest::TestOneof2 message1, message2;
+ message1.set_foo_enum(unittest::TestOneof2::BAR);
+ EXPECT_TRUE(message1.has_foo_enum());
+
+ message2.CopyFrom(message1);
+ EXPECT_TRUE(message2.has_foo_enum());
+ EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::BAR);
+
+ // Copying from self should be a no-op.
+ message2.CopyFrom(message2);
+ EXPECT_TRUE(message2.has_foo_enum());
+ EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::BAR);
+}
+
+TEST_F(OneofTest, CopyAssignmentOperator) {
+ unittest::TestOneof2 message1;
+ message1.mutable_foo_message()->set_qux_int(123);
+ EXPECT_TRUE(message1.has_foo_message());
+
+ unittest::TestOneof2 message2;
+ message2 = message1;
+ EXPECT_EQ(message2.foo_message().qux_int(), 123);
+
+ // Make sure that self-assignment does something sane.
+ message2 = message2;
+ EXPECT_EQ(message2.foo_message().qux_int(), 123);
+}
+
+TEST_F(OneofTest, UpcastCopyFrom) {
+ // Test the CopyFrom method that takes in the generic const Message&
+ // parameter.
+ unittest::TestOneof2 message1, message2;
+ message1.mutable_foogroup()->set_a(123);
+ EXPECT_TRUE(message1.has_foogroup());
+
+ const Message* source = implicit_cast<const Message*>(&message1);
+ message2.CopyFrom(*source);
+
+ EXPECT_TRUE(message2.has_foogroup());
+ EXPECT_EQ(message2.foogroup().a(), 123);
+}
+
+// Test the generated SerializeWithCachedSizesToArray(),
+// This indirectly tests MergePartialFromCodedStream()
+// We have to test each field type separately because we cannot set them at the
+// same time
+TEST_F(OneofTest, SerializationToArray) {
+ // Primitive type
+ {
+ unittest::TestOneof2 message1, message2;
+ string data;
+ message1.set_foo_int(123);
+ int size = message1.ByteSize();
+ data.resize(size);
+ uint8* start = reinterpret_cast<uint8*>(string_as_array(&data));
+ uint8* end = message1.SerializeWithCachedSizesToArray(start);
+ EXPECT_EQ(size, end - start);
+ EXPECT_TRUE(message2.ParseFromString(data));
+ EXPECT_EQ(message2.foo_int(), 123);
+ }
+
+ // String
+ {
+ unittest::TestOneof2 message1, message2;
+ string data;
+ message1.set_foo_string("foo");
+ int size = message1.ByteSize();
+ data.resize(size);
+ uint8* start = reinterpret_cast<uint8*>(string_as_array(&data));
+ uint8* end = message1.SerializeWithCachedSizesToArray(start);
+ EXPECT_EQ(size, end - start);
+ EXPECT_TRUE(message2.ParseFromString(data));
+ EXPECT_EQ(message2.foo_string(), "foo");
+ }
+
+
+ // Bytes
+ {
+ unittest::TestOneof2 message1, message2;
+ string data;
+ message1.set_foo_bytes("qux");
+ int size = message1.ByteSize();
+ data.resize(size);
+ uint8* start = reinterpret_cast<uint8*>(string_as_array(&data));
+ uint8* end = message1.SerializeWithCachedSizesToArray(start);
+ EXPECT_EQ(size, end - start);
+ EXPECT_TRUE(message2.ParseFromString(data));
+ EXPECT_EQ(message2.foo_bytes(), "qux");
+ }
+
+ // Enum
+ {
+ unittest::TestOneof2 message1, message2;
+ string data;
+ message1.set_foo_enum(unittest::TestOneof2::FOO);
+ int size = message1.ByteSize();
+ data.resize(size);
+ uint8* start = reinterpret_cast<uint8*>(string_as_array(&data));
+ uint8* end = message1.SerializeWithCachedSizesToArray(start);
+ EXPECT_EQ(size, end - start);
+ EXPECT_TRUE(message2.ParseFromString(data));
+ EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::FOO);
+ }
+
+ // Message
+ {
+ unittest::TestOneof2 message1, message2;
+ string data;
+ message1.mutable_foo_message()->set_qux_int(234);
+ int size = message1.ByteSize();
+ data.resize(size);
+ uint8* start = reinterpret_cast<uint8*>(string_as_array(&data));
+ uint8* end = message1.SerializeWithCachedSizesToArray(start);
+ EXPECT_EQ(size, end - start);
+ EXPECT_TRUE(message2.ParseFromString(data));
+ EXPECT_EQ(message2.foo_message().qux_int(), 234);
+ }
+
+ // Group
+ {
+ unittest::TestOneof2 message1, message2;
+ string data;
+ message1.mutable_foogroup()->set_a(345);
+ int size = message1.ByteSize();
+ data.resize(size);
+ uint8* start = reinterpret_cast<uint8*>(string_as_array(&data));
+ uint8* end = message1.SerializeWithCachedSizesToArray(start);
+ EXPECT_EQ(size, end - start);
+ EXPECT_TRUE(message2.ParseFromString(data));
+ EXPECT_EQ(message2.foogroup().a(), 345);
+ }
+
+}
+
+// Test the generated SerializeWithCachedSizes() by forcing the buffer to write
+// one byte at a time.
+// This indirectly tests MergePartialFromCodedStream()
+// We have to test each field type separately because we cannot set them at the
+// same time
+TEST_F(OneofTest, SerializationToStream) {
+ // Primitive type
+ {
+ unittest::TestOneof2 message1, message2;
+ string data;
+ message1.set_foo_int(123);
+ int size = message1.ByteSize();
+ data.resize(size);
+
+ {
+ // Allow the output stream to buffer only one byte at a time.
+ io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
+ io::CodedOutputStream output_stream(&array_stream);
+ message1.SerializeWithCachedSizes(&output_stream);
+ EXPECT_FALSE(output_stream.HadError());
+ EXPECT_EQ(size, output_stream.ByteCount());
+ }
+
+ EXPECT_TRUE(message2.ParseFromString(data));
+ EXPECT_EQ(message2.foo_int(), 123);
+ }
+
+ // String
+ {
+ unittest::TestOneof2 message1, message2;
+ string data;
+ message1.set_foo_string("foo");
+ int size = message1.ByteSize();
+ data.resize(size);
+
+ {
+ // Allow the output stream to buffer only one byte at a time.
+ io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
+ io::CodedOutputStream output_stream(&array_stream);
+ message1.SerializeWithCachedSizes(&output_stream);
+ EXPECT_FALSE(output_stream.HadError());
+ EXPECT_EQ(size, output_stream.ByteCount());
+ }
+
+ EXPECT_TRUE(message2.ParseFromString(data));
+ EXPECT_EQ(message2.foo_string(), "foo");
+ }
+
+
+ // Bytes
+ {
+ unittest::TestOneof2 message1, message2;
+ string data;
+ message1.set_foo_bytes("qux");
+ int size = message1.ByteSize();
+ data.resize(size);
+
+ {
+ // Allow the output stream to buffer only one byte at a time.
+ io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
+ io::CodedOutputStream output_stream(&array_stream);
+ message1.SerializeWithCachedSizes(&output_stream);
+ EXPECT_FALSE(output_stream.HadError());
+ EXPECT_EQ(size, output_stream.ByteCount());
+ }
+
+ EXPECT_TRUE(message2.ParseFromString(data));
+ EXPECT_EQ(message2.foo_bytes(), "qux");
+ }
+
+ // Enum
+ {
+ unittest::TestOneof2 message1, message2;
+ string data;
+ message1.set_foo_enum(unittest::TestOneof2::FOO);
+ int size = message1.ByteSize();
+ data.resize(size);
+
+ {
+ // Allow the output stream to buffer only one byte at a time.
+ io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
+ io::CodedOutputStream output_stream(&array_stream);
+ message1.SerializeWithCachedSizes(&output_stream);
+ EXPECT_FALSE(output_stream.HadError());
+ EXPECT_EQ(size, output_stream.ByteCount());
+ }
+
+ EXPECT_TRUE(message2.ParseFromString(data));
+ EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::FOO);
+ }
+
+ // Message
+ {
+ unittest::TestOneof2 message1, message2;
+ string data;
+ message1.mutable_foo_message()->set_qux_int(234);
+ int size = message1.ByteSize();
+ data.resize(size);
+
+ {
+ // Allow the output stream to buffer only one byte at a time.
+ io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
+ io::CodedOutputStream output_stream(&array_stream);
+ message1.SerializeWithCachedSizes(&output_stream);
+ EXPECT_FALSE(output_stream.HadError());
+ EXPECT_EQ(size, output_stream.ByteCount());
+ }
+
+ EXPECT_TRUE(message2.ParseFromString(data));
+ EXPECT_EQ(message2.foo_message().qux_int(), 234);
+ }
+
+ // Group
+ {
+ unittest::TestOneof2 message1, message2;
+ string data;
+ message1.mutable_foogroup()->set_a(345);
+ int size = message1.ByteSize();
+ data.resize(size);
+
+ {
+ // Allow the output stream to buffer only one byte at a time.
+ io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
+ io::CodedOutputStream output_stream(&array_stream);
+ message1.SerializeWithCachedSizes(&output_stream);
+ EXPECT_FALSE(output_stream.HadError());
+ EXPECT_EQ(size, output_stream.ByteCount());
+ }
+
+ EXPECT_TRUE(message2.ParseFromString(data));
+ EXPECT_EQ(message2.foogroup().a(), 345);
+ }
+
+}
+
+TEST_F(OneofTest, MergeFrom) {
+ unittest::TestOneof2 message1, message2;
+
+ message1.set_foo_int(123);
+ message2.MergeFrom(message1);
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
+ EXPECT_TRUE(message2.has_foo_int());
+ EXPECT_EQ(message2.foo_int(), 123);
+
+ message1.set_foo_string("foo");
+ message2.MergeFrom(message1);
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
+ EXPECT_TRUE(message2.has_foo_string());
+ EXPECT_EQ(message2.foo_string(), "foo");
+
+
+ message1.set_foo_bytes("qux");
+ message2.MergeFrom(message1);
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
+ EXPECT_TRUE(message2.has_foo_bytes());
+ EXPECT_EQ(message2.foo_bytes(), "qux");
+
+ message1.set_foo_enum(unittest::TestOneof2::FOO);
+ message2.MergeFrom(message1);
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
+ EXPECT_TRUE(message2.has_foo_enum());
+ EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::FOO);
+
+ message1.mutable_foo_message()->set_qux_int(234);
+ message2.MergeFrom(message1);
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
+ EXPECT_TRUE(message2.has_foo_message());
+ EXPECT_EQ(message2.foo_message().qux_int(), 234);
+
+ message1.mutable_foogroup()->set_a(345);
+ message2.MergeFrom(message1);
+ TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
+ EXPECT_TRUE(message2.has_foogroup());
+ EXPECT_EQ(message2.foogroup().a(), 345);
+
+}
+
} // namespace cpp_unittest
} // namespace cpp
} // namespace compiler
diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.h b/src/google/protobuf/compiler/cpp/cpp_unittest.h
new file mode 100644
index 0000000..69c8f44
--- /dev/null
+++ b/src/google/protobuf/compiler/cpp/cpp_unittest.h
@@ -0,0 +1,51 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This header declares the namespace google::protobuf::protobuf_unittest in order to expose
+// any problems with the generated class names. We use this header to ensure
+// unittest.cc will declare the namespace prior to other includes, while obeying
+// normal include ordering.
+//
+// When generating a class name of "foo.Bar" we must ensure we prefix the class
+// name with "::", in case the namespace google::protobuf::foo exists. We intentionally
+// trigger that case here by declaring google::protobuf::protobuf_unittest.
+//
+// See ClassName in helpers.h for more details.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_UNITTEST_H__
+#define GOOGLE_PROTOBUF_COMPILER_CPP_UNITTEST_H__
+
+namespace google {
+namespace protobuf {
+namespace protobuf_unittest {}
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_COMPILER_CPP_UNITTEST_H__
diff --git a/src/google/protobuf/compiler/importer.cc b/src/google/protobuf/compiler/importer.cc
index 7689ce9..e6c446a 100644
--- a/src/google/protobuf/compiler/importer.cc
+++ b/src/google/protobuf/compiler/importer.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -43,6 +43,7 @@
#include <errno.h>
#include <algorithm>
+#include <memory>
#include <google/protobuf/compiler/importer.h>
@@ -124,7 +125,8 @@ bool SourceTreeDescriptorDatabase::FindFileByName(
scoped_ptr<io::ZeroCopyInputStream> input(source_tree_->Open(filename));
if (input == NULL) {
if (error_collector_ != NULL) {
- error_collector_->AddError(filename, -1, 0, "File not found.");
+ error_collector_->AddError(filename, -1, 0,
+ source_tree_->GetLastErrorMessage());
}
return false;
}
@@ -186,6 +188,7 @@ Importer::Importer(SourceTree* source_tree,
MultiFileErrorCollector* error_collector)
: database_(source_tree),
pool_(&database_, database_.GetValidationErrorCollector()) {
+ pool_.EnforceWeakDependencies(true);
database_.RecordErrorsTo(error_collector);
}
@@ -195,10 +198,22 @@ const FileDescriptor* Importer::Import(const string& filename) {
return pool_.FindFileByName(filename);
}
+void Importer::AddUnusedImportTrackFile(const string& file_name) {
+ pool_.AddUnusedImportTrackFile(file_name);
+}
+
+void Importer::ClearUnusedImportTrackFiles() {
+ pool_.ClearUnusedImportTrackFiles();
+}
+
// ===================================================================
SourceTree::~SourceTree() {}
+string SourceTree::GetLastErrorMessage() {
+ return "File not found.";
+}
+
DiskSourceTree::DiskSourceTree() {}
DiskSourceTree::~DiskSourceTree() {}
@@ -231,12 +246,17 @@ static string CanonicalizePath(string path) {
// The Win32 API accepts forward slashes as a path delimiter even though
// backslashes are standard. Let's avoid confusion and use only forward
// slashes.
- path = StringReplace(path, "\\", "/", true);
+ if (HasPrefixString(path, "\\\\")) {
+ // Avoid converting two leading backslashes.
+ path = "\\\\" + StringReplace(path.substr(2), "\\", "/", true);
+ } else {
+ path = StringReplace(path, "\\", "/", true);
+ }
#endif
- vector<string> parts;
vector<string> canonical_parts;
- SplitStringUsing(path, "/", &parts); // Note: Removes empty parts.
+ vector<string> parts = Split(
+ path, "/", true); // Note: Removes empty parts.
for (int i = 0; i < parts.size(); i++) {
if (parts[i] == ".") {
// Ignore.
@@ -244,7 +264,7 @@ static string CanonicalizePath(string path) {
canonical_parts.push_back(parts[i]);
}
}
- string result = JoinStrings(canonical_parts, "/");
+ string result = Join(canonical_parts, "/");
if (!path.empty() && path[0] == '/') {
// Restore leading slash.
result = '/' + result;
@@ -390,8 +410,8 @@ DiskSourceTree::DiskFileToVirtualFile(
bool DiskSourceTree::VirtualFileToDiskFile(const string& virtual_file,
string* disk_file) {
- scoped_ptr<io::ZeroCopyInputStream> stream(OpenVirtualFile(virtual_file,
- disk_file));
+ scoped_ptr<io::ZeroCopyInputStream> stream(
+ OpenVirtualFile(virtual_file, disk_file));
return stream != NULL;
}
@@ -399,6 +419,10 @@ io::ZeroCopyInputStream* DiskSourceTree::Open(const string& filename) {
return OpenVirtualFile(filename, NULL);
}
+string DiskSourceTree::GetLastErrorMessage() {
+ return last_error_message_;
+}
+
io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile(
const string& virtual_file,
string* disk_file) {
@@ -407,6 +431,8 @@ io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile(
// We do not allow importing of paths containing things like ".." or
// consecutive slashes since the compiler expects files to be uniquely
// identified by file name.
+ last_error_message_ = "Backslashes, consecutive slashes, \".\", or \"..\" "
+ "are not allowed in the virtual path";
return NULL;
}
@@ -424,13 +450,13 @@ io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile(
if (errno == EACCES) {
// The file exists but is not readable.
- // TODO(kenton): Find a way to report this more nicely.
- GOOGLE_LOG(WARNING) << "Read access is denied for file: " << temp_disk_file;
+ last_error_message_ = "Read access is denied for file: " +
+ temp_disk_file;
return NULL;
}
}
}
-
+ last_error_message_ = "File not found.";
return NULL;
}
diff --git a/src/google/protobuf/compiler/importer.h b/src/google/protobuf/compiler/importer.h
index 7a2efc2..f010fd0 100644
--- a/src/google/protobuf/compiler/importer.h
+++ b/src/google/protobuf/compiler/importer.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -166,6 +166,9 @@ class LIBPROTOBUF_EXPORT Importer {
return &pool_;
}
+ void AddUnusedImportTrackFile(const string& file_name);
+ void ClearUnusedImportTrackFiles();
+
private:
SourceTreeDescriptorDatabase database_;
DescriptorPool pool_;
@@ -204,6 +207,13 @@ class LIBPROTOBUF_EXPORT SourceTree {
// contain "." or ".." components.
virtual io::ZeroCopyInputStream* Open(const string& filename) = 0;
+ // If Open() returns NULL, calling this method immediately will return an
+ // description of the error.
+ // Subclasses should implement this method and return a meaningful value for
+ // better error reporting.
+ // TODO(xiaofeng): change this to a pure virtual function.
+ virtual string GetLastErrorMessage();
+
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SourceTree);
};
@@ -273,17 +283,21 @@ class LIBPROTOBUF_EXPORT DiskSourceTree : public SourceTree {
bool VirtualFileToDiskFile(const string& virtual_file, string* disk_file);
// implements SourceTree -------------------------------------------
- io::ZeroCopyInputStream* Open(const string& filename);
+ virtual io::ZeroCopyInputStream* Open(const string& filename);
+
+ virtual string GetLastErrorMessage();
private:
struct Mapping {
string virtual_path;
string disk_path;
- inline Mapping(const string& virtual_path, const string& disk_path)
- : virtual_path(virtual_path), disk_path(disk_path) {}
+ inline Mapping(const string& virtual_path_param,
+ const string& disk_path_param)
+ : virtual_path(virtual_path_param), disk_path(disk_path_param) {}
};
vector<Mapping> mappings_;
+ string last_error_message_;
// Like Open(), but returns the on-disk path in disk_file if disk_file is
// non-NULL and the file could be successfully opened.
diff --git a/src/google/protobuf/compiler/importer_unittest.cc b/src/google/protobuf/compiler/importer_unittest.cc
index 56fad56..387f135 100644
--- a/src/google/protobuf/compiler/importer_unittest.cc
+++ b/src/google/protobuf/compiler/importer_unittest.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -33,12 +33,13 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/stubs/hash.h>
+#include <memory>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <google/protobuf/stubs/map-util.h>
+#include <google/protobuf/stubs/map_util.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/stubs/strutil.h>
@@ -92,6 +93,10 @@ class MockSourceTree : public SourceTree {
}
}
+ string GetLastErrorMessage() {
+ return "File not found.";
+ }
+
private:
hash_map<string, const char*> files_;
};
@@ -324,6 +329,7 @@ TEST_F(ImporterTest, MapFieldKeyNotScalar) {
EXPECT_SUBSTRING("must name a scalar or string", error());
}
+
// ===================================================================
class DiskSourceTreeTest : public testing::Test {
@@ -336,7 +342,7 @@ class DiskSourceTreeTest : public testing::Test {
if (File::Exists(dirnames_[i])) {
File::DeleteRecursively(dirnames_[i], NULL, NULL);
}
- GOOGLE_CHECK(File::CreateDir(dirnames_[i].c_str(), DEFAULT_FILE_MODE));
+ GOOGLE_CHECK_OK(File::CreateDir(dirnames_[i], 0777));
}
}
@@ -347,11 +353,11 @@ class DiskSourceTreeTest : public testing::Test {
}
void AddFile(const string& filename, const char* contents) {
- File::WriteStringToFileOrDie(contents, filename);
+ GOOGLE_CHECK_OK(File::SetContents(filename, contents, true));
}
void AddSubdir(const string& dirname) {
- GOOGLE_CHECK(File::CreateDir(dirname.c_str(), DEFAULT_FILE_MODE));
+ GOOGLE_CHECK_OK(File::CreateDir(dirname, 0777));
}
void ExpectFileContents(const string& filename,
@@ -371,9 +377,11 @@ class DiskSourceTreeTest : public testing::Test {
EXPECT_EQ(expected_contents, file_contents);
}
- void ExpectFileNotFound(const string& filename) {
+ void ExpectCannotOpenFile(const string& filename,
+ const string& error_message) {
scoped_ptr<io::ZeroCopyInputStream> input(source_tree_.Open(filename));
EXPECT_TRUE(input == NULL);
+ EXPECT_EQ(error_message, source_tree_.GetLastErrorMessage());
}
DiskSourceTree source_tree_;
@@ -389,7 +397,7 @@ TEST_F(DiskSourceTreeTest, MapRoot) {
source_tree_.MapPath("", dirnames_[0]);
ExpectFileContents("foo", "Hello World!");
- ExpectFileNotFound("bar");
+ ExpectCannotOpenFile("bar", "File not found.");
}
TEST_F(DiskSourceTreeTest, MapDirectory) {
@@ -400,15 +408,21 @@ TEST_F(DiskSourceTreeTest, MapDirectory) {
source_tree_.MapPath("baz", dirnames_[0]);
ExpectFileContents("baz/foo", "Hello World!");
- ExpectFileNotFound("baz/bar");
- ExpectFileNotFound("foo");
- ExpectFileNotFound("bar");
+ ExpectCannotOpenFile("baz/bar", "File not found.");
+ ExpectCannotOpenFile("foo", "File not found.");
+ ExpectCannotOpenFile("bar", "File not found.");
// Non-canonical file names should not work.
- ExpectFileNotFound("baz//foo");
- ExpectFileNotFound("baz/../baz/foo");
- ExpectFileNotFound("baz/./foo");
- ExpectFileNotFound("baz/foo/");
+ ExpectCannotOpenFile("baz//foo",
+ "Backslashes, consecutive slashes, \".\", or \"..\" are "
+ "not allowed in the virtual path");
+ ExpectCannotOpenFile("baz/../baz/foo",
+ "Backslashes, consecutive slashes, \".\", or \"..\" are "
+ "not allowed in the virtual path");
+ ExpectCannotOpenFile("baz/./foo",
+ "Backslashes, consecutive slashes, \".\", or \"..\" are "
+ "not allowed in the virtual path");
+ ExpectCannotOpenFile("baz/foo/", "File not found.");
}
TEST_F(DiskSourceTreeTest, NoParent) {
@@ -420,8 +434,12 @@ TEST_F(DiskSourceTreeTest, NoParent) {
source_tree_.MapPath("", dirnames_[0] + "/bar");
ExpectFileContents("baz", "Blah.");
- ExpectFileNotFound("../foo");
- ExpectFileNotFound("../bar/baz");
+ ExpectCannotOpenFile("../foo",
+ "Backslashes, consecutive slashes, \".\", or \"..\" are "
+ "not allowed in the virtual path");
+ ExpectCannotOpenFile("../bar/baz",
+ "Backslashes, consecutive slashes, \".\", or \"..\" are "
+ "not allowed in the virtual path");
}
TEST_F(DiskSourceTreeTest, MapFile) {
@@ -431,7 +449,7 @@ TEST_F(DiskSourceTreeTest, MapFile) {
source_tree_.MapPath("foo", dirnames_[0] + "/foo");
ExpectFileContents("foo", "Hello World!");
- ExpectFileNotFound("bar");
+ ExpectCannotOpenFile("bar", "File not found.");
}
TEST_F(DiskSourceTreeTest, SearchMultipleDirectories) {
@@ -445,7 +463,7 @@ TEST_F(DiskSourceTreeTest, SearchMultipleDirectories) {
ExpectFileContents("foo", "Hello World!");
ExpectFileContents("bar", "Goodbye World!");
- ExpectFileNotFound("baz");
+ ExpectCannotOpenFile("baz", "File not found.");
}
TEST_F(DiskSourceTreeTest, OrderingTrumpsSpecificity) {
@@ -453,8 +471,7 @@ TEST_F(DiskSourceTreeTest, OrderingTrumpsSpecificity) {
// directory is more-specific than a former one.
// Create the "bar" directory so we can put a file in it.
- ASSERT_TRUE(File::CreateDir((dirnames_[0] + "/bar").c_str(),
- DEFAULT_FILE_MODE));
+ GOOGLE_CHECK_OK(File::CreateDir(dirnames_[0] + "/bar", 0777));
// Add files and map paths.
AddFile(dirnames_[0] + "/bar/foo", "Hello World!");
diff --git a/src/google/protobuf/compiler/java/java_context.cc b/src/google/protobuf/compiler/java/java_context.cc
new file mode 100644
index 0000000..7d21fe6
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_context.cc
@@ -0,0 +1,195 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// 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_context.h>
+
+#include <google/protobuf/compiler/java/java_field.h>
+#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/compiler/java/java_name_resolver.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/map_util.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+Context::Context(const FileDescriptor* file)
+ : name_resolver_(new ClassNameResolver) {
+ InitializeFieldGeneratorInfo(file);
+}
+
+Context::~Context() {
+}
+
+ClassNameResolver* Context::GetNameResolver() {
+ return name_resolver_.get();
+}
+
+namespace {
+// Whether two fields have conflicting accessors (assuming name1 and name2
+// are different). name1 and name2 are field1 and field2's camel-case name
+// respectively.
+bool IsConflicting(const FieldDescriptor* field1, const string& name1,
+ const FieldDescriptor* field2, const string& name2,
+ string* info) {
+ if (field1->is_repeated()) {
+ if (field2->is_repeated()) {
+ // Both fields are repeated.
+ return false;
+ } else {
+ // field1 is repeated, and field2 is not.
+ if (name1 + "Count" == name2) {
+ *info = "both repeated field \"" + field1->name() + "\" and singular " +
+ "field \"" + field2->name() + "\" generates the method \"" +
+ "get" + name1 + "Count()\"";
+ return true;
+ }
+ if (name1 + "List" == name2) {
+ *info = "both repeated field \"" + field1->name() + "\" and singular " +
+ "field \"" + field2->name() + "\" generates the method \"" +
+ "get" + name1 + "List()\"";
+ return true;
+ }
+ // Well, there are obviously many more conflicting cases, but it probably
+ // doesn't worth the effort to exhaust all of them because they rarely
+ // happen and as we are continuing adding new methods/changing existing
+ // methods the number of different conflicting cases will keep growing.
+ // We can just add more cases here when they are found in the real world.
+ return false;
+ }
+ } else {
+ if (field2->is_repeated()) {
+ return IsConflicting(field2, name2, field1, name1, info);
+ } else {
+ // None of the two fields are repeated.
+ return false;
+ }
+ }
+}
+} // namespace
+
+void Context::InitializeFieldGeneratorInfo(const FileDescriptor* file) {
+ for (int i = 0; i < file->message_type_count(); ++i) {
+ InitializeFieldGeneratorInfoForMessage(file->message_type(i));
+ }
+}
+
+void Context::InitializeFieldGeneratorInfoForMessage(
+ const Descriptor* message) {
+ for (int i = 0; i < message->nested_type_count(); ++i) {
+ InitializeFieldGeneratorInfoForMessage(message->nested_type(i));
+ }
+ vector<const FieldDescriptor*> fields;
+ for (int i = 0; i < message->field_count(); ++i) {
+ fields.push_back(message->field(i));
+ }
+ InitializeFieldGeneratorInfoForFields(fields);
+
+ for (int i = 0; i < message->oneof_decl_count(); ++i) {
+ const OneofDescriptor* oneof = message->oneof_decl(i);
+ OneofGeneratorInfo info;
+ info.name = UnderscoresToCamelCase(oneof->name(), false);
+ info.capitalized_name = UnderscoresToCamelCase(oneof->name(), true);
+ oneof_generator_info_map_[oneof] = info;
+ }
+}
+
+void Context::InitializeFieldGeneratorInfoForFields(
+ const vector<const FieldDescriptor*>& fields) {
+ // Find out all fields that conflict with some other field in the same
+ // message.
+ vector<bool> is_conflict(fields.size());
+ vector<string> conflict_reason(fields.size());
+ for (int i = 0; i < fields.size(); ++i) {
+ const FieldDescriptor* field = fields[i];
+ const string& name = UnderscoresToCapitalizedCamelCase(field);
+ for (int j = i + 1; j < fields.size(); ++j) {
+ const FieldDescriptor* other = fields[j];
+ const string& other_name = UnderscoresToCapitalizedCamelCase(other);
+ if (name == other_name) {
+ is_conflict[i] = is_conflict[j] = true;
+ conflict_reason[i] = conflict_reason[j] =
+ "capitalized name of field \"" + field->name() +
+ "\" conflicts with field \"" + other->name() + "\"";
+ } else if (IsConflicting(field, name, other, other_name,
+ &conflict_reason[j])) {
+ is_conflict[i] = is_conflict[j] = true;
+ conflict_reason[i] = conflict_reason[j];
+ }
+ }
+ if (is_conflict[i]) {
+ GOOGLE_LOG(WARNING) << "field \"" << field->full_name() << "\" is conflicting "
+ << "with another field: " << conflict_reason[i];
+ }
+ }
+ for (int i = 0; i < fields.size(); ++i) {
+ const FieldDescriptor* field = fields[i];
+ FieldGeneratorInfo info;
+ info.name = UnderscoresToCamelCase(field);
+ info.capitalized_name = UnderscoresToCapitalizedCamelCase(field);
+ // For fields conflicting with some other fields, we append the field
+ // number to their field names in generated code to avoid conflicts.
+ if (is_conflict[i]) {
+ info.name += SimpleItoa(field->number());
+ info.capitalized_name += SimpleItoa(field->number());
+ info.disambiguated_reason = conflict_reason[i];
+ }
+ field_generator_info_map_[field] = info;
+ }
+}
+
+const FieldGeneratorInfo* Context::GetFieldGeneratorInfo(
+ const FieldDescriptor* field) const {
+ const FieldGeneratorInfo* result =
+ FindOrNull(field_generator_info_map_, field);
+ if (result == NULL) {
+ GOOGLE_LOG(FATAL) << "Can not find FieldGeneratorInfo for field: "
+ << field->full_name();
+ }
+ return result;
+}
+
+const OneofGeneratorInfo* Context::GetOneofGeneratorInfo(
+ const OneofDescriptor* oneof) const {
+ const OneofGeneratorInfo* result =
+ FindOrNull(oneof_generator_info_map_, oneof);
+ if (result == NULL) {
+ GOOGLE_LOG(FATAL) << "Can not find OneofGeneratorInfo for oneof: "
+ << oneof->name();
+ }
+ return result;
+}
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/compiler/java/java_context.h b/src/google/protobuf/compiler/java/java_context.h
new file mode 100644
index 0000000..5791445
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_context.h
@@ -0,0 +1,95 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// 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_CONTEXT_H__
+#define GOOGLE_PROTOBUF_COMPILER_JAVA_CONTEXT_H__
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+ class FileDescriptor;
+ class FieldDescriptor;
+ class OneofDescriptor;
+ class Descriptor;
+ namespace compiler {
+ namespace java {
+ class ClassNameResolver; // name_resolver.h
+ }
+ }
+} // namespace protobuf
+
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+struct FieldGeneratorInfo;
+struct OneofGeneratorInfo;
+// A context object holds the information that is shared among all code
+// generators.
+class Context {
+ public:
+ explicit Context(const FileDescriptor* file);
+ ~Context();
+
+ // Get the name resolver associated with this context. The resolver
+ // can be used to map descriptors to Java class names.
+ ClassNameResolver* GetNameResolver();
+
+ // Get the FieldGeneratorInfo for a given field.
+ const FieldGeneratorInfo* GetFieldGeneratorInfo(
+ const FieldDescriptor* field) const;
+
+ // Get the OneofGeneratorInfo for a given oneof.
+ const OneofGeneratorInfo* GetOneofGeneratorInfo(
+ const OneofDescriptor* oneof) const;
+
+ private:
+ void InitializeFieldGeneratorInfo(const FileDescriptor* file);
+ void InitializeFieldGeneratorInfoForMessage(const Descriptor* message);
+ void InitializeFieldGeneratorInfoForFields(
+ const vector<const FieldDescriptor*>& fields);
+
+ scoped_ptr<ClassNameResolver> name_resolver_;
+ map<const FieldDescriptor*, FieldGeneratorInfo> field_generator_info_map_;
+ map<const OneofDescriptor*, OneofGeneratorInfo> oneof_generator_info_map_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Context);
+};
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_CONTEXT_H__
diff --git a/src/google/protobuf/compiler/java/java_doc_comment.cc b/src/google/protobuf/compiler/java/java_doc_comment.cc
new file mode 100644
index 0000000..663f0c9
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_doc_comment.cc
@@ -0,0 +1,233 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: kenton@google.com (Kenton Varda)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/compiler/java/java_doc_comment.h>
+
+#include <vector>
+
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/stubs/strutil.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+string EscapeJavadoc(const string& input) {
+ string result;
+ result.reserve(input.size() * 2);
+
+ char prev = '*';
+
+ for (string::size_type i = 0; i < input.size(); i++) {
+ char c = input[i];
+ switch (c) {
+ case '*':
+ // Avoid "/*".
+ if (prev == '/') {
+ result.append("&#42;");
+ } else {
+ result.push_back(c);
+ }
+ break;
+ case '/':
+ // Avoid "*/".
+ if (prev == '*') {
+ result.append("&#47;");
+ } else {
+ result.push_back(c);
+ }
+ break;
+ case '@':
+ // '@' starts javadoc tags including the @deprecated tag, which will
+ // cause a compile-time error if inserted before a declaration that
+ // does not have a corresponding @Deprecated annotation.
+ result.append("&#64;");
+ break;
+ case '<':
+ // Avoid interpretation as HTML.
+ result.append("&lt;");
+ break;
+ case '>':
+ // Avoid interpretation as HTML.
+ result.append("&gt;");
+ break;
+ case '&':
+ // Avoid interpretation as HTML.
+ result.append("&amp;");
+ break;
+ case '\\':
+ // Java interprets Unicode escape sequences anywhere!
+ result.append("&#92;");
+ break;
+ default:
+ result.push_back(c);
+ break;
+ }
+
+ prev = c;
+ }
+
+ return result;
+}
+
+static void WriteDocCommentBodyForLocation(
+ io::Printer* printer, const SourceLocation& location) {
+ string comments = location.leading_comments.empty() ?
+ location.trailing_comments : location.leading_comments;
+ if (!comments.empty()) {
+ // TODO(kenton): Ideally we should parse the comment text as Markdown and
+ // write it back as HTML, but this requires a Markdown parser. For now
+ // we just use <pre> to get fixed-width text formatting.
+
+ // If the comment itself contains block comment start or end markers,
+ // HTML-escape them so that they don't accidentally close the doc comment.
+ comments = EscapeJavadoc(comments);
+
+ vector<string> lines = Split(comments, "\n");
+ while (!lines.empty() && lines.back().empty()) {
+ lines.pop_back();
+ }
+
+ printer->Print(
+ " *\n"
+ " * <pre>\n");
+ for (int i = 0; i < lines.size(); i++) {
+ // Most lines should start with a space. Watch out for lines that start
+ // with a /, since putting that right after the leading asterisk will
+ // close the comment.
+ if (!lines[i].empty() && lines[i][0] == '/') {
+ printer->Print(" * $line$\n", "line", lines[i]);
+ } else {
+ printer->Print(" *$line$\n", "line", lines[i]);
+ }
+ }
+ printer->Print(" * </pre>\n");
+ }
+}
+
+template <typename DescriptorType>
+static void WriteDocCommentBody(
+ io::Printer* printer, const DescriptorType* descriptor) {
+ SourceLocation location;
+ if (descriptor->GetSourceLocation(&location)) {
+ WriteDocCommentBodyForLocation(printer, location);
+ }
+}
+
+static string FirstLineOf(const string& value) {
+ string result = value;
+
+ string::size_type pos = result.find_first_of('\n');
+ if (pos != string::npos) {
+ result.erase(pos);
+ }
+
+ // If line ends in an opening brace, make it "{ ... }" so it looks nice.
+ if (!result.empty() && result[result.size() - 1] == '{') {
+ result.append(" ... }");
+ }
+
+ return result;
+}
+
+void WriteMessageDocComment(io::Printer* printer, const Descriptor* message) {
+ printer->Print(
+ "/**\n"
+ " * Protobuf type {@code $fullname$}\n",
+ "fullname", EscapeJavadoc(message->full_name()));
+ WriteDocCommentBody(printer, message);
+ printer->Print(" */\n");
+}
+
+void WriteFieldDocComment(io::Printer* printer, const FieldDescriptor* field) {
+ // In theory we should have slightly different comments for setters, getters,
+ // etc., but in practice everyone already knows the difference between these
+ // so it's redundant information.
+
+ // We use the field declaration as the first line of the comment, e.g.:
+ // optional string foo = 5;
+ // This communicates a lot of information about the field in a small space.
+ // If the field is a group, the debug string might end with {.
+ printer->Print(
+ "/**\n"
+ " * <code>$def$</code>\n",
+ "def", EscapeJavadoc(FirstLineOf(field->DebugString())));
+ WriteDocCommentBody(printer, field);
+ printer->Print(" */\n");
+}
+
+void WriteEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_) {
+ printer->Print(
+ "/**\n"
+ " * Protobuf enum {@code $fullname$}\n",
+ "fullname", EscapeJavadoc(enum_->full_name()));
+ WriteDocCommentBody(printer, enum_);
+ printer->Print(" */\n");
+}
+
+void WriteEnumValueDocComment(io::Printer* printer,
+ const EnumValueDescriptor* value) {
+ printer->Print(
+ "/**\n"
+ " * <code>$def$</code>\n",
+ "def", EscapeJavadoc(FirstLineOf(value->DebugString())));
+ WriteDocCommentBody(printer, value);
+ printer->Print(" */\n");
+}
+
+void WriteServiceDocComment(io::Printer* printer,
+ const ServiceDescriptor* service) {
+ printer->Print(
+ "/**\n"
+ " * Protobuf service {@code $fullname$}\n",
+ "fullname", EscapeJavadoc(service->full_name()));
+ WriteDocCommentBody(printer, service);
+ printer->Print(" */\n");
+}
+
+void WriteMethodDocComment(io::Printer* printer,
+ const MethodDescriptor* method) {
+ printer->Print(
+ "/**\n"
+ " * <code>$def$</code>\n",
+ "def", EscapeJavadoc(FirstLineOf(method->DebugString())));
+ WriteDocCommentBody(printer, method);
+ printer->Print(" */\n");
+}
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/compiler/java/java_doc_comment.h b/src/google/protobuf/compiler/java/java_doc_comment.h
new file mode 100644
index 0000000..7d9535c
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_doc_comment.h
@@ -0,0 +1,69 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: kenton@google.com (Kenton Varda)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_DOC_COMMENT_H__
+#define GOOGLE_PROTOBUF_COMPILER_JAVA_DOC_COMMENT_H__
+
+#include <google/protobuf/descriptor.h>
+
+namespace google {
+namespace protobuf {
+ namespace io {
+ class Printer; // printer.h
+ }
+}
+
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+void WriteMessageDocComment(io::Printer* printer, const Descriptor* message);
+void WriteFieldDocComment(io::Printer* printer, const FieldDescriptor* field);
+void WriteEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_);
+void WriteEnumValueDocComment(io::Printer* printer,
+ const EnumValueDescriptor* value);
+void WriteServiceDocComment(io::Printer* printer,
+ const ServiceDescriptor* service);
+void WriteMethodDocComment(io::Printer* printer,
+ const MethodDescriptor* method);
+
+// Exposed for testing only.
+LIBPROTOC_EXPORT string EscapeJavadoc(const string& input);
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_DOC_COMMENT_H__
diff --git a/src/google/protobuf/compiler/java/java_doc_comment_unittest.cc b/src/google/protobuf/compiler/java/java_doc_comment_unittest.cc
new file mode 100644
index 0000000..ae582ea
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_doc_comment_unittest.cc
@@ -0,0 +1,67 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: kenton@google.com (Kenton Varda)
+
+#include <google/protobuf/compiler/java/java_doc_comment.h>
+
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace java {
+namespace {
+
+TEST(JavaDocCommentTest, Escaping) {
+ EXPECT_EQ("foo /&#42; bar *&#47; baz", EscapeJavadoc("foo /* bar */ baz"));
+ EXPECT_EQ("foo /&#42;&#47; baz", EscapeJavadoc("foo /*/ baz"));
+ EXPECT_EQ("{&#64;foo}", EscapeJavadoc("{@foo}"));
+ EXPECT_EQ("&lt;i&gt;&amp;&lt;/i&gt;", EscapeJavadoc("<i>&</i>"));
+ EXPECT_EQ("foo&#92;u1234bar", EscapeJavadoc("foo\\u1234bar"));
+ EXPECT_EQ("&#64;deprecated", EscapeJavadoc("@deprecated"));
+}
+
+// TODO(kenton): It's hard to write a robust test of the doc comments -- we
+// can only really compare the output against a golden value, which is a
+// fairly tedious and fragile testing strategy. If we want to go that route,
+// it probably makes sense to bite the bullet and write a test that compares
+// the whole generated output for unittest.proto against a golden value, with
+// a very simple script that can be run to regenerate it with the latest code.
+// This would mean that updates to the golden file would have to be included
+// in any change to the code generator, which would actually be fairly useful
+// as it allows the reviewer to see clearly how the generated code is
+// changing.
+
+} // namespace
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/compiler/java/java_enum.cc b/src/google/protobuf/compiler/java/java_enum.cc
index 85e39f5..668377a 100644
--- a/src/google/protobuf/compiler/java/java_enum.cc
+++ b/src/google/protobuf/compiler/java/java_enum.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -35,8 +35,11 @@
#include <map>
#include <string>
+#include <google/protobuf/compiler/java/java_context.h>
#include <google/protobuf/compiler/java/java_enum.h>
+#include <google/protobuf/compiler/java/java_doc_comment.h>
#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/compiler/java/java_name_resolver.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/stubs/strutil.h>
@@ -46,8 +49,22 @@ namespace protobuf {
namespace compiler {
namespace java {
-EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor)
- : descriptor_(descriptor) {
+namespace {
+bool EnumHasCustomOptions(const EnumDescriptor* descriptor) {
+ if (descriptor->options().unknown_fields().field_count() > 0) return true;
+ for (int i = 0; i < descriptor->value_count(); ++i) {
+ const EnumValueDescriptor* value = descriptor->value(i);
+ if (value->options().unknown_fields().field_count() > 0) return true;
+ }
+ return false;
+}
+} // namespace
+
+EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
+ bool immutable_api,
+ Context* context)
+ : descriptor_(descriptor), immutable_api_(immutable_api),
+ name_resolver_(context->GetNameResolver()) {
for (int i = 0; i < descriptor_->value_count(); i++) {
const EnumValueDescriptor* value = descriptor_->value(i);
const EnumValueDescriptor* canonical_value =
@@ -67,6 +84,7 @@ EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor)
EnumGenerator::~EnumGenerator() {}
void EnumGenerator::Generate(io::Printer* printer) {
+ WriteEnumDocComment(printer, descriptor_);
if (HasDescriptorMethods(descriptor_)) {
printer->Print(
"public enum $classname$\n"
@@ -85,6 +103,10 @@ void EnumGenerator::Generate(io::Printer* printer) {
vars["name"] = canonical_values_[i]->name();
vars["index"] = SimpleItoa(canonical_values_[i]->index());
vars["number"] = SimpleItoa(canonical_values_[i]->number());
+ WriteEnumValueDocComment(printer, canonical_values_[i]);
+ if (canonical_values_[i]->options().deprecated()) {
+ printer->Print("@java.lang.Deprecated\n");
+ }
printer->Print(vars,
"$name$($index$, $number$),\n");
}
@@ -100,10 +122,21 @@ void EnumGenerator::Generate(io::Printer* printer) {
vars["classname"] = descriptor_->name();
vars["name"] = aliases_[i].value->name();
vars["canonical_name"] = aliases_[i].canonical_value->name();
+ WriteEnumValueDocComment(printer, aliases_[i].value);
printer->Print(vars,
"public static final $classname$ $name$ = $canonical_name$;\n");
}
+ for (int i = 0; i < descriptor_->value_count(); i++) {
+ map<string, string> vars;
+ vars["name"] = descriptor_->value(i)->name();
+ vars["number"] = SimpleItoa(descriptor_->value(i)->number());
+ WriteEnumValueDocComment(printer, descriptor_->value(i));
+ printer->Print(vars,
+ "public static final int $name$_VALUE = $number$;\n");
+ }
+ printer->Print("\n");
+
// -----------------------------------------------------------------
printer->Print(
@@ -138,7 +171,7 @@ void EnumGenerator::Generate(io::Printer* printer) {
" internalValueMap =\n"
" new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n"
" public $classname$ findValueByNumber(int number) {\n"
- " return $classname$.valueOf(number)\n;"
+ " return $classname$.valueOf(number);\n"
" }\n"
" };\n"
"\n",
@@ -164,32 +197,87 @@ void EnumGenerator::Generate(io::Printer* printer) {
// at module init time because it wouldn't work with descriptor.proto, but
// we can cache the value the first time getDescriptor() is called.
if (descriptor_->containing_type() == NULL) {
- printer->Print(
- " return $file$.getDescriptor().getEnumTypes().get($index$);\n",
- "file", ClassName(descriptor_->file()),
- "index", SimpleItoa(descriptor_->index()));
+ if (!MultipleJavaFiles(descriptor_->file(), immutable_api_)) {
+ printer->Print(
+ " return $file$.getDescriptor().getEnumTypes().get($index$);\n",
+ "file", name_resolver_->GetClassName(descriptor_->file(),
+ immutable_api_),
+ "index", SimpleItoa(descriptor_->index()));
+ } else {
+ printer->Indent();
+ if (EnumHasCustomOptions(descriptor_)) {
+ // We need to load the immutable classes in order to parse custom
+ // options. However, since file level enums (no outer class) are
+ // shared by immutable code and mutable code, the immutable classes
+ // may not exist. So we try to use Java reflection to retrieve the
+ // descriptor from immutable classes.
+ printer->Print(
+ "try {\n"
+ " java.lang.Class immutableFileClass =\n"
+ " java.lang.Class.forName(\"$immutable_file_class_name$\");\n"
+ " @java.lang.SuppressWarnings(\"unchecked\")\n"
+ " java.lang.reflect.Method m =\n"
+ " immutableFileClass.getMethod(\"getDescriptor\");\n"
+ " com.google.protobuf.Descriptors.FileDescriptor file =\n"
+ " (com.google.protobuf.Descriptors.FileDescriptor)\n"
+ " m.invoke(immutableFileClass);\n"
+ " return file.getEnumTypes().get($index$);\n"
+ "} catch (Exception e) {\n"
+ // Immutable classes cannot be found. Proceed as if custom options
+ // don't exist.
+ "}\n",
+ "immutable_file_class_name",
+ name_resolver_->GetImmutableClassName(descriptor_->file()),
+ "index", SimpleItoa(descriptor_->index()));
+ }
+ printer->Print(
+ "return $immutable_package$.$descriptor_class$.getDescriptor()\n"
+ " .getEnumTypes().get($index$);\n",
+ "immutable_package", FileJavaPackage(descriptor_->file(), true),
+ "descriptor_class",
+ name_resolver_->GetDescriptorClassName(descriptor_->file()),
+ "index", SimpleItoa(descriptor_->index()));
+ printer->Outdent();
+ }
} else {
printer->Print(
- " return $parent$.getDescriptor().getEnumTypes().get($index$);\n",
- "parent", ClassName(descriptor_->containing_type()),
- "index", SimpleItoa(descriptor_->index()));
+ " return $parent$.$descriptor$.getEnumTypes().get($index$);\n",
+ "parent", name_resolver_->GetClassName(descriptor_->containing_type(),
+ immutable_api_),
+ "descriptor", descriptor_->containing_type()->options()
+ .no_standard_descriptor_accessor()
+ ? "getDefaultInstance().getDescriptorForType()"
+ : "getDescriptor()",
+ "index", SimpleItoa(descriptor_->index()));
}
printer->Print(
"}\n"
"\n"
- "private static final $classname$[] VALUES = {\n"
- " ",
+ "private static final $classname$[] VALUES = ",
"classname", descriptor_->name());
- for (int i = 0; i < descriptor_->value_count(); i++) {
- printer->Print("$name$, ",
- "name", descriptor_->value(i)->name());
+ if (CanUseEnumValues()) {
+ // If the constants we are going to output are exactly the ones we
+ // have declared in the Java enum in the same order, then we can use
+ // the values() method that the Java compiler automatically generates
+ // for every enum.
+ printer->Print("values();\n");
+ } else {
+ printer->Print(
+ "{\n"
+ " ");
+ for (int i = 0; i < descriptor_->value_count(); i++) {
+ printer->Print("$name$, ",
+ "name", descriptor_->value(i)->name());
+ }
+ printer->Print(
+ "\n"
+ "};\n");
}
printer->Print(
"\n"
- "};\n"
"public static $classname$ valueOf(\n"
" com.google.protobuf.Descriptors.EnumValueDescriptor desc) {\n"
" if (desc.getType() != getDescriptor()) {\n"
@@ -197,31 +285,26 @@ void EnumGenerator::Generate(io::Printer* printer) {
" \"EnumValueDescriptor is not for this type.\");\n"
" }\n"
" return VALUES[desc.getIndex()];\n"
- "}\n",
+ "}\n"
+ "\n",
"classname", descriptor_->name());
+
+ // index is only used for reflection; lite implementation does not need it
+ printer->Print("private final int index;\n");
}
// -----------------------------------------------------------------
printer->Print(
- "private final int index;\n"
- "private final int value;\n"
- "private $classname$(int index, int value) {\n"
- " this.index = index;\n"
- " this.value = value;\n"
- "}\n",
+ "private final int value;\n\n"
+ "private $classname$(int index, int value) {\n",
"classname", descriptor_->name());
-
if (HasDescriptorMethods(descriptor_)) {
- // Force the static initialization code for the file to run, since it may
- // initialize static variables declared in this class.
- printer->Print(
- "\n"
- "static {\n"
- " $file$.getDescriptor();\n"
- "}\n",
- "file", ClassName(descriptor_->file()));
+ printer->Print(" this.index = index;\n");
}
+ printer->Print(
+ " this.value = value;\n"
+ "}\n");
printer->Print(
"\n"
@@ -232,6 +315,18 @@ void EnumGenerator::Generate(io::Printer* printer) {
printer->Print("}\n\n");
}
+bool EnumGenerator::CanUseEnumValues() {
+ if (canonical_values_.size() != descriptor_->value_count()) {
+ return false;
+ }
+ for (int i = 0; i < descriptor_->value_count(); i++) {
+ if (descriptor_->value(i)->name() != canonical_values_[i]->name()) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace java
} // namespace compiler
} // namespace protobuf
diff --git a/src/google/protobuf/compiler/java/java_enum.h b/src/google/protobuf/compiler/java/java_enum.h
index 05ece1f..a0d91f5 100644
--- a/src/google/protobuf/compiler/java/java_enum.h
+++ b/src/google/protobuf/compiler/java/java_enum.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -41,6 +41,12 @@
namespace google {
namespace protobuf {
+ namespace compiler {
+ namespace java {
+ class Context; // context.h
+ class ClassNameResolver; // name_resolver.h
+ }
+ }
namespace io {
class Printer; // printer.h
}
@@ -52,7 +58,9 @@ namespace java {
class EnumGenerator {
public:
- explicit EnumGenerator(const EnumDescriptor* descriptor);
+ explicit EnumGenerator(const EnumDescriptor* descriptor,
+ bool immutable_api,
+ Context* context);
~EnumGenerator();
void Generate(io::Printer* printer);
@@ -73,6 +81,13 @@ class EnumGenerator {
};
vector<Alias> aliases_;
+ bool immutable_api_;
+
+ Context* context_;
+ ClassNameResolver* name_resolver_;
+
+ bool CanUseEnumValues();
+
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator);
};
diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc
index af6b1cd..1f0c4af 100644
--- a/src/google/protobuf/compiler/java/java_enum_field.cc
+++ b/src/google/protobuf/compiler/java/java_enum_field.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -35,9 +35,12 @@
#include <map>
#include <string>
-#include <google/protobuf/compiler/java/java_enum_field.h>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/compiler/java/java_context.h>
+#include <google/protobuf/compiler/java/java_doc_comment.h>
+#include <google/protobuf/compiler/java/java_enum_field.h>
#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/compiler/java/java_name_resolver.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
@@ -49,145 +52,456 @@ namespace java {
namespace {
-// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of
-// repeat code between this and the other field types.
void SetEnumVariables(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ const FieldGeneratorInfo* info,
+ ClassNameResolver* name_resolver,
map<string, string>* variables) {
- (*variables)["name"] =
- UnderscoresToCamelCase(descriptor);
- (*variables)["capitalized_name"] =
- UnderscoresToCapitalizedCamelCase(descriptor);
- (*variables)["number"] = SimpleItoa(descriptor->number());
- (*variables)["type"] = ClassName(descriptor->enum_type());
- (*variables)["default"] = DefaultValue(descriptor);
+ SetCommonFieldVariables(descriptor, info, variables);
+
+ (*variables)["type"] =
+ name_resolver->GetImmutableClassName(descriptor->enum_type());
+ (*variables)["mutable_type"] =
+ name_resolver->GetMutableClassName(descriptor->enum_type());
+ (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver);
(*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor));
(*variables)["tag_size"] = SimpleItoa(
internal::WireFormat::TagSize(descriptor->number(), GetType(descriptor)));
+ // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported
+ // by the proto compiler
+ (*variables)["deprecation"] = descriptor->options().deprecated()
+ ? "@java.lang.Deprecated " : "";
+ (*variables)["on_changed"] =
+ HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : "";
+
+ if (SupportFieldPresence(descriptor->file())) {
+ // For singular messages and builders, one bit is used for the hasField bit.
+ (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex);
+ (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex);
+
+ // Note that these have a trailing ";".
+ (*variables)["set_has_field_bit_message"] =
+ GenerateSetBit(messageBitIndex) + ";";
+ (*variables)["set_has_field_bit_builder"] =
+ GenerateSetBit(builderBitIndex) + ";";
+ (*variables)["clear_has_field_bit_builder"] =
+ GenerateClearBit(builderBitIndex) + ";";
+
+ (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex);
+ } else {
+ (*variables)["set_has_field_bit_message"] = "";
+ (*variables)["set_has_field_bit_builder"] = "";
+ (*variables)["clear_has_field_bit_builder"] = "";
+
+ (*variables)["is_field_present_message"] =
+ (*variables)["name"] + "_ != " + (*variables)["default"];
+ }
+
+ // For repated builders, one bit is used for whether the array is immutable.
+ (*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex);
+ (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex);
+ (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex);
+
+ // For repeated fields, one bit is used for whether the array is immutable
+ // in the parsing constructor.
+ (*variables)["get_mutable_bit_parser"] =
+ GenerateGetBitMutableLocal(builderBitIndex);
+ (*variables)["set_mutable_bit_parser"] =
+ GenerateSetBitMutableLocal(builderBitIndex);
+
+ (*variables)["get_has_field_bit_from_local"] =
+ GenerateGetBitFromLocal(builderBitIndex);
+ (*variables)["set_has_field_bit_to_local"] =
+ GenerateSetBitToLocal(messageBitIndex);
}
} // namespace
// ===================================================================
-EnumFieldGenerator::
-EnumFieldGenerator(const FieldDescriptor* descriptor)
- : descriptor_(descriptor) {
- SetEnumVariables(descriptor, &variables_);
+ImmutableEnumFieldGenerator::
+ImmutableEnumFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : descriptor_(descriptor), messageBitIndex_(messageBitIndex),
+ builderBitIndex_(builderBitIndex),
+ name_resolver_(context->GetNameResolver()) {
+ SetEnumVariables(descriptor, messageBitIndex, builderBitIndex,
+ context->GetFieldGeneratorInfo(descriptor),
+ name_resolver_, &variables_);
+}
+
+ImmutableEnumFieldGenerator::~ImmutableEnumFieldGenerator() {}
+
+int ImmutableEnumFieldGenerator::GetNumBitsForMessage() const {
+ return 1;
}
-EnumFieldGenerator::~EnumFieldGenerator() {}
+int ImmutableEnumFieldGenerator::GetNumBitsForBuilder() const {
+ return 1;
+}
-void EnumFieldGenerator::
+void ImmutableEnumFieldGenerator::
+GenerateInterfaceMembers(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$boolean has$capitalized_name$();\n");
+ }
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$$type$ get$capitalized_name$();\n");
+}
+
+void ImmutableEnumFieldGenerator::
GenerateMembers(io::Printer* printer) const {
printer->Print(variables_,
- "private boolean has$capitalized_name$;\n"
- "private $type$ $name$_;\n"
- "public boolean has$capitalized_name$() { return has$capitalized_name$; }\n"
- "public $type$ get$capitalized_name$() { return $name$_; }\n");
+ "private $type$ $name$_;\n");
+ PrintExtraFieldInfo(variables_, printer);
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $get_has_field_bit_message$;\n"
+ "}\n");
+ }
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " return $name$_;\n"
+ "}\n");
}
-void EnumFieldGenerator::
+void ImmutableEnumFieldGenerator::
GenerateBuilderMembers(io::Printer* printer) const {
printer->Print(variables_,
- "public boolean has$capitalized_name$() {\n"
- " return result.has$capitalized_name$();\n"
- "}\n"
- "public $type$ get$capitalized_name$() {\n"
- " return result.get$capitalized_name$();\n"
- "}\n"
- "public Builder set$capitalized_name$($type$ value) {\n"
+ "private $type$ $name$_ = $default$;\n");
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $get_has_field_bit_builder$;\n"
+ "}\n");
+ }
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " return $name$_;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder set$capitalized_name$($type$ value) {\n"
" if (value == null) {\n"
" throw new NullPointerException();\n"
" }\n"
- " result.has$capitalized_name$ = true;\n"
- " result.$name$_ = value;\n"
+ " $set_has_field_bit_builder$\n"
+ " $name$_ = value;\n"
+ " $on_changed$\n"
" return this;\n"
- "}\n"
- "public Builder clear$capitalized_name$() {\n"
- " result.has$capitalized_name$ = false;\n"
- " result.$name$_ = $default$;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder clear$capitalized_name$() {\n"
+ " $clear_has_field_bit_builder$\n"
+ " $name$_ = $default$;\n"
+ " $on_changed$\n"
" return this;\n"
"}\n");
}
-void EnumFieldGenerator::
+void ImmutableEnumFieldGenerator::
+GenerateFieldBuilderInitializationCode(io::Printer* printer) const {
+ // noop for enums
+}
+
+void ImmutableEnumFieldGenerator::
GenerateInitializationCode(io::Printer* printer) const {
printer->Print(variables_, "$name$_ = $default$;\n");
}
-void EnumFieldGenerator::
+void ImmutableEnumFieldGenerator::
+GenerateBuilderClearCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "$name$_ = $default$;\n"
+ "$clear_has_field_bit_builder$\n");
+}
+
+void ImmutableEnumFieldGenerator::
GenerateMergingCode(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ printer->Print(variables_,
+ "if (other.has$capitalized_name$()) {\n"
+ " set$capitalized_name$(other.get$capitalized_name$());\n"
+ "}\n");
+ } else {
+ printer->Print(variables_,
+ "if (other.get$capitalized_name$() != $default$) {\n"
+ " set$capitalized_name$(other.get$capitalized_name$());\n"
+ "}\n");
+ }
+}
+
+void ImmutableEnumFieldGenerator::
+GenerateBuildingCode(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ printer->Print(variables_,
+ "if ($get_has_field_bit_from_local$) {\n"
+ " $set_has_field_bit_to_local$;\n"
+ "}\n");
+ }
+ printer->Print(variables_,
+ "result.$name$_ = $name$_;\n");
+}
+
+void ImmutableEnumFieldGenerator::
+GenerateParsingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "int rawValue = input.readEnum();\n"
+ "$type$ value = $type$.valueOf(rawValue);\n"
+ "if (value == null) {\n");
+ if (UseUnknownFieldSet(descriptor_->containing_type())) {
+ printer->Print(variables_,
+ " unknownFields.mergeVarintField($number$, rawValue);\n");
+ } else {
+ printer->Print(variables_,
+ " unknownFieldsCodedOutput.writeRawVarint32(tag);\n"
+ " unknownFieldsCodedOutput.writeRawVarint32(rawValue);\n");
+ }
+ printer->Print(variables_,
+ "} else {\n"
+ " $set_has_field_bit_message$\n"
+ " $name$_ = value;\n"
+ "}\n");
+}
+
+void ImmutableEnumFieldGenerator::
+GenerateParsingDoneCode(io::Printer* printer) const {
+ // noop for enums
+}
+
+void ImmutableEnumFieldGenerator::
+GenerateSerializationCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($is_field_present_message$) {\n"
+ " output.writeEnum($number$, $name$_.getNumber());\n"
+ "}\n");
+}
+
+void ImmutableEnumFieldGenerator::
+GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print(variables_,
- "if (other.has$capitalized_name$()) {\n"
- " set$capitalized_name$(other.get$capitalized_name$());\n"
+ "if ($is_field_present_message$) {\n"
+ " size += com.google.protobuf.CodedOutputStream\n"
+ " .computeEnumSize($number$, $name$_.getNumber());\n"
"}\n");
}
-void EnumFieldGenerator::
+void ImmutableEnumFieldGenerator::
+GenerateEqualsCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "result = result &&\n"
+ " (get$capitalized_name$() == other.get$capitalized_name$());\n");
+}
+
+void ImmutableEnumFieldGenerator::
+GenerateHashCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "hash = (37 * hash) + $constant_name$;\n"
+ "hash = (53 * hash) + com.google.protobuf.Internal.hashEnum(\n"
+ " get$capitalized_name$());\n");
+}
+
+string ImmutableEnumFieldGenerator::GetBoxedType() const {
+ return name_resolver_->GetImmutableClassName(descriptor_->enum_type());
+}
+
+// ===================================================================
+
+ImmutableEnumOneofFieldGenerator::
+ImmutableEnumOneofFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : ImmutableEnumFieldGenerator(
+ descriptor, messageBitIndex, builderBitIndex, context) {
+ const OneofGeneratorInfo* info =
+ context->GetOneofGeneratorInfo(descriptor->containing_oneof());
+ SetCommonOneofVariables(descriptor, info, &variables_);
+}
+
+ImmutableEnumOneofFieldGenerator::
+~ImmutableEnumOneofFieldGenerator() {}
+
+void ImmutableEnumOneofFieldGenerator::
+GenerateMembers(io::Printer* printer) const {
+ PrintExtraFieldInfo(variables_, printer);
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $has_oneof_case_message$;\n"
+ "}\n");
+ }
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " if ($has_oneof_case_message$) {\n"
+ " return ($type$) $oneof_name$_;\n"
+ " }\n"
+ " return $default$;\n"
+ "}\n");
+}
+
+void ImmutableEnumOneofFieldGenerator::
+GenerateBuilderMembers(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $has_oneof_case_message$;\n"
+ "}\n");
+ }
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " if ($has_oneof_case_message$) {\n"
+ " return ($type$) $oneof_name$_;\n"
+ " }\n"
+ " return $default$;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder set$capitalized_name$($type$ value) {\n"
+ " if (value == null) {\n"
+ " throw new NullPointerException();\n"
+ " }\n"
+ " $set_oneof_case_message$;\n"
+ " $oneof_name$_ = value;\n"
+ " $on_changed$\n"
+ " return this;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder clear$capitalized_name$() {\n"
+ " if ($has_oneof_case_message$) {\n"
+ " $clear_oneof_case_message$;\n"
+ " $oneof_name$_ = null;\n"
+ " $on_changed$\n"
+ " }\n"
+ " return this;\n"
+ "}\n");
+}
+
+void ImmutableEnumOneofFieldGenerator::
GenerateBuildingCode(io::Printer* printer) const {
- // Nothing to do here for enum types.
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n"
+ " result.$oneof_name$_ = $oneof_name$_;\n"
+ "}\n");
}
-void EnumFieldGenerator::
+void ImmutableEnumOneofFieldGenerator::
+GenerateMergingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "set$capitalized_name$(other.get$capitalized_name$());\n");
+}
+
+void ImmutableEnumOneofFieldGenerator::
GenerateParsingCode(io::Printer* printer) const {
printer->Print(variables_,
"int rawValue = input.readEnum();\n"
- "$type$ value = $type$.valueOf(rawValue);\n");
- if (HasUnknownFields(descriptor_->containing_type())) {
+ "$type$ value = $type$.valueOf(rawValue);\n"
+ "if (value == null) {\n");
+ if (UseUnknownFieldSet(descriptor_->containing_type())) {
printer->Print(variables_,
- "if (value == null) {\n"
- " unknownFields.mergeVarintField($number$, rawValue);\n"
- "} else {\n");
+ " unknownFields.mergeVarintField($number$, rawValue);\n");
} else {
printer->Print(variables_,
- "if (value != null) {\n");
+ " unknownFieldsCodedOutput.writeRawVarint32(tag);\n"
+ " unknownFieldsCodedOutput.writeRawVarint32(rawValue);\n");
}
printer->Print(variables_,
- " set$capitalized_name$(value);\n"
+ "} else {\n"
+ " $set_oneof_case_message$;\n"
+ " $oneof_name$_ = value;\n"
"}\n");
}
-void EnumFieldGenerator::
+void ImmutableEnumOneofFieldGenerator::
GenerateSerializationCode(io::Printer* printer) const {
printer->Print(variables_,
- "if (has$capitalized_name$()) {\n"
- " output.writeEnum($number$, get$capitalized_name$().getNumber());\n"
+ "if ($has_oneof_case_message$) {\n"
+ " output.writeEnum($number$, (($type$) $oneof_name$_).getNumber());\n"
"}\n");
}
-void EnumFieldGenerator::
+void ImmutableEnumOneofFieldGenerator::
GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print(variables_,
- "if (has$capitalized_name$()) {\n"
+ "if ($has_oneof_case_message$) {\n"
" size += com.google.protobuf.CodedOutputStream\n"
- " .computeEnumSize($number$, get$capitalized_name$().getNumber());\n"
+ " .computeEnumSize($number$, (($type$) $oneof_name$_).getNumber());\n"
"}\n");
}
-string EnumFieldGenerator::GetBoxedType() const {
- return ClassName(descriptor_->enum_type());
+// ===================================================================
+
+RepeatedImmutableEnumFieldGenerator::
+RepeatedImmutableEnumFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : descriptor_(descriptor), messageBitIndex_(messageBitIndex),
+ builderBitIndex_(builderBitIndex), context_(context),
+ name_resolver_(context->GetNameResolver()) {
+ SetEnumVariables(descriptor, messageBitIndex, builderBitIndex,
+ context->GetFieldGeneratorInfo(descriptor),
+ name_resolver_, &variables_);
}
-// ===================================================================
+RepeatedImmutableEnumFieldGenerator::~RepeatedImmutableEnumFieldGenerator() {}
-RepeatedEnumFieldGenerator::
-RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor)
- : descriptor_(descriptor) {
- SetEnumVariables(descriptor, &variables_);
+int RepeatedImmutableEnumFieldGenerator::GetNumBitsForMessage() const {
+ return 0;
}
-RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {}
+int RepeatedImmutableEnumFieldGenerator::GetNumBitsForBuilder() const {
+ return 1;
+}
-void RepeatedEnumFieldGenerator::
+void RepeatedImmutableEnumFieldGenerator::
+GenerateInterfaceMembers(io::Printer* printer) const {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$java.util.List<$type$> get$capitalized_name$List();\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$int get$capitalized_name$Count();\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$$type$ get$capitalized_name$(int index);\n");
+}
+
+void RepeatedImmutableEnumFieldGenerator::
GenerateMembers(io::Printer* printer) const {
printer->Print(variables_,
- "private java.util.List<$type$> $name$_ =\n"
- " java.util.Collections.emptyList();\n"
- "public java.util.List<$type$> get$capitalized_name$List() {\n"
+ "private java.util.List<$type$> $name$_;\n");
+ PrintExtraFieldInfo(variables_, printer);
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n"
" return $name$_;\n" // note: unmodifiable list
- "}\n"
- "public int get$capitalized_name$Count() { return $name$_.size(); }\n"
- "public $type$ get$capitalized_name$(int index) {\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public int get$capitalized_name$Count() {\n"
+ " return $name$_.size();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$(int index) {\n"
" return $name$_.get(index);\n"
"}\n");
@@ -198,99 +512,165 @@ GenerateMembers(io::Printer* printer) const {
}
}
-void RepeatedEnumFieldGenerator::
+void RepeatedImmutableEnumFieldGenerator::
GenerateBuilderMembers(io::Printer* printer) const {
printer->Print(variables_,
+ // One field is the list and the other field keeps track of whether the
+ // list is immutable. If it's immutable, the invariant is that it must
+ // either an instance of Collections.emptyList() or it's an ArrayList
+ // wrapped in a Collections.unmodifiableList() wrapper and nobody else has
+ // a refererence to the underlying ArrayList. This invariant allows us to
+ // share instances of lists between protocol buffers avoiding expensive
+ // memory allocations. Note, immutable is a strong guarantee here -- not
+ // just that the list cannot be modified via the reference but that the
+ // list can never be modified.
+ "private java.util.List<$type$> $name$_ =\n"
+ " java.util.Collections.emptyList();\n"
+
+ "private void ensure$capitalized_name$IsMutable() {\n"
+ " if (!$get_mutable_bit_builder$) {\n"
+ " $name$_ = new java.util.ArrayList<$type$>($name$_);\n"
+ " $set_mutable_bit_builder$;\n"
+ " }\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
// Note: We return an unmodifiable list because otherwise the caller
// could hold on to the returned list and modify it after the message
// has been built, thus mutating the message which is supposed to be
// immutable.
- "public java.util.List<$type$> get$capitalized_name$List() {\n"
- " return java.util.Collections.unmodifiableList(result.$name$_);\n"
- "}\n"
- "public int get$capitalized_name$Count() {\n"
- " return result.get$capitalized_name$Count();\n"
- "}\n"
- "public $type$ get$capitalized_name$(int index) {\n"
- " return result.get$capitalized_name$(index);\n"
- "}\n"
- "public Builder set$capitalized_name$(int index, $type$ value) {\n"
+ "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n"
+ " return java.util.Collections.unmodifiableList($name$_);\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public int get$capitalized_name$Count() {\n"
+ " return $name$_.size();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$(int index) {\n"
+ " return $name$_.get(index);\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " int index, $type$ value) {\n"
" if (value == null) {\n"
" throw new NullPointerException();\n"
" }\n"
- " result.$name$_.set(index, value);\n"
+ " ensure$capitalized_name$IsMutable();\n"
+ " $name$_.set(index, value);\n"
+ " $on_changed$\n"
" return this;\n"
- "}\n"
- "public Builder add$capitalized_name$($type$ value) {\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder add$capitalized_name$($type$ value) {\n"
" if (value == null) {\n"
" throw new NullPointerException();\n"
" }\n"
- " if (result.$name$_.isEmpty()) {\n"
- " result.$name$_ = new java.util.ArrayList<$type$>();\n"
- " }\n"
- " result.$name$_.add(value);\n"
+ " ensure$capitalized_name$IsMutable();\n"
+ " $name$_.add(value);\n"
+ " $on_changed$\n"
" return this;\n"
- "}\n"
- "public Builder addAll$capitalized_name$(\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder addAll$capitalized_name$(\n"
" java.lang.Iterable<? extends $type$> values) {\n"
- " if (result.$name$_.isEmpty()) {\n"
- " result.$name$_ = new java.util.ArrayList<$type$>();\n"
- " }\n"
- " super.addAll(values, result.$name$_);\n"
+ " ensure$capitalized_name$IsMutable();\n"
+ " com.google.protobuf.AbstractMessageLite.Builder.addAll(\n"
+ " values, $name$_);\n"
+ " $on_changed$\n"
" return this;\n"
- "}\n"
- "public Builder clear$capitalized_name$() {\n"
- " result.$name$_ = java.util.Collections.emptyList();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder clear$capitalized_name$() {\n"
+ " $name$_ = java.util.Collections.emptyList();\n"
+ " $clear_mutable_bit_builder$;\n"
+ " $on_changed$\n"
" return this;\n"
"}\n");
}
-void RepeatedEnumFieldGenerator::
+void RepeatedImmutableEnumFieldGenerator::
+GenerateFieldBuilderInitializationCode(io::Printer* printer) const {
+ // noop for enums
+}
+
+void RepeatedImmutableEnumFieldGenerator::
GenerateInitializationCode(io::Printer* printer) const {
- // Initialized inline.
+ printer->Print(variables_, "$name$_ = java.util.Collections.emptyList();\n");
}
-void RepeatedEnumFieldGenerator::
+void RepeatedImmutableEnumFieldGenerator::
+GenerateBuilderClearCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "$name$_ = java.util.Collections.emptyList();\n"
+ "$clear_mutable_bit_builder$;\n");
+}
+
+void RepeatedImmutableEnumFieldGenerator::
GenerateMergingCode(io::Printer* printer) const {
+ // The code below does two optimizations:
+ // 1. If the other list is empty, there's nothing to do. This ensures we
+ // don't allocate a new array if we already have an immutable one.
+ // 2. If the other list is non-empty and our current list is empty, we can
+ // reuse the other list which is guaranteed to be immutable.
printer->Print(variables_,
"if (!other.$name$_.isEmpty()) {\n"
- " if (result.$name$_.isEmpty()) {\n"
- " result.$name$_ = new java.util.ArrayList<$type$>();\n"
+ " if ($name$_.isEmpty()) {\n"
+ " $name$_ = other.$name$_;\n"
+ " $clear_mutable_bit_builder$;\n"
+ " } else {\n"
+ " ensure$capitalized_name$IsMutable();\n"
+ " $name$_.addAll(other.$name$_);\n"
" }\n"
- " result.$name$_.addAll(other.$name$_);\n"
+ " $on_changed$\n"
"}\n");
}
-void RepeatedEnumFieldGenerator::
+void RepeatedImmutableEnumFieldGenerator::
GenerateBuildingCode(io::Printer* printer) const {
+ // The code below ensures that the result has an immutable list. If our
+ // list is immutable, we can just reuse it. If not, we make it immutable.
printer->Print(variables_,
- "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n"
- " result.$name$_ =\n"
- " java.util.Collections.unmodifiableList(result.$name$_);\n"
- "}\n");
+ "if ($get_mutable_bit_builder$) {\n"
+ " $name$_ = java.util.Collections.unmodifiableList($name$_);\n"
+ " $clear_mutable_bit_builder$;\n"
+ "}\n"
+ "result.$name$_ = $name$_;\n");
}
-void RepeatedEnumFieldGenerator::
+void RepeatedImmutableEnumFieldGenerator::
GenerateParsingCode(io::Printer* printer) const {
// Read and store the enum
printer->Print(variables_,
"int rawValue = input.readEnum();\n"
- "$type$ value = $type$.valueOf(rawValue);\n");
- if (HasUnknownFields(descriptor_->containing_type())) {
+ "$type$ value = $type$.valueOf(rawValue);\n"
+ "if (value == null) {\n");
+ if (UseUnknownFieldSet(descriptor_->containing_type())) {
printer->Print(variables_,
- "if (value == null) {\n"
- " unknownFields.mergeVarintField($number$, rawValue);\n"
- "} else {\n");
+ " unknownFields.mergeVarintField($number$, rawValue);\n");
} else {
printer->Print(variables_,
- "if (value != null) {\n");
+ " unknownFieldsCodedOutput.writeRawVarint32(tag);\n"
+ " unknownFieldsCodedOutput.writeRawVarint32(rawValue);\n");
}
printer->Print(variables_,
- " add$capitalized_name$(value);\n"
+ " } else {\n"
+ " if (!$get_mutable_bit_parser$) {\n"
+ " $name$_ = new java.util.ArrayList<$type$>();\n"
+ " $set_mutable_bit_parser$;\n"
+ " }\n"
+ " $name$_.add(value);\n"
"}\n");
}
-void RepeatedEnumFieldGenerator::
+void RepeatedImmutableEnumFieldGenerator::
GenerateParsingCodeFromPacked(io::Printer* printer) const {
// Wrap GenerateParsingCode's contents with a while loop.
@@ -308,7 +688,15 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const {
"input.popLimit(oldLimit);\n");
}
-void RepeatedEnumFieldGenerator::
+void RepeatedImmutableEnumFieldGenerator::
+GenerateParsingDoneCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($get_mutable_bit_parser$) {\n"
+ " $name$_ = java.util.Collections.unmodifiableList($name$_);\n"
+ "}\n");
+}
+
+void RepeatedImmutableEnumFieldGenerator::
GenerateSerializationCode(io::Printer* printer) const {
if (descriptor_->options().packed()) {
printer->Print(variables_,
@@ -316,18 +704,18 @@ GenerateSerializationCode(io::Printer* printer) const {
" output.writeRawVarint32($tag$);\n"
" output.writeRawVarint32($name$MemoizedSerializedSize);\n"
"}\n"
- "for ($type$ element : get$capitalized_name$List()) {\n"
- " output.writeEnumNoTag(element.getNumber());\n"
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
+ " output.writeEnumNoTag($name$_.get(i).getNumber());\n"
"}\n");
} else {
printer->Print(variables_,
- "for ($type$ element : get$capitalized_name$List()) {\n"
- " output.writeEnum($number$, element.getNumber());\n"
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
+ " output.writeEnum($number$, $name$_.get(i).getNumber());\n"
"}\n");
}
}
-void RepeatedEnumFieldGenerator::
+void RepeatedImmutableEnumFieldGenerator::
GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print(variables_,
"{\n"
@@ -335,9 +723,9 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Indent();
printer->Print(variables_,
- "for ($type$ element : get$capitalized_name$List()) {\n"
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
" dataSize += com.google.protobuf.CodedOutputStream\n"
- " .computeEnumSizeNoTag(element.getNumber());\n"
+ " .computeEnumSizeNoTag($name$_.get(i).getNumber());\n"
"}\n");
printer->Print(
"size += dataSize;\n");
@@ -350,7 +738,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}");
} else {
printer->Print(variables_,
- "size += $tag_size$ * get$capitalized_name$List().size();\n");
+ "size += $tag_size$ * $name$_.size();\n");
}
// cache the data size for packed fields.
@@ -363,8 +751,25 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print("}\n");
}
-string RepeatedEnumFieldGenerator::GetBoxedType() const {
- return ClassName(descriptor_->enum_type());
+void RepeatedImmutableEnumFieldGenerator::
+GenerateEqualsCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "result = result && get$capitalized_name$List()\n"
+ " .equals(other.get$capitalized_name$List());\n");
+}
+
+void RepeatedImmutableEnumFieldGenerator::
+GenerateHashCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (get$capitalized_name$Count() > 0) {\n"
+ " hash = (37 * hash) + $constant_name$;\n"
+ " hash = (53 * hash) + com.google.protobuf.Internal.hashEnumList(\n"
+ " get$capitalized_name$List());\n"
+ "}\n");
+}
+
+string RepeatedImmutableEnumFieldGenerator::GetBoxedType() const {
+ return name_resolver_->GetImmutableClassName(descriptor_->enum_type());
}
} // namespace java
diff --git a/src/google/protobuf/compiler/java/java_enum_field.h b/src/google/protobuf/compiler/java/java_enum_field.h
index c54a0fa..6a9535a 100644
--- a/src/google/protobuf/compiler/java/java_enum_field.h
+++ b/src/google/protobuf/compiler/java/java_enum_field.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -41,56 +41,113 @@
namespace google {
namespace protobuf {
+ namespace compiler {
+ namespace java {
+ class Context; // context.h
+ class ClassNameResolver; // name_resolver.h
+ }
+ }
+}
+
+namespace protobuf {
namespace compiler {
namespace java {
-class EnumFieldGenerator : public FieldGenerator {
+class ImmutableEnumFieldGenerator : public ImmutableFieldGenerator {
public:
- explicit EnumFieldGenerator(const FieldDescriptor* descriptor);
- ~EnumFieldGenerator();
-
- // implements FieldGenerator ---------------------------------------
+ explicit ImmutableEnumFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~ImmutableEnumFieldGenerator();
+
+ // implements ImmutableFieldGenerator ---------------------------------------
+ int GetNumBitsForMessage() const;
+ int GetNumBitsForBuilder() const;
+ void GenerateInterfaceMembers(io::Printer* printer) const;
void GenerateMembers(io::Printer* printer) const;
void GenerateBuilderMembers(io::Printer* printer) const;
void GenerateInitializationCode(io::Printer* printer) const;
+ void GenerateBuilderClearCode(io::Printer* printer) const;
void GenerateMergingCode(io::Printer* printer) const;
void GenerateBuildingCode(io::Printer* printer) const;
void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateParsingDoneCode(io::Printer* printer) const;
void GenerateSerializationCode(io::Printer* printer) const;
void GenerateSerializedSizeCode(io::Printer* printer) const;
+ void GenerateFieldBuilderInitializationCode(io::Printer* printer) const;
+ void GenerateEqualsCode(io::Printer* printer) const;
+ void GenerateHashCode(io::Printer* printer) const;
string GetBoxedType() const;
- private:
+ protected:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ const int messageBitIndex_;
+ const int builderBitIndex_;
+ Context* context_;
+ ClassNameResolver* name_resolver_;
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator);
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableEnumFieldGenerator);
};
-class RepeatedEnumFieldGenerator : public FieldGenerator {
+class ImmutableEnumOneofFieldGenerator : public ImmutableEnumFieldGenerator {
public:
- explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor);
- ~RepeatedEnumFieldGenerator();
+ ImmutableEnumOneofFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~ImmutableEnumOneofFieldGenerator();
- // implements FieldGenerator ---------------------------------------
+ void GenerateMembers(io::Printer* printer) const;
+ void GenerateBuilderMembers(io::Printer* printer) const;
+ void GenerateMergingCode(io::Printer* printer) const;
+ void GenerateBuildingCode(io::Printer* printer) const;
+ void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateSerializationCode(io::Printer* printer) const;
+ void GenerateSerializedSizeCode(io::Printer* printer) const;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableEnumOneofFieldGenerator);
+};
+
+class RepeatedImmutableEnumFieldGenerator : public ImmutableFieldGenerator {
+ public:
+ explicit RepeatedImmutableEnumFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~RepeatedImmutableEnumFieldGenerator();
+
+ // implements ImmutableFieldGenerator ---------------------------------------
+ int GetNumBitsForMessage() const;
+ int GetNumBitsForBuilder() const;
+ void GenerateInterfaceMembers(io::Printer* printer) const;
void GenerateMembers(io::Printer* printer) const;
void GenerateBuilderMembers(io::Printer* printer) const;
void GenerateInitializationCode(io::Printer* printer) const;
+ void GenerateBuilderClearCode(io::Printer* printer) const;
void GenerateMergingCode(io::Printer* printer) const;
void GenerateBuildingCode(io::Printer* printer) const;
void GenerateParsingCode(io::Printer* printer) const;
void GenerateParsingCodeFromPacked(io::Printer* printer) const;
+ void GenerateParsingDoneCode(io::Printer* printer) const;
void GenerateSerializationCode(io::Printer* printer) const;
void GenerateSerializedSizeCode(io::Printer* printer) const;
+ void GenerateFieldBuilderInitializationCode(io::Printer* printer) const;
+ void GenerateEqualsCode(io::Printer* printer) const;
+ void GenerateHashCode(io::Printer* printer) const;
string GetBoxedType() const;
private:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ const int messageBitIndex_;
+ const int builderBitIndex_;
+ Context* context_;
+ ClassNameResolver* name_resolver_;
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator);
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableEnumFieldGenerator);
};
} // namespace java
diff --git a/src/google/protobuf/compiler/java/java_extension.cc b/src/google/protobuf/compiler/java/java_extension.cc
index 903b0a9..27cf416 100644
--- a/src/google/protobuf/compiler/java/java_extension.cc
+++ b/src/google/protobuf/compiler/java/java_extension.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -33,162 +33,168 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/java/java_extension.h>
+
+#include <google/protobuf/compiler/java/java_context.h>
+#include <google/protobuf/compiler/java/java_doc_comment.h>
#include <google/protobuf/compiler/java/java_helpers.h>
-#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/compiler/java/java_name_resolver.h>
#include <google/protobuf/io/printer.h>
+#include <google/protobuf/stubs/strutil.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace java {
-namespace {
-
-const char* TypeName(FieldDescriptor::Type field_type) {
- switch (field_type) {
- case FieldDescriptor::TYPE_INT32 : return "INT32";
- case FieldDescriptor::TYPE_UINT32 : return "UINT32";
- case FieldDescriptor::TYPE_SINT32 : return "SINT32";
- case FieldDescriptor::TYPE_FIXED32 : return "FIXED32";
- case FieldDescriptor::TYPE_SFIXED32: return "SFIXED32";
- case FieldDescriptor::TYPE_INT64 : return "INT64";
- case FieldDescriptor::TYPE_UINT64 : return "UINT64";
- case FieldDescriptor::TYPE_SINT64 : return "SINT64";
- case FieldDescriptor::TYPE_FIXED64 : return "FIXED64";
- case FieldDescriptor::TYPE_SFIXED64: return "SFIXED64";
- case FieldDescriptor::TYPE_FLOAT : return "FLOAT";
- case FieldDescriptor::TYPE_DOUBLE : return "DOUBLE";
- case FieldDescriptor::TYPE_BOOL : return "BOOL";
- case FieldDescriptor::TYPE_STRING : return "STRING";
- case FieldDescriptor::TYPE_BYTES : return "BYTES";
- case FieldDescriptor::TYPE_ENUM : return "ENUM";
- case FieldDescriptor::TYPE_GROUP : return "GROUP";
- case FieldDescriptor::TYPE_MESSAGE : return "MESSAGE";
-
- // No default because we want the compiler to complain if any new
- // types are added.
- }
-
- GOOGLE_LOG(FATAL) << "Can't get here.";
- return NULL;
-}
-
-}
-
-ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor)
- : descriptor_(descriptor) {
+ImmutableExtensionGenerator::ImmutableExtensionGenerator(
+ const FieldDescriptor* descriptor, Context* context)
+ : descriptor_(descriptor), context_(context),
+ name_resolver_(context->GetNameResolver()) {
if (descriptor_->extension_scope() != NULL) {
- scope_ = ClassName(descriptor_->extension_scope());
+ scope_ = name_resolver_->GetImmutableClassName(
+ descriptor_->extension_scope());
} else {
- scope_ = ClassName(descriptor_->file());
+ scope_ = name_resolver_->GetImmutableClassName(descriptor_->file());
}
}
-ExtensionGenerator::~ExtensionGenerator() {}
-
-void ExtensionGenerator::Generate(io::Printer* printer) {
- map<string, string> vars;
- vars["name"] = UnderscoresToCamelCase(descriptor_);
- vars["containing_type"] = ClassName(descriptor_->containing_type());
- vars["number"] = SimpleItoa(descriptor_->number());
- vars["constant_name"] = FieldConstantName(descriptor_);
- vars["lite"] = HasDescriptorMethods(descriptor_->file()) ? "" : "Lite";
+ImmutableExtensionGenerator::~ImmutableExtensionGenerator() {}
+
+// Initializes the vars referenced in the generated code templates.
+void ExtensionGenerator::InitTemplateVars(const FieldDescriptor* descriptor,
+ const string& scope,
+ bool immutable,
+ ClassNameResolver* name_resolver,
+ map<string, string>* vars_pointer) {
+ map<string, string> &vars = *vars_pointer;
+ vars["scope"] = scope;
+ vars["name"] = UnderscoresToCamelCase(descriptor);
+ vars["containing_type"] =
+ name_resolver->GetClassName(descriptor->containing_type(), immutable);
+ vars["number"] = SimpleItoa(descriptor->number());
+ vars["constant_name"] = FieldConstantName(descriptor);
+ vars["index"] = SimpleItoa(descriptor->index());
+ vars["default"] = descriptor->is_repeated() ?
+ "" : DefaultValue(descriptor, immutable, name_resolver);
+ vars["type_constant"] = FieldTypeName(GetType(descriptor));
+ vars["packed"] = descriptor->options().packed() ? "true" : "false";
+ vars["enum_map"] = "null";
+ vars["prototype"] = "null";
- JavaType java_type = GetJavaType(descriptor_);
+ JavaType java_type = GetJavaType(descriptor);
string singular_type;
switch (java_type) {
case JAVATYPE_MESSAGE:
- vars["type"] = ClassName(descriptor_->message_type());
+ singular_type = name_resolver->GetClassName(descriptor->message_type(),
+ immutable);
+ vars["prototype"] = singular_type + ".getDefaultInstance()";
break;
case JAVATYPE_ENUM:
- vars["type"] = ClassName(descriptor_->enum_type());
+ singular_type = name_resolver->GetClassName(descriptor->enum_type(),
+ immutable);
+ vars["enum_map"] = singular_type + ".internalGetValueMap()";
+ break;
+ case JAVATYPE_STRING:
+ singular_type = "java.lang.String";
+ break;
+ case JAVATYPE_BYTES:
+ singular_type = immutable ? "com.google.protobuf.ByteString" : "byte[]";
break;
default:
- vars["type"] = BoxedPrimitiveTypeName(java_type);
+ singular_type = BoxedPrimitiveTypeName(java_type);
break;
}
-
- printer->Print(vars,
- "public static final int $constant_name$ = $number$;\n");
- if (descriptor_->is_repeated()) {
- printer->Print(vars,
- "public static final\n"
- " com.google.protobuf.GeneratedMessage$lite$.GeneratedExtension<\n"
- " $containing_type$,\n"
- " java.util.List<$type$>> $name$ =\n"
- " com.google.protobuf.GeneratedMessage$lite$\n"
- " .newGeneratedExtension();\n");
- } else {
- printer->Print(vars,
- "public static final\n"
- " com.google.protobuf.GeneratedMessage$lite$.GeneratedExtension<\n"
- " $containing_type$,\n"
- " $type$> $name$ =\n"
- " com.google.protobuf.GeneratedMessage$lite$\n"
- " .newGeneratedExtension();\n");
- }
+ vars["type"] = descriptor->is_repeated() ?
+ "java.util.List<" + singular_type + ">" : singular_type;
+ vars["singular_type"] = singular_type;
}
-void ExtensionGenerator::GenerateInitializationCode(io::Printer* printer) {
+void ImmutableExtensionGenerator::Generate(io::Printer* printer) {
map<string, string> vars;
- vars["name"] = UnderscoresToCamelCase(descriptor_);
- vars["scope"] = scope_;
- vars["index"] = SimpleItoa(descriptor_->index());
- vars["extendee"] = ClassName(descriptor_->containing_type());
- vars["default"] = descriptor_->is_repeated() ? "" : DefaultValue(descriptor_);
- vars["number"] = SimpleItoa(descriptor_->number());
- vars["type_constant"] = TypeName(GetType(descriptor_));
- vars["packed"] = descriptor_->options().packed() ? "true" : "false";
- vars["enum_map"] = "null";
- vars["prototype"] = "null";
-
- JavaType java_type = GetJavaType(descriptor_);
- string singular_type;
- switch (java_type) {
- case JAVATYPE_MESSAGE:
- vars["type"] = ClassName(descriptor_->message_type());
- vars["prototype"] = ClassName(descriptor_->message_type()) +
- ".getDefaultInstance()";
- break;
- case JAVATYPE_ENUM:
- vars["type"] = ClassName(descriptor_->enum_type());
- vars["enum_map"] = ClassName(descriptor_->enum_type()) +
- ".internalGetValueMap()";
- break;
- default:
- vars["type"] = BoxedPrimitiveTypeName(java_type);
- break;
- }
+ 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 (HasDescriptorMethods(descriptor_->file())) {
- printer->Print(vars,
- "$scope$.$name$.internalInit(\n"
- " $scope$.getDescriptor().getExtensions().get($index$),\n"
- " $type$.class);\n");
+ // Non-lite extensions
+ if (descriptor_->extension_scope() == NULL) {
+ // Non-nested
+ printer->Print(
+ vars,
+ "public static final\n"
+ " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n"
+ " $containing_type$,\n"
+ " $type$> $name$ = com.google.protobuf.GeneratedMessage\n"
+ " .newFileScopedGeneratedExtension(\n"
+ " $singular_type$.class,\n"
+ " $prototype$);\n");
+ } else {
+ // Nested
+ printer->Print(
+ vars,
+ "public static final\n"
+ " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n"
+ " $containing_type$,\n"
+ " $type$> $name$ = com.google.protobuf.GeneratedMessage\n"
+ " .newMessageScopedGeneratedExtension(\n"
+ " $scope$.getDefaultInstance(),\n"
+ " $index$,\n"
+ " $singular_type$.class,\n"
+ " $prototype$);\n");
+ }
} else {
+ // Lite extensions
if (descriptor_->is_repeated()) {
- printer->Print(vars,
- "$scope$.$name$.internalInitRepeated(\n"
- " $extendee$.getDefaultInstance(),\n"
- " $prototype$,\n"
- " $enum_map$,\n"
- " $number$,\n"
- " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n"
- " $packed$);\n");
+ printer->Print(
+ vars,
+ "public static final\n"
+ " com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n"
+ " $containing_type$,\n"
+ " $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n"
+ " .newRepeatedGeneratedExtension(\n"
+ " $containing_type$.getDefaultInstance(),\n"
+ " $prototype$,\n"
+ " $enum_map$,\n"
+ " $number$,\n"
+ " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n"
+ " $packed$,\n"
+ " $singular_type$.class);\n");
} else {
- printer->Print(vars,
- "$scope$.$name$.internalInitSingular(\n"
- " $extendee$.getDefaultInstance(),\n"
- " $default$,\n"
- " $prototype$,\n"
- " $enum_map$,\n"
- " $number$,\n"
- " com.google.protobuf.WireFormat.FieldType.$type_constant$);\n");
+ printer->Print(
+ vars,
+ "public static final\n"
+ " com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n"
+ " $containing_type$,\n"
+ " $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n"
+ " .newSingularGeneratedExtension(\n"
+ " $containing_type$.getDefaultInstance(),\n"
+ " $default$,\n"
+ " $prototype$,\n"
+ " $enum_map$,\n"
+ " $number$,\n"
+ " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n"
+ " $singular_type$.class);\n");
}
}
}
-void ExtensionGenerator::GenerateRegistrationCode(io::Printer* printer) {
+void ImmutableExtensionGenerator::GenerateNonNestedInitializationCode(
+ io::Printer* printer) {
+ if (descriptor_->extension_scope() == NULL &&
+ HasDescriptorMethods(descriptor_->file())) {
+ // Only applies to non-nested, non-lite extensions.
+ printer->Print(
+ "$name$.internalInit(descriptor.getExtensions().get($index$));\n",
+ "name", UnderscoresToCamelCase(descriptor_),
+ "index", SimpleItoa(descriptor_->index()));
+ }
+}
+
+void ImmutableExtensionGenerator::GenerateRegistrationCode(
+ io::Printer* printer) {
printer->Print(
"registry.add($scope$.$name$);\n",
"scope", scope_,
diff --git a/src/google/protobuf/compiler/java/java_extension.h b/src/google/protobuf/compiler/java/java_extension.h
index 1e42304..f1701fb 100644
--- a/src/google/protobuf/compiler/java/java_extension.h
+++ b/src/google/protobuf/compiler/java/java_extension.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -35,6 +35,7 @@
#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H__
#define GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H__
+#include <map>
#include <string>
#include <google/protobuf/stubs/common.h>
@@ -42,6 +43,12 @@
namespace google {
namespace protobuf {
class FieldDescriptor; // descriptor.h
+ namespace compiler {
+ namespace java {
+ class Context; // context.h
+ class ClassNameResolver; // name_resolver.h
+ }
+ }
namespace io {
class Printer; // printer.h
}
@@ -56,17 +63,42 @@ namespace java {
// since extensions are just simple identifiers with interesting types.
class ExtensionGenerator {
public:
- explicit ExtensionGenerator(const FieldDescriptor* descriptor);
- ~ExtensionGenerator();
+ explicit ExtensionGenerator() {}
+ virtual ~ExtensionGenerator() {}
+
+ virtual void Generate(io::Printer* printer) = 0;
+ virtual void GenerateNonNestedInitializationCode(io::Printer* printer) = 0;
+ virtual void GenerateRegistrationCode(io::Printer* printer) = 0;
- void Generate(io::Printer* printer);
- void GenerateInitializationCode(io::Printer* printer);
- void GenerateRegistrationCode(io::Printer* printer);
+ protected:
+ static void InitTemplateVars(const FieldDescriptor* descriptor,
+ const string& scope,
+ bool immutable,
+ ClassNameResolver* name_resolver,
+ map<string, string>* vars_pointer);
private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator);
+};
+
+class ImmutableExtensionGenerator : public ExtensionGenerator {
+ public:
+ explicit ImmutableExtensionGenerator(const FieldDescriptor* descriptor,
+ Context* context);
+ virtual ~ImmutableExtensionGenerator();
+
+ virtual void Generate(io::Printer* printer);
+ virtual void GenerateNonNestedInitializationCode(io::Printer* printer);
+ virtual void GenerateRegistrationCode(io::Printer* printer);
+
+ protected:
const FieldDescriptor* descriptor_;
+ Context* context_;
+ ClassNameResolver* name_resolver_;
string scope_;
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator);
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableExtensionGenerator);
};
} // namespace java
@@ -74,4 +106,4 @@ class ExtensionGenerator {
} // namespace protobuf
} // namespace google
-#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__
+#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H__
diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc
index 978c8f3..d7b0f3f 100644
--- a/src/google/protobuf/compiler/java/java_field.cc
+++ b/src/google/protobuf/compiler/java/java_field.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -33,77 +33,178 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/java/java_field.h>
-#include <google/protobuf/compiler/java/java_helpers.h>
-#include <google/protobuf/compiler/java/java_primitive_field.h>
+
+#include <memory>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/compiler/java/java_context.h>
#include <google/protobuf/compiler/java/java_enum_field.h>
+#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/compiler/java/java_lazy_message_field.h>
#include <google/protobuf/compiler/java/java_message_field.h>
-#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/compiler/java/java_primitive_field.h>
+#include <google/protobuf/compiler/java/java_string_field.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/substitute.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace java {
-FieldGenerator::~FieldGenerator() {}
-
-void FieldGenerator::GenerateParsingCodeFromPacked(io::Printer* printer) const {
- // Reaching here indicates a bug. Cases are:
- // - This FieldGenerator should support packing, but this method should be
- // overridden.
- // - This FieldGenerator doesn't support packing, and this method should
- // never have been called.
- GOOGLE_LOG(FATAL) << "GenerateParsingCodeFromPacked() "
- << "called on field generator that does not support packing.";
-}
-
-FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor)
- : descriptor_(descriptor),
- field_generators_(
- new scoped_ptr<FieldGenerator>[descriptor->field_count()]),
- extension_generators_(
- new scoped_ptr<FieldGenerator>[descriptor->extension_count()]) {
+namespace {
- // Construct all the FieldGenerators.
- for (int i = 0; i < descriptor->field_count(); i++) {
- field_generators_[i].reset(MakeGenerator(descriptor->field(i)));
- }
- for (int i = 0; i < descriptor->extension_count(); i++) {
- extension_generators_[i].reset(MakeGenerator(descriptor->extension(i)));
- }
-}
-
-FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field) {
+ImmutableFieldGenerator* MakeImmutableGenerator(
+ const FieldDescriptor* field, int messageBitIndex, int builderBitIndex,
+ Context* context) {
if (field->is_repeated()) {
switch (GetJavaType(field)) {
case JAVATYPE_MESSAGE:
- return new RepeatedMessageFieldGenerator(field);
+ if (IsLazy(field)) {
+ return new RepeatedImmutableLazyMessageFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ } else {
+ return new RepeatedImmutableMessageFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ }
case JAVATYPE_ENUM:
- return new RepeatedEnumFieldGenerator(field);
+ return new RepeatedImmutableEnumFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ case JAVATYPE_STRING:
+ return new RepeatedImmutableStringFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
default:
- return new RepeatedPrimitiveFieldGenerator(field);
+ return new RepeatedImmutablePrimitiveFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
}
} else {
- switch (GetJavaType(field)) {
- case JAVATYPE_MESSAGE:
- return new MessageFieldGenerator(field);
- case JAVATYPE_ENUM:
- return new EnumFieldGenerator(field);
- default:
- return new PrimitiveFieldGenerator(field);
+ if (field->containing_oneof()) {
+ switch (GetJavaType(field)) {
+ case JAVATYPE_MESSAGE:
+ if (IsLazy(field)) {
+ return new ImmutableLazyMessageOneofFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ } else {
+ return new ImmutableMessageOneofFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ }
+ case JAVATYPE_ENUM:
+ return new ImmutableEnumOneofFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ case JAVATYPE_STRING:
+ return new ImmutableStringOneofFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ default:
+ return new ImmutablePrimitiveOneofFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ }
+ } else {
+ switch (GetJavaType(field)) {
+ case JAVATYPE_MESSAGE:
+ if (IsLazy(field)) {
+ return new ImmutableLazyMessageFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ } else {
+ return new ImmutableMessageFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ }
+ case JAVATYPE_ENUM:
+ return new ImmutableEnumFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ case JAVATYPE_STRING:
+ return new ImmutableStringFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ default:
+ return new ImmutablePrimitiveFieldGenerator(
+ field, messageBitIndex, builderBitIndex, context);
+ }
}
}
}
-FieldGeneratorMap::~FieldGeneratorMap() {}
-const FieldGenerator& FieldGeneratorMap::get(
- const FieldDescriptor* field) const {
- GOOGLE_CHECK_EQ(field->containing_type(), descriptor_);
- return *field_generators_[field->index()];
+static inline void ReportUnexpectedPackedFieldsCall(io::Printer* printer) {
+ // Reaching here indicates a bug. Cases are:
+ // - This FieldGenerator should support packing,
+ // but this method should be overridden.
+ // - This FieldGenerator doesn't support packing, and this method
+ // should never have been called.
+ GOOGLE_LOG(FATAL) << "GenerateParsingCodeFromPacked() "
+ << "called on field generator that does not support packing.";
}
-const FieldGenerator& FieldGeneratorMap::get_extension(int index) const {
- return *extension_generators_[index];
+} // namespace
+
+ImmutableFieldGenerator::~ImmutableFieldGenerator() {}
+
+void ImmutableFieldGenerator::
+GenerateParsingCodeFromPacked(io::Printer* printer) const {
+ ReportUnexpectedPackedFieldsCall(printer);
+}
+
+// ===================================================================
+
+template <>
+FieldGeneratorMap<ImmutableFieldGenerator>::FieldGeneratorMap(
+ const Descriptor* descriptor, Context* context)
+ : descriptor_(descriptor),
+ field_generators_(new scoped_ptr<
+ ImmutableFieldGenerator>[descriptor->field_count()]) {
+
+ // Construct all the FieldGenerators and assign them bit indices for their
+ // bit fields.
+ int messageBitIndex = 0;
+ int builderBitIndex = 0;
+ for (int i = 0; i < descriptor->field_count(); i++) {
+ ImmutableFieldGenerator* generator = MakeImmutableGenerator(
+ descriptor->field(i), messageBitIndex, builderBitIndex, context);
+ field_generators_[i].reset(generator);
+ messageBitIndex += generator->GetNumBitsForMessage();
+ builderBitIndex += generator->GetNumBitsForBuilder();
+ }
+}
+
+template<>
+FieldGeneratorMap<ImmutableFieldGenerator>::~FieldGeneratorMap() {}
+
+
+void SetCommonFieldVariables(const FieldDescriptor* descriptor,
+ const FieldGeneratorInfo* info,
+ map<string, string>* variables) {
+ (*variables)["field_name"] = descriptor->name();
+ (*variables)["name"] = info->name;
+ (*variables)["capitalized_name"] = info->capitalized_name;
+ (*variables)["disambiguated_reason"] = info->disambiguated_reason;
+ (*variables)["constant_name"] = FieldConstantName(descriptor);
+ (*variables)["number"] = SimpleItoa(descriptor->number());
+}
+
+void SetCommonOneofVariables(const FieldDescriptor* descriptor,
+ const OneofGeneratorInfo* info,
+ map<string, string>* variables) {
+ (*variables)["oneof_name"] = info->name;
+ (*variables)["oneof_capitalized_name"] = info->capitalized_name;
+ (*variables)["oneof_index"] =
+ SimpleItoa(descriptor->containing_oneof()->index());
+ (*variables)["set_oneof_case_message"] = info->name +
+ "Case_ = " + SimpleItoa(descriptor->number());
+ (*variables)["clear_oneof_case_message"] = info->name +
+ "Case_ = 0";
+ (*variables)["has_oneof_case_message"] = info->name +
+ "Case_ == " + SimpleItoa(descriptor->number());
+}
+
+void PrintExtraFieldInfo(const map<string, string>& variables,
+ io::Printer* printer) {
+ const map<string, string>::const_iterator it =
+ variables.find("disambiguated_reason");
+ if (it != variables.end() && !it->second.empty()) {
+ printer->Print(
+ variables,
+ "// An alternative name is used for field \"$field_name$\" because:\n"
+ "// $disambiguated_reason$\n");
+ }
}
} // namespace java
diff --git a/src/google/protobuf/compiler/java/java_field.h b/src/google/protobuf/compiler/java/java_field.h
index f5bef7a..db339d5 100644
--- a/src/google/protobuf/compiler/java/java_field.h
+++ b/src/google/protobuf/compiler/java/java_field.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -35,14 +35,23 @@
#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_JAVA_FIELD_H__
+#include <map>
+#include <memory>
#include <string>
+
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/descriptor.h>
namespace google {
namespace protobuf {
+ namespace compiler {
+ namespace java {
+ class Context; // context.h
+ class ClassNameResolver; // name_resolver.h
+ }
+ }
namespace io {
- class Printer; // printer.h
+ class Printer; // printer.h
}
}
@@ -50,46 +59,101 @@ namespace protobuf {
namespace compiler {
namespace java {
-class FieldGenerator {
+class ImmutableFieldGenerator {
public:
- FieldGenerator() {}
- virtual ~FieldGenerator();
+ ImmutableFieldGenerator() {}
+ virtual ~ImmutableFieldGenerator();
+ virtual int GetNumBitsForMessage() const = 0;
+ virtual int GetNumBitsForBuilder() const = 0;
+ virtual void GenerateInterfaceMembers(io::Printer* printer) const = 0;
virtual void GenerateMembers(io::Printer* printer) const = 0;
virtual void GenerateBuilderMembers(io::Printer* printer) const = 0;
virtual void GenerateInitializationCode(io::Printer* printer) const = 0;
+ virtual void GenerateBuilderClearCode(io::Printer* printer) const = 0;
virtual void GenerateMergingCode(io::Printer* printer) const = 0;
virtual void GenerateBuildingCode(io::Printer* printer) const = 0;
virtual void GenerateParsingCode(io::Printer* printer) const = 0;
virtual void GenerateParsingCodeFromPacked(io::Printer* printer) const;
+ virtual void GenerateParsingDoneCode(io::Printer* printer) const = 0;
virtual void GenerateSerializationCode(io::Printer* printer) const = 0;
virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0;
+ virtual void GenerateFieldBuilderInitializationCode(io::Printer* printer)
+ const = 0;
+
+ virtual void GenerateEqualsCode(io::Printer* printer) const = 0;
+ virtual void GenerateHashCode(io::Printer* printer) const = 0;
virtual string GetBoxedType() const = 0;
private:
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGenerator);
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableFieldGenerator);
};
+
// Convenience class which constructs FieldGenerators for a Descriptor.
+template<typename FieldGeneratorType>
class FieldGeneratorMap {
public:
- explicit FieldGeneratorMap(const Descriptor* descriptor);
+ explicit FieldGeneratorMap(const Descriptor* descriptor,
+ Context* context);
~FieldGeneratorMap();
- const FieldGenerator& get(const FieldDescriptor* field) const;
- const FieldGenerator& get_extension(int index) const;
+ const FieldGeneratorType& get(const FieldDescriptor* field) const;
private:
const Descriptor* descriptor_;
- scoped_array<scoped_ptr<FieldGenerator> > field_generators_;
- scoped_array<scoped_ptr<FieldGenerator> > extension_generators_;
-
- static FieldGenerator* MakeGenerator(const FieldDescriptor* field);
+ Context* context_;
+ ClassNameResolver* name_resolver_;
+ scoped_array<scoped_ptr<FieldGeneratorType> > field_generators_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap);
};
+template<typename FieldGeneratorType>
+inline const FieldGeneratorType&
+FieldGeneratorMap<FieldGeneratorType>::get(const FieldDescriptor* field) const {
+ GOOGLE_CHECK_EQ(field->containing_type(), descriptor_);
+ return *field_generators_[field->index()];
+}
+
+// Instantiate template for mutable and immutable maps.
+template<>
+FieldGeneratorMap<ImmutableFieldGenerator>::
+FieldGeneratorMap(const Descriptor* descriptor,
+ Context* context);
+
+template<>
+FieldGeneratorMap<ImmutableFieldGenerator>::~FieldGeneratorMap();
+
+
+// Field information used in FieldGeneartors.
+struct FieldGeneratorInfo {
+ string name;
+ string capitalized_name;
+ string disambiguated_reason;
+};
+
+// Oneof information used in OneofFieldGeneartors.
+struct OneofGeneratorInfo {
+ string name;
+ string capitalized_name;
+};
+
+// Set some common variables used in variable FieldGenerators.
+void SetCommonFieldVariables(const FieldDescriptor* descriptor,
+ const FieldGeneratorInfo* info,
+ map<string, string>* variables);
+
+// Set some common oneof variables used in OneofFieldGenerators.
+void SetCommonOneofVariables(const FieldDescriptor* descriptor,
+ const OneofGeneratorInfo* info,
+ map<string, string>* variables);
+
+// Print useful comments before a field's accessors.
+void PrintExtraFieldInfo(const map<string, string>& variables,
+ io::Printer* printer);
+
} // namespace java
} // namespace compiler
} // namespace protobuf
diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc
index 7ea127c..231b144 100644
--- a/src/google/protobuf/compiler/java/java_file.cc
+++ b/src/google/protobuf/compiler/java/java_file.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -33,15 +33,23 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/java/java_file.h>
+
+#include <memory>
+
+#include <google/protobuf/compiler/java/java_context.h>
#include <google/protobuf/compiler/java/java_enum.h>
-#include <google/protobuf/compiler/java/java_service.h>
#include <google/protobuf/compiler/java/java_extension.h>
+#include <google/protobuf/compiler/java/java_generator_factory.h>
#include <google/protobuf/compiler/java/java_helpers.h>
#include <google/protobuf/compiler/java/java_message.h>
+#include <google/protobuf/compiler/java/java_name_resolver.h>
+#include <google/protobuf/compiler/java/java_service.h>
+#include <google/protobuf/compiler/java/java_shared_code_generator.h>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {
@@ -51,18 +59,24 @@ namespace java {
namespace {
-// Recursively searches the given message to see if it contains any extensions.
-bool UsesExtensions(const Message& message) {
+
+// Recursively searches the given message to collect extensions.
+// Returns true if all the extensions can be recognized. The extensions will be
+// appended in to the extensions parameter.
+// Returns false when there are unknown fields, in which case the data in the
+// extensions output parameter is not reliable and should be discarded.
+bool CollectExtensions(const Message& message,
+ vector<const FieldDescriptor*>* extensions) {
const Reflection* reflection = message.GetReflection();
- // We conservatively assume that unknown fields are extensions.
- if (reflection->GetUnknownFields(message).field_count() > 0) return true;
+ // There are unknown fields that could be extensions, thus this call fails.
+ if (reflection->GetUnknownFields(message).field_count() > 0) return false;
vector<const FieldDescriptor*> fields;
reflection->ListFields(message, &fields);
for (int i = 0; i < fields.size(); i++) {
- if (fields[i]->is_extension()) return true;
+ if (fields[i]->is_extension()) extensions->push_back(fields[i]);
if (GetJavaType(fields[i]) == JAVATYPE_MESSAGE) {
if (fields[i]->is_repeated()) {
@@ -70,25 +84,83 @@ bool UsesExtensions(const Message& message) {
for (int j = 0; j < size; j++) {
const Message& sub_message =
reflection->GetRepeatedMessage(message, fields[i], j);
- if (UsesExtensions(sub_message)) return true;
+ if (!CollectExtensions(sub_message, extensions)) return false;
}
} else {
const Message& sub_message = reflection->GetMessage(message, fields[i]);
- if (UsesExtensions(sub_message)) return true;
+ if (!CollectExtensions(sub_message, extensions)) return false;
}
}
}
- return false;
+ return true;
+}
+
+// Finds all extensions in the given message and its sub-messages. If the
+// message contains unknown fields (which could be extensions), then those
+// extensions are defined in alternate_pool.
+// The message will be converted to a DynamicMessage backed by alternate_pool
+// in order to handle this case.
+void CollectExtensions(const FileDescriptorProto& file_proto,
+ const DescriptorPool& alternate_pool,
+ vector<const FieldDescriptor*>* extensions,
+ const string& file_data) {
+ if (!CollectExtensions(file_proto, extensions)) {
+ // There are unknown fields in the file_proto, which are probably
+ // extensions. We need to parse the data into a dynamic message based on the
+ // builder-pool to find out all extensions.
+ const Descriptor* file_proto_desc = alternate_pool.FindMessageTypeByName(
+ file_proto.GetDescriptor()->full_name());
+ GOOGLE_CHECK(file_proto_desc)
+ << "Find unknown fields in FileDescriptorProto when building "
+ << file_proto.name()
+ << ". It's likely that those fields are custom options, however, "
+ "descriptor.proto is not in the transitive dependencies. "
+ "This normally should not happen. Please report a bug.";
+ DynamicMessageFactory factory;
+ scoped_ptr<Message> dynamic_file_proto(
+ factory.GetPrototype(file_proto_desc)->New());
+ GOOGLE_CHECK(dynamic_file_proto.get() != NULL);
+ GOOGLE_CHECK(dynamic_file_proto->ParseFromString(file_data));
+
+ // Collect the extensions again from the dynamic message. There should be no
+ // more unknown fields this time, i.e. all the custom options should be
+ // parsed as extensions now.
+ extensions->clear();
+ GOOGLE_CHECK(CollectExtensions(*dynamic_file_proto, extensions))
+ << "Find unknown fields in FileDescriptorProto when building "
+ << file_proto.name()
+ << ". It's likely that those fields are custom options, however, "
+ "those options cannot be recognized in the builder pool. "
+ "This normally should not happen. Please report a bug.";
+ }
}
} // namespace
-FileGenerator::FileGenerator(const FileDescriptor* file)
- : file_(file),
- java_package_(FileJavaPackage(file)),
- classname_(FileClassName(file)) {}
+FileGenerator::FileGenerator(const FileDescriptor* file, bool immutable_api)
+ : file_(file),
+ java_package_(FileJavaPackage(file, immutable_api)),
+ message_generators_(
+ new scoped_ptr<MessageGenerator>[file->message_type_count()]),
+ extension_generators_(
+ new scoped_ptr<ExtensionGenerator>[file->extension_count()]),
+ context_(new Context(file)),
+ name_resolver_(context_->GetNameResolver()),
+ immutable_api_(immutable_api) {
+ classname_ = name_resolver_->GetFileClassName(file, immutable_api);
+ generator_factory_.reset(
+ new ImmutableGeneratorFactory(context_.get()));
+ for (int i = 0; i < file_->message_type_count(); ++i) {
+ message_generators_[i].reset(
+ generator_factory_->NewMessageGenerator(file_->message_type(i)));
+ }
+ for (int i = 0; i < file_->extension_count(); ++i) {
+ extension_generators_[i].reset(
+ generator_factory_->NewExtensionGenerator(file_->extension(i)));
+ }
+}
FileGenerator::~FileGenerator() {}
@@ -97,25 +169,7 @@ bool FileGenerator::Validate(string* error) {
// problem that leads to Java compile errors that can be hard to understand.
// It's especially bad when using the java_multiple_files, since we would
// end up overwriting the outer class with one of the inner ones.
-
- bool found_conflict = false;
- for (int i = 0; i < file_->enum_type_count() && !found_conflict; i++) {
- if (file_->enum_type(i)->name() == classname_) {
- found_conflict = true;
- }
- }
- for (int i = 0; i < file_->message_type_count() && !found_conflict; i++) {
- if (file_->message_type(i)->name() == classname_) {
- found_conflict = true;
- }
- }
- for (int i = 0; i < file_->service_count() && !found_conflict; i++) {
- if (file_->service(i)->name() == classname_) {
- found_conflict = true;
- }
- }
-
- if (found_conflict) {
+ if (name_resolver_->HasConflictingClassName(file_, classname_)) {
error->assign(file_->name());
error->append(
": Cannot generate Java output because the file's outer class name, \"");
@@ -126,7 +180,29 @@ bool FileGenerator::Validate(string* error) {
"option to specify a different outer class name for the .proto file.");
return false;
}
-
+ // If java_outer_classname option is not set and the default outer class name
+ // conflicts with a type defined in the message, we will append a suffix to
+ // avoid the conflict. This allows proto1 API protos to be dual-compiled into
+ // proto2 API without code change. When this happens we'd like to issue an
+ // warning to let the user know that the outer class name has been changed.
+ // Although we only do this automatic naming fix for immutable API, mutable
+ // outer class name will also be affected as it's contructed from immutable
+ // outer class name with an additional "Mutable" prefix. Since the naming
+ // change in mutable API is not caused by a naming conflict, we generate the
+ // warning for immutable API only.
+ if (immutable_api_ && !file_->options().has_java_outer_classname()) {
+ string default_classname =
+ name_resolver_->GetFileDefaultImmutableClassName(file_);
+ if (default_classname != classname_) {
+ GOOGLE_LOG(WARNING) << file_->name() << ": The default outer class name, \""
+ << default_classname << "\", conflicts with a type "
+ << "declared in the proto file and an alternative outer "
+ << "class name is used: \"" << classname_ << "\". To avoid "
+ << "this warning, please use the java_outer_classname "
+ << "option to specify a different outer class name for "
+ << "the .proto file.";
+ }
+ }
return true;
}
@@ -160,12 +236,11 @@ void FileGenerator::Generate(io::Printer* printer) {
printer->Indent();
for (int i = 0; i < file_->extension_count(); i++) {
- ExtensionGenerator(file_->extension(i)).GenerateRegistrationCode(printer);
+ extension_generators_[i]->GenerateRegistrationCode(printer);
}
for (int i = 0; i < file_->message_type_count(); i++) {
- MessageGenerator(file_->message_type(i))
- .GenerateExtensionRegistrationCode(printer);
+ message_generators_[i]->GenerateExtensionRegistrationCode(printer);
}
printer->Outdent();
@@ -174,16 +249,20 @@ void FileGenerator::Generate(io::Printer* printer) {
// -----------------------------------------------------------------
- if (!file_->options().java_multiple_files()) {
+ if (!MultipleJavaFiles(file_, immutable_api_)) {
for (int i = 0; i < file_->enum_type_count(); i++) {
- EnumGenerator(file_->enum_type(i)).Generate(printer);
+ EnumGenerator(file_->enum_type(i), immutable_api_, context_.get())
+ .Generate(printer);
}
for (int i = 0; i < file_->message_type_count(); i++) {
- MessageGenerator(file_->message_type(i)).Generate(printer);
+ message_generators_[i]->GenerateInterface(printer);
+ message_generators_[i]->Generate(printer);
}
if (HasGenericServices(file_)) {
for (int i = 0; i < file_->service_count(); i++) {
- ServiceGenerator(file_->service(i)).Generate(printer);
+ scoped_ptr<ServiceGenerator> generator(
+ generator_factory_->NewServiceGenerator(file_->service(i)));
+ generator->Generate(printer);
}
}
}
@@ -191,34 +270,29 @@ void FileGenerator::Generate(io::Printer* printer) {
// Extensions must be generated in the outer class since they are values,
// not classes.
for (int i = 0; i < file_->extension_count(); i++) {
- ExtensionGenerator(file_->extension(i)).Generate(printer);
+ extension_generators_[i]->Generate(printer);
}
// Static variables.
for (int i = 0; i < file_->message_type_count(); i++) {
- // TODO(kenton): Reuse MessageGenerator objects?
- MessageGenerator(file_->message_type(i)).GenerateStaticVariables(printer);
+ message_generators_[i]->GenerateStaticVariables(printer);
}
printer->Print("\n");
if (HasDescriptorMethods(file_)) {
- GenerateEmbeddedDescriptor(printer);
+ if (immutable_api_) {
+ GenerateDescriptorInitializationCodeForImmutable(printer);
+ } else {
+ GenerateDescriptorInitializationCodeForMutable(printer);
+ }
} else {
printer->Print(
"static {\n");
printer->Indent();
for (int i = 0; i < file_->message_type_count(); i++) {
- // TODO(kenton): Reuse MessageGenerator objects?
- MessageGenerator(file_->message_type(i))
- .GenerateStaticVariableInitializers(printer);
- }
-
- for (int i = 0; i < file_->extension_count(); i++) {
- // TODO(kenton): Reuse ExtensionGenerator objects?
- ExtensionGenerator(file_->extension(i))
- .GenerateInitializationCode(printer);
+ message_generators_[i]->GenerateStaticVariableInitializers(printer);
}
printer->Outdent();
@@ -226,13 +300,6 @@ void FileGenerator::Generate(io::Printer* printer) {
"}\n");
}
- // Dummy function we can use to force the static initialization block to
- // run. Needed by inner classes. Cannot be private due to
- // java_multiple_files option.
- printer->Print(
- "\n"
- "public static void internalForceInit() {}\n");
-
printer->Print(
"\n"
"// @@protoc_insertion_point(outer_class_scope)\n");
@@ -241,23 +308,8 @@ void FileGenerator::Generate(io::Printer* printer) {
printer->Print("}\n");
}
-void FileGenerator::GenerateEmbeddedDescriptor(io::Printer* printer) {
- // Embed the descriptor. We simply serialize the entire FileDescriptorProto
- // and embed it as a string literal, which is parsed and built into real
- // descriptors at initialization time. We unfortunately have to put it in
- // a string literal, not a byte array, because apparently using a literal
- // byte array causes the Java compiler to generate *instructions* to
- // initialize each and every byte of the array, e.g. as if you typed:
- // b[0] = 123; b[1] = 456; b[2] = 789;
- // This makes huge bytecode files and can easily hit the compiler's internal
- // code size limits (error "code to large"). String literals are apparently
- // embedded raw, which is what we want.
- FileDescriptorProto file_proto;
- file_->CopyTo(&file_proto);
-
- string file_data;
- file_proto.SerializeToString(&file_data);
-
+void FileGenerator::GenerateDescriptorInitializationCodeForImmutable(
+ io::Printer* printer) {
printer->Print(
"public static com.google.protobuf.Descriptors.FileDescriptor\n"
" getDescriptor() {\n"
@@ -265,104 +317,135 @@ void FileGenerator::GenerateEmbeddedDescriptor(io::Printer* printer) {
"}\n"
"private static com.google.protobuf.Descriptors.FileDescriptor\n"
" descriptor;\n"
- "static {\n"
- " java.lang.String[] descriptorData = {\n");
- printer->Indent();
+ "static {\n");
printer->Indent();
- // Only write 40 bytes per line.
- static const int kBytesPerLine = 40;
- for (int i = 0; i < file_data.size(); i += kBytesPerLine) {
- if (i > 0) {
- // Every 400 lines, start a new string literal, in order to avoid the
- // 64k length limit.
- if (i % 400 == 0) {
- printer->Print(",\n");
- } else {
- printer->Print(" +\n");
- }
- }
- printer->Print("\"$data$\"",
- "data", CEscape(file_data.substr(i, kBytesPerLine)));
- }
-
- printer->Outdent();
- printer->Print("\n};\n");
-
- // -----------------------------------------------------------------
- // Create the InternalDescriptorAssigner.
-
- printer->Print(
- "com.google.protobuf.Descriptors.FileDescriptor."
- "InternalDescriptorAssigner assigner =\n"
- " new com.google.protobuf.Descriptors.FileDescriptor."
- "InternalDescriptorAssigner() {\n"
- " public com.google.protobuf.ExtensionRegistry assignDescriptors(\n"
- " com.google.protobuf.Descriptors.FileDescriptor root) {\n"
- " descriptor = root;\n");
-
- printer->Indent();
- printer->Indent();
- printer->Indent();
+ SharedCodeGenerator shared_code_generator(file_);
+ shared_code_generator.GenerateDescriptors(printer);
for (int i = 0; i < file_->message_type_count(); i++) {
- // TODO(kenton): Reuse MessageGenerator objects?
- MessageGenerator(file_->message_type(i))
- .GenerateStaticVariableInitializers(printer);
+ message_generators_[i]->GenerateStaticVariableInitializers(printer);
}
-
for (int i = 0; i < file_->extension_count(); i++) {
- // TODO(kenton): Reuse ExtensionGenerator objects?
- ExtensionGenerator(file_->extension(i))
- .GenerateInitializationCode(printer);
+ extension_generators_[i]->GenerateNonNestedInitializationCode(printer);
}
- if (UsesExtensions(file_proto)) {
- // Must construct an ExtensionRegistry containing all possible extensions
- // and return it.
+ // Proto compiler builds a DescriptorPool, which holds all the descriptors to
+ // generate, when processing the ".proto" files. We call this DescriptorPool
+ // the parsed pool (a.k.a. file_->pool()).
+ //
+ // Note that when users try to extend the (.*)DescriptorProto in their
+ // ".proto" files, it does not affect the pre-built FileDescriptorProto class
+ // in proto compiler. When we put the descriptor data in the file_proto, those
+ // extensions become unknown fields.
+ //
+ // Now we need to find out all the extension value to the (.*)DescriptorProto
+ // in the file_proto message, and prepare an ExtensionRegistry to return.
+ //
+ // To find those extensions, we need to parse the data into a dynamic message
+ // of the FileDescriptor based on the builder-pool, then we can use
+ // reflections to find all extension fields
+ FileDescriptorProto file_proto;
+ file_->CopyTo(&file_proto);
+ string file_data;
+ file_proto.SerializeToString(&file_data);
+ vector<const FieldDescriptor*> extensions;
+ CollectExtensions(file_proto, *file_->pool(), &extensions, file_data);
+
+ if (extensions.size() > 0) {
+ // Must construct an ExtensionRegistry containing all existing extensions
+ // and use it to parse the descriptor data again to recognize extensions.
printer->Print(
"com.google.protobuf.ExtensionRegistry registry =\n"
- " com.google.protobuf.ExtensionRegistry.newInstance();\n"
- "registerAllExtensions(registry);\n");
- for (int i = 0; i < file_->dependency_count(); i++) {
- printer->Print(
- "$dependency$.registerAllExtensions(registry);\n",
- "dependency", ClassName(file_->dependency(i)));
+ " com.google.protobuf.ExtensionRegistry.newInstance();\n");
+ for (int i = 0; i < extensions.size(); i++) {
+ scoped_ptr<ExtensionGenerator> generator(
+ generator_factory_->NewExtensionGenerator(extensions[i]));
+ generator->GenerateRegistrationCode(printer);
}
printer->Print(
- "return registry;\n");
- } else {
- printer->Print(
- "return null;\n");
+ "com.google.protobuf.Descriptors.FileDescriptor\n"
+ " .internalUpdateFileDescriptor(descriptor, registry);\n");
}
- printer->Outdent();
- printer->Outdent();
- printer->Outdent();
+ // Force descriptor initialization of all dependencies.
+ for (int i = 0; i < file_->dependency_count(); i++) {
+ if (ShouldIncludeDependency(file_->dependency(i), true)) {
+ string dependency =
+ name_resolver_->GetImmutableClassName(file_->dependency(i));
+ printer->Print(
+ "$dependency$.getDescriptor();\n",
+ "dependency", dependency);
+ }
+ }
+ printer->Outdent();
printer->Print(
- " }\n"
- " };\n");
+ "}\n");
+}
- // -----------------------------------------------------------------
- // Invoke internalBuildGeneratedFileFrom() to build the file.
+void FileGenerator::GenerateDescriptorInitializationCodeForMutable(io::Printer* printer) {
+ printer->Print(
+ "public static com.google.protobuf.Descriptors.FileDescriptor\n"
+ " getDescriptor() {\n"
+ " return descriptor;\n"
+ "}\n"
+ "private static com.google.protobuf.Descriptors.FileDescriptor\n"
+ " descriptor;\n"
+ "static {\n");
+ printer->Indent();
printer->Print(
- "com.google.protobuf.Descriptors.FileDescriptor\n"
- " .internalBuildGeneratedFileFrom(descriptorData,\n"
- " new com.google.protobuf.Descriptors.FileDescriptor[] {\n");
+ "descriptor = $immutable_package$.$descriptor_classname$.descriptor;\n",
+ "immutable_package", FileJavaPackage(file_, true),
+ "descriptor_classname", name_resolver_->GetDescriptorClassName(file_));
+
+ for (int i = 0; i < file_->message_type_count(); i++) {
+ message_generators_[i]->GenerateStaticVariableInitializers(printer);
+ }
+ for (int i = 0; i < file_->extension_count(); i++) {
+ extension_generators_[i]->GenerateNonNestedInitializationCode(printer);
+ }
+
+ // Check if custom options exist. If any, try to load immutable classes since
+ // custom options are only represented with immutable messages.
+ FileDescriptorProto file_proto;
+ file_->CopyTo(&file_proto);
+ string file_data;
+ file_proto.SerializeToString(&file_data);
+ vector<const FieldDescriptor*> extensions;
+ CollectExtensions(file_proto, *file_->pool(), &extensions, file_data);
+
+ if (extensions.size() > 0) {
+ // Try to load immutable messages' outer class. Its initialization code
+ // will take care of interpreting custom options.
+ printer->Print(
+ "try {\n"
+ // Note that we have to load the immutable class dynamically here as
+ // we want the mutable code to be independent from the immutable code
+ // at compile time. It is required to implement dual-compile for
+ // mutable and immutable API in blaze.
+ " java.lang.Class immutableClass = java.lang.Class.forName(\n"
+ " \"$immutable_classname$\");\n"
+ "} catch (java.lang.ClassNotFoundException e) {\n"
+ // The immutable class can not be found. Custom options are left
+ // as unknown fields.
+ // TODO(xiaofeng): inform the user with a warning?
+ "}\n",
+ "immutable_classname", name_resolver_->GetImmutableClassName(file_));
+ }
+ // Force descriptor initialization of all dependencies.
for (int i = 0; i < file_->dependency_count(); i++) {
- if (ShouldIncludeDependency(file_->dependency(i))) {
+ if (ShouldIncludeDependency(file_->dependency(i), false)) {
+ string dependency = name_resolver_->GetMutableClassName(
+ file_->dependency(i));
printer->Print(
- " $dependency$.getDescriptor(),\n",
- "dependency", ClassName(file_->dependency(i)));
+ "$dependency$.getDescriptor();\n",
+ "dependency", dependency);
}
}
- printer->Print(
- " }, assigner);\n");
-
printer->Outdent();
printer->Print(
"}\n");
@@ -372,18 +455,22 @@ template<typename GeneratorClass, typename DescriptorClass>
static void GenerateSibling(const string& package_dir,
const string& java_package,
const DescriptorClass* descriptor,
- OutputDirectory* output_directory,
- vector<string>* file_list) {
- string filename = package_dir + descriptor->name() + ".java";
+ GeneratorContext* context,
+ vector<string>* file_list,
+ const string& name_suffix,
+ GeneratorClass* generator,
+ void (GeneratorClass::*pfn)(io::Printer* printer)) {
+ string filename = package_dir + descriptor->name() + name_suffix + ".java";
file_list->push_back(filename);
- scoped_ptr<io::ZeroCopyOutputStream> output(
- output_directory->Open(filename));
+ scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
io::Printer printer(output.get(), '$');
printer.Print(
"// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
- "\n");
+ "// source: $filename$\n"
+ "\n",
+ "filename", descriptor->file()->name());
if (!java_package.empty()) {
printer.Print(
"package $package$;\n"
@@ -391,34 +478,53 @@ static void GenerateSibling(const string& package_dir,
"package", java_package);
}
- GeneratorClass(descriptor).Generate(&printer);
+ (generator->*pfn)(&printer);
}
void FileGenerator::GenerateSiblings(const string& package_dir,
- OutputDirectory* output_directory,
+ GeneratorContext* context,
vector<string>* file_list) {
- if (file_->options().java_multiple_files()) {
+ if (MultipleJavaFiles(file_, immutable_api_)) {
for (int i = 0; i < file_->enum_type_count(); i++) {
+ EnumGenerator generator(file_->enum_type(i), immutable_api_,
+ context_.get());
GenerateSibling<EnumGenerator>(package_dir, java_package_,
file_->enum_type(i),
- output_directory, file_list);
+ context, file_list, "",
+ &generator,
+ &EnumGenerator::Generate);
}
for (int i = 0; i < file_->message_type_count(); i++) {
+ if (immutable_api_) {
+ GenerateSibling<MessageGenerator>(package_dir, java_package_,
+ file_->message_type(i),
+ context, file_list,
+ "OrBuilder",
+ message_generators_[i].get(),
+ &MessageGenerator::GenerateInterface);
+ }
GenerateSibling<MessageGenerator>(package_dir, java_package_,
file_->message_type(i),
- output_directory, file_list);
+ context, file_list, "",
+ message_generators_[i].get(),
+ &MessageGenerator::Generate);
}
if (HasGenericServices(file_)) {
for (int i = 0; i < file_->service_count(); i++) {
+ scoped_ptr<ServiceGenerator> generator(
+ generator_factory_->NewServiceGenerator(file_->service(i)));
GenerateSibling<ServiceGenerator>(package_dir, java_package_,
file_->service(i),
- output_directory, file_list);
+ context, file_list, "",
+ generator.get(),
+ &ServiceGenerator::Generate);
}
}
}
}
-bool FileGenerator::ShouldIncludeDependency(const FileDescriptor* descriptor) {
+bool FileGenerator::ShouldIncludeDependency(
+ const FileDescriptor* descriptor, bool immutable_api) {
return true;
}
diff --git a/src/google/protobuf/compiler/java/java_file.h b/src/google/protobuf/compiler/java/java_file.h
index 9e35d33..c805b9a 100644
--- a/src/google/protobuf/compiler/java/java_file.h
+++ b/src/google/protobuf/compiler/java/java_file.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -35,18 +35,26 @@
#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_FILE_H__
#define GOOGLE_PROTOBUF_COMPILER_JAVA_FILE_H__
+#include <memory>
#include <string>
#include <vector>
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
- class FileDescriptor; // descriptor.h
+ class FileDescriptor; // descriptor.h
namespace io {
- class Printer; // printer.h
+ class Printer; // printer.h
}
namespace compiler {
- class OutputDirectory; // code_generator.h
+ class GeneratorContext; // code_generator.h
+ namespace java {
+ class Context; // context.h
+ class MessageGenerator; // message.h
+ class GeneratorFactory; // generator_factory.h
+ class ExtensionGenerator; // extension.h
+ class ClassNameResolver; // name_resolver.h
+ }
}
}
@@ -56,7 +64,7 @@ namespace java {
class FileGenerator {
public:
- explicit FileGenerator(const FileDescriptor* file);
+ FileGenerator(const FileDescriptor* file, bool immutable_api = true);
~FileGenerator();
// Checks for problems that would otherwise lead to cryptic compile errors.
@@ -70,23 +78,31 @@ class FileGenerator {
// files other than the outer file (i.e. one for each message, enum, and
// service type).
void GenerateSiblings(const string& package_dir,
- OutputDirectory* output_directory,
+ GeneratorContext* generator_context,
vector<string>* file_list);
const string& java_package() { return java_package_; }
const string& classname() { return classname_; }
+
private:
- // Returns whether the dependency should be included in the output file.
- // Always returns true for opensource, but used internally at Google to help
- // improve compatibility with version 1 of protocol buffers.
- bool ShouldIncludeDependency(const FileDescriptor* descriptor);
+ void GenerateDescriptorInitializationCodeForImmutable(io::Printer* printer);
+ void GenerateDescriptorInitializationCodeForMutable(io::Printer* printer);
+
+ bool ShouldIncludeDependency(const FileDescriptor* descriptor,
+ bool immutable_api_);
const FileDescriptor* file_;
string java_package_;
string classname_;
- void GenerateEmbeddedDescriptor(io::Printer* printer);
+ scoped_array<scoped_ptr<MessageGenerator> > message_generators_;
+ scoped_array<scoped_ptr<ExtensionGenerator> > extension_generators_;
+ scoped_ptr<GeneratorFactory> generator_factory_;
+ scoped_ptr<Context> context_;
+ ClassNameResolver* name_resolver_;
+ bool immutable_api_;
+
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator);
};
diff --git a/src/google/protobuf/compiler/java/java_generator.cc b/src/google/protobuf/compiler/java/java_generator.cc
index 745b55a..c3a47e3 100644
--- a/src/google/protobuf/compiler/java/java_generator.cc
+++ b/src/google/protobuf/compiler/java/java_generator.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -33,8 +33,13 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/java/java_generator.h>
+
+#include <memory>
+
#include <google/protobuf/compiler/java/java_file.h>
+#include <google/protobuf/compiler/java/java_generator_factory.h>
#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/compiler/java/java_shared_code_generator.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/descriptor.pb.h>
@@ -51,11 +56,8 @@ JavaGenerator::~JavaGenerator() {}
bool JavaGenerator::Generate(const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* context,
string* error) const {
- vector<pair<string, string> > options;
- ParseGeneratorParameter(parameter, &options);
-
// -----------------------------------------------------------------
// parse generator options
@@ -63,50 +65,84 @@ bool JavaGenerator::Generate(const FileDescriptor* file,
// per line.
string output_list_file;
+
+ vector<pair<string, string> > options;
+ ParseGeneratorParameter(parameter, &options);
+
+ bool generate_immutable_code = false;
+ bool generate_mutable_code = false;
+ bool generate_shared_code = false;
for (int i = 0; i < options.size(); i++) {
if (options[i].first == "output_list_file") {
output_list_file = options[i].second;
+ } else if (options[i].first == "immutable") {
+ generate_immutable_code = true;
+ } else if (options[i].first == "mutable") {
+ generate_mutable_code = true;
+ } else if (options[i].first == "shared") {
+ generate_shared_code = true;
} else {
*error = "Unknown generator option: " + options[i].first;
return false;
}
}
+ // By default we generate immutable code and shared code for immutable API.
+ if (!generate_immutable_code && !generate_mutable_code &&
+ !generate_shared_code) {
+ generate_immutable_code = true;
+ generate_shared_code = true;
+ }
// -----------------------------------------------------------------
- FileGenerator file_generator(file);
- if (!file_generator.Validate(error)) {
- return false;
+ vector<string> all_files;
+
+ vector<FileGenerator*> file_generators;
+ if (generate_immutable_code) {
+ file_generators.push_back(new FileGenerator(file, /* immutable = */ true));
+ }
+ for (int i = 0; i < file_generators.size(); ++i) {
+ if (!file_generators[i]->Validate(error)) {
+ for (int j = 0; j < file_generators.size(); ++j) {
+ delete file_generators[j];
+ }
+ return false;
+ }
}
- string package_dir =
- StringReplace(file_generator.java_package(), ".", "/", true);
- if (!package_dir.empty()) package_dir += "/";
+ for (int i = 0; i < file_generators.size(); ++i) {
+ FileGenerator* file_generator = file_generators[i];
- vector<string> all_files;
+ string package_dir = JavaPackageToDir(file_generator->java_package());
+
+ string java_filename = package_dir;
+ java_filename += file_generator->classname();
+ java_filename += ".java";
+ all_files.push_back(java_filename);
- string java_filename = package_dir;
- java_filename += file_generator.classname();
- java_filename += ".java";
- all_files.push_back(java_filename);
+ // Generate main java file.
+ scoped_ptr<io::ZeroCopyOutputStream> output(
+ context->Open(java_filename));
+ io::Printer printer(output.get(), '$');
+ file_generator->Generate(&printer);
- // Generate main java file.
- scoped_ptr<io::ZeroCopyOutputStream> output(
- output_directory->Open(java_filename));
- io::Printer printer(output.get(), '$');
- file_generator.Generate(&printer);
+ // Generate sibling files.
+ file_generator->GenerateSiblings(package_dir, context, &all_files);
+ }
- // Generate sibling files.
- file_generator.GenerateSiblings(package_dir, output_directory, &all_files);
+ for (int i = 0; i < file_generators.size(); ++i) {
+ delete file_generators[i];
+ }
+ file_generators.clear();
// Generate output list if requested.
if (!output_list_file.empty()) {
// Generate output list. This is just a simple text file placed in a
// deterministic location which lists the .java files being generated.
scoped_ptr<io::ZeroCopyOutputStream> srclist_raw_output(
- output_directory->Open(output_list_file));
+ context->Open(output_list_file));
io::Printer srclist_printer(srclist_raw_output.get(), '$');
for (int i = 0; i < all_files.size(); i++) {
srclist_printer.Print("$filename$\n", "filename", all_files[i]);
diff --git a/src/google/protobuf/compiler/java/java_generator.h b/src/google/protobuf/compiler/java/java_generator.h
index c91c905..47f76be 100644
--- a/src/google/protobuf/compiler/java/java_generator.h
+++ b/src/google/protobuf/compiler/java/java_generator.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -57,7 +57,7 @@ class LIBPROTOC_EXPORT JavaGenerator : public CodeGenerator {
// implements CodeGenerator ----------------------------------------
bool Generate(const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* context,
string* error) const;
private:
diff --git a/src/google/protobuf/compiler/java/java_generator_factory.cc b/src/google/protobuf/compiler/java/java_generator_factory.cc
new file mode 100644
index 0000000..2d1437f
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_generator_factory.cc
@@ -0,0 +1,77 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: liujisi@google.com (Pherl Liu)
+
+#include <google/protobuf/compiler/java/java_generator_factory.h>
+
+#include <google/protobuf/compiler/java/java_context.h>
+#include <google/protobuf/compiler/java/java_enum_field.h>
+#include <google/protobuf/compiler/java/java_extension.h>
+#include <google/protobuf/compiler/java/java_field.h>
+#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/compiler/java/java_message.h>
+#include <google/protobuf/compiler/java/java_service.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+GeneratorFactory::GeneratorFactory() {}
+GeneratorFactory::~GeneratorFactory() {}
+
+// ===================================================================
+
+ImmutableGeneratorFactory::ImmutableGeneratorFactory(
+ Context* context) : context_(context) {
+}
+ImmutableGeneratorFactory::~ImmutableGeneratorFactory() {}
+
+MessageGenerator* ImmutableGeneratorFactory::NewMessageGenerator(
+ const Descriptor* descriptor) const {
+ return new ImmutableMessageGenerator(descriptor, context_);
+}
+
+ExtensionGenerator* ImmutableGeneratorFactory::NewExtensionGenerator(
+ const FieldDescriptor* descriptor) const {
+ return new ImmutableExtensionGenerator(descriptor, context_);
+}
+
+ServiceGenerator* ImmutableGeneratorFactory::NewServiceGenerator(
+ const ServiceDescriptor* descriptor) const {
+ return new ImmutableServiceGenerator(descriptor, context_);
+}
+
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/compiler/java/java_generator_factory.h b/src/google/protobuf/compiler/java/java_generator_factory.h
new file mode 100644
index 0000000..55365a9
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_generator_factory.h
@@ -0,0 +1,101 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: liujisi@google.com (Pherl Liu)
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_GENERATOR_FACTORY_H__
+#define GOOGLE_PROTOBUF_COMPILER_JAVA_GENERATOR_FACTORY_H__
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+ class FieldDescriptor; // descriptor.h
+ class Descriptor; // descriptor.h
+ class ServiceDescriptor; // descriptor.h
+ namespace compiler {
+ namespace java {
+ class MessageGenerator; // message.h
+ class ExtensionGenerator; // extension.h
+ class ServiceGenerator; // service.h
+ class Context; // context.h
+ }
+ }
+}
+
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+class GeneratorFactory {
+ public:
+ GeneratorFactory();
+ virtual ~GeneratorFactory();
+
+ virtual MessageGenerator* NewMessageGenerator(
+ const Descriptor* descriptor) const = 0;
+
+ virtual ExtensionGenerator* NewExtensionGenerator(
+ const FieldDescriptor* descriptor) const = 0;
+
+ virtual ServiceGenerator* NewServiceGenerator(
+ const ServiceDescriptor* descriptor) const = 0;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GeneratorFactory);
+};
+
+// Factory that creates generators for immutable-default messages.
+class ImmutableGeneratorFactory : public GeneratorFactory {
+ public:
+ ImmutableGeneratorFactory(Context* context);
+ virtual ~ImmutableGeneratorFactory();
+
+ virtual MessageGenerator* NewMessageGenerator(
+ const Descriptor* descriptor) const;
+
+ virtual ExtensionGenerator* NewExtensionGenerator(
+ const FieldDescriptor* descriptor) const;
+
+ virtual ServiceGenerator* NewServiceGenerator(
+ const ServiceDescriptor* descriptor) const;
+
+ private:
+ Context* context_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableGeneratorFactory);
+};
+
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_GENERATOR_FACTORY_H__
diff --git a/src/google/protobuf/compiler/java/java_helpers.cc b/src/google/protobuf/compiler/java/java_helpers.cc
index 7ed0c3c..3efd7ed 100644
--- a/src/google/protobuf/compiler/java/java_helpers.cc
+++ b/src/google/protobuf/compiler/java/java_helpers.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -32,11 +32,15 @@
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
+#include <algorithm>
+#include <google/protobuf/stubs/hash.h>
#include <limits>
#include <vector>
#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/compiler/java/java_name_resolver.h>
#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
@@ -45,6 +49,9 @@ namespace protobuf {
namespace compiler {
namespace java {
+using internal::WireFormat;
+using internal::WireFormatLite;
+
const char kThickSeparator[] =
"// ===================================================================\n";
const char kThinSeparator[] =
@@ -54,18 +61,47 @@ namespace {
const char* kDefaultPackage = "";
-const string& FieldName(const FieldDescriptor* field) {
+// Names that should be avoided as field names.
+// Using them will cause the compiler to generate accessors whose names are
+// colliding with methods defined in base classes.
+const char* kForbiddenWordList[] = {
+ // message base class:
+ "cached_size", "serialized_size",
+ // java.lang.Object:
+ "class",
+};
+
+bool IsForbidden(const string& field_name) {
+ for (int i = 0; i < GOOGLE_ARRAYSIZE(kForbiddenWordList); ++i) {
+ if (field_name == kForbiddenWordList[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+string FieldName(const FieldDescriptor* field) {
+ string field_name;
// Groups are hacky: The name of the field is just the lower-cased name
// of the group type. In Java, though, we would like to retain the original
// capitalization of the type name.
if (GetType(field) == FieldDescriptor::TYPE_GROUP) {
- return field->message_type()->name();
+ field_name = field->message_type()->name();
} else {
- return field->name();
+ field_name = field->name();
}
+ if (IsForbidden(field_name)) {
+ // Append a trailing "#" to indicate that the name should be decorated to
+ // avoid collision with other names.
+ field_name += "#";
+ }
+ return field_name;
}
-string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) {
+
+} // namespace
+
+string UnderscoresToCamelCase(const string& input, bool cap_next_letter) {
string result;
// Note: I distrust ctype.h due to locales.
for (int i = 0; i < input.size(); i++) {
@@ -93,21 +129,27 @@ string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) {
cap_next_letter = true;
}
}
+ // Add a trailing "_" if the name should be altered.
+ if (input[input.size() - 1] == '#') {
+ result += '_';
+ }
return result;
}
-} // namespace
-
string UnderscoresToCamelCase(const FieldDescriptor* field) {
- return UnderscoresToCamelCaseImpl(FieldName(field), false);
+ return UnderscoresToCamelCase(FieldName(field), false);
}
string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) {
- return UnderscoresToCamelCaseImpl(FieldName(field), true);
+ return UnderscoresToCamelCase(FieldName(field), true);
}
string UnderscoresToCamelCase(const MethodDescriptor* method) {
- return UnderscoresToCamelCaseImpl(method->name(), false);
+ return UnderscoresToCamelCase(method->name(), false);
+}
+
+string UniqueFileScopeIdentifier(const Descriptor* descriptor) {
+ return "static_" + StringReplace(descriptor->full_name(), ".", "_", true);
}
string StripProto(const string& filename) {
@@ -118,35 +160,38 @@ string StripProto(const string& filename) {
}
}
-string FileClassName(const FileDescriptor* file) {
- if (file->options().has_java_outer_classname()) {
- return file->options().java_outer_classname();
- } else {
- string basename;
- string::size_type last_slash = file->name().find_last_of('/');
- if (last_slash == string::npos) {
- basename = file->name();
- } else {
- basename = file->name().substr(last_slash + 1);
- }
- return UnderscoresToCamelCaseImpl(StripProto(basename), true);
- }
+string FileClassName(const FileDescriptor* file, bool immutable) {
+ ClassNameResolver name_resolver;
+ return name_resolver.GetFileClassName(file, immutable);
}
-string FileJavaPackage(const FileDescriptor* file) {
+string FileJavaPackage(const FileDescriptor* file, bool immutable) {
+ string result;
+
if (file->options().has_java_package()) {
- return file->options().java_package();
+ result = file->options().java_package();
} else {
- string result = kDefaultPackage;
+ result = kDefaultPackage;
if (!file->package().empty()) {
if (!result.empty()) result += '.';
result += file->package();
}
- return result;
}
+
+ return result;
+}
+
+string JavaPackageToDir(string package_name) {
+ string package_dir =
+ StringReplace(package_name, ".", "/", true);
+ if (!package_dir.empty()) package_dir += "/";
+ return package_dir;
}
-string ToJavaName(const string& full_name, const FileDescriptor* file) {
+// TODO(xiaofeng): This function is only kept for it's publicly referenced.
+// It should be removed after mutable API up-integration.
+string ToJavaName(const string& full_name,
+ const FileDescriptor* file) {
string result;
if (file->options().java_multiple_files()) {
result = FileJavaPackage(file);
@@ -166,11 +211,43 @@ string ToJavaName(const string& full_name, const FileDescriptor* file) {
return result;
}
+string ClassName(const Descriptor* descriptor) {
+ ClassNameResolver name_resolver;
+ return name_resolver.GetClassName(descriptor, true);
+}
+
+string ClassName(const EnumDescriptor* descriptor) {
+ ClassNameResolver name_resolver;
+ return name_resolver.GetClassName(descriptor, true);
+}
+
+string ClassName(const ServiceDescriptor* descriptor) {
+ ClassNameResolver name_resolver;
+ return name_resolver.GetClassName(descriptor, true);
+}
+
string ClassName(const FileDescriptor* descriptor) {
- string result = FileJavaPackage(descriptor);
- if (!result.empty()) result += '.';
- result += FileClassName(descriptor);
- return result;
+ ClassNameResolver name_resolver;
+ return name_resolver.GetClassName(descriptor, true);
+}
+
+string ExtraMessageInterfaces(const Descriptor* descriptor) {
+ string interfaces = "// @@protoc_insertion_point(message_implements:"
+ + descriptor->full_name() + ")";
+ return interfaces;
+}
+
+
+string ExtraBuilderInterfaces(const Descriptor* descriptor) {
+ string interfaces = "// @@protoc_insertion_point(builder_implements:"
+ + descriptor->full_name() + ")";
+ return interfaces;
+}
+
+string ExtraMessageOrBuilderInterfaces(const Descriptor* descriptor) {
+ string interfaces = "// @@protoc_insertion_point(interface_extends:"
+ + descriptor->full_name() + ")";
+ return interfaces;
}
string FieldConstantName(const FieldDescriptor *field) {
@@ -249,6 +326,35 @@ const char* BoxedPrimitiveTypeName(JavaType type) {
return NULL;
}
+const char* FieldTypeName(FieldDescriptor::Type field_type) {
+ switch (field_type) {
+ case FieldDescriptor::TYPE_INT32 : return "INT32";
+ case FieldDescriptor::TYPE_UINT32 : return "UINT32";
+ case FieldDescriptor::TYPE_SINT32 : return "SINT32";
+ case FieldDescriptor::TYPE_FIXED32 : return "FIXED32";
+ case FieldDescriptor::TYPE_SFIXED32: return "SFIXED32";
+ case FieldDescriptor::TYPE_INT64 : return "INT64";
+ case FieldDescriptor::TYPE_UINT64 : return "UINT64";
+ case FieldDescriptor::TYPE_SINT64 : return "SINT64";
+ case FieldDescriptor::TYPE_FIXED64 : return "FIXED64";
+ case FieldDescriptor::TYPE_SFIXED64: return "SFIXED64";
+ case FieldDescriptor::TYPE_FLOAT : return "FLOAT";
+ case FieldDescriptor::TYPE_DOUBLE : return "DOUBLE";
+ case FieldDescriptor::TYPE_BOOL : return "BOOL";
+ case FieldDescriptor::TYPE_STRING : return "STRING";
+ case FieldDescriptor::TYPE_BYTES : return "BYTES";
+ case FieldDescriptor::TYPE_ENUM : return "ENUM";
+ case FieldDescriptor::TYPE_GROUP : return "GROUP";
+ case FieldDescriptor::TYPE_MESSAGE : return "MESSAGE";
+
+ // No default because we want the compiler to complain if any new
+ // types are added.
+ }
+
+ GOOGLE_LOG(FATAL) << "Can't get here.";
+ return NULL;
+}
+
bool AllAscii(const string& text) {
for (int i = 0; i < text.size(); i++) {
if ((text[i] & 0x80) != 0) {
@@ -258,7 +364,8 @@ bool AllAscii(const string& text) {
return true;
}
-string DefaultValue(const FieldDescriptor* field) {
+string DefaultValue(const FieldDescriptor* field, bool immutable,
+ ClassNameResolver* name_resolver) {
// Switch on CppType since we need to know which default_value_* method
// of FieldDescriptor to call.
switch (field->cpp_type()) {
@@ -315,17 +422,18 @@ string DefaultValue(const FieldDescriptor* field) {
} else {
// See comments in Internal.java for gory details.
return strings::Substitute(
- "com.google.protobuf.Internal.stringDefaultValue(\"$0\")",
- CEscape(field->default_value_string()));
+ "com.google.protobuf.Internal.stringDefaultValue(\"$0\")",
+ CEscape(field->default_value_string()));
}
}
case FieldDescriptor::CPPTYPE_ENUM:
- return ClassName(field->enum_type()) + "." +
- field->default_value_enum()->name();
+ return name_resolver->GetClassName(field->enum_type(), immutable) + "." +
+ field->default_value_enum()->name();
case FieldDescriptor::CPPTYPE_MESSAGE:
- return ClassName(field->message_type()) + ".getDefaultInstance()";
+ return name_resolver->GetClassName(field->message_type(), immutable) +
+ ".getDefaultInstance()";
// No default because we want the compiler to complain if any new
// types are added.
@@ -335,6 +443,294 @@ string DefaultValue(const FieldDescriptor* field) {
return "";
}
+bool IsDefaultValueJavaDefault(const FieldDescriptor* field) {
+ // Switch on CppType since we need to know which default_value_* method
+ // of FieldDescriptor to call.
+ switch (field->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32:
+ return field->default_value_int32() == 0;
+ case FieldDescriptor::CPPTYPE_UINT32:
+ return field->default_value_uint32() == 0;
+ case FieldDescriptor::CPPTYPE_INT64:
+ return field->default_value_int64() == 0L;
+ case FieldDescriptor::CPPTYPE_UINT64:
+ return field->default_value_uint64() == 0L;
+ case FieldDescriptor::CPPTYPE_DOUBLE:
+ return field->default_value_double() == 0.0;
+ case FieldDescriptor::CPPTYPE_FLOAT:
+ return field->default_value_float() == 0.0;
+ case FieldDescriptor::CPPTYPE_BOOL:
+ return field->default_value_bool() == false;
+
+ case FieldDescriptor::CPPTYPE_STRING:
+ case FieldDescriptor::CPPTYPE_ENUM:
+ case FieldDescriptor::CPPTYPE_MESSAGE:
+ return false;
+
+ // No default because we want the compiler to complain if any new
+ // types are added.
+ }
+
+ GOOGLE_LOG(FATAL) << "Can't get here.";
+ return false;
+}
+
+const char* bit_masks[] = {
+ "0x00000001",
+ "0x00000002",
+ "0x00000004",
+ "0x00000008",
+ "0x00000010",
+ "0x00000020",
+ "0x00000040",
+ "0x00000080",
+
+ "0x00000100",
+ "0x00000200",
+ "0x00000400",
+ "0x00000800",
+ "0x00001000",
+ "0x00002000",
+ "0x00004000",
+ "0x00008000",
+
+ "0x00010000",
+ "0x00020000",
+ "0x00040000",
+ "0x00080000",
+ "0x00100000",
+ "0x00200000",
+ "0x00400000",
+ "0x00800000",
+
+ "0x01000000",
+ "0x02000000",
+ "0x04000000",
+ "0x08000000",
+ "0x10000000",
+ "0x20000000",
+ "0x40000000",
+ "0x80000000",
+};
+
+string GetBitFieldName(int index) {
+ string varName = "bitField";
+ varName += SimpleItoa(index);
+ varName += "_";
+ return varName;
+}
+
+string GetBitFieldNameForBit(int bitIndex) {
+ return GetBitFieldName(bitIndex / 32);
+}
+
+namespace {
+
+string GenerateGetBitInternal(const string& prefix, int bitIndex) {
+ string varName = prefix + GetBitFieldNameForBit(bitIndex);
+ int bitInVarIndex = bitIndex % 32;
+
+ string mask = bit_masks[bitInVarIndex];
+ string result = "((" + varName + " & " + mask + ") == " + mask + ")";
+ return result;
+}
+
+string GenerateSetBitInternal(const string& prefix, int bitIndex) {
+ string varName = prefix + GetBitFieldNameForBit(bitIndex);
+ int bitInVarIndex = bitIndex % 32;
+
+ string mask = bit_masks[bitInVarIndex];
+ string result = varName + " |= " + mask;
+ return result;
+}
+
+} // namespace
+
+string GenerateGetBit(int bitIndex) {
+ return GenerateGetBitInternal("", bitIndex);
+}
+
+string GenerateSetBit(int bitIndex) {
+ return GenerateSetBitInternal("", bitIndex);
+}
+
+string GenerateClearBit(int bitIndex) {
+ string varName = GetBitFieldNameForBit(bitIndex);
+ int bitInVarIndex = bitIndex % 32;
+
+ string mask = bit_masks[bitInVarIndex];
+ string result = varName + " = (" + varName + " & ~" + mask + ")";
+ return result;
+}
+
+string GenerateGetBitFromLocal(int bitIndex) {
+ return GenerateGetBitInternal("from_", bitIndex);
+}
+
+string GenerateSetBitToLocal(int bitIndex) {
+ return GenerateSetBitInternal("to_", bitIndex);
+}
+
+string GenerateGetBitMutableLocal(int bitIndex) {
+ return GenerateGetBitInternal("mutable_", bitIndex);
+}
+
+string GenerateSetBitMutableLocal(int bitIndex) {
+ return GenerateSetBitInternal("mutable_", bitIndex);
+}
+
+bool IsReferenceType(JavaType type) {
+ switch (type) {
+ case JAVATYPE_INT : return false;
+ case JAVATYPE_LONG : return false;
+ case JAVATYPE_FLOAT : return false;
+ case JAVATYPE_DOUBLE : return false;
+ case JAVATYPE_BOOLEAN: return false;
+ case JAVATYPE_STRING : return true;
+ case JAVATYPE_BYTES : return true;
+ case JAVATYPE_ENUM : return true;
+ case JAVATYPE_MESSAGE: return true;
+
+ // No default because we want the compiler to complain if any new
+ // JavaTypes are added.
+ }
+
+ GOOGLE_LOG(FATAL) << "Can't get here.";
+ return false;
+}
+
+const char* GetCapitalizedType(const FieldDescriptor* field, bool immutable) {
+ switch (GetType(field)) {
+ case FieldDescriptor::TYPE_INT32 : return "Int32";
+ case FieldDescriptor::TYPE_UINT32 : return "UInt32";
+ case FieldDescriptor::TYPE_SINT32 : return "SInt32";
+ case FieldDescriptor::TYPE_FIXED32 : return "Fixed32";
+ case FieldDescriptor::TYPE_SFIXED32: return "SFixed32";
+ case FieldDescriptor::TYPE_INT64 : return "Int64";
+ case FieldDescriptor::TYPE_UINT64 : return "UInt64";
+ case FieldDescriptor::TYPE_SINT64 : return "SInt64";
+ case FieldDescriptor::TYPE_FIXED64 : return "Fixed64";
+ case FieldDescriptor::TYPE_SFIXED64: return "SFixed64";
+ case FieldDescriptor::TYPE_FLOAT : return "Float";
+ case FieldDescriptor::TYPE_DOUBLE : return "Double";
+ case FieldDescriptor::TYPE_BOOL : return "Bool";
+ case FieldDescriptor::TYPE_STRING : return "String";
+ case FieldDescriptor::TYPE_BYTES : {
+ return "Bytes";
+ }
+ case FieldDescriptor::TYPE_ENUM : return "Enum";
+ case FieldDescriptor::TYPE_GROUP : return "Group";
+ case FieldDescriptor::TYPE_MESSAGE : return "Message";
+
+ // No default because we want the compiler to complain if any new
+ // types are added.
+ }
+
+ GOOGLE_LOG(FATAL) << "Can't get here.";
+ return NULL;
+}
+
+// For encodings with fixed sizes, returns that size in bytes. Otherwise
+// returns -1.
+int FixedSize(FieldDescriptor::Type type) {
+ switch (type) {
+ case FieldDescriptor::TYPE_INT32 : return -1;
+ case FieldDescriptor::TYPE_INT64 : return -1;
+ case FieldDescriptor::TYPE_UINT32 : return -1;
+ case FieldDescriptor::TYPE_UINT64 : return -1;
+ case FieldDescriptor::TYPE_SINT32 : return -1;
+ case FieldDescriptor::TYPE_SINT64 : return -1;
+ case FieldDescriptor::TYPE_FIXED32 : return WireFormatLite::kFixed32Size;
+ case FieldDescriptor::TYPE_FIXED64 : return WireFormatLite::kFixed64Size;
+ case FieldDescriptor::TYPE_SFIXED32: return WireFormatLite::kSFixed32Size;
+ case FieldDescriptor::TYPE_SFIXED64: return WireFormatLite::kSFixed64Size;
+ case FieldDescriptor::TYPE_FLOAT : return WireFormatLite::kFloatSize;
+ case FieldDescriptor::TYPE_DOUBLE : return WireFormatLite::kDoubleSize;
+
+ case FieldDescriptor::TYPE_BOOL : return WireFormatLite::kBoolSize;
+ case FieldDescriptor::TYPE_ENUM : return -1;
+
+ case FieldDescriptor::TYPE_STRING : return -1;
+ case FieldDescriptor::TYPE_BYTES : return -1;
+ case FieldDescriptor::TYPE_GROUP : return -1;
+ case FieldDescriptor::TYPE_MESSAGE : return -1;
+
+ // No default because we want the compiler to complain if any new
+ // types are added.
+ }
+ GOOGLE_LOG(FATAL) << "Can't get here.";
+ return -1;
+}
+
+// Sort the fields of the given Descriptor by number into a new[]'d array
+// and return it. The caller should delete the returned array.
+const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) {
+ const FieldDescriptor** fields =
+ new const FieldDescriptor*[descriptor->field_count()];
+ for (int i = 0; i < descriptor->field_count(); i++) {
+ fields[i] = descriptor->field(i);
+ }
+ sort(fields, fields + descriptor->field_count(),
+ FieldOrderingByNumber());
+ return fields;
+}
+
+// Returns true if the message type has any required fields. If it doesn't,
+// we can optimize out calls to its isInitialized() method.
+//
+// already_seen is used to avoid checking the same type multiple times
+// (and also to protect against recursion).
+bool HasRequiredFields(
+ const Descriptor* type,
+ hash_set<const Descriptor*>* already_seen) {
+ if (already_seen->count(type) > 0) {
+ // The type is already in cache. This means that either:
+ // a. The type has no required fields.
+ // b. We are in the midst of checking if the type has required fields,
+ // somewhere up the stack. In this case, we know that if the type
+ // has any required fields, they'll be found when we return to it,
+ // and the whole call to HasRequiredFields() will return true.
+ // Therefore, we don't have to check if this type has required fields
+ // here.
+ return false;
+ }
+ already_seen->insert(type);
+
+ // If the type has extensions, an extension with message type could contain
+ // required fields, so we have to be conservative and assume such an
+ // extension exists.
+ if (type->extension_range_count() > 0) return true;
+
+ for (int i = 0; i < type->field_count(); i++) {
+ const FieldDescriptor* field = type->field(i);
+ if (field->is_required()) {
+ return true;
+ }
+ if (GetJavaType(field) == JAVATYPE_MESSAGE) {
+ if (HasRequiredFields(field->message_type(), already_seen)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool HasRequiredFields(const Descriptor* type) {
+ hash_set<const Descriptor*> already_seen;
+ return HasRequiredFields(type, &already_seen);
+}
+
+bool HasRepeatedFields(const Descriptor* descriptor) {
+ for (int i = 0; i < descriptor->field_count(); ++i) {
+ const FieldDescriptor* field = descriptor->field(i);
+ if (field->is_repeated()) {
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace java
} // namespace compiler
} // namespace protobuf
diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h
index 3c8974c..93f23a9 100644
--- a/src/google/protobuf/compiler/java/java_helpers.h
+++ b/src/google/protobuf/compiler/java/java_helpers.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -49,6 +49,9 @@ namespace java {
extern const char kThickSeparator[];
extern const char kThinSeparator[];
+// Converts a name to camel-case. If cap_first_letter is true, capitalize the
+// first letter.
+string UnderscoresToCamelCase(const string& name, bool cap_first_letter);
// Converts the field's name to camel-case, e.g. "foo_bar_baz" becomes
// "fooBarBaz" or "FooBarBaz", respectively.
string UnderscoresToCamelCase(const FieldDescriptor* field);
@@ -58,36 +61,70 @@ string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field);
// of lower-casing the first letter of the name.)
string UnderscoresToCamelCase(const MethodDescriptor* method);
+// Get an identifier that uniquely identifies this type within the file.
+// This is used to declare static variables related to this type at the
+// outermost file scope.
+string UniqueFileScopeIdentifier(const Descriptor* descriptor);
+
// Strips ".proto" or ".protodevel" from the end of a filename.
string StripProto(const string& filename);
-// Gets the unqualified class name for the file. Each .proto file becomes a
-// single Java class, with all its contents nested in that class.
-string FileClassName(const FileDescriptor* file);
+// Gets the unqualified class name for the file. For each .proto file, there
+// will be one Java class containing all the immutable messages and another
+// Java class containing all the mutable messages.
+// TODO(xiaofeng): remove the default value after updating client code.
+string FileClassName(const FileDescriptor* file, bool immutable = true);
// Returns the file's Java package name.
-string FileJavaPackage(const FileDescriptor* file);
+string FileJavaPackage(const FileDescriptor* file, bool immutable = true);
+
+// Returns output directory for the given package name.
+string JavaPackageToDir(string package_name);
// Converts the given fully-qualified name in the proto namespace to its
// fully-qualified name in the Java namespace, given that it is in the given
// file.
-string ToJavaName(const string& full_name, const FileDescriptor* file);
+// TODO(xiaofeng): this method is deprecated and should be removed in the
+// future.
+string ToJavaName(const string& full_name,
+ const FileDescriptor* file);
-// These return the fully-qualified class name corresponding to the given
-// descriptor.
-inline string ClassName(const Descriptor* descriptor) {
- return ToJavaName(descriptor->full_name(), descriptor->file());
-}
-inline string ClassName(const EnumDescriptor* descriptor) {
- return ToJavaName(descriptor->full_name(), descriptor->file());
-}
-inline string ClassName(const ServiceDescriptor* descriptor) {
- return ToJavaName(descriptor->full_name(), descriptor->file());
+// TODO(xiaofeng): the following methods are kept for they are exposed
+// publicly in //google/protobuf/compiler/java/names.h. They return
+// immutable names only and should be removed after mutable API is
+// integrated into google3.
+string ClassName(const Descriptor* descriptor);
+string ClassName(const EnumDescriptor* descriptor);
+string ClassName(const ServiceDescriptor* descriptor);
+string ClassName(const FileDescriptor* descriptor);
+
+// Comma-separate list of option-specified interfaces implemented by the
+// Message, to follow the "implements" declaration of the Message definition.
+string ExtraMessageInterfaces(const Descriptor* descriptor);
+// Comma-separate list of option-specified interfaces implemented by the
+// MutableMessage, to follow the "implements" declaration of the MutableMessage
+// definition.
+string ExtraMutableMessageInterfaces(const Descriptor* descriptor);
+// Comma-separate list of option-specified interfaces implemented by the
+// Builder, to follow the "implements" declaration of the Builder definition.
+string ExtraBuilderInterfaces(const Descriptor* descriptor);
+// Comma-separate list of option-specified interfaces extended by the
+// MessageOrBuilder, to follow the "extends" declaration of the
+// MessageOrBuilder definition.
+string ExtraMessageOrBuilderInterfaces(const Descriptor* descriptor);
+
+// Get the unqualified Java class name for mutable messages. i.e. without
+// package or outer classnames.
+inline string ShortMutableJavaClassName(const Descriptor* descriptor) {
+ return descriptor->name();
}
-inline string ExtensionIdentifierName(const FieldDescriptor* descriptor) {
- return ToJavaName(descriptor->full_name(), descriptor->file());
+
+
+// Whether we should generate multiple java files for messages.
+inline bool MultipleJavaFiles(
+ const FileDescriptor* descriptor, bool immutable) {
+ return descriptor->options().java_multiple_files();
}
-string ClassName(const FileDescriptor* descriptor);
// Get the unqualified name that should be used for a field's field
// number constant.
@@ -117,10 +154,23 @@ JavaType GetJavaType(const FieldDescriptor* field);
// types.
const char* BoxedPrimitiveTypeName(JavaType type);
-string DefaultValue(const FieldDescriptor* field);
+// Get the name of the java enum constant representing this type. E.g.,
+// "INT32" for FieldDescriptor::TYPE_INT32. The enum constant's full
+// name is "com.google.protobuf.WireFormat.FieldType.INT32".
+const char* FieldTypeName(const FieldDescriptor::Type field_type);
-// Does this message class keep track of unknown fields?
-inline bool HasUnknownFields(const Descriptor* descriptor) {
+class ClassNameResolver;
+string DefaultValue(const FieldDescriptor* field, bool immutable,
+ ClassNameResolver* name_resolver);
+inline string ImmutableDefaultValue(const FieldDescriptor* field,
+ ClassNameResolver* name_resolver) {
+ return DefaultValue(field, true, name_resolver);
+}
+bool IsDefaultValueJavaDefault(const FieldDescriptor* field);
+
+// Does this message class use UnknownFieldSet?
+// Otherwise, unknown fields will be stored in a ByteString object
+inline bool UseUnknownFieldSet(const Descriptor* descriptor) {
return descriptor->file()->options().optimize_for() !=
FileOptions::LITE_RUNTIME;
}
@@ -132,6 +182,11 @@ inline bool HasGeneratedMethods(const Descriptor* descriptor) {
FileOptions::CODE_SIZE;
}
+// Does this message have specialized equals() and hashCode() methods?
+inline bool HasEqualsAndHashCode(const Descriptor* descriptor) {
+ return descriptor->file()->options().java_generate_equals_and_hash();
+}
+
// Does this message class have descriptor and reflection methods?
inline bool HasDescriptorMethods(const Descriptor* descriptor) {
return descriptor->file()->options().optimize_for() !=
@@ -146,6 +201,12 @@ inline bool HasDescriptorMethods(const FileDescriptor* descriptor) {
FileOptions::LITE_RUNTIME;
}
+inline bool HasNestedBuilders(const Descriptor* descriptor) {
+ // The proto-lite version doesn't support nested builders.
+ return descriptor->file()->options().optimize_for() !=
+ FileOptions::LITE_RUNTIME;
+}
+
// Should we generate generic services for this file?
inline bool HasGenericServices(const FileDescriptor *file) {
return file->service_count() > 0 &&
@@ -153,6 +214,106 @@ inline bool HasGenericServices(const FileDescriptor *file) {
file->options().java_generic_services();
}
+inline bool IsLazy(const FieldDescriptor* descriptor) {
+ // Currently, the proto-lite version suports lazy field.
+ // TODO(niwasaki): Support lazy fields also for other proto runtimes.
+ if (descriptor->file()->options().optimize_for() !=
+ FileOptions::LITE_RUNTIME) {
+ return false;
+ }
+ return descriptor->options().lazy();
+}
+
+// Methods for shared bitfields.
+
+// Gets the name of the shared bitfield for the given index.
+string GetBitFieldName(int index);
+
+// Gets the name of the shared bitfield for the given bit index.
+// Effectively, GetBitFieldName(bitIndex / 32)
+string GetBitFieldNameForBit(int bitIndex);
+
+// Generates the java code for the expression that returns the boolean value
+// of the bit of the shared bitfields for the given bit index.
+// Example: "((bitField1_ & 0x04) == 0x04)"
+string GenerateGetBit(int bitIndex);
+
+// Generates the java code for the expression that sets the bit of the shared
+// bitfields for the given bit index.
+// Example: "bitField1_ = (bitField1_ | 0x04)"
+string GenerateSetBit(int bitIndex);
+
+// Generates the java code for the expression that clears the bit of the shared
+// bitfields for the given bit index.
+// Example: "bitField1_ = (bitField1_ & ~0x04)"
+string GenerateClearBit(int bitIndex);
+
+// Does the same as GenerateGetBit but operates on the bit field on a local
+// variable. This is used by the builder to copy the value in the builder to
+// the message.
+// Example: "((from_bitField1_ & 0x04) == 0x04)"
+string GenerateGetBitFromLocal(int bitIndex);
+
+// Does the same as GenerateSetBit but operates on the bit field on a local
+// variable. This is used by the builder to copy the value in the builder to
+// the message.
+// Example: "to_bitField1_ = (to_bitField1_ | 0x04)"
+string GenerateSetBitToLocal(int bitIndex);
+
+// Does the same as GenerateGetBit but operates on the bit field on a local
+// variable. This is used by the parsing constructor to record if a repeated
+// field is mutable.
+// Example: "((mutable_bitField1_ & 0x04) == 0x04)"
+string GenerateGetBitMutableLocal(int bitIndex);
+
+// Does the same as GenerateSetBit but operates on the bit field on a local
+// variable. This is used by the parsing constructor to record if a repeated
+// field is mutable.
+// Example: "mutable_bitField1_ = (mutable_bitField1_ | 0x04)"
+string GenerateSetBitMutableLocal(int bitIndex);
+
+// Returns whether the JavaType is a reference type.
+bool IsReferenceType(JavaType type);
+
+// Returns the capitalized name for calling relative functions in
+// CodedInputStream
+const char* GetCapitalizedType(const FieldDescriptor* field, bool immutable);
+
+// For encodings with fixed sizes, returns that size in bytes. Otherwise
+// returns -1.
+int FixedSize(FieldDescriptor::Type type);
+
+// Comparators used to sort fields in MessageGenerator
+struct FieldOrderingByNumber {
+ inline bool operator()(const FieldDescriptor* a,
+ const FieldDescriptor* b) const {
+ return a->number() < b->number();
+ }
+};
+
+struct ExtensionRangeOrdering {
+ bool operator()(const Descriptor::ExtensionRange* a,
+ const Descriptor::ExtensionRange* b) const {
+ return a->start < b->start;
+ }
+};
+
+// Sort the fields of the given Descriptor by number into a new[]'d array
+// and return it. The caller should delete the returned array.
+const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor);
+
+// Check a message type and its sub-message types recursively to see if any of
+// them has a required field. Return true if a required field is found.
+bool HasRequiredFields(const Descriptor* descriptor);
+
+// Whether a .proto file supports field presence test for non-message types.
+inline bool SupportFieldPresence(const FileDescriptor* descriptor) {
+ return true;
+}
+
+// Check whether a mesasge has repeated fields.
+bool HasRepeatedFields(const Descriptor* descriptor);
+
} // namespace java
} // namespace compiler
} // namespace protobuf
diff --git a/src/google/protobuf/compiler/java/java_lazy_message_field.cc b/src/google/protobuf/compiler/java/java_lazy_message_field.cc
new file mode 100644
index 0000000..21cab7d
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_lazy_message_field.cc
@@ -0,0 +1,826 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: niwasaki@google.com (Naoki Iwasaki)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/compiler/java/java_context.h>
+#include <google/protobuf/compiler/java/java_lazy_message_field.h>
+#include <google/protobuf/compiler/java/java_doc_comment.h>
+#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/io/printer.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+ImmutableLazyMessageFieldGenerator::
+ImmutableLazyMessageFieldGenerator(
+ const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : ImmutableMessageFieldGenerator(
+ descriptor, messageBitIndex, builderBitIndex, context) {
+}
+
+ImmutableLazyMessageFieldGenerator::~ImmutableLazyMessageFieldGenerator() {}
+
+void ImmutableLazyMessageFieldGenerator::
+GenerateMembers(io::Printer* printer) const {
+ printer->Print(variables_,
+ "private com.google.protobuf.LazyFieldLite $name$_ =\n"
+ " new com.google.protobuf.LazyFieldLite();\n");
+
+ PrintExtraFieldInfo(variables_, printer);
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $get_has_field_bit_message$;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " return ($type$) $name$_.getValue($type$.getDefaultInstance());\n"
+ "}\n");
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n"
+ " return $name$_;\n"
+ "}\n");
+ }
+}
+
+void ImmutableLazyMessageFieldGenerator::
+GenerateBuilderMembers(io::Printer* printer) const {
+ // When using nested-builders, the code initially works just like the
+ // non-nested builder case. It only creates a nested builder lazily on
+ // demand and then forever delegates to it after creation.
+
+ printer->Print(variables_,
+ "private com.google.protobuf.LazyFieldLite $name$_ =\n"
+ " new com.google.protobuf.LazyFieldLite();\n");
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ printer->Print(variables_,
+ // If this builder is non-null, it is used and the other fields are
+ // ignored.
+ "private com.google.protobuf.SingleFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;"
+ "\n");
+ }
+
+ // The comments above the methods below are based on a hypothetical
+ // field of type "Field" called "Field".
+
+ // boolean hasField()
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $get_has_field_bit_builder$;\n"
+ "}\n");
+
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " return ($type$) $name$_.getValue($type$.getDefaultInstance());\n"
+ "}\n");
+
+ // Field.Builder setField(Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder set$capitalized_name$($type$ value)",
+
+ "if (value == null) {\n"
+ " throw new NullPointerException();\n"
+ "}\n"
+ "$name$_.setValue(value);\n"
+ "$on_changed$\n",
+
+ NULL, // Lazy fields are supported only for lite-runtime.
+
+ "$set_has_field_bit_builder$;\n"
+ "return this;\n");
+
+ // Field.Builder setField(Field.Builder builderForValue)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " $type$.Builder builderForValue)",
+
+ "$name$_.setValue(builderForValue.build());\n"
+ "$on_changed$\n",
+
+ NULL,
+
+ "$set_has_field_bit_builder$;\n"
+ "return this;\n");
+
+ // Field.Builder mergeField(Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder merge$capitalized_name$($type$ value)",
+
+ "if ($get_has_field_bit_builder$ &&\n"
+ " !$name$_.containsDefaultInstance()) {\n"
+ " $name$_.setValue(\n"
+ " $type$.newBuilder(\n"
+ " get$capitalized_name$()).mergeFrom(value).buildPartial());\n"
+ "} else {\n"
+ " $name$_.setValue(value);\n"
+ "}\n"
+ "$on_changed$\n",
+
+ NULL,
+
+ "$set_has_field_bit_builder$;\n"
+ "return this;\n");
+
+ // Field.Builder clearField()
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder clear$capitalized_name$()",
+
+ "$name$_.clear();\n"
+ "$on_changed$\n",
+
+ NULL,
+
+ "$clear_has_field_bit_builder$;\n"
+ "return this;\n");
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n"
+ " $set_has_field_bit_builder$;\n"
+ " $on_changed$\n"
+ " return get$capitalized_name$FieldBuilder().getBuilder();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n"
+ " if ($name$Builder_ != null) {\n"
+ " return $name$Builder_.getMessageOrBuilder();\n"
+ " } else {\n"
+ " return $name$_;\n"
+ " }\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "private com.google.protobuf.SingleFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder> \n"
+ " get$capitalized_name$FieldBuilder() {\n"
+ " if ($name$Builder_ == null) {\n"
+ " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder>(\n"
+ " $name$_,\n"
+ " getParentForChildren(),\n"
+ " isClean());\n"
+ " $name$_ = null;\n"
+ " }\n"
+ " return $name$Builder_;\n"
+ "}\n");
+ }
+}
+
+
+void ImmutableLazyMessageFieldGenerator::
+GenerateInitializationCode(io::Printer* printer) const {
+ printer->Print(variables_, "$name$_.clear();\n");
+}
+
+void ImmutableLazyMessageFieldGenerator::
+GenerateBuilderClearCode(io::Printer* printer) const {
+ printer->Print(variables_, "$name$_.clear();\n");
+ printer->Print(variables_, "$clear_has_field_bit_builder$;\n");
+}
+
+void ImmutableLazyMessageFieldGenerator::
+GenerateMergingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (other.has$capitalized_name$()) {\n"
+ " $name$_.merge(other.$name$_);\n"
+ " $set_has_field_bit_builder$;\n"
+ "}\n");
+}
+
+void ImmutableLazyMessageFieldGenerator::
+GenerateBuildingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($get_has_field_bit_from_local$) {\n"
+ " $set_has_field_bit_to_local$;\n"
+ "}\n");
+
+ printer->Print(variables_,
+ "result.$name$_.setByteString(\n"
+ " $name$_.toByteString(),\n"
+ " $name$_.getExtensionRegistry());\n");
+}
+
+void ImmutableLazyMessageFieldGenerator::
+GenerateParsingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "$name$_.setByteString(input.readBytes(), extensionRegistry);\n");
+ printer->Print(variables_,
+ "$set_has_field_bit_message$;\n");
+}
+
+void ImmutableLazyMessageFieldGenerator::
+GenerateSerializationCode(io::Printer* printer) const {
+ // Do not de-serialize lazy fields.
+ printer->Print(variables_,
+ "if ($get_has_field_bit_message$) {\n"
+ " output.writeBytes($number$, $name$_.toByteString());\n"
+ "}\n");
+}
+
+void ImmutableLazyMessageFieldGenerator::
+GenerateSerializedSizeCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($get_has_field_bit_message$) {\n"
+ " size += com.google.protobuf.CodedOutputStream\n"
+ " .computeLazyFieldSize($number$, $name$_);\n"
+ "}\n");
+}
+
+// ===================================================================
+
+ImmutableLazyMessageOneofFieldGenerator::
+ImmutableLazyMessageOneofFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : ImmutableLazyMessageFieldGenerator(
+ descriptor, messageBitIndex, builderBitIndex, context) {
+ const OneofGeneratorInfo* info =
+ context->GetOneofGeneratorInfo(descriptor->containing_oneof());
+ SetCommonOneofVariables(descriptor, info, &variables_);
+ variables_["lazy_type"] = "com.google.protobuf.LazyFieldLite";
+}
+
+ImmutableLazyMessageOneofFieldGenerator::
+~ImmutableLazyMessageOneofFieldGenerator() {}
+
+void ImmutableLazyMessageOneofFieldGenerator::
+GenerateMembers(io::Printer* printer) const {
+ PrintExtraFieldInfo(variables_, printer);
+ WriteFieldDocComment(printer, descriptor_);
+
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $has_oneof_case_message$;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " if ($has_oneof_case_message$) {\n"
+ " return ($type$) (($lazy_type$) $oneof_name$_).getValue(\n"
+ " $type$.getDefaultInstance());\n"
+ " }\n"
+ " return $type$.getDefaultInstance();\n"
+ "}\n");
+}
+
+void ImmutableLazyMessageOneofFieldGenerator::
+GenerateBuilderMembers(io::Printer* printer) const {
+ // boolean hasField()
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $has_oneof_case_message$;\n"
+ "}\n");
+
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " if ($has_oneof_case_message$) {\n"
+ " return ($type$) (($lazy_type$) $oneof_name$_).getValue(\n"
+ " $type$.getDefaultInstance());\n"
+ " }\n"
+ " return $type$.getDefaultInstance();\n"
+ "}\n");
+
+ // Field.Builder setField(Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder set$capitalized_name$($type$ value)",
+
+ "if (value == null) {\n"
+ " throw new NullPointerException();\n"
+ "}\n"
+ "if (!($has_oneof_case_message$)) {\n"
+ " $oneof_name$_ = new $lazy_type$();\n"
+ " $set_oneof_case_message$;\n"
+ "}\n"
+ "(($lazy_type$) $oneof_name$_).setValue(value);\n"
+ "$on_changed$\n",
+
+ NULL, // Lazy fields are supported only for lite-runtime.
+
+ "return this;\n");
+
+ // Field.Builder setField(Field.Builder builderForValue)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " $type$.Builder builderForValue)",
+
+ "if (!($has_oneof_case_message$)) {\n"
+ " $oneof_name$_ = new $lazy_type$();\n"
+ " $set_oneof_case_message$;\n"
+ "}\n"
+ "(($lazy_type$) $oneof_name$_).setValue(builderForValue.build());\n"
+ "$on_changed$\n",
+
+ NULL,
+
+ "return this;\n");
+
+ // Field.Builder mergeField(Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder merge$capitalized_name$($type$ value)",
+
+ "if ($has_oneof_case_message$ &&\n"
+ " !(($lazy_type$) $oneof_name$_).containsDefaultInstance()) {\n"
+ " (($lazy_type$) $oneof_name$_).setValue(\n"
+ " $type$.newBuilder(\n"
+ " get$capitalized_name$()).mergeFrom(value).buildPartial());\n"
+ "} else {\n"
+ " if (!($has_oneof_case_message$)) {\n"
+ " $oneof_name$_ = new $lazy_type$();\n"
+ " $set_oneof_case_message$;\n"
+ " }\n"
+ " (($lazy_type$) $oneof_name$_).setValue(value);\n"
+ "}\n"
+ "$on_changed$\n",
+
+ NULL,
+
+ "return this;\n");
+
+ // Field.Builder clearField()
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder clear$capitalized_name$()",
+
+ "if ($has_oneof_case_message$) {\n"
+ " $clear_oneof_case_message$;\n"
+ " $oneof_name$_ = null;\n"
+ " $on_changed$\n"
+ "}\n",
+
+ NULL,
+
+ "return this;\n");
+}
+
+void ImmutableLazyMessageOneofFieldGenerator::
+GenerateMergingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (!($has_oneof_case_message$)) {\n"
+ " $oneof_name$_ = new $lazy_type$();\n"
+ "}\n"
+ "(($lazy_type$) $oneof_name$_).merge(\n"
+ " ($lazy_type$) other.$oneof_name$_);\n"
+ "$set_oneof_case_message$;\n");
+}
+
+void ImmutableLazyMessageOneofFieldGenerator::
+GenerateBuildingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n");
+ printer->Indent();
+
+ printer->Print(variables_,
+ "result.$oneof_name$_ = new $lazy_type$();\n"
+ "(($lazy_type$) result.$oneof_name$_).setByteString(\n"
+ " (($lazy_type$) $oneof_name$_).toByteString(),\n"
+ " (($lazy_type$) $oneof_name$_).getExtensionRegistry());\n");
+ printer->Outdent();
+ printer->Print("}\n");
+}
+
+void ImmutableLazyMessageOneofFieldGenerator::
+GenerateParsingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (!($has_oneof_case_message$)) {\n"
+ " $oneof_name$_ = new $lazy_type$();\n"
+ "}\n"
+ "(($lazy_type$) $oneof_name$_).setByteString(\n"
+ " input.readBytes(), extensionRegistry);\n"
+ "$set_oneof_case_message$;\n");
+}
+
+void ImmutableLazyMessageOneofFieldGenerator::
+GenerateSerializationCode(io::Printer* printer) const {
+ // Do not de-serialize lazy fields.
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n"
+ " output.writeBytes(\n"
+ " $number$, (($lazy_type$) $oneof_name$_).toByteString());\n"
+ "}\n");
+}
+
+void ImmutableLazyMessageOneofFieldGenerator::
+GenerateSerializedSizeCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n"
+ " size += com.google.protobuf.CodedOutputStream\n"
+ " .computeLazyFieldSize($number$, ($lazy_type$) $oneof_name$_);\n"
+ "}\n");
+}
+
+// ===================================================================
+
+RepeatedImmutableLazyMessageFieldGenerator::
+RepeatedImmutableLazyMessageFieldGenerator(
+ const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : RepeatedImmutableMessageFieldGenerator(
+ descriptor, messageBitIndex, builderBitIndex, context) {
+}
+
+
+RepeatedImmutableLazyMessageFieldGenerator::
+~RepeatedImmutableLazyMessageFieldGenerator() {}
+
+void RepeatedImmutableLazyMessageFieldGenerator::
+GenerateMembers(io::Printer* printer) const {
+ printer->Print(variables_,
+ "private java.util.List<com.google.protobuf.LazyFieldLite> $name$_;\n");
+ PrintExtraFieldInfo(variables_, printer);
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.util.List<$type$>\n"
+ " get$capitalized_name$List() {\n"
+ " java.util.List<$type$> list =\n"
+ " new java.util.ArrayList<$type$>($name$_.size());\n"
+ " for (com.google.protobuf.LazyFieldLite lf : $name$_) {\n"
+ " list.add(($type$) lf.getValue($type$.getDefaultInstance()));\n"
+ " }\n"
+ " return list;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.util.List<? extends $type$OrBuilder>\n"
+ " get$capitalized_name$OrBuilderList() {\n"
+ " return get$capitalized_name$List();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public int get$capitalized_name$Count() {\n"
+ " return $name$_.size();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$(int index) {\n"
+ " return ($type$)\n"
+ " $name$_.get(index).getValue($type$.getDefaultInstance());\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n"
+ " int index) {\n"
+ " return ($type$OrBuilder)\n"
+ " $name$_.get(index).getValue($type$.getDefaultInstance());\n"
+ "}\n");
+}
+
+void RepeatedImmutableLazyMessageFieldGenerator::
+GenerateBuilderMembers(io::Printer* printer) const {
+ // When using nested-builders, the code initially works just like the
+ // non-nested builder case. It only creates a nested builder lazily on
+ // demand and then forever delegates to it after creation.
+
+ printer->Print(variables_,
+ "private java.util.List<com.google.protobuf.LazyFieldLite> $name$_ =\n"
+ " java.util.Collections.emptyList();\n"
+
+ "private void ensure$capitalized_name$IsMutable() {\n"
+ " if (!$get_mutable_bit_builder$) {\n"
+ " $name$_ =\n"
+ " new java.util.ArrayList<com.google.protobuf.LazyFieldLite>(\n"
+ " $name$_);\n"
+ " $set_mutable_bit_builder$;\n"
+ " }\n"
+ "}\n"
+ "\n");
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ printer->Print(variables_,
+ // If this builder is non-null, it is used and the other fields are
+ // ignored.
+ "private com.google.protobuf.RepeatedFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;\n"
+ "\n");
+ }
+
+ // The comments above the methods below are based on a hypothetical
+ // repeated field of type "Field" called "RepeatedField".
+
+ // List<Field> getRepeatedFieldList()
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public java.util.List<$type$> get$capitalized_name$List()",
+
+ "java.util.List<$type$> list =\n"
+ " new java.util.ArrayList<$type$>($name$_.size());\n"
+ "for (com.google.protobuf.LazyFieldLite lf : $name$_) {\n"
+ " list.add(($type$) lf.getValue($type$.getDefaultInstance()));\n"
+ "}\n"
+ "return java.util.Collections.unmodifiableList(list);\n",
+
+ "return $name$Builder_.getMessageList();\n",
+
+ NULL);
+
+ // int getRepeatedFieldCount()
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public int get$capitalized_name$Count()",
+
+ "return $name$_.size();\n",
+ "return $name$Builder_.getCount();\n",
+
+ NULL);
+
+ // Field getRepeatedField(int index)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public $type$ get$capitalized_name$(int index)",
+
+ "return ($type$) $name$_.get(index).getValue(\n"
+ " $type$.getDefaultInstance());\n",
+
+ "return $name$Builder_.getMessage(index);\n",
+
+ NULL);
+
+ // Builder setRepeatedField(int index, Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " int index, $type$ value)",
+ "if (value == null) {\n"
+ " throw new NullPointerException();\n"
+ "}\n"
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.set(index, com.google.protobuf.LazyFieldLite.fromValue(value));\n"
+ "$on_changed$\n",
+ "$name$Builder_.setMessage(index, value);\n",
+ "return this;\n");
+
+ // Builder setRepeatedField(int index, Field.Builder builderForValue)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " int index, $type$.Builder builderForValue)",
+
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.set(index, com.google.protobuf.LazyFieldLite.fromValue(\n"
+ " builderForValue.build()));\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.setMessage(index, builderForValue.build());\n",
+
+ "return this;\n");
+
+ // Builder addRepeatedField(Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder add$capitalized_name$($type$ value)",
+
+ "if (value == null) {\n"
+ " throw new NullPointerException();\n"
+ "}\n"
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.add(com.google.protobuf.LazyFieldLite.fromValue(value));\n"
+
+ "$on_changed$\n",
+
+ "$name$Builder_.addMessage(value);\n",
+
+ "return this;\n");
+
+ // Builder addRepeatedField(int index, Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder add$capitalized_name$(\n"
+ " int index, $type$ value)",
+
+ "if (value == null) {\n"
+ " throw new NullPointerException();\n"
+ "}\n"
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.add(index, com.google.protobuf.LazyFieldLite.fromValue(value));\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.addMessage(index, value);\n",
+
+ "return this;\n");
+
+ // Builder addRepeatedField(Field.Builder builderForValue)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder add$capitalized_name$(\n"
+ " $type$.Builder builderForValue)",
+
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.add(com.google.protobuf.LazyFieldLite.fromValue(\n"
+ " builderForValue.build()));\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.addMessage(builderForValue.build());\n",
+
+ "return this;\n");
+
+ // Builder addRepeatedField(int index, Field.Builder builderForValue)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder add$capitalized_name$(\n"
+ " int index, $type$.Builder builderForValue)",
+
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.add(index, com.google.protobuf.LazyFieldLite.fromValue(\n"
+ " builderForValue.build()));\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.addMessage(index, builderForValue.build());\n",
+
+ "return this;\n");
+
+ // Builder addAllRepeatedField(Iterable<Field> values)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder addAll$capitalized_name$(\n"
+ " java.lang.Iterable<? extends $type$> values)",
+
+ "ensure$capitalized_name$IsMutable();\n"
+ "for (com.google.protobuf.MessageLite v : values) {\n"
+ " $name$_.add(com.google.protobuf.LazyFieldLite.fromValue(v));\n"
+ "}\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.addAllMessages(values);\n",
+
+ "return this;\n");
+
+ // Builder clearAllRepeatedField()
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder clear$capitalized_name$()",
+
+ "$name$_ = java.util.Collections.emptyList();\n"
+ "$clear_mutable_bit_builder$;\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.clear();\n",
+
+ "return this;\n");
+
+ // Builder removeRepeatedField(int index)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder remove$capitalized_name$(int index)",
+
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.remove(index);\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.remove(index);\n",
+
+ "return this;\n");
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$.Builder get$capitalized_name$Builder(\n"
+ " int index) {\n"
+ " return get$capitalized_name$FieldBuilder().getBuilder(index);\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n"
+ " int index) {\n"
+ " if ($name$Builder_ == null) {\n"
+ " return $name$_.get(index);"
+ " } else {\n"
+ " return $name$Builder_.getMessageOrBuilder(index);\n"
+ " }\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.util.List<? extends $type$OrBuilder> \n"
+ " get$capitalized_name$OrBuilderList() {\n"
+ " if ($name$Builder_ != null) {\n"
+ " return $name$Builder_.getMessageOrBuilderList();\n"
+ " } else {\n"
+ " return java.util.Collections.unmodifiableList($name$_);\n"
+ " }\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$.Builder add$capitalized_name$Builder() {\n"
+ " return get$capitalized_name$FieldBuilder().addBuilder(\n"
+ " $type$.getDefaultInstance());\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$.Builder add$capitalized_name$Builder(\n"
+ " int index) {\n"
+ " return get$capitalized_name$FieldBuilder().addBuilder(\n"
+ " index, $type$.getDefaultInstance());\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.util.List<$type$.Builder> \n"
+ " get$capitalized_name$BuilderList() {\n"
+ " return get$capitalized_name$FieldBuilder().getBuilderList();\n"
+ "}\n"
+ "private com.google.protobuf.RepeatedFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder> \n"
+ " get$capitalized_name$FieldBuilder() {\n"
+ " if ($name$Builder_ == null) {\n"
+ " $name$Builder_ = new com.google.protobuf.RepeatedFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder>(\n"
+ " $name$_,\n"
+ " $get_mutable_bit_builder$,\n"
+ " getParentForChildren(),\n"
+ " isClean());\n"
+ " $name$_ = null;\n"
+ " }\n"
+ " return $name$Builder_;\n"
+ "}\n");
+ }
+}
+
+void RepeatedImmutableLazyMessageFieldGenerator::
+GenerateParsingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (!$get_mutable_bit_parser$) {\n"
+ " $name$_ =\n"
+ " new java.util.ArrayList<com.google.protobuf.LazyFieldLite>();\n"
+ " $set_mutable_bit_parser$;\n"
+ "}\n"
+ "$name$_.add(new com.google.protobuf.LazyFieldLite(\n"
+ " extensionRegistry, input.readBytes()));\n");
+}
+
+void RepeatedImmutableLazyMessageFieldGenerator::
+GenerateSerializationCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
+ " output.writeBytes($number$, $name$_.get(i).toByteString());\n"
+ "}\n");
+}
+
+void RepeatedImmutableLazyMessageFieldGenerator::
+GenerateSerializedSizeCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
+ " size += com.google.protobuf.CodedOutputStream\n"
+ " .computeLazyFieldSize($number$, $name$_.get(i));\n"
+ "}\n");
+}
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/compiler/java/java_lazy_message_field.h b/src/google/protobuf/compiler/java/java_lazy_message_field.h
new file mode 100644
index 0000000..b1b7f28
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_lazy_message_field.h
@@ -0,0 +1,121 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: niwasaki@google.com (Naoki Iwasaki)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_LAZY_MESSAGE_FIELD_H__
+#define GOOGLE_PROTOBUF_COMPILER_JAVA_LAZY_MESSAGE_FIELD_H__
+
+#include <google/protobuf/compiler/java/java_field.h>
+#include <google/protobuf/compiler/java/java_message_field.h>
+
+namespace google {
+namespace protobuf {
+ namespace compiler {
+ namespace java {
+ class Context; // context.h
+ }
+ }
+}
+
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+class ImmutableLazyMessageFieldGenerator
+ : public ImmutableMessageFieldGenerator {
+ public:
+ explicit ImmutableLazyMessageFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~ImmutableLazyMessageFieldGenerator();
+
+ // overroads ImmutableMessageFieldGenerator ---------------------------------
+ void GenerateMembers(io::Printer* printer) const;
+ void GenerateBuilderMembers(io::Printer* printer) const;
+ void GenerateInitializationCode(io::Printer* printer) const;
+ void GenerateBuilderClearCode(io::Printer* printer) const;
+ void GenerateMergingCode(io::Printer* printer) const;
+ void GenerateBuildingCode(io::Printer* printer) const;
+ void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateSerializationCode(io::Printer* printer) const;
+ void GenerateSerializedSizeCode(io::Printer* printer) const;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableLazyMessageFieldGenerator);
+};
+
+class ImmutableLazyMessageOneofFieldGenerator
+ : public ImmutableLazyMessageFieldGenerator {
+ public:
+ ImmutableLazyMessageOneofFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~ImmutableLazyMessageOneofFieldGenerator();
+
+ void GenerateMembers(io::Printer* printer) const;
+ void GenerateBuilderMembers(io::Printer* printer) const;
+ void GenerateMergingCode(io::Printer* printer) const;
+ void GenerateBuildingCode(io::Printer* printer) const;
+ void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateSerializationCode(io::Printer* printer) const;
+ void GenerateSerializedSizeCode(io::Printer* printer) const;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableLazyMessageOneofFieldGenerator);
+};
+
+class RepeatedImmutableLazyMessageFieldGenerator
+ : public RepeatedImmutableMessageFieldGenerator {
+ public:
+ explicit RepeatedImmutableLazyMessageFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~RepeatedImmutableLazyMessageFieldGenerator();
+
+ // overroads RepeatedImmutableMessageFieldGenerator -------------------------
+ void GenerateMembers(io::Printer* printer) const;
+ void GenerateBuilderMembers(io::Printer* printer) const;
+ void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateSerializationCode(io::Printer* printer) const;
+ void GenerateSerializedSizeCode(io::Printer* printer) const;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableLazyMessageFieldGenerator);
+};
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_LAZY_MESSAGE_FIELD_H__
diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc
index a326057..8aa89ac 100644
--- a/src/google/protobuf/compiler/java/java_message.cc
+++ b/src/google/protobuf/compiler/java/java_message.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -32,17 +32,27 @@
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
+#include <google/protobuf/compiler/java/java_message.h>
+
#include <algorithm>
#include <google/protobuf/stubs/hash.h>
-#include <google/protobuf/compiler/java/java_message.h>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <google/protobuf/compiler/java/java_context.h>
+#include <google/protobuf/compiler/java/java_doc_comment.h>
#include <google/protobuf/compiler/java/java_enum.h>
#include <google/protobuf/compiler/java/java_extension.h>
+#include <google/protobuf/compiler/java/java_generator_factory.h>
#include <google/protobuf/compiler/java/java_helpers.h>
-#include <google/protobuf/stubs/strutil.h>
-#include <google/protobuf/io/printer.h>
+#include <google/protobuf/compiler/java/java_name_resolver.h>
#include <google/protobuf/io/coded_stream.h>
-#include <google/protobuf/wire_format.h>
+#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/substitute.h>
namespace google {
namespace protobuf {
@@ -53,107 +63,31 @@ using internal::WireFormat;
using internal::WireFormatLite;
namespace {
-
-void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) {
- // Print the field's proto-syntax definition as a comment. We don't want to
- // print group bodies so we cut off after the first line.
- string def = field->DebugString();
- printer->Print("// $def$\n",
- "def", def.substr(0, def.find_first_of('\n')));
-}
-
-struct FieldOrderingByNumber {
- inline bool operator()(const FieldDescriptor* a,
- const FieldDescriptor* b) const {
- return a->number() < b->number();
- }
-};
-
-struct ExtensionRangeOrdering {
- bool operator()(const Descriptor::ExtensionRange* a,
- const Descriptor::ExtensionRange* b) const {
- return a->start < b->start;
- }
-};
-
-// Sort the fields of the given Descriptor by number into a new[]'d array
-// and return it.
-const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) {
- const FieldDescriptor** fields =
- new const FieldDescriptor*[descriptor->field_count()];
- for (int i = 0; i < descriptor->field_count(); i++) {
- fields[i] = descriptor->field(i);
- }
- sort(fields, fields + descriptor->field_count(),
- FieldOrderingByNumber());
- return fields;
-}
-
-// Get an identifier that uniquely identifies this type within the file.
-// This is used to declare static variables related to this type at the
-// outermost file scope.
-string UniqueFileScopeIdentifier(const Descriptor* descriptor) {
- return "static_" + StringReplace(descriptor->full_name(), ".", "_", true);
-}
-
-// Returns true if the message type has any required fields. If it doesn't,
-// we can optimize out calls to its isInitialized() method.
-//
-// already_seen is used to avoid checking the same type multiple times
-// (and also to protect against recursion).
-static bool HasRequiredFields(
- const Descriptor* type,
- hash_set<const Descriptor*>* already_seen) {
- if (already_seen->count(type) > 0) {
- // The type is already in cache. This means that either:
- // a. The type has no required fields.
- // b. We are in the midst of checking if the type has required fields,
- // somewhere up the stack. In this case, we know that if the type
- // has any required fields, they'll be found when we return to it,
- // and the whole call to HasRequiredFields() will return true.
- // Therefore, we don't have to check if this type has required fields
- // here.
- return false;
- }
- already_seen->insert(type);
-
- // If the type has extensions, an extension with message type could contain
- // required fields, so we have to be conservative and assume such an
- // extension exists.
- if (type->extension_range_count() > 0) return true;
-
- for (int i = 0; i < type->field_count(); i++) {
- const FieldDescriptor* field = type->field(i);
- if (field->is_required()) {
- return true;
- }
- if (GetJavaType(field) == JAVATYPE_MESSAGE) {
- if (HasRequiredFields(field->message_type(), already_seen)) {
- return true;
- }
- }
- }
-
- return false;
-}
-
-static bool HasRequiredFields(const Descriptor* type) {
- hash_set<const Descriptor*> already_seen;
- return HasRequiredFields(type, &already_seen);
+bool GenerateHasBits(const Descriptor* descriptor) {
+ return SupportFieldPresence(descriptor->file()) ||
+ HasRepeatedFields(descriptor);
}
-
} // namespace
// ===================================================================
MessageGenerator::MessageGenerator(const Descriptor* descriptor)
- : descriptor_(descriptor),
- field_generators_(descriptor) {
-}
+ : descriptor_(descriptor) {}
MessageGenerator::~MessageGenerator() {}
-void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
+// ===================================================================
+// TODO(api): Move this class to a separate immutable_message.cc file.
+ImmutableMessageGenerator::ImmutableMessageGenerator(
+ const Descriptor* descriptor, Context* context)
+ : MessageGenerator(descriptor), context_(context),
+ name_resolver_(context->GetNameResolver()),
+ field_generators_(descriptor, context_) {
+}
+
+ImmutableMessageGenerator::~ImmutableMessageGenerator() {}
+
+void ImmutableMessageGenerator::GenerateStaticVariables(io::Printer* printer) {
if (HasDescriptorMethods(descriptor_)) {
// Because descriptor.proto (com.google.protobuf.DescriptorProtos) is
// used in the construction of descriptors, we have a tricky bootstrapping
@@ -165,12 +99,12 @@ void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
map<string, string> vars;
vars["identifier"] = UniqueFileScopeIdentifier(descriptor_);
vars["index"] = SimpleItoa(descriptor_->index());
- vars["classname"] = ClassName(descriptor_);
+ vars["classname"] = name_resolver_->GetImmutableClassName(descriptor_);
if (descriptor_->containing_type() != NULL) {
vars["parent"] = UniqueFileScopeIdentifier(
descriptor_->containing_type());
}
- if (descriptor_->file()->options().java_multiple_files()) {
+ if (MultipleJavaFiles(descriptor_->file(), /* immutable = */ true)) {
// We can only make these package-private since the classes that use them
// are in separate files.
vars["private"] = "";
@@ -180,31 +114,28 @@ void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
// The descriptor for this type.
printer->Print(vars,
- "$private$static com.google.protobuf.Descriptors.Descriptor\n"
+ "$private$static final com.google.protobuf.Descriptors.Descriptor\n"
" internal_$identifier$_descriptor;\n");
// And the FieldAccessorTable.
- printer->Print(vars,
- "$private$static\n"
- " com.google.protobuf.GeneratedMessage.FieldAccessorTable\n"
- " internal_$identifier$_fieldAccessorTable;\n");
+ GenerateFieldAccessorTable(printer);
}
// Generate static members for all nested types.
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
// TODO(kenton): Reuse MessageGenerator objects?
- MessageGenerator(descriptor_->nested_type(i))
+ ImmutableMessageGenerator(descriptor_->nested_type(i), context_)
.GenerateStaticVariables(printer);
}
}
-void MessageGenerator::GenerateStaticVariableInitializers(
+void ImmutableMessageGenerator::GenerateStaticVariableInitializers(
io::Printer* printer) {
if (HasDescriptorMethods(descriptor_)) {
map<string, string> vars;
vars["identifier"] = UniqueFileScopeIdentifier(descriptor_);
vars["index"] = SimpleItoa(descriptor_->index());
- vars["classname"] = ClassName(descriptor_);
+ vars["classname"] = name_resolver_->GetImmutableClassName(descriptor_);
if (descriptor_->containing_type() != NULL) {
vars["parent"] = UniqueFileScopeIdentifier(
descriptor_->containing_type());
@@ -222,82 +153,191 @@ void MessageGenerator::GenerateStaticVariableInitializers(
}
// And the FieldAccessorTable.
- printer->Print(vars,
- "internal_$identifier$_fieldAccessorTable = new\n"
- " com.google.protobuf.GeneratedMessage.FieldAccessorTable(\n"
- " internal_$identifier$_descriptor,\n"
- " new java.lang.String[] { ");
- for (int i = 0; i < descriptor_->field_count(); i++) {
- printer->Print(
- "\"$field_name$\", ",
- "field_name",
- UnderscoresToCapitalizedCamelCase(descriptor_->field(i)));
- }
- printer->Print("},\n"
- " $classname$.class,\n"
- " $classname$.Builder.class);\n",
- "classname", ClassName(descriptor_));
+ GenerateFieldAccessorTableInitializer(printer);
}
// Generate static member initializers for all nested types.
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
// TODO(kenton): Reuse MessageGenerator objects?
- MessageGenerator(descriptor_->nested_type(i))
+ ImmutableMessageGenerator(descriptor_->nested_type(i), context_)
.GenerateStaticVariableInitializers(printer);
}
+}
- for (int i = 0; i < descriptor_->extension_count(); i++) {
- // TODO(kenton): Reuse ExtensionGenerator objects?
- ExtensionGenerator(descriptor_->extension(i))
- .GenerateInitializationCode(printer);
+void ImmutableMessageGenerator::
+GenerateFieldAccessorTable(io::Printer* printer) {
+ map<string, string> vars;
+ vars["identifier"] = UniqueFileScopeIdentifier(descriptor_);
+ if (MultipleJavaFiles(descriptor_->file(), /* immutable = */ true)) {
+ // We can only make these package-private since the classes that use them
+ // are in separate files.
+ vars["private"] = "";
+ } else {
+ vars["private"] = "private ";
}
+ printer->Print(vars,
+ "$private$static\n"
+ " com.google.protobuf.GeneratedMessage.FieldAccessorTable\n"
+ " internal_$identifier$_fieldAccessorTable;\n");
}
-void MessageGenerator::Generate(io::Printer* printer) {
- bool is_own_file =
- descriptor_->containing_type() == NULL &&
- descriptor_->file()->options().java_multiple_files();
+void ImmutableMessageGenerator::
+GenerateFieldAccessorTableInitializer(io::Printer* printer) {
+ printer->Print(
+ "internal_$identifier$_fieldAccessorTable = new\n"
+ " com.google.protobuf.GeneratedMessage.FieldAccessorTable(\n"
+ " internal_$identifier$_descriptor,\n"
+ " new java.lang.String[] { ",
+ "identifier",
+ UniqueFileScopeIdentifier(descriptor_));
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ const FieldDescriptor* field = descriptor_->field(i);
+ const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field);
+ printer->Print(
+ "\"$field_name$\", ",
+ "field_name", info->capitalized_name);
+ }
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ const OneofDescriptor* oneof = descriptor_->oneof_decl(i);
+ const OneofGeneratorInfo* info = context_->GetOneofGeneratorInfo(oneof);
+ printer->Print(
+ "\"$oneof_name$\", ",
+ "oneof_name", info->capitalized_name);
+ }
+ printer->Print("});\n");
+}
+// ===================================================================
+
+void ImmutableMessageGenerator::GenerateInterface(io::Printer* printer) {
if (descriptor_->extension_range_count() > 0) {
if (HasDescriptorMethods(descriptor_)) {
printer->Print(
- "public $static$ final class $classname$ extends\n"
- " com.google.protobuf.GeneratedMessage.ExtendableMessage<\n"
- " $classname$> {\n",
- "static", is_own_file ? "" : "static",
+ "public interface $classname$OrBuilder extends\n"
+ " $extra_interfaces$\n"
+ " com.google.protobuf.GeneratedMessage.\n"
+ " ExtendableMessageOrBuilder<$classname$> {\n",
+ "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_),
"classname", descriptor_->name());
} else {
printer->Print(
- "public $static$ final class $classname$ extends\n"
- " com.google.protobuf.GeneratedMessageLite.ExtendableMessage<\n"
- " $classname$> {\n",
- "static", is_own_file ? "" : "static",
+ "public interface $classname$OrBuilder extends \n"
+ " $extra_interfaces$\n"
+ " com.google.protobuf.GeneratedMessageLite.\n"
+ " ExtendableMessageOrBuilder<$classname$> {\n",
+ "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_),
"classname", descriptor_->name());
}
} else {
if (HasDescriptorMethods(descriptor_)) {
printer->Print(
- "public $static$ final class $classname$ extends\n"
- " com.google.protobuf.GeneratedMessage {\n",
- "static", is_own_file ? "" : "static",
+ "public interface $classname$OrBuilder extends\n"
+ " $extra_interfaces$\n"
+ " com.google.protobuf.MessageOrBuilder {\n",
+ "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_),
"classname", descriptor_->name());
} else {
printer->Print(
- "public $static$ final class $classname$ extends\n"
- " com.google.protobuf.GeneratedMessageLite {\n",
- "static", is_own_file ? "" : "static",
+ "public interface $classname$OrBuilder extends\n"
+ " $extra_interfaces$\n"
+ " com.google.protobuf.MessageLiteOrBuilder {\n",
+ "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_),
"classname", descriptor_->name());
}
}
+
+ printer->Indent();
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ printer->Print("\n");
+ field_generators_.get(descriptor_->field(i))
+ .GenerateInterfaceMembers(printer);
+ }
+ printer->Outdent();
+
+ printer->Print("}\n");
+}
+
+// ===================================================================
+
+void ImmutableMessageGenerator::Generate(io::Printer* printer) {
+ bool is_own_file =
+ descriptor_->containing_type() == NULL &&
+ MultipleJavaFiles(descriptor_->file(), /* immutable = */ true);
+
+ WriteMessageDocComment(printer, descriptor_);
+
+ // The builder_type stores the super type name of the nested Builder class.
+ string builder_type;
+ if (descriptor_->extension_range_count() > 0) {
+ if (HasDescriptorMethods(descriptor_)) {
+ printer->Print(
+ "public$static$final class $classname$ extends\n"
+ " com.google.protobuf.GeneratedMessage.ExtendableMessage<\n"
+ " $classname$> implements\n"
+ " $extra_interfaces$\n"
+ " $classname$OrBuilder {\n",
+ "static", is_own_file ? " " : " static ",
+ "classname", descriptor_->name(),
+ "extra_interfaces", ExtraMessageInterfaces(descriptor_));
+ builder_type = strings::Substitute(
+ "com.google.protobuf.GeneratedMessage.ExtendableBuilder<$0, ?>",
+ name_resolver_->GetImmutableClassName(descriptor_));
+ } else {
+ printer->Print(
+ "public$static$final class $classname$ extends\n"
+ " com.google.protobuf.GeneratedMessageLite.ExtendableMessage<\n"
+ " $classname$> implements\n"
+ " $extra_interfaces$\n"
+ " $classname$OrBuilder {\n",
+ "static", is_own_file ? " " : " static ",
+ "classname", descriptor_->name(),
+ "extra_interfaces", ExtraMessageInterfaces(descriptor_));
+ builder_type = strings::Substitute(
+ "com.google.protobuf.GeneratedMessageLite.ExtendableBuilder<$0, ?>",
+ name_resolver_->GetImmutableClassName(descriptor_));
+ }
+ } else {
+ if (HasDescriptorMethods(descriptor_)) {
+ printer->Print(
+ "public$static$final class $classname$ extends\n"
+ " com.google.protobuf.GeneratedMessage implements\n"
+ " $extra_interfaces$\n"
+ " $classname$OrBuilder {\n",
+ "static", is_own_file ? " " : " static ",
+ "classname", descriptor_->name(),
+ "extra_interfaces", ExtraMessageInterfaces(descriptor_));
+ builder_type = "com.google.protobuf.GeneratedMessage.Builder<?>";
+ } else {
+ printer->Print(
+ "public$static$final class $classname$ extends\n"
+ " com.google.protobuf.GeneratedMessageLite implements\n"
+ " $extra_interfaces$\n"
+ " $classname$OrBuilder {\n",
+ "static", is_own_file ? " " : " static ",
+ "classname", descriptor_->name(),
+ "extra_interfaces", ExtraMessageInterfaces(descriptor_));
+ builder_type = "com.google.protobuf.GeneratedMessageLite.Builder";
+ }
+ }
printer->Indent();
+ // Using builder_type, instead of Builder, prevents the Builder class from
+ // being loaded into PermGen space when the default instance is created.
+ // This optimizes the PermGen space usage for clients that do not modify
+ // messages.
printer->Print(
"// Use $classname$.newBuilder() to construct.\n"
- "private $classname$() {\n"
- " initFields();\n"
- "}\n"
+ "private $classname$($buildertype$ builder) {\n"
+ " super(builder);\n"
+ "$set_unknown_fields$\n"
+ "}\n",
+ "classname", descriptor_->name(),
+ "buildertype", builder_type,
+ "set_unknown_fields",
+ " this.unknownFields = builder.getUnknownFields();");
+ printer->Print(
// Used when constructing the default instance, which cannot be initialized
// immediately because it may cyclically refer to other default instances.
- "private $classname$(boolean noInit) {}\n"
+ "private $classname$(boolean noInit) {$set_default_unknown_fields$}\n"
"\n"
"private static final $classname$ defaultInstance;\n"
"public static $classname$ getDefaultInstance() {\n"
@@ -308,40 +348,131 @@ void MessageGenerator::Generate(io::Printer* printer) {
" return defaultInstance;\n"
"}\n"
"\n",
- "classname", descriptor_->name());
+ "classname", descriptor_->name(),
+ "set_default_unknown_fields", UseUnknownFieldSet(descriptor_)
+ ? " this.unknownFields ="
+ " com.google.protobuf.UnknownFieldSet.getDefaultInstance(); "
+ : " this.unknownFields = com.google.protobuf.ByteString.EMPTY;");
- if (HasDescriptorMethods(descriptor_)) {
+ if (UseUnknownFieldSet(descriptor_)) {
printer->Print(
- "public static final com.google.protobuf.Descriptors.Descriptor\n"
- " getDescriptor() {\n"
- " return $fileclass$.internal_$identifier$_descriptor;\n"
- "}\n"
- "\n"
- "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n"
- " internalGetFieldAccessorTable() {\n"
- " return $fileclass$.internal_$identifier$_fieldAccessorTable;\n"
- "}\n"
- "\n",
- "fileclass", ClassName(descriptor_->file()),
- "identifier", UniqueFileScopeIdentifier(descriptor_));
+ "private final com.google.protobuf.UnknownFieldSet unknownFields;\n"
+ ""
+ "@java.lang.Override\n"
+ "public final com.google.protobuf.UnknownFieldSet\n"
+ " getUnknownFields() {\n"
+ " return this.unknownFields;\n"
+ "}\n");
+ } else {
+ printer->Print(
+ "private final com.google.protobuf.ByteString unknownFields;\n");
}
- // Nested types and extensions
+ if (HasGeneratedMethods(descriptor_)) {
+ GenerateParsingConstructor(printer);
+ }
+
+ GenerateDescriptorMethods(printer);
+ GenerateParser(printer);
+
+ // Nested types
for (int i = 0; i < descriptor_->enum_type_count(); i++) {
- EnumGenerator(descriptor_->enum_type(i)).Generate(printer);
+ EnumGenerator(descriptor_->enum_type(i), true, context_)
+ .Generate(printer);
}
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
- MessageGenerator(descriptor_->nested_type(i)).Generate(printer);
+ ImmutableMessageGenerator messageGenerator(
+ descriptor_->nested_type(i), context_);
+ messageGenerator.GenerateInterface(printer);
+ messageGenerator.Generate(printer);
}
- for (int i = 0; i < descriptor_->extension_count(); i++) {
- ExtensionGenerator(descriptor_->extension(i)).Generate(printer);
+ if (GenerateHasBits(descriptor_)) {
+ // Integers for bit fields.
+ int totalBits = 0;
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ totalBits += field_generators_.get(descriptor_->field(i))
+ .GetNumBitsForMessage();
+ }
+ int totalInts = (totalBits + 31) / 32;
+ for (int i = 0; i < totalInts; i++) {
+ printer->Print("private int $bit_field_name$;\n",
+ "bit_field_name", GetBitFieldName(i));
+ }
+ }
+
+ // oneof
+ map<string, string> vars;
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ vars["oneof_name"] = context_->GetOneofGeneratorInfo(
+ descriptor_->oneof_decl(i))->name;
+ vars["oneof_capitalized_name"] = context_->GetOneofGeneratorInfo(
+ descriptor_->oneof_decl(i))->capitalized_name;
+ vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index());
+ // oneofCase_ and oneof_
+ printer->Print(vars,
+ "private int $oneof_name$Case_ = 0;\n"
+ "private java.lang.Object $oneof_name$_;\n");
+ // OneofCase enum
+ printer->Print(vars,
+ "public enum $oneof_capitalized_name$Case\n"
+ " implements com.google.protobuf.Internal.EnumLite {\n");
+ printer->Indent();
+ for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+ const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j);
+ printer->Print(
+ "$field_name$($field_number$),\n",
+ "field_name",
+ ToUpper(field->name()),
+ "field_number",
+ SimpleItoa(field->number()));
+ }
+ printer->Print(
+ "$cap_oneof_name$_NOT_SET(0);\n",
+ "cap_oneof_name",
+ ToUpper(vars["oneof_name"]));
+ printer->Print(vars,
+ "private int value = 0;\n"
+ "private $oneof_capitalized_name$Case(int value) {\n"
+ " this.value = value;\n"
+ "}\n");
+ printer->Print(vars,
+ "public static $oneof_capitalized_name$Case valueOf(int value) {\n"
+ " switch (value) {\n");
+ for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+ const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j);
+ printer->Print(
+ " case $field_number$: return $field_name$;\n",
+ "field_number",
+ SimpleItoa(field->number()),
+ "field_name",
+ ToUpper(field->name()));
+ }
+ printer->Print(
+ " case 0: return $cap_oneof_name$_NOT_SET;\n"
+ " default: throw new java.lang.IllegalArgumentException(\n"
+ " \"Value is undefined for this oneof enum.\");\n"
+ " }\n"
+ "}\n"
+ "public int getNumber() {\n"
+ " return this.value;\n"
+ "}\n",
+ "cap_oneof_name", ToUpper(vars["oneof_name"]));
+ printer->Outdent();
+ printer->Print("};\n\n");
+ // oneofCase()
+ printer->Print(vars,
+ "public $oneof_capitalized_name$Case\n"
+ "get$oneof_capitalized_name$Case() {\n"
+ " return $oneof_capitalized_name$Case.valueOf(\n"
+ " $oneof_name$Case_);\n"
+ "}\n"
+ "\n");
}
// Fields
for (int i = 0; i < descriptor_->field_count(); i++) {
- PrintFieldComment(printer, descriptor_->field(i));
printer->Print("public static final int $constant_name$ = $number$;\n",
"constant_name", FieldConstantName(descriptor_->field(i)),
"number", SimpleItoa(descriptor_->field(i)->number()));
@@ -354,48 +485,60 @@ void MessageGenerator::Generate(io::Printer* printer) {
printer->Print("private void initFields() {\n");
printer->Indent();
for (int i = 0; i < descriptor_->field_count(); i++) {
- field_generators_.get(descriptor_->field(i))
- .GenerateInitializationCode(printer);
+ if (!descriptor_->field(i)->containing_oneof()) {
+ field_generators_.get(descriptor_->field(i))
+ .GenerateInitializationCode(printer);
+ }
}
+
printer->Outdent();
printer->Print("}\n");
if (HasGeneratedMethods(descriptor_)) {
- GenerateIsInitialized(printer);
+ GenerateIsInitialized(printer, MEMOIZE);
GenerateMessageSerializationMethods(printer);
}
+ if (HasEqualsAndHashCode(descriptor_)) {
+ GenerateEqualsAndHashCode(printer);
+ }
+
+
GenerateParseFromMethods(printer);
GenerateBuilder(printer);
- // Force initialization of outer class. Otherwise, nested extensions may
- // not be initialized. Also carefully initialize the default instance in
- // such a way that it doesn't conflict with other initialization.
+ // Carefully initialize the default instance in such a way that it doesn't
+ // conflict with other initialization.
printer->Print(
"\n"
"static {\n"
" defaultInstance = new $classname$(true);\n"
- " $file$.internalForceInit();\n"
" defaultInstance.initFields();\n"
- "}\n",
- "file", ClassName(descriptor_->file()),
- "classname", descriptor_->name());
-
- printer->Print(
+ "}\n"
"\n"
"// @@protoc_insertion_point(class_scope:$full_name$)\n",
+ "classname", descriptor_->name(),
"full_name", descriptor_->full_name());
+ // Extensions must be declared after the defaultInstance is initialized
+ // because the defaultInstance is used by the extension to lazily retrieve
+ // the outer class's FileDescriptor.
+ for (int i = 0; i < descriptor_->extension_count(); i++) {
+ ImmutableExtensionGenerator(descriptor_->extension(i), context_)
+ .Generate(printer);
+ }
+
printer->Outdent();
printer->Print("}\n\n");
}
+
// ===================================================================
-void MessageGenerator::
+void ImmutableMessageGenerator::
GenerateMessageSerializationMethods(io::Printer* printer) {
scoped_array<const FieldDescriptor*> sorted_fields(
- SortFieldsByNumber(descriptor_));
+ SortFieldsByNumber(descriptor_));
vector<const Descriptor::ExtensionRange*> sorted_extensions;
for (int i = 0; i < descriptor_->extension_range_count(); ++i) {
@@ -420,15 +563,18 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
if (descriptor_->extension_range_count() > 0) {
if (descriptor_->options().message_set_wire_format()) {
printer->Print(
- "com.google.protobuf.GeneratedMessage$lite$.ExtendableMessage\n"
- " .ExtensionWriter extensionWriter =\n"
+ "com.google.protobuf.GeneratedMessage$lite$\n"
+ " .ExtendableMessage<$classname$>.ExtensionWriter extensionWriter =\n"
" newMessageSetExtensionWriter();\n",
- "lite", HasDescriptorMethods(descriptor_) ? "" : "Lite");
+ "lite", HasDescriptorMethods(descriptor_) ? "" : "Lite",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
} else {
printer->Print(
- "com.google.protobuf.GeneratedMessage$lite$.ExtendableMessage\n"
- " .ExtensionWriter extensionWriter = newExtensionWriter();\n",
- "lite", HasDescriptorMethods(descriptor_) ? "" : "Lite");
+ "com.google.protobuf.GeneratedMessage$lite$\n"
+ " .ExtendableMessage<$classname$>.ExtensionWriter extensionWriter =\n"
+ " newExtensionWriter();\n",
+ "lite", HasDescriptorMethods(descriptor_) ? "" : "Lite",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
}
}
@@ -447,7 +593,7 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
}
}
- if (HasUnknownFields(descriptor_)) {
+ if (UseUnknownFieldSet(descriptor_)) {
if (descriptor_->options().message_set_wire_format()) {
printer->Print(
"getUnknownFields().writeAsMessageSetTo(output);\n");
@@ -455,6 +601,9 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
printer->Print(
"getUnknownFields().writeTo(output);\n");
}
+ } else {
+ printer->Print(
+ "output.writeRawBytes(unknownFields);\n");
}
printer->Outdent();
@@ -483,7 +632,7 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
}
}
- if (HasUnknownFields(descriptor_)) {
+ if (UseUnknownFieldSet(descriptor_)) {
if (descriptor_->options().message_set_wire_format()) {
printer->Print(
"size += getUnknownFields().getSerializedSizeAsMessageSet();\n");
@@ -491,6 +640,9 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
printer->Print(
"size += getUnknownFields().getSerializedSize();\n");
}
+ } else {
+ printer->Print(
+ "size += unknownFields.size();\n");
}
printer->Outdent();
@@ -499,9 +651,18 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
" return size;\n"
"}\n"
"\n");
+
+ printer->Print(
+ "private static final long serialVersionUID = 0L;\n"
+ "@java.lang.Override\n"
+ "protected java.lang.Object writeReplace()\n"
+ " throws java.io.ObjectStreamException {\n"
+ " return super.writeReplace();\n"
+ "}\n"
+ "\n");
}
-void MessageGenerator::
+void ImmutableMessageGenerator::
GenerateParseFromMethods(io::Printer* printer) {
// Note: These are separate from GenerateMessageSerializationMethods()
// because they need to be generated even for messages that are optimized
@@ -510,79 +671,65 @@ GenerateParseFromMethods(io::Printer* printer) {
"public static $classname$ parseFrom(\n"
" com.google.protobuf.ByteString data)\n"
" throws com.google.protobuf.InvalidProtocolBufferException {\n"
- " return newBuilder().mergeFrom(data).buildParsed();\n"
+ " return PARSER.parseFrom(data);\n"
"}\n"
"public static $classname$ parseFrom(\n"
" com.google.protobuf.ByteString data,\n"
" com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n"
" throws com.google.protobuf.InvalidProtocolBufferException {\n"
- " return newBuilder().mergeFrom(data, extensionRegistry)\n"
- " .buildParsed();\n"
+ " return PARSER.parseFrom(data, extensionRegistry);\n"
"}\n"
"public static $classname$ parseFrom(byte[] data)\n"
" throws com.google.protobuf.InvalidProtocolBufferException {\n"
- " return newBuilder().mergeFrom(data).buildParsed();\n"
+ " return PARSER.parseFrom(data);\n"
"}\n"
"public static $classname$ parseFrom(\n"
" byte[] data,\n"
" com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n"
" throws com.google.protobuf.InvalidProtocolBufferException {\n"
- " return newBuilder().mergeFrom(data, extensionRegistry)\n"
- " .buildParsed();\n"
+ " return PARSER.parseFrom(data, extensionRegistry);\n"
"}\n"
"public static $classname$ parseFrom(java.io.InputStream input)\n"
" throws java.io.IOException {\n"
- " return newBuilder().mergeFrom(input).buildParsed();\n"
+ " return PARSER.parseFrom(input);\n"
"}\n"
"public static $classname$ parseFrom(\n"
" java.io.InputStream input,\n"
" com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n"
" throws java.io.IOException {\n"
- " return newBuilder().mergeFrom(input, extensionRegistry)\n"
- " .buildParsed();\n"
+ " return PARSER.parseFrom(input, extensionRegistry);\n"
"}\n"
"public static $classname$ parseDelimitedFrom(java.io.InputStream input)\n"
" throws java.io.IOException {\n"
- " Builder builder = newBuilder();\n"
- " if (builder.mergeDelimitedFrom(input)) {\n"
- " return builder.buildParsed();\n"
- " } else {\n"
- " return null;\n"
- " }\n"
+ " return PARSER.parseDelimitedFrom(input);\n"
"}\n"
"public static $classname$ parseDelimitedFrom(\n"
" java.io.InputStream input,\n"
" com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n"
" throws java.io.IOException {\n"
- " Builder builder = newBuilder();\n"
- " if (builder.mergeDelimitedFrom(input, extensionRegistry)) {\n"
- " return builder.buildParsed();\n"
- " } else {\n"
- " return null;\n"
- " }\n"
+ " return PARSER.parseDelimitedFrom(input, extensionRegistry);\n"
"}\n"
"public static $classname$ parseFrom(\n"
" com.google.protobuf.CodedInputStream input)\n"
" throws java.io.IOException {\n"
- " return newBuilder().mergeFrom(input).buildParsed();\n"
+ " return PARSER.parseFrom(input);\n"
"}\n"
"public static $classname$ parseFrom(\n"
" com.google.protobuf.CodedInputStream input,\n"
" com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n"
" throws java.io.IOException {\n"
- " return newBuilder().mergeFrom(input, extensionRegistry)\n"
- " .buildParsed();\n"
+ " return PARSER.parseFrom(input, extensionRegistry);\n"
"}\n"
"\n",
- "classname", ClassName(descriptor_));
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
}
-void MessageGenerator::GenerateSerializeOneField(
+void ImmutableMessageGenerator::GenerateSerializeOneField(
io::Printer* printer, const FieldDescriptor* field) {
field_generators_.get(field).GenerateSerializationCode(printer);
}
-void MessageGenerator::GenerateSerializeOneExtensionRange(
+void ImmutableMessageGenerator::GenerateSerializeOneExtensionRange(
io::Printer* printer, const Descriptor::ExtensionRange* range) {
printer->Print(
"extensionWriter.writeUntil($end$, output);\n",
@@ -591,7 +738,7 @@ void MessageGenerator::GenerateSerializeOneExtensionRange(
// ===================================================================
-void MessageGenerator::GenerateBuilder(io::Printer* printer) {
+void ImmutableMessageGenerator::GenerateBuilder(io::Printer* printer) {
printer->Print(
"public static Builder newBuilder() { return Builder.create(); }\n"
"public Builder newBuilderForType() { return newBuilder(); }\n"
@@ -600,47 +747,119 @@ void MessageGenerator::GenerateBuilder(io::Printer* printer) {
"}\n"
"public Builder toBuilder() { return newBuilder(this); }\n"
"\n",
- "classname", ClassName(descriptor_));
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
+
+ if (HasNestedBuilders(descriptor_)) {
+ printer->Print(
+ "@java.lang.Override\n"
+ "protected Builder newBuilderForType(\n"
+ " com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n"
+ " Builder builder = new Builder(parent);\n"
+ " return builder;\n"
+ "}\n");
+ }
+
+ WriteMessageDocComment(printer, descriptor_);
if (descriptor_->extension_range_count() > 0) {
if (HasDescriptorMethods(descriptor_)) {
printer->Print(
"public static final class Builder extends\n"
" com.google.protobuf.GeneratedMessage.ExtendableBuilder<\n"
- " $classname$, Builder> {\n",
- "classname", ClassName(descriptor_));
+ " $classname$, Builder> implements\n"
+ " $extra_interfaces$\n"
+ " $classname$OrBuilder {\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_),
+ "extra_interfaces", ExtraBuilderInterfaces(descriptor_));
} else {
printer->Print(
"public static final class Builder extends\n"
" com.google.protobuf.GeneratedMessageLite.ExtendableBuilder<\n"
- " $classname$, Builder> {\n",
- "classname", ClassName(descriptor_));
+ " $classname$, Builder> implements\n"
+ " $extra_interfaces$\n"
+ " $classname$OrBuilder {\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_),
+ "extra_interfaces", ExtraBuilderInterfaces(descriptor_));
}
} else {
if (HasDescriptorMethods(descriptor_)) {
printer->Print(
"public static final class Builder extends\n"
- " com.google.protobuf.GeneratedMessage.Builder<Builder> {\n",
- "classname", ClassName(descriptor_));
+ " com.google.protobuf.GeneratedMessage.Builder<Builder> implements\n"
+ " $extra_interfaces$\n"
+ " $classname$OrBuilder {\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_),
+ "extra_interfaces", ExtraBuilderInterfaces(descriptor_));
} else {
printer->Print(
"public static final class Builder extends\n"
" com.google.protobuf.GeneratedMessageLite.Builder<\n"
- " $classname$, Builder> {\n",
- "classname", ClassName(descriptor_));
+ " $classname$, Builder>\n"
+ " implements\n"
+ " $extra_interfaces$\n"
+ " $classname$OrBuilder {\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_),
+ "extra_interfaces", ExtraBuilderInterfaces(descriptor_));
}
}
printer->Indent();
+ GenerateDescriptorMethods(printer);
GenerateCommonBuilderMethods(printer);
if (HasGeneratedMethods(descriptor_)) {
+ GenerateIsInitialized(printer, DONT_MEMOIZE);
GenerateBuilderParsingMethods(printer);
}
+ // oneof
+ map<string, string> vars;
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ vars["oneof_name"] = context_->GetOneofGeneratorInfo(
+ descriptor_->oneof_decl(i))->name;
+ vars["oneof_capitalized_name"] = context_->GetOneofGeneratorInfo(
+ descriptor_->oneof_decl(i))->capitalized_name;
+ vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index());
+ // oneofCase_ and oneof_
+ printer->Print(vars,
+ "private int $oneof_name$Case_ = 0;\n"
+ "private java.lang.Object $oneof_name$_;\n");
+ // oneofCase() and clearOneof()
+ printer->Print(vars,
+ "public $oneof_capitalized_name$Case\n"
+ " get$oneof_capitalized_name$Case() {\n"
+ " return $oneof_capitalized_name$Case.valueOf(\n"
+ " $oneof_name$Case_);\n"
+ "}\n"
+ "\n"
+ "public Builder clear$oneof_capitalized_name$() {\n"
+ " $oneof_name$Case_ = 0;\n"
+ " $oneof_name$_ = null;\n");
+ if (HasDescriptorMethods(descriptor_)) {
+ printer->Print(" onChanged();\n");
+ }
+ printer->Print(
+ " return this;\n"
+ "}\n"
+ "\n");
+ }
+
+ if (GenerateHasBits(descriptor_)) {
+ // Integers for bit fields.
+ int totalBits = 0;
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ totalBits += field_generators_.get(descriptor_->field(i))
+ .GetNumBitsForBuilder();
+ }
+ int totalInts = (totalBits + 31) / 32;
+ for (int i = 0; i < totalInts; i++) {
+ printer->Print("private int $bit_field_name$;\n",
+ "bit_field_name", GetBitFieldName(i));
+ }
+ }
+
for (int i = 0; i < descriptor_->field_count(); i++) {
printer->Print("\n");
- PrintFieldComment(printer, descriptor_->field(i));
field_generators_.get(descriptor_->field(i))
.GenerateBuilderMembers(printer);
}
@@ -654,98 +873,209 @@ void MessageGenerator::GenerateBuilder(io::Printer* printer) {
printer->Print("}\n");
}
+void ImmutableMessageGenerator::
+GenerateDescriptorMethods(io::Printer* printer) {
+ if (HasDescriptorMethods(descriptor_)) {
+ if (!descriptor_->options().no_standard_descriptor_accessor()) {
+ printer->Print(
+ "public static final com.google.protobuf.Descriptors.Descriptor\n"
+ " getDescriptor() {\n"
+ " return $fileclass$.internal_$identifier$_descriptor;\n"
+ "}\n"
+ "\n",
+ "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()),
+ "identifier", UniqueFileScopeIdentifier(descriptor_));
+ }
+ printer->Print(
+ "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n"
+ " internalGetFieldAccessorTable() {\n"
+ " return $fileclass$.internal_$identifier$_fieldAccessorTable\n"
+ " .ensureFieldAccessorsInitialized(\n"
+ " $classname$.class, $classname$.Builder.class);\n"
+ "}\n"
+ "\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_),
+ "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()),
+ "identifier", UniqueFileScopeIdentifier(descriptor_));
+ }
+}
+
// ===================================================================
-void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) {
+void ImmutableMessageGenerator::
+GenerateCommonBuilderMethods(io::Printer* printer) {
printer->Print(
- "private $classname$ result;\n"
- "\n"
"// Construct using $classname$.newBuilder()\n"
- "private Builder() {}\n"
- "\n"
- "private static Builder create() {\n"
- " Builder builder = new Builder();\n"
- " builder.result = new $classname$();\n"
- " return builder;\n"
+ "private Builder() {\n"
+ " maybeForceBuilderInitialization();\n"
"}\n"
- "\n"
- "protected $classname$ internalGetResult() {\n"
- " return result;\n"
+ "\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
+
+ if (HasDescriptorMethods(descriptor_)) {
+ printer->Print(
+ "private Builder(\n"
+ " com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n"
+ " super(parent);\n"
+ " maybeForceBuilderInitialization();\n"
+ "}\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
+ }
+
+
+ if (HasNestedBuilders(descriptor_)) {
+ printer->Print(
+ "private void maybeForceBuilderInitialization() {\n"
+ " if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {\n");
+
+ printer->Indent();
+ printer->Indent();
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ if (!descriptor_->field(i)->containing_oneof()) {
+ field_generators_.get(descriptor_->field(i))
+ .GenerateFieldBuilderInitializationCode(printer);
+ }
+ }
+ printer->Outdent();
+ printer->Outdent();
+
+ printer->Print(
+ " }\n"
+ "}\n");
+ } else {
+ printer->Print(
+ "private void maybeForceBuilderInitialization() {\n"
+ "}\n");
+ }
+
+ printer->Print(
+ "private static Builder create() {\n"
+ " return new Builder();\n"
"}\n"
"\n"
"public Builder clear() {\n"
- " if (result == null) {\n"
- " throw new IllegalStateException(\n"
- " \"Cannot call clear() after build().\");\n"
- " }\n"
- " result = new $classname$();\n"
+ " super.clear();\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
+
+ printer->Indent();
+
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ if (!descriptor_->field(i)->containing_oneof()) {
+ field_generators_.get(descriptor_->field(i))
+ .GenerateBuilderClearCode(printer);
+ }
+ }
+
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ printer->Print(
+ "$oneof_name$Case_ = 0;\n"
+ "$oneof_name$_ = null;\n",
+ "oneof_name", context_->GetOneofGeneratorInfo(
+ descriptor_->oneof_decl(i))->name);
+ }
+
+ printer->Outdent();
+
+ printer->Print(
" return this;\n"
"}\n"
"\n"
"public Builder clone() {\n"
- " return create().mergeFrom(result);\n"
+ " return create().mergeFrom(buildPartial());\n"
"}\n"
"\n",
- "classname", ClassName(descriptor_));
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
if (HasDescriptorMethods(descriptor_)) {
printer->Print(
"public com.google.protobuf.Descriptors.Descriptor\n"
" getDescriptorForType() {\n"
- " return $classname$.getDescriptor();\n"
+ " return $fileclass$.internal_$identifier$_descriptor;\n"
"}\n"
"\n",
- "classname", ClassName(descriptor_));
+ "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()),
+ "identifier", UniqueFileScopeIdentifier(descriptor_));
}
printer->Print(
"public $classname$ getDefaultInstanceForType() {\n"
" return $classname$.getDefaultInstance();\n"
"}\n"
- "\n"
- "public boolean isInitialized() {\n"
- " return result.isInitialized();\n"
- "}\n",
- "classname", ClassName(descriptor_));
+ "\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
// -----------------------------------------------------------------
printer->Print(
"public $classname$ build() {\n"
- // If result == null, we'll throw an appropriate exception later.
- " if (result != null && !isInitialized()) {\n"
+ " $classname$ result = buildPartial();\n"
+ " if (!result.isInitialized()) {\n"
" throw newUninitializedMessageException(result);\n"
" }\n"
- " return buildPartial();\n"
- "}\n"
- "\n"
- "private $classname$ buildParsed()\n"
- " throws com.google.protobuf.InvalidProtocolBufferException {\n"
- " if (!isInitialized()) {\n"
- " throw newUninitializedMessageException(\n"
- " result).asInvalidProtocolBufferException();\n"
- " }\n"
- " return buildPartial();\n"
+ " return result;\n"
"}\n"
"\n"
"public $classname$ buildPartial() {\n"
- " if (result == null) {\n"
- " throw new IllegalStateException(\n"
- " \"build() has already been called on this Builder.\");\n"
- " }\n",
- "classname", ClassName(descriptor_));
+ " $classname$ result = new $classname$(this);\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
+
printer->Indent();
+ int totalBuilderBits = 0;
+ int totalMessageBits = 0;
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ const ImmutableFieldGenerator& field =
+ field_generators_.get(descriptor_->field(i));
+ totalBuilderBits += field.GetNumBitsForBuilder();
+ totalMessageBits += field.GetNumBitsForMessage();
+ }
+ int totalBuilderInts = (totalBuilderBits + 31) / 32;
+ int totalMessageInts = (totalMessageBits + 31) / 32;
+
+ if (GenerateHasBits(descriptor_)) {
+ // Local vars for from and to bit fields to avoid accessing the builder and
+ // message over and over for these fields. Seems to provide a slight
+ // perforamance improvement in micro benchmark and this is also what proto1
+ // code does.
+ for (int i = 0; i < totalBuilderInts; i++) {
+ printer->Print("int from_$bit_field_name$ = $bit_field_name$;\n",
+ "bit_field_name", GetBitFieldName(i));
+ }
+ for (int i = 0; i < totalMessageInts; i++) {
+ printer->Print("int to_$bit_field_name$ = 0;\n",
+ "bit_field_name", GetBitFieldName(i));
+ }
+ }
+
+ // Output generation code for each field.
for (int i = 0; i < descriptor_->field_count(); i++) {
field_generators_.get(descriptor_->field(i)).GenerateBuildingCode(printer);
}
+ if (GenerateHasBits(descriptor_)) {
+ // Copy the bit field results to the generated message
+ for (int i = 0; i < totalMessageInts; i++) {
+ printer->Print("result.$bit_field_name$ = to_$bit_field_name$;\n",
+ "bit_field_name", GetBitFieldName(i));
+ }
+ }
+
+ for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+ printer->Print("result.$oneof_name$Case_ = $oneof_name$Case_;\n",
+ "oneof_name", context_->GetOneofGeneratorInfo(
+ descriptor_->oneof_decl(i))->name);
+ }
+
printer->Outdent();
+
+ if (HasDescriptorMethods(descriptor_)) {
+ printer->Print(
+ " onBuilt();\n");
+ }
+
printer->Print(
- " $classname$ returnMe = result;\n"
- " result = null;\n"
- " return returnMe;\n"
+ " return result;\n"
"}\n"
"\n",
- "classname", ClassName(descriptor_));
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
// -----------------------------------------------------------------
@@ -763,7 +1093,7 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) {
" }\n"
"}\n"
"\n",
- "classname", ClassName(descriptor_));
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
}
printer->Print(
@@ -771,11 +1101,48 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) {
// Optimization: If other is the default instance, we know none of its
// fields are set so we can skip the merge.
" if (other == $classname$.getDefaultInstance()) return this;\n",
- "classname", ClassName(descriptor_));
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
printer->Indent();
for (int i = 0; i < descriptor_->field_count(); i++) {
- field_generators_.get(descriptor_->field(i)).GenerateMergingCode(printer);
+ if (!descriptor_->field(i)->containing_oneof()) {
+ field_generators_.get(
+ descriptor_->field(i)).GenerateMergingCode(printer);
+ }
+ }
+
+ // Merge oneof fields.
+ for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) {
+ printer->Print(
+ "switch (other.get$oneof_capitalized_name$Case()) {\n",
+ "oneof_capitalized_name",
+ context_->GetOneofGeneratorInfo(
+ descriptor_->oneof_decl(i))->capitalized_name);
+ printer->Indent();
+ for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+ const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j);
+ printer->Print(
+ "case $field_name$: {\n",
+ "field_name",
+ ToUpper(field->name()));
+ printer->Indent();
+ field_generators_.get(field).GenerateMergingCode(printer);
+ printer->Print(
+ "break;\n");
+ printer->Outdent();
+ printer->Print(
+ "}\n");
+ }
+ printer->Print(
+ "case $cap_oneof_name$_NOT_SET: {\n"
+ " break;\n"
+ "}\n",
+ "cap_oneof_name",
+ ToUpper(context_->GetOneofGeneratorInfo(
+ descriptor_->oneof_decl(i))->name));
+ printer->Outdent();
+ printer->Print(
+ "}\n");
}
printer->Outdent();
@@ -786,9 +1153,13 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) {
" this.mergeExtensionFields(other);\n");
}
- if (HasUnknownFields(descriptor_)) {
+ if (UseUnknownFieldSet(descriptor_)) {
printer->Print(
" this.mergeUnknownFields(other.getUnknownFields());\n");
+ } else {
+ printer->Print(
+ " setUnknownFields(\n"
+ " getUnknownFields().concat(other.unknownFields));\n");
}
printer->Print(
@@ -800,57 +1171,358 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) {
// ===================================================================
-void MessageGenerator::GenerateBuilderParsingMethods(io::Printer* printer) {
- scoped_array<const FieldDescriptor*> sorted_fields(
- SortFieldsByNumber(descriptor_));
-
+void ImmutableMessageGenerator::
+GenerateBuilderParsingMethods(io::Printer* printer) {
printer->Print(
"public Builder mergeFrom(\n"
" com.google.protobuf.CodedInputStream input,\n"
" com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n"
- " throws java.io.IOException {\n");
+ " throws java.io.IOException {\n"
+ " $classname$ parsedMessage = null;\n"
+ " try {\n"
+ " parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n"
+ " } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n"
+ " parsedMessage = ($classname$) e.getUnfinishedMessage();\n"
+ " throw e;\n"
+ " } finally {\n"
+ " if (parsedMessage != null) {\n"
+ " mergeFrom(parsedMessage);\n"
+ " }\n"
+ " }\n"
+ " return this;\n"
+ "}\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
+}
+
+// ===================================================================
+
+void ImmutableMessageGenerator::GenerateIsInitialized(
+ io::Printer* printer, UseMemoization useMemoization) {
+ bool memoization = useMemoization == MEMOIZE;
+ if (memoization) {
+ // Memoizes whether the protocol buffer is fully initialized (has all
+ // required fields). -1 means not yet computed. 0 means false and 1 means
+ // true.
+ printer->Print(
+ "private byte memoizedIsInitialized = -1;\n");
+ }
+ printer->Print(
+ "public final boolean isInitialized() {\n");
printer->Indent();
- if (HasUnknownFields(descriptor_)) {
+ if (memoization) {
+ // Don't directly compare to -1 to avoid an Android x86 JIT bug.
printer->Print(
- "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n"
- " com.google.protobuf.UnknownFieldSet.newBuilder(\n"
- " this.getUnknownFields());\n");
+ "byte isInitialized = memoizedIsInitialized;\n"
+ "if (isInitialized == 1) return true;\n"
+ "if (isInitialized == 0) return false;\n"
+ "\n");
+ }
+
+ // Check that all required fields in this message are set.
+ // TODO(kenton): We can optimize this when we switch to putting all the
+ // "has" fields into a single bitfield.
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ const FieldDescriptor* field = descriptor_->field(i);
+ const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field);
+
+ if (field->is_required()) {
+ printer->Print(
+ "if (!has$name$()) {\n"
+ " $memoize$\n"
+ " return false;\n"
+ "}\n",
+ "name", info->capitalized_name,
+ "memoize", memoization ? "memoizedIsInitialized = 0;" : "");
+ }
+ }
+
+ // Now check that all embedded messages are initialized.
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ const FieldDescriptor* field = descriptor_->field(i);
+ const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field);
+ if (GetJavaType(field) == JAVATYPE_MESSAGE &&
+ HasRequiredFields(field->message_type())) {
+ switch (field->label()) {
+ case FieldDescriptor::LABEL_REQUIRED:
+ printer->Print(
+ "if (!get$name$().isInitialized()) {\n"
+ " $memoize$\n"
+ " return false;\n"
+ "}\n",
+ "type", name_resolver_->GetImmutableClassName(
+ field->message_type()),
+ "name", info->capitalized_name,
+ "memoize", memoization ? "memoizedIsInitialized = 0;" : "");
+ break;
+ case FieldDescriptor::LABEL_OPTIONAL:
+ printer->Print(
+ "if (has$name$()) {\n"
+ " if (!get$name$().isInitialized()) {\n"
+ " $memoize$\n"
+ " return false;\n"
+ " }\n"
+ "}\n",
+ "type", name_resolver_->GetImmutableClassName(
+ field->message_type()),
+ "name", info->capitalized_name,
+ "memoize", memoization ? "memoizedIsInitialized = 0;" : "");
+ break;
+ case FieldDescriptor::LABEL_REPEATED:
+ printer->Print(
+ "for (int i = 0; i < get$name$Count(); i++) {\n"
+ " if (!get$name$(i).isInitialized()) {\n"
+ " $memoize$\n"
+ " return false;\n"
+ " }\n"
+ "}\n",
+ "type", name_resolver_->GetImmutableClassName(
+ field->message_type()),
+ "name", info->capitalized_name,
+ "memoize", memoization ? "memoizedIsInitialized = 0;" : "");
+ break;
+ }
+ }
+ }
+
+ if (descriptor_->extension_range_count() > 0) {
+ printer->Print(
+ "if (!extensionsAreInitialized()) {\n"
+ " $memoize$\n"
+ " return false;\n"
+ "}\n",
+ "memoize", memoization ? "memoizedIsInitialized = 0;" : "");
+ }
+
+ printer->Outdent();
+
+ if (memoization) {
+ printer->Print(
+ " memoizedIsInitialized = 1;\n");
}
printer->Print(
- "while (true) {\n");
+ " return true;\n"
+ "}\n"
+ "\n");
+}
+
+// ===================================================================
+
+namespace {
+bool CheckHasBitsForEqualsAndHashCode(const FieldDescriptor* field) {
+ if (field->is_repeated()) {
+ return false;
+ }
+ if (SupportFieldPresence(field->file())) {
+ return true;
+ }
+ return GetJavaType(field) == JAVATYPE_MESSAGE &&
+ field->containing_oneof() == NULL;
+}
+} // namespace
+
+void ImmutableMessageGenerator::
+GenerateEqualsAndHashCode(io::Printer* printer) {
+ printer->Print(
+ "@java.lang.Override\n"
+ "public boolean equals(final java.lang.Object obj) {\n");
printer->Indent();
+ printer->Print(
+ "if (obj == this) {\n"
+ " return true;\n"
+ "}\n"
+ "if (!(obj instanceof $classname$)) {\n"
+ " return super.equals(obj);\n"
+ "}\n"
+ "$classname$ other = ($classname$) obj;\n"
+ "\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
+
+ printer->Print("boolean result = true;\n");
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ const FieldDescriptor* field = descriptor_->field(i);
+ const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field);
+ bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field);
+ if (check_has_bits) {
+ printer->Print(
+ "result = result && (has$name$() == other.has$name$());\n"
+ "if (has$name$()) {\n",
+ "name", info->capitalized_name);
+ printer->Indent();
+ }
+ field_generators_.get(field).GenerateEqualsCode(printer);
+ if (check_has_bits) {
+ printer->Outdent();
+ printer->Print(
+ "}\n");
+ }
+ }
+ if (HasDescriptorMethods(descriptor_)) {
+ printer->Print(
+ "result = result &&\n"
+ " getUnknownFields().equals(other.getUnknownFields());\n");
+ if (descriptor_->extension_range_count() > 0) {
+ printer->Print(
+ "result = result &&\n"
+ " getExtensionFields().equals(other.getExtensionFields());\n");
+ }
+ }
+ printer->Print(
+ "return result;\n");
+ printer->Outdent();
+ printer->Print(
+ "}\n"
+ "\n");
printer->Print(
- "int tag = input.readTag();\n"
- "switch (tag) {\n");
+ "@java.lang.Override\n"
+ "public int hashCode() {\n");
+ printer->Indent();
+ printer->Print(
+ "if (memoizedHashCode != 0) {\n");
printer->Indent();
+ printer->Print(
+ "return memoizedHashCode;\n");
+ printer->Outdent();
+ printer->Print(
+ "}\n"
+ "int hash = 41;\n");
- if (HasUnknownFields(descriptor_)) {
+ if (HasDescriptorMethods(descriptor_)) {
+ printer->Print("hash = (19 * hash) + getDescriptorForType().hashCode();\n");
+ } else {
+ // Include the hash of the class so that two objects with different types
+ // but the same field values will probably have different hashes.
+ printer->Print("hash = (19 * hash) + $classname$.class.hashCode();\n",
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
+ }
+
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ const FieldDescriptor* field = descriptor_->field(i);
+ 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");
+ }
+ }
+ if (HasDescriptorMethods(descriptor_)) {
+ if (descriptor_->extension_range_count() > 0) {
+ printer->Print(
+ "hash = hashFields(hash, getExtensionFields());\n");
+ }
+ }
+
+ if (UseUnknownFieldSet(descriptor_)) {
printer->Print(
- "case 0:\n" // zero signals EOF / limit reached
- " this.setUnknownFields(unknownFields.build());\n"
- " return this;\n"
- "default: {\n"
- " if (!parseUnknownField(input, unknownFields,\n"
- " extensionRegistry, tag)) {\n"
- " this.setUnknownFields(unknownFields.build());\n"
- " return this;\n" // it's an endgroup tag
- " }\n"
- " break;\n"
- "}\n");
+ "hash = (29 * hash) + getUnknownFields().hashCode();\n");
} else {
printer->Print(
- "case 0:\n" // zero signals EOF / limit reached
- " return this;\n"
- "default: {\n"
- " if (!parseUnknownField(input, extensionRegistry, tag)) {\n"
- " return this;\n" // it's an endgroup tag
- " }\n"
- " break;\n"
- "}\n");
+ "hash = (29 * hash) + unknownFields.hashCode();\n");
+ }
+ printer->Print(
+ "memoizedHashCode = hash;\n"
+ "return hash;\n");
+ printer->Outdent();
+ printer->Print(
+ "}\n"
+ "\n");
+}
+
+// ===================================================================
+
+void ImmutableMessageGenerator::
+GenerateExtensionRegistrationCode(io::Printer* printer) {
+ for (int i = 0; i < descriptor_->extension_count(); i++) {
+ ImmutableExtensionGenerator(descriptor_->extension(i), context_)
+ .GenerateRegistrationCode(printer);
+ }
+
+ for (int i = 0; i < descriptor_->nested_type_count(); i++) {
+ ImmutableMessageGenerator(descriptor_->nested_type(i), context_)
+ .GenerateExtensionRegistrationCode(printer);
+ }
+}
+
+// ===================================================================
+void ImmutableMessageGenerator::
+GenerateParsingConstructor(io::Printer* printer) {
+ scoped_array<const FieldDescriptor*> sorted_fields(
+ SortFieldsByNumber(descriptor_));
+
+ printer->Print(
+ "private $classname$(\n"
+ " com.google.protobuf.CodedInputStream input,\n"
+ " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n"
+ " throws com.google.protobuf.InvalidProtocolBufferException {\n",
+ "classname", descriptor_->name());
+ printer->Indent();
+
+ // Initialize all fields to default.
+ printer->Print(
+ "initFields();\n");
+
+ // Use builder bits to track mutable repeated fields.
+ int totalBuilderBits = 0;
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ const ImmutableFieldGenerator& field =
+ field_generators_.get(descriptor_->field(i));
+ totalBuilderBits += field.GetNumBitsForBuilder();
}
+ int totalBuilderInts = (totalBuilderBits + 31) / 32;
+ for (int i = 0; i < totalBuilderInts; i++) {
+ printer->Print("int mutable_$bit_field_name$ = 0;\n",
+ "bit_field_name", GetBitFieldName(i));
+ }
+
+ if (UseUnknownFieldSet(descriptor_)) {
+ printer->Print(
+ "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n"
+ " com.google.protobuf.UnknownFieldSet.newBuilder();\n");
+ } else {
+ printer->Print(
+ "com.google.protobuf.ByteString.Output unknownFieldsOutput =\n"
+ " com.google.protobuf.ByteString.newOutput();\n"
+ "com.google.protobuf.CodedOutputStream unknownFieldsCodedOutput =\n"
+ " com.google.protobuf.CodedOutputStream.newInstance(\n"
+ " unknownFieldsOutput);\n");
+ }
+
+ printer->Print(
+ "try {\n");
+ printer->Indent();
+
+ printer->Print(
+ "boolean done = false;\n"
+ "while (!done) {\n");
+ printer->Indent();
+
+ printer->Print(
+ "int tag = input.readTag();\n"
+ "switch (tag) {\n");
+ printer->Indent();
+
+ printer->Print(
+ "case 0:\n" // zero signals EOF / limit reached
+ " done = true;\n"
+ " break;\n"
+ "default: {\n"
+ " if (!parseUnknownField(input,$unknown_fields$\n"
+ " extensionRegistry, tag)) {\n"
+ " done = true;\n" // it's an endgroup tag
+ " }\n"
+ " break;\n"
+ "}\n",
+ "unknown_fields", UseUnknownFieldSet(descriptor_)
+ ? " unknownFields," : " unknownFieldsCodedOutput,");
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = sorted_fields[i];
@@ -890,92 +1562,104 @@ void MessageGenerator::GenerateBuilderParsingMethods(io::Printer* printer) {
printer->Outdent();
printer->Outdent();
- printer->Outdent();
printer->Print(
- " }\n" // switch (tag)
- " }\n" // while (true)
- "}\n"
- "\n");
-}
-
-// ===================================================================
+ " }\n" // switch (tag)
+ "}\n"); // while (!done)
-void MessageGenerator::GenerateIsInitialized(io::Printer* printer) {
+ printer->Outdent();
printer->Print(
- "public final boolean isInitialized() {\n");
+ "} catch (com.google.protobuf.InvalidProtocolBufferException e) {\n"
+ " throw e.setUnfinishedMessage(this);\n"
+ "} catch (java.io.IOException e) {\n"
+ " throw new com.google.protobuf.InvalidProtocolBufferException(\n"
+ " e.getMessage()).setUnfinishedMessage(this);\n"
+ "} finally {\n");
printer->Indent();
- // Check that all required fields in this message are set.
- // TODO(kenton): We can optimize this when we switch to putting all the
- // "has" fields into a single bitfield.
+ // Make repeated field list immutable.
for (int i = 0; i < descriptor_->field_count(); i++) {
- const FieldDescriptor* field = descriptor_->field(i);
-
- if (field->is_required()) {
- printer->Print(
- "if (!has$name$) return false;\n",
- "name", UnderscoresToCapitalizedCamelCase(field));
- }
- }
-
- // Now check that all embedded messages are initialized.
- for (int i = 0; i < descriptor_->field_count(); i++) {
- const FieldDescriptor* field = descriptor_->field(i);
- if (GetJavaType(field) == JAVATYPE_MESSAGE &&
- HasRequiredFields(field->message_type())) {
- switch (field->label()) {
- case FieldDescriptor::LABEL_REQUIRED:
- printer->Print(
- "if (!get$name$().isInitialized()) return false;\n",
- "type", ClassName(field->message_type()),
- "name", UnderscoresToCapitalizedCamelCase(field));
- break;
- case FieldDescriptor::LABEL_OPTIONAL:
- printer->Print(
- "if (has$name$()) {\n"
- " if (!get$name$().isInitialized()) return false;\n"
- "}\n",
- "type", ClassName(field->message_type()),
- "name", UnderscoresToCapitalizedCamelCase(field));
- break;
- case FieldDescriptor::LABEL_REPEATED:
- printer->Print(
- "for ($type$ element : get$name$List()) {\n"
- " if (!element.isInitialized()) return false;\n"
- "}\n",
- "type", ClassName(field->message_type()),
- "name", UnderscoresToCapitalizedCamelCase(field));
- break;
- }
- }
+ const FieldDescriptor* field = sorted_fields[i];
+ field_generators_.get(field).GenerateParsingDoneCode(printer);
}
- if (descriptor_->extension_range_count() > 0) {
+ // Make unknown fields immutable.
+ if (UseUnknownFieldSet(descriptor_)) {
+ printer->Print(
+ "this.unknownFields = unknownFields.build();\n");
+ } else {
printer->Print(
- "if (!extensionsAreInitialized()) return false;\n");
+ "try {\n"
+ " unknownFieldsCodedOutput.flush();\n"
+ "} catch (java.io.IOException e) {\n"
+ "// Should not happen\n"
+ "} finally {\n"
+ " unknownFields = unknownFieldsOutput.toByteString();\n"
+ "}\n");
}
+ // Make extensions immutable.
+ printer->Print(
+ "makeExtensionsImmutable();\n");
+
+ printer->Outdent();
printer->Outdent();
printer->Print(
- " return true;\n"
- "}\n"
- "\n");
+ " }\n" // finally
+ "}\n");
}
// ===================================================================
-
-void MessageGenerator::GenerateExtensionRegistrationCode(io::Printer* printer) {
- for (int i = 0; i < descriptor_->extension_count(); i++) {
- ExtensionGenerator(descriptor_->extension(i))
- .GenerateRegistrationCode(printer);
+void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) {
+ printer->Print(
+ "public static com.google.protobuf.Parser<$classname$> PARSER =\n"
+ " new com.google.protobuf.AbstractParser<$classname$>() {\n",
+ "classname", descriptor_->name());
+ printer->Indent();
+ printer->Print(
+ "public $classname$ parsePartialFrom(\n"
+ " com.google.protobuf.CodedInputStream input,\n"
+ " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n"
+ " throws com.google.protobuf.InvalidProtocolBufferException {\n",
+ "classname", descriptor_->name());
+ if (HasGeneratedMethods(descriptor_)) {
+ printer->Print(
+ " return new $classname$(input, extensionRegistry);\n",
+ "classname", descriptor_->name());
+ } else {
+ // When parsing constructor isn't generated, use builder to parse messages.
+ // Note, will fallback to use reflection based mergeFieldFrom() in
+ // AbstractMessage.Builder.
+ printer->Indent();
+ printer->Print(
+ "Builder builder = newBuilder();\n"
+ "try {\n"
+ " builder.mergeFrom(input, extensionRegistry);\n"
+ "} catch (com.google.protobuf.InvalidProtocolBufferException e) {\n"
+ " throw e.setUnfinishedMessage(builder.buildPartial());\n"
+ "} catch (java.io.IOException e) {\n"
+ " throw new com.google.protobuf.InvalidProtocolBufferException(\n"
+ " e.getMessage()).setUnfinishedMessage(builder.buildPartial());\n"
+ "}\n"
+ "return builder.buildPartial();\n");
+ printer->Outdent();
}
+ printer->Print(
+ "}\n");
+ printer->Outdent();
+ printer->Print(
+ "};\n"
+ "\n");
- for (int i = 0; i < descriptor_->nested_type_count(); i++) {
- MessageGenerator(descriptor_->nested_type(i))
- .GenerateExtensionRegistrationCode(printer);
- }
+ printer->Print(
+ "@java.lang.Override\n"
+ "public com.google.protobuf.Parser<$classname$> getParserForType() {\n"
+ " return PARSER;\n"
+ "}\n"
+ "\n",
+ "classname", descriptor_->name());
}
+
} // namespace java
} // namespace compiler
} // namespace protobuf
diff --git a/src/google/protobuf/compiler/java/java_message.h b/src/google/protobuf/compiler/java/java_message.h
index 50ffae0..fece1c2 100644
--- a/src/google/protobuf/compiler/java/java_message.h
+++ b/src/google/protobuf/compiler/java/java_message.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -36,11 +36,17 @@
#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__
#include <string>
-#include <google/protobuf/stubs/common.h>
+#include <map>
#include <google/protobuf/compiler/java/java_field.h>
namespace google {
namespace protobuf {
+ namespace compiler {
+ namespace java {
+ class Context; // context.h
+ class ClassNameResolver; // name_resolver.h
+ }
+ }
namespace io {
class Printer; // printer.h
}
@@ -53,25 +59,55 @@ namespace java {
class MessageGenerator {
public:
explicit MessageGenerator(const Descriptor* descriptor);
- ~MessageGenerator();
+ virtual ~MessageGenerator();
// All static variables have to be declared at the top-level of the file
// so that we can control initialization order, which is important for
// DescriptorProto bootstrapping to work.
- void GenerateStaticVariables(io::Printer* printer);
+ virtual void GenerateStaticVariables(io::Printer* printer) = 0;
// Output code which initializes the static variables generated by
// GenerateStaticVariables().
- void GenerateStaticVariableInitializers(io::Printer* printer);
+ virtual void GenerateStaticVariableInitializers(io::Printer* printer) = 0;
// Generate the class itself.
- void Generate(io::Printer* printer);
+ virtual void Generate(io::Printer* printer) = 0;
+
+ // Generates the base interface that both the class and its builder implement
+ virtual void GenerateInterface(io::Printer* printer) = 0;
// Generate code to register all contained extensions with an
// ExtensionRegistry.
- void GenerateExtensionRegistrationCode(io::Printer* printer);
+ virtual void GenerateExtensionRegistrationCode(io::Printer* printer) = 0;
+
+ protected:
+ const Descriptor* descriptor_;
private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator);
+};
+
+class ImmutableMessageGenerator : public MessageGenerator {
+ public:
+ explicit ImmutableMessageGenerator(const Descriptor* descriptor,
+ Context* context);
+ virtual ~ImmutableMessageGenerator();
+
+ virtual void Generate(io::Printer* printer);
+ virtual void GenerateInterface(io::Printer* printer);
+ virtual void GenerateExtensionRegistrationCode(io::Printer* printer);
+ virtual void GenerateStaticVariables(io::Printer* printer);
+ virtual void GenerateStaticVariableInitializers(io::Printer* printer);
+
+ private:
+ enum UseMemoization {
+ MEMOIZE,
+ DONT_MEMOIZE
+ };
+
+ void GenerateFieldAccessorTable(io::Printer* printer);
+ void GenerateFieldAccessorTableInitializer(io::Printer* printer);
+
void GenerateMessageSerializationMethods(io::Printer* printer);
void GenerateParseFromMethods(io::Printer* printer);
void GenerateSerializeOneField(io::Printer* printer,
@@ -81,13 +117,19 @@ class MessageGenerator {
void GenerateBuilder(io::Printer* printer);
void GenerateCommonBuilderMethods(io::Printer* printer);
+ void GenerateDescriptorMethods(io::Printer* printer);
void GenerateBuilderParsingMethods(io::Printer* printer);
- void GenerateIsInitialized(io::Printer* printer);
+ void GenerateIsInitialized(io::Printer* printer,
+ UseMemoization useMemoization);
+ void GenerateEqualsAndHashCode(io::Printer* printer);
+ void GenerateParser(io::Printer* printer);
+ void GenerateParsingConstructor(io::Printer* printer);
- const Descriptor* descriptor_;
- FieldGeneratorMap field_generators_;
+ Context* context_;
+ ClassNameResolver* name_resolver_;
+ FieldGeneratorMap<ImmutableFieldGenerator> field_generators_;
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator);
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableMessageGenerator);
};
} // namespace java
diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc
index 71edc02..e681314 100644
--- a/src/google/protobuf/compiler/java/java_message_field.cc
+++ b/src/google/protobuf/compiler/java/java_message_field.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -35,8 +35,11 @@
#include <map>
#include <string>
+#include <google/protobuf/compiler/java/java_context.h>
#include <google/protobuf/compiler/java/java_message_field.h>
+#include <google/protobuf/compiler/java/java_doc_comment.h>
#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/compiler/java/java_name_resolver.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
@@ -48,88 +51,413 @@ namespace java {
namespace {
-// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of
-// repeat code between this and the other field types.
void SetMessageVariables(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ const FieldGeneratorInfo* info,
+ ClassNameResolver* name_resolver,
map<string, string>* variables) {
- (*variables)["name"] =
- UnderscoresToCamelCase(descriptor);
- (*variables)["capitalized_name"] =
- UnderscoresToCapitalizedCamelCase(descriptor);
- (*variables)["number"] = SimpleItoa(descriptor->number());
- (*variables)["type"] = ClassName(descriptor->message_type());
+ SetCommonFieldVariables(descriptor, info, variables);
+
+ (*variables)["type"] =
+ name_resolver->GetImmutableClassName(descriptor->message_type());
+ (*variables)["mutable_type"] =
+ name_resolver->GetMutableClassName(descriptor->message_type());
(*variables)["group_or_message"] =
(GetType(descriptor) == FieldDescriptor::TYPE_GROUP) ?
"Group" : "Message";
+ // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported
+ // by the proto compiler
+ (*variables)["deprecation"] = descriptor->options().deprecated()
+ ? "@java.lang.Deprecated " : "";
+ (*variables)["on_changed"] =
+ HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : "";
+
+ if (SupportFieldPresence(descriptor->file())) {
+ // For singular messages and builders, one bit is used for the hasField bit.
+ (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex);
+ (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex);
+
+ // Note that these have a trailing ";".
+ (*variables)["set_has_field_bit_message"] =
+ GenerateSetBit(messageBitIndex) + ";";
+ (*variables)["set_has_field_bit_builder"] =
+ GenerateSetBit(builderBitIndex) + ";";
+ (*variables)["clear_has_field_bit_builder"] =
+ GenerateClearBit(builderBitIndex) + ";";
+
+ (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex);
+ } else {
+ (*variables)["set_has_field_bit_message"] = "";
+ (*variables)["set_has_field_bit_builder"] = "";
+ (*variables)["clear_has_field_bit_builder"] = "";
+
+ (*variables)["is_field_present_message"] =
+ (*variables)["name"] + "_ != null";
+ }
+
+ // For repated builders, one bit is used for whether the array is immutable.
+ (*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex);
+ (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex);
+ (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex);
+
+ // For repeated fields, one bit is used for whether the array is immutable
+ // in the parsing constructor.
+ (*variables)["get_mutable_bit_parser"] =
+ GenerateGetBitMutableLocal(builderBitIndex);
+ (*variables)["set_mutable_bit_parser"] =
+ GenerateSetBitMutableLocal(builderBitIndex);
+
+ (*variables)["get_has_field_bit_from_local"] =
+ GenerateGetBitFromLocal(builderBitIndex);
+ (*variables)["set_has_field_bit_to_local"] =
+ GenerateSetBitToLocal(messageBitIndex);
}
} // namespace
// ===================================================================
-MessageFieldGenerator::
-MessageFieldGenerator(const FieldDescriptor* descriptor)
- : descriptor_(descriptor) {
- SetMessageVariables(descriptor, &variables_);
+ImmutableMessageFieldGenerator::
+ImmutableMessageFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : descriptor_(descriptor), messageBitIndex_(messageBitIndex),
+ builderBitIndex_(builderBitIndex), context_(context),
+ name_resolver_(context->GetNameResolver()) {
+ SetMessageVariables(descriptor, messageBitIndex, builderBitIndex,
+ context->GetFieldGeneratorInfo(descriptor),
+ name_resolver_, &variables_);
}
-MessageFieldGenerator::~MessageFieldGenerator() {}
+ImmutableMessageFieldGenerator::~ImmutableMessageFieldGenerator() {}
-void MessageFieldGenerator::
+int ImmutableMessageFieldGenerator::GetNumBitsForMessage() const {
+ return 1;
+}
+
+int ImmutableMessageFieldGenerator::GetNumBitsForBuilder() const {
+ return 1;
+}
+
+void ImmutableMessageFieldGenerator::
+GenerateInterfaceMembers(io::Printer* printer) const {
+ // TODO(jonp): In the future, consider having a method specific to the
+ // interface so that builders can choose dynamically to either return a
+ // message or a nested builder, so that asking for the interface doesn't
+ // cause a message to ever be built.
+ if (SupportFieldPresence(descriptor_->file()) ||
+ descriptor_->containing_oneof() == NULL) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$boolean has$capitalized_name$();\n");
+ }
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$$type$ get$capitalized_name$();\n");
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder();\n");
+ }
+}
+
+void ImmutableMessageFieldGenerator::
GenerateMembers(io::Printer* printer) const {
printer->Print(variables_,
- "private boolean has$capitalized_name$;\n"
- "private $type$ $name$_;\n"
- "public boolean has$capitalized_name$() { return has$capitalized_name$; }\n"
- "public $type$ get$capitalized_name$() { return $name$_; }\n");
+ "private $type$ $name$_;\n");
+ PrintExtraFieldInfo(variables_, printer);
+
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $get_has_field_bit_message$;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " return $name$_;\n"
+ "}\n");
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$OrBuilder "
+ "get$capitalized_name$OrBuilder() {\n"
+ " return $name$_;\n"
+ "}\n");
+ }
+ } else {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $name$_ != null;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n"
+ "}\n");
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$OrBuilder "
+ "get$capitalized_name$OrBuilder() {\n"
+ " return get$capitalized_name$();\n"
+ "}\n");
+ }
+ }
}
-void MessageFieldGenerator::
+void ImmutableMessageFieldGenerator::PrintNestedBuilderCondition(
+ io::Printer* printer,
+ const char* regular_case,
+ const char* nested_builder_case) const {
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ printer->Print(variables_, "if ($name$Builder_ == null) {\n");
+ printer->Indent();
+ printer->Print(variables_, regular_case);
+ printer->Outdent();
+ printer->Print("} else {\n");
+ printer->Indent();
+ printer->Print(variables_, nested_builder_case);
+ printer->Outdent();
+ printer->Print("}\n");
+ } else {
+ printer->Print(variables_, regular_case);
+ }
+}
+
+void ImmutableMessageFieldGenerator::PrintNestedBuilderFunction(
+ io::Printer* printer,
+ const char* method_prototype,
+ const char* regular_case,
+ const char* nested_builder_case,
+ const char* trailing_code) const {
+ printer->Print(variables_, method_prototype);
+ printer->Print(" {\n");
+ printer->Indent();
+ PrintNestedBuilderCondition(printer, regular_case, nested_builder_case);
+ if (trailing_code != NULL) {
+ printer->Print(variables_, trailing_code);
+ }
+ printer->Outdent();
+ printer->Print("}\n");
+}
+
+void ImmutableMessageFieldGenerator::
GenerateBuilderMembers(io::Printer* printer) const {
- printer->Print(variables_,
- "public boolean has$capitalized_name$() {\n"
- " return result.has$capitalized_name$();\n"
- "}\n"
- "public $type$ get$capitalized_name$() {\n"
- " return result.get$capitalized_name$();\n"
- "}\n"
- "public Builder set$capitalized_name$($type$ value) {\n"
- " if (value == null) {\n"
- " throw new NullPointerException();\n"
- " }\n"
- " result.has$capitalized_name$ = true;\n"
- " result.$name$_ = value;\n"
- " return this;\n"
- "}\n"
- "public Builder set$capitalized_name$($type$.Builder builderForValue) {\n"
- " result.has$capitalized_name$ = true;\n"
- " result.$name$_ = builderForValue.build();\n"
- " return this;\n"
- "}\n"
- "public Builder merge$capitalized_name$($type$ value) {\n"
- " if (result.has$capitalized_name$() &&\n"
- " result.$name$_ != $type$.getDefaultInstance()) {\n"
- " result.$name$_ =\n"
- " $type$.newBuilder(result.$name$_).mergeFrom(value).buildPartial();\n"
- " } else {\n"
- " result.$name$_ = value;\n"
- " }\n"
- " result.has$capitalized_name$ = true;\n"
- " return this;\n"
+ // When using nested-builders, the code initially works just like the
+ // non-nested builder case. It only creates a nested builder lazily on
+ // demand and then forever delegates to it after creation.
+
+ bool support_field_presence = SupportFieldPresence(descriptor_->file());
+
+ if (support_field_presence) {
+ printer->Print(variables_,
+ // Used when the builder is null.
+ "private $type$ $name$_ = $type$.getDefaultInstance();\n");
+ } else {
+ printer->Print(variables_,
+ "private $type$ $name$_ = null;\n");
+ }
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ printer->Print(variables_,
+ // If this builder is non-null, it is used and the other fields are
+ // ignored.
+ "private com.google.protobuf.SingleFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;"
+ "\n");
+ }
+
+ // The comments above the methods below are based on a hypothetical
+ // field of type "Field" called "Field".
+
+ // boolean hasField()
+ WriteFieldDocComment(printer, descriptor_);
+ if (support_field_presence) {
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $get_has_field_bit_builder$;\n"
+ "}\n");
+ } else {
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $name$Builder_ != null || $name$_ != null;\n"
+ "}\n");
+ }
+
+ // Field getField()
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public $type$ get$capitalized_name$()",
+
+ support_field_presence
+ ? "return $name$_;\n"
+ : "return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n",
+
+ "return $name$Builder_.getMessage();\n",
+
+ NULL);
+
+ // Field.Builder setField(Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder set$capitalized_name$($type$ value)",
+
+ "if (value == null) {\n"
+ " throw new NullPointerException();\n"
"}\n"
- "public Builder clear$capitalized_name$() {\n"
- " result.has$capitalized_name$ = false;\n"
- " result.$name$_ = $type$.getDefaultInstance();\n"
- " return this;\n"
- "}\n");
+ "$name$_ = value;\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.setMessage(value);\n",
+
+ "$set_has_field_bit_builder$\n"
+ "return this;\n");
+
+ // Field.Builder setField(Field.Builder builderForValue)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " $type$.Builder builderForValue)",
+
+ "$name$_ = builderForValue.build();\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.setMessage(builderForValue.build());\n",
+
+ "$set_has_field_bit_builder$\n"
+ "return this;\n");
+
+ // Field.Builder mergeField(Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder merge$capitalized_name$($type$ value)",
+
+ support_field_presence
+ ? "if ($get_has_field_bit_builder$ &&\n"
+ " $name$_ != $type$.getDefaultInstance()) {\n"
+ " $name$_ =\n"
+ " $type$.newBuilder($name$_).mergeFrom(value).buildPartial();\n"
+ "} else {\n"
+ " $name$_ = value;\n"
+ "}\n"
+ "$on_changed$\n"
+ : "if ($name$_ != null) {\n"
+ " $name$_ =\n"
+ " $type$.newBuilder($name$_).mergeFrom(value).buildPartial();\n"
+ "} else {\n"
+ " $name$_ = value;\n"
+ "}\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.mergeFrom(value);\n",
+
+ "$set_has_field_bit_builder$\n"
+ "return this;\n");
+
+ // Field.Builder clearField()
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder clear$capitalized_name$()",
+
+ support_field_presence
+ ? "$name$_ = $type$.getDefaultInstance();\n"
+ "$on_changed$\n"
+ : "$name$_ = null;\n"
+ "$on_changed$\n",
+
+ support_field_presence
+ ? "$name$Builder_.clear();\n"
+ : "$name$_ = null;\n"
+ "$name$Builder_ = null;\n",
+
+ "$clear_has_field_bit_builder$\n"
+ "return this;\n");
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n"
+ " $set_has_field_bit_builder$\n"
+ " $on_changed$\n"
+ " return get$capitalized_name$FieldBuilder().getBuilder();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n"
+ " if ($name$Builder_ != null) {\n"
+ " return $name$Builder_.getMessageOrBuilder();\n"
+ " } else {\n");
+ if (support_field_presence) {
+ printer->Print(variables_,
+ " return $name$_;\n");
+ } else {
+ printer->Print(variables_,
+ " return $name$_ == null ?\n"
+ " $type$.getDefaultInstance() : $name$_;\n");
+ }
+ printer->Print(variables_,
+ " }\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "private com.google.protobuf.SingleFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder> \n"
+ " get$capitalized_name$FieldBuilder() {\n"
+ " if ($name$Builder_ == null) {\n"
+ " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder>(\n"
+ " get$capitalized_name$(),\n"
+ " getParentForChildren(),\n"
+ " isClean());\n"
+ " $name$_ = null;\n"
+ " }\n"
+ " return $name$Builder_;\n"
+ "}\n");
+ }
}
-void MessageFieldGenerator::
+void ImmutableMessageFieldGenerator::
+GenerateFieldBuilderInitializationCode(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ printer->Print(variables_,
+ "get$capitalized_name$FieldBuilder();\n");
+ }
+}
+
+
+void ImmutableMessageFieldGenerator::
GenerateInitializationCode(io::Printer* printer) const {
- printer->Print(variables_, "$name$_ = $type$.getDefaultInstance();\n");
+ if (SupportFieldPresence(descriptor_->file())) {
+ printer->Print(variables_, "$name$_ = $type$.getDefaultInstance();\n");
+ }
}
-void MessageFieldGenerator::
+void ImmutableMessageFieldGenerator::
+GenerateBuilderClearCode(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ PrintNestedBuilderCondition(printer,
+ "$name$_ = $type$.getDefaultInstance();\n",
+
+ "$name$Builder_.clear();\n");
+ printer->Print(variables_, "$clear_has_field_bit_builder$\n");
+ } else {
+ PrintNestedBuilderCondition(printer,
+ "$name$_ = null;\n",
+
+ "$name$_ = null;\n"
+ "$name$Builder_ = null;\n");
+ }
+}
+
+void ImmutableMessageFieldGenerator::
GenerateMergingCode(io::Printer* printer) const {
printer->Print(variables_,
"if (other.has$capitalized_name$()) {\n"
@@ -137,196 +465,876 @@ GenerateMergingCode(io::Printer* printer) const {
"}\n");
}
-void MessageFieldGenerator::
+void ImmutableMessageFieldGenerator::
GenerateBuildingCode(io::Printer* printer) const {
- // Nothing to do for singular fields.
+ if (SupportFieldPresence(descriptor_->file())) {
+ printer->Print(variables_,
+ "if ($get_has_field_bit_from_local$) {\n"
+ " $set_has_field_bit_to_local$;\n"
+ "}\n");
+ }
+
+ PrintNestedBuilderCondition(printer,
+ "result.$name$_ = $name$_;\n",
+
+ "result.$name$_ = $name$Builder_.build();\n");
}
-void MessageFieldGenerator::
+void ImmutableMessageFieldGenerator::
GenerateParsingCode(io::Printer* printer) const {
printer->Print(variables_,
- "$type$.Builder subBuilder = $type$.newBuilder();\n"
- "if (has$capitalized_name$()) {\n"
- " subBuilder.mergeFrom(get$capitalized_name$());\n"
+ "$type$.Builder subBuilder = null;\n"
+ "if ($is_field_present_message$) {\n"
+ " subBuilder = $name$_.toBuilder();\n"
"}\n");
if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) {
printer->Print(variables_,
- "input.readGroup($number$, subBuilder, extensionRegistry);\n");
+ "$name$_ = input.readGroup($number$, $type$.PARSER,\n"
+ " extensionRegistry);\n");
} else {
printer->Print(variables_,
- "input.readMessage(subBuilder, extensionRegistry);\n");
+ "$name$_ = input.readMessage($type$.PARSER, extensionRegistry);\n");
}
printer->Print(variables_,
- "set$capitalized_name$(subBuilder.buildPartial());\n");
+ "if (subBuilder != null) {\n"
+ " subBuilder.mergeFrom($name$_);\n"
+ " $name$_ = subBuilder.buildPartial();\n"
+ "}\n"
+ "$set_has_field_bit_message$\n");
}
-void MessageFieldGenerator::
+void ImmutableMessageFieldGenerator::
+GenerateParsingDoneCode(io::Printer* printer) const {
+ // noop for messages.
+}
+
+void ImmutableMessageFieldGenerator::
GenerateSerializationCode(io::Printer* printer) const {
printer->Print(variables_,
- "if (has$capitalized_name$()) {\n"
- " output.write$group_or_message$($number$, get$capitalized_name$());\n"
+ "if ($is_field_present_message$) {\n"
+ " output.write$group_or_message$($number$, $name$_);\n"
"}\n");
}
-void MessageFieldGenerator::
+void ImmutableMessageFieldGenerator::
GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print(variables_,
- "if (has$capitalized_name$()) {\n"
+ "if ($is_field_present_message$) {\n"
" size += com.google.protobuf.CodedOutputStream\n"
- " .compute$group_or_message$Size($number$, get$capitalized_name$());\n"
+ " .compute$group_or_message$Size($number$, $name$_);\n"
"}\n");
}
-string MessageFieldGenerator::GetBoxedType() const {
- return ClassName(descriptor_->message_type());
+void ImmutableMessageFieldGenerator::
+GenerateEqualsCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "result = result && get$capitalized_name$()\n"
+ " .equals(other.get$capitalized_name$());\n");
+}
+
+void ImmutableMessageFieldGenerator::
+GenerateHashCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "hash = (37 * hash) + $constant_name$;\n"
+ "hash = (53 * hash) + get$capitalized_name$().hashCode();\n");
+}
+
+string ImmutableMessageFieldGenerator::GetBoxedType() const {
+ return name_resolver_->GetImmutableClassName(descriptor_->message_type());
}
// ===================================================================
-RepeatedMessageFieldGenerator::
-RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor)
- : descriptor_(descriptor) {
- SetMessageVariables(descriptor, &variables_);
+ImmutableMessageOneofFieldGenerator::
+ImmutableMessageOneofFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : ImmutableMessageFieldGenerator(
+ descriptor, messageBitIndex, builderBitIndex, context) {
+ const OneofGeneratorInfo* info =
+ context->GetOneofGeneratorInfo(descriptor->containing_oneof());
+ SetCommonOneofVariables(descriptor, info, &variables_);
}
-RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {}
+ImmutableMessageOneofFieldGenerator::
+~ImmutableMessageOneofFieldGenerator() {}
-void RepeatedMessageFieldGenerator::
+void ImmutableMessageOneofFieldGenerator::
GenerateMembers(io::Printer* printer) const {
+ PrintExtraFieldInfo(variables_, printer);
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $has_oneof_case_message$;\n"
+ "}\n");
+ }
+ WriteFieldDocComment(printer, descriptor_);
printer->Print(variables_,
- "private java.util.List<$type$> $name$_ =\n"
- " java.util.Collections.emptyList();\n"
- "public java.util.List<$type$> get$capitalized_name$List() {\n"
- " return $name$_;\n" // note: unmodifiable list
- "}\n"
- "public int get$capitalized_name$Count() { return $name$_.size(); }\n"
- "public $type$ get$capitalized_name$(int index) {\n"
- " return $name$_.get(index);\n"
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " if ($has_oneof_case_message$) {\n"
+ " return ($type$) $oneof_name$_;\n"
+ " }\n"
+ " return $type$.getDefaultInstance();\n"
"}\n");
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n"
+ " if ($has_oneof_case_message$) {\n"
+ " return ($type$) $oneof_name$_;\n"
+ " }\n"
+ " return $type$.getDefaultInstance();\n"
+ "}\n");
+ }
}
-void RepeatedMessageFieldGenerator::
+void ImmutableMessageOneofFieldGenerator::
GenerateBuilderMembers(io::Printer* printer) const {
- printer->Print(variables_,
- // Note: We return an unmodifiable list because otherwise the caller
- // could hold on to the returned list and modify it after the message
- // has been built, thus mutating the message which is supposed to be
- // immutable.
- "public java.util.List<$type$> get$capitalized_name$List() {\n"
- " return java.util.Collections.unmodifiableList(result.$name$_);\n"
+ // When using nested-builders, the code initially works just like the
+ // non-nested builder case. It only creates a nested builder lazily on
+ // demand and then forever delegates to it after creation.
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ printer->Print(variables_,
+ // If this builder is non-null, it is used and the other fields are
+ // ignored.
+ "private com.google.protobuf.SingleFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;"
+ "\n");
+ }
+
+ // The comments above the methods below are based on a hypothetical
+ // field of type "Field" called "Field".
+
+ if (SupportFieldPresence(descriptor_->file())) {
+ // boolean hasField()
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $has_oneof_case_message$;\n"
+ "}\n");
+ }
+
+ // Field getField()
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public $type$ get$capitalized_name$()",
+
+ "if ($has_oneof_case_message$) {\n"
+ " return ($type$) $oneof_name$_;\n"
"}\n"
- "public int get$capitalized_name$Count() {\n"
- " return result.get$capitalized_name$Count();\n"
+ "return $type$.getDefaultInstance();\n",
+
+ "if ($has_oneof_case_message$) {\n"
+ " return $name$Builder_.getMessage();\n"
"}\n"
- "public $type$ get$capitalized_name$(int index) {\n"
- " return result.get$capitalized_name$(index);\n"
+ "return $type$.getDefaultInstance();\n",
+
+ NULL);
+
+ // Field.Builder setField(Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder set$capitalized_name$($type$ value)",
+
+ "if (value == null) {\n"
+ " throw new NullPointerException();\n"
"}\n"
- "public Builder set$capitalized_name$(int index, $type$ value) {\n"
- " if (value == null) {\n"
- " throw new NullPointerException();\n"
- " }\n"
- " result.$name$_.set(index, value);\n"
- " return this;\n"
+ "$oneof_name$_ = value;\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.setMessage(value);\n",
+
+ "$set_oneof_case_message$;\n"
+ "return this;\n");
+
+ // Field.Builder setField(Field.Builder builderForValue)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " $type$.Builder builderForValue)",
+
+ "$oneof_name$_ = builderForValue.build();\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.setMessage(builderForValue.build());\n",
+
+ "$set_oneof_case_message$;\n"
+ "return this;\n");
+
+ // Field.Builder mergeField(Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder merge$capitalized_name$($type$ value)",
+
+ "if ($has_oneof_case_message$ &&\n"
+ " $oneof_name$_ != $type$.getDefaultInstance()) {\n"
+ " $oneof_name$_ = $type$.newBuilder(($type$) $oneof_name$_)\n"
+ " .mergeFrom(value).buildPartial();\n"
+ "} else {\n"
+ " $oneof_name$_ = value;\n"
"}\n"
- "public Builder set$capitalized_name$(int index, "
- "$type$.Builder builderForValue) {\n"
- " result.$name$_.set(index, builderForValue.build());\n"
- " return this;\n"
+ "$on_changed$\n",
+
+ "if ($has_oneof_case_message$) {\n"
+ " $name$Builder_.mergeFrom(value);\n"
"}\n"
- "public Builder add$capitalized_name$($type$ value) {\n"
- " if (value == null) {\n"
- " throw new NullPointerException();\n"
- " }\n"
- " if (result.$name$_.isEmpty()) {\n"
- " result.$name$_ = new java.util.ArrayList<$type$>();\n"
- " }\n"
- " result.$name$_.add(value);\n"
- " return this;\n"
+ "$name$Builder_.setMessage(value);\n",
+
+ "$set_oneof_case_message$;\n"
+ "return this;\n");
+
+ // Field.Builder clearField()
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder clear$capitalized_name$()",
+
+ "if ($has_oneof_case_message$) {\n"
+ " $clear_oneof_case_message$;\n"
+ " $oneof_name$_ = null;\n"
+ " $on_changed$\n"
+ "}\n",
+
+ "if ($has_oneof_case_message$) {\n"
+ " $clear_oneof_case_message$;\n"
+ " $oneof_name$_ = null;\n"
"}\n"
- "public Builder add$capitalized_name$($type$.Builder builderForValue) {\n"
- " if (result.$name$_.isEmpty()) {\n"
- " result.$name$_ = new java.util.ArrayList<$type$>();\n"
- " }\n"
- " result.$name$_.add(builderForValue.build());\n"
- " return this;\n"
+ "$name$Builder_.clear();\n",
+
+ "return this;\n");
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n"
+ " return get$capitalized_name$FieldBuilder().getBuilder();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n"
+ " if (($has_oneof_case_message$) && ($name$Builder_ != null)) {\n"
+ " return $name$Builder_.getMessageOrBuilder();\n"
+ " } else {\n"
+ " if ($has_oneof_case_message$) {\n"
+ " return ($type$) $oneof_name$_;\n"
+ " }\n"
+ " return $type$.getDefaultInstance();\n"
+ " }\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "private com.google.protobuf.SingleFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder> \n"
+ " get$capitalized_name$FieldBuilder() {\n"
+ " if ($name$Builder_ == null) {\n"
+ " if (!($has_oneof_case_message$)) {\n"
+ " $oneof_name$_ = $type$.getDefaultInstance();\n"
+ " }\n"
+ " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder>(\n"
+ " ($type$) $oneof_name$_,\n"
+ " getParentForChildren(),\n"
+ " isClean());\n"
+ " $oneof_name$_ = null;\n"
+ " }\n"
+ " $set_oneof_case_message$;\n"
+ " return $name$Builder_;\n"
+ "}\n");
+ }
+}
+
+void ImmutableMessageOneofFieldGenerator::
+GenerateBuildingCode(io::Printer* printer) const {
+
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n");
+ printer->Indent();
+
+ PrintNestedBuilderCondition(printer,
+ "result.$oneof_name$_ = $oneof_name$_;\n",
+
+ "result.$oneof_name$_ = $name$Builder_.build();\n");
+
+ printer->Outdent();
+ printer->Print("}\n");
+}
+
+void ImmutableMessageOneofFieldGenerator::
+GenerateMergingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "merge$capitalized_name$(other.get$capitalized_name$());\n");
+}
+
+void ImmutableMessageOneofFieldGenerator::
+GenerateParsingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "$type$.Builder subBuilder = null;\n"
+ "if ($has_oneof_case_message$) {\n"
+ " subBuilder = (($type$) $oneof_name$_).toBuilder();\n"
+ "}\n");
+
+ if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) {
+ printer->Print(variables_,
+ "$oneof_name$_ = input.readGroup($number$, $type$.PARSER,\n"
+ " extensionRegistry);\n");
+ } else {
+ printer->Print(variables_,
+ "$oneof_name$_ = input.readMessage($type$.PARSER, extensionRegistry);\n");
+ }
+
+ printer->Print(variables_,
+ "if (subBuilder != null) {\n"
+ " subBuilder.mergeFrom(($type$) $oneof_name$_);\n"
+ " $oneof_name$_ = subBuilder.buildPartial();\n"
+ "}\n");
+ printer->Print(variables_,
+ "$set_oneof_case_message$;\n");
+}
+
+void ImmutableMessageOneofFieldGenerator::
+GenerateSerializationCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n"
+ " output.write$group_or_message$($number$, ($type$) $oneof_name$_);\n"
+ "}\n");
+}
+
+void ImmutableMessageOneofFieldGenerator::
+GenerateSerializedSizeCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n"
+ " size += com.google.protobuf.CodedOutputStream\n"
+ " .compute$group_or_message$Size($number$, ($type$) $oneof_name$_);\n"
+ "}\n");
+}
+
+// ===================================================================
+
+RepeatedImmutableMessageFieldGenerator::
+RepeatedImmutableMessageFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : descriptor_(descriptor), messageBitIndex_(messageBitIndex),
+ builderBitIndex_(builderBitIndex), context_(context),
+ name_resolver_(context->GetNameResolver()) {
+ SetMessageVariables(descriptor, messageBitIndex, builderBitIndex,
+ context->GetFieldGeneratorInfo(descriptor),
+ name_resolver_, &variables_);
+}
+
+RepeatedImmutableMessageFieldGenerator::
+~RepeatedImmutableMessageFieldGenerator() {}
+
+int RepeatedImmutableMessageFieldGenerator::GetNumBitsForMessage() const {
+ return 0;
+}
+
+int RepeatedImmutableMessageFieldGenerator::GetNumBitsForBuilder() const {
+ return 1;
+}
+
+void RepeatedImmutableMessageFieldGenerator::
+GenerateInterfaceMembers(io::Printer* printer) const {
+ // TODO(jonp): In the future, consider having methods specific to the
+ // interface so that builders can choose dynamically to either return a
+ // message or a nested builder, so that asking for the interface doesn't
+ // cause a message to ever be built.
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$java.util.List<$type$> \n"
+ " get$capitalized_name$List();\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$$type$ get$capitalized_name$(int index);\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$int get$capitalized_name$Count();\n");
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$java.util.List<? extends $type$OrBuilder> \n"
+ " get$capitalized_name$OrBuilderList();\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder(\n"
+ " int index);\n");
+ }
+}
+
+void RepeatedImmutableMessageFieldGenerator::
+GenerateMembers(io::Printer* printer) const {
+ printer->Print(variables_,
+ "private java.util.List<$type$> $name$_;\n");
+ PrintExtraFieldInfo(variables_, printer);
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n"
+ " return $name$_;\n" // note: unmodifiable list
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.util.List<? extends $type$OrBuilder> \n"
+ " get$capitalized_name$OrBuilderList() {\n"
+ " return $name$_;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public int get$capitalized_name$Count() {\n"
+ " return $name$_.size();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$(int index) {\n"
+ " return $name$_.get(index);\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n"
+ " int index) {\n"
+ " return $name$_.get(index);\n"
+ "}\n");
+
+}
+
+void RepeatedImmutableMessageFieldGenerator::PrintNestedBuilderCondition(
+ io::Printer* printer,
+ const char* regular_case,
+ const char* nested_builder_case) const {
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ printer->Print(variables_, "if ($name$Builder_ == null) {\n");
+ printer->Indent();
+ printer->Print(variables_, regular_case);
+ printer->Outdent();
+ printer->Print("} else {\n");
+ printer->Indent();
+ printer->Print(variables_, nested_builder_case);
+ printer->Outdent();
+ printer->Print("}\n");
+ } else {
+ printer->Print(variables_, regular_case);
+ }
+}
+
+void RepeatedImmutableMessageFieldGenerator::PrintNestedBuilderFunction(
+ io::Printer* printer,
+ const char* method_prototype,
+ const char* regular_case,
+ const char* nested_builder_case,
+ const char* trailing_code) const {
+ printer->Print(variables_, method_prototype);
+ printer->Print(" {\n");
+ printer->Indent();
+ PrintNestedBuilderCondition(printer, regular_case, nested_builder_case);
+ if (trailing_code != NULL) {
+ printer->Print(variables_, trailing_code);
+ }
+ printer->Outdent();
+ printer->Print("}\n");
+}
+
+void RepeatedImmutableMessageFieldGenerator::
+GenerateBuilderMembers(io::Printer* printer) const {
+ // When using nested-builders, the code initially works just like the
+ // non-nested builder case. It only creates a nested builder lazily on
+ // demand and then forever delegates to it after creation.
+
+ printer->Print(variables_,
+ // Used when the builder is null.
+ // One field is the list and the other field keeps track of whether the
+ // list is immutable. If it's immutable, the invariant is that it must
+ // either an instance of Collections.emptyList() or it's an ArrayList
+ // wrapped in a Collections.unmodifiableList() wrapper and nobody else has
+ // a refererence to the underlying ArrayList. This invariant allows us to
+ // share instances of lists between protocol buffers avoiding expensive
+ // memory allocations. Note, immutable is a strong guarantee here -- not
+ // just that the list cannot be modified via the reference but that the
+ // list can never be modified.
+ "private java.util.List<$type$> $name$_ =\n"
+ " java.util.Collections.emptyList();\n"
+
+ "private void ensure$capitalized_name$IsMutable() {\n"
+ " if (!$get_mutable_bit_builder$) {\n"
+ " $name$_ = new java.util.ArrayList<$type$>($name$_);\n"
+ " $set_mutable_bit_builder$;\n"
+ " }\n"
"}\n"
- "public Builder addAll$capitalized_name$(\n"
- " java.lang.Iterable<? extends $type$> values) {\n"
- " if (result.$name$_.isEmpty()) {\n"
- " result.$name$_ = new java.util.ArrayList<$type$>();\n"
- " }\n"
- " super.addAll(values, result.$name$_);\n"
- " return this;\n"
+ "\n");
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ printer->Print(variables_,
+ // If this builder is non-null, it is used and the other fields are
+ // ignored.
+ "private com.google.protobuf.RepeatedFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;\n"
+ "\n");
+ }
+
+ // The comments above the methods below are based on a hypothetical
+ // repeated field of type "Field" called "RepeatedField".
+
+ // List<Field> getRepeatedFieldList()
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public java.util.List<$type$> get$capitalized_name$List()",
+
+ "return java.util.Collections.unmodifiableList($name$_);\n",
+ "return $name$Builder_.getMessageList();\n",
+
+ NULL);
+
+ // int getRepeatedFieldCount()
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public int get$capitalized_name$Count()",
+
+ "return $name$_.size();\n",
+ "return $name$Builder_.getCount();\n",
+
+ NULL);
+
+ // Field getRepeatedField(int index)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public $type$ get$capitalized_name$(int index)",
+
+ "return $name$_.get(index);\n",
+
+ "return $name$Builder_.getMessage(index);\n",
+
+ NULL);
+
+ // Builder setRepeatedField(int index, Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " int index, $type$ value)",
+ "if (value == null) {\n"
+ " throw new NullPointerException();\n"
"}\n"
- "public Builder clear$capitalized_name$() {\n"
- " result.$name$_ = java.util.Collections.emptyList();\n"
- " return this;\n"
- "}\n");
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.set(index, value);\n"
+ "$on_changed$\n",
+ "$name$Builder_.setMessage(index, value);\n",
+ "return this;\n");
+
+ // Builder setRepeatedField(int index, Field.Builder builderForValue)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " int index, $type$.Builder builderForValue)",
+
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.set(index, builderForValue.build());\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.setMessage(index, builderForValue.build());\n",
+
+ "return this;\n");
+
+ // Builder addRepeatedField(Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder add$capitalized_name$($type$ value)",
+
+ "if (value == null) {\n"
+ " throw new NullPointerException();\n"
+ "}\n"
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.add(value);\n"
+
+ "$on_changed$\n",
+
+ "$name$Builder_.addMessage(value);\n",
+
+ "return this;\n");
+
+ // Builder addRepeatedField(int index, Field value)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder add$capitalized_name$(\n"
+ " int index, $type$ value)",
+
+ "if (value == null) {\n"
+ " throw new NullPointerException();\n"
+ "}\n"
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.add(index, value);\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.addMessage(index, value);\n",
+
+ "return this;\n");
+
+ // Builder addRepeatedField(Field.Builder builderForValue)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder add$capitalized_name$(\n"
+ " $type$.Builder builderForValue)",
+
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.add(builderForValue.build());\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.addMessage(builderForValue.build());\n",
+
+ "return this;\n");
+
+ // Builder addRepeatedField(int index, Field.Builder builderForValue)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder add$capitalized_name$(\n"
+ " int index, $type$.Builder builderForValue)",
+
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.add(index, builderForValue.build());\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.addMessage(index, builderForValue.build());\n",
+
+ "return this;\n");
+
+ // Builder addAllRepeatedField(Iterable<Field> values)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder addAll$capitalized_name$(\n"
+ " java.lang.Iterable<? extends $type$> values)",
+
+ "ensure$capitalized_name$IsMutable();\n"
+ "com.google.protobuf.AbstractMessageLite.Builder.addAll(\n"
+ " values, $name$_);\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.addAllMessages(values);\n",
+
+ "return this;\n");
+
+ // Builder clearAllRepeatedField()
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder clear$capitalized_name$()",
+
+ "$name$_ = java.util.Collections.emptyList();\n"
+ "$clear_mutable_bit_builder$;\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.clear();\n",
+
+ "return this;\n");
+
+ // Builder removeRepeatedField(int index)
+ WriteFieldDocComment(printer, descriptor_);
+ PrintNestedBuilderFunction(printer,
+ "$deprecation$public Builder remove$capitalized_name$(int index)",
+
+ "ensure$capitalized_name$IsMutable();\n"
+ "$name$_.remove(index);\n"
+ "$on_changed$\n",
+
+ "$name$Builder_.remove(index);\n",
+
+ "return this;\n");
+
+ if (HasNestedBuilders(descriptor_->containing_type())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$.Builder get$capitalized_name$Builder(\n"
+ " int index) {\n"
+ " return get$capitalized_name$FieldBuilder().getBuilder(index);\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n"
+ " int index) {\n"
+ " if ($name$Builder_ == null) {\n"
+ " return $name$_.get(index);"
+ " } else {\n"
+ " return $name$Builder_.getMessageOrBuilder(index);\n"
+ " }\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.util.List<? extends $type$OrBuilder> \n"
+ " get$capitalized_name$OrBuilderList() {\n"
+ " if ($name$Builder_ != null) {\n"
+ " return $name$Builder_.getMessageOrBuilderList();\n"
+ " } else {\n"
+ " return java.util.Collections.unmodifiableList($name$_);\n"
+ " }\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$.Builder add$capitalized_name$Builder() {\n"
+ " return get$capitalized_name$FieldBuilder().addBuilder(\n"
+ " $type$.getDefaultInstance());\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$.Builder add$capitalized_name$Builder(\n"
+ " int index) {\n"
+ " return get$capitalized_name$FieldBuilder().addBuilder(\n"
+ " index, $type$.getDefaultInstance());\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.util.List<$type$.Builder> \n"
+ " get$capitalized_name$BuilderList() {\n"
+ " return get$capitalized_name$FieldBuilder().getBuilderList();\n"
+ "}\n"
+ "private com.google.protobuf.RepeatedFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder> \n"
+ " get$capitalized_name$FieldBuilder() {\n"
+ " if ($name$Builder_ == null) {\n"
+ " $name$Builder_ = new com.google.protobuf.RepeatedFieldBuilder<\n"
+ " $type$, $type$.Builder, $type$OrBuilder>(\n"
+ " $name$_,\n"
+ " $get_mutable_bit_builder$,\n"
+ " getParentForChildren(),\n"
+ " isClean());\n"
+ " $name$_ = null;\n"
+ " }\n"
+ " return $name$Builder_;\n"
+ "}\n");
+ }
+}
+
+void RepeatedImmutableMessageFieldGenerator::
+GenerateFieldBuilderInitializationCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "get$capitalized_name$FieldBuilder();\n");
}
-void RepeatedMessageFieldGenerator::
+void RepeatedImmutableMessageFieldGenerator::
GenerateInitializationCode(io::Printer* printer) const {
- // Initialized inline.
+ printer->Print(variables_, "$name$_ = java.util.Collections.emptyList();\n");
+}
+
+void RepeatedImmutableMessageFieldGenerator::
+GenerateBuilderClearCode(io::Printer* printer) const {
+ PrintNestedBuilderCondition(printer,
+ "$name$_ = java.util.Collections.emptyList();\n"
+ "$clear_mutable_bit_builder$;\n",
+
+ "$name$Builder_.clear();\n");
}
-void RepeatedMessageFieldGenerator::
+void RepeatedImmutableMessageFieldGenerator::
GenerateMergingCode(io::Printer* printer) const {
- printer->Print(variables_,
+ // The code below does two optimizations (non-nested builder case):
+ // 1. If the other list is empty, there's nothing to do. This ensures we
+ // don't allocate a new array if we already have an immutable one.
+ // 2. If the other list is non-empty and our current list is empty, we can
+ // reuse the other list which is guaranteed to be immutable.
+ PrintNestedBuilderCondition(printer,
+ "if (!other.$name$_.isEmpty()) {\n"
+ " if ($name$_.isEmpty()) {\n"
+ " $name$_ = other.$name$_;\n"
+ " $clear_mutable_bit_builder$;\n"
+ " } else {\n"
+ " ensure$capitalized_name$IsMutable();\n"
+ " $name$_.addAll(other.$name$_);\n"
+ " }\n"
+ " $on_changed$\n"
+ "}\n",
+
"if (!other.$name$_.isEmpty()) {\n"
- " if (result.$name$_.isEmpty()) {\n"
- " result.$name$_ = new java.util.ArrayList<$type$>();\n"
+ " if ($name$Builder_.isEmpty()) {\n"
+ " $name$Builder_.dispose();\n"
+ " $name$Builder_ = null;\n"
+ " $name$_ = other.$name$_;\n"
+ " $clear_mutable_bit_builder$;\n"
+ " $name$Builder_ = \n"
+ " com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?\n"
+ " get$capitalized_name$FieldBuilder() : null;\n"
+ " } else {\n"
+ " $name$Builder_.addAllMessages(other.$name$_);\n"
" }\n"
- " result.$name$_.addAll(other.$name$_);\n"
"}\n");
}
-void RepeatedMessageFieldGenerator::
+void RepeatedImmutableMessageFieldGenerator::
GenerateBuildingCode(io::Printer* printer) const {
- printer->Print(variables_,
- "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n"
- " result.$name$_ =\n"
- " java.util.Collections.unmodifiableList(result.$name$_);\n"
- "}\n");
+ // The code below (non-nested builder case) ensures that the result has an
+ // immutable list. If our list is immutable, we can just reuse it. If not,
+ // we make it immutable.
+ PrintNestedBuilderCondition(printer,
+ "if ($get_mutable_bit_builder$) {\n"
+ " $name$_ = java.util.Collections.unmodifiableList($name$_);\n"
+ " $clear_mutable_bit_builder$;\n"
+ "}\n"
+ "result.$name$_ = $name$_;\n",
+
+ "result.$name$_ = $name$Builder_.build();\n");
}
-void RepeatedMessageFieldGenerator::
+void RepeatedImmutableMessageFieldGenerator::
GenerateParsingCode(io::Printer* printer) const {
printer->Print(variables_,
- "$type$.Builder subBuilder = $type$.newBuilder();\n");
+ "if (!$get_mutable_bit_parser$) {\n"
+ " $name$_ = new java.util.ArrayList<$type$>();\n"
+ " $set_mutable_bit_parser$;\n"
+ "}\n");
if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) {
printer->Print(variables_,
- "input.readGroup($number$, subBuilder, extensionRegistry);\n");
+ "$name$_.add(input.readGroup($number$, $type$.PARSER,\n"
+ " extensionRegistry));\n");
} else {
printer->Print(variables_,
- "input.readMessage(subBuilder, extensionRegistry);\n");
+ "$name$_.add(input.readMessage($type$.PARSER, extensionRegistry));\n");
}
+}
+void RepeatedImmutableMessageFieldGenerator::
+GenerateParsingDoneCode(io::Printer* printer) const {
printer->Print(variables_,
- "add$capitalized_name$(subBuilder.buildPartial());\n");
+ "if ($get_mutable_bit_parser$) {\n"
+ " $name$_ = java.util.Collections.unmodifiableList($name$_);\n"
+ "}\n");
}
-void RepeatedMessageFieldGenerator::
+void RepeatedImmutableMessageFieldGenerator::
GenerateSerializationCode(io::Printer* printer) const {
printer->Print(variables_,
- "for ($type$ element : get$capitalized_name$List()) {\n"
- " output.write$group_or_message$($number$, element);\n"
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
+ " output.write$group_or_message$($number$, $name$_.get(i));\n"
"}\n");
}
-void RepeatedMessageFieldGenerator::
+void RepeatedImmutableMessageFieldGenerator::
GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print(variables_,
- "for ($type$ element : get$capitalized_name$List()) {\n"
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
" size += com.google.protobuf.CodedOutputStream\n"
- " .compute$group_or_message$Size($number$, element);\n"
+ " .compute$group_or_message$Size($number$, $name$_.get(i));\n"
+ "}\n");
+}
+
+void RepeatedImmutableMessageFieldGenerator::
+GenerateEqualsCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "result = result && get$capitalized_name$List()\n"
+ " .equals(other.get$capitalized_name$List());\n");
+}
+
+void RepeatedImmutableMessageFieldGenerator::
+GenerateHashCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (get$capitalized_name$Count() > 0) {\n"
+ " hash = (37 * hash) + $constant_name$;\n"
+ " hash = (53 * hash) + get$capitalized_name$List().hashCode();\n"
"}\n");
}
-string RepeatedMessageFieldGenerator::GetBoxedType() const {
- return ClassName(descriptor_->message_type());
+string RepeatedImmutableMessageFieldGenerator::GetBoxedType() const {
+ return name_resolver_->GetImmutableClassName(descriptor_->message_type());
}
} // namespace java
diff --git a/src/google/protobuf/compiler/java/java_message_field.h b/src/google/protobuf/compiler/java/java_message_field.h
index 66bdd88..ea8225a 100644
--- a/src/google/protobuf/compiler/java/java_message_field.h
+++ b/src/google/protobuf/compiler/java/java_message_field.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -41,55 +41,128 @@
namespace google {
namespace protobuf {
+ namespace compiler {
+ namespace java {
+ class Context; // context.h
+ class ClassNameResolver; // name_resolver.h
+ }
+ }
+}
+
+namespace protobuf {
namespace compiler {
namespace java {
-class MessageFieldGenerator : public FieldGenerator {
+class ImmutableMessageFieldGenerator : public ImmutableFieldGenerator {
public:
- explicit MessageFieldGenerator(const FieldDescriptor* descriptor);
- ~MessageFieldGenerator();
-
- // implements FieldGenerator ---------------------------------------
+ explicit ImmutableMessageFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~ImmutableMessageFieldGenerator();
+
+ // implements ImmutableFieldGenerator ---------------------------------------
+ int GetNumBitsForMessage() const;
+ int GetNumBitsForBuilder() const;
+ void GenerateInterfaceMembers(io::Printer* printer) const;
void GenerateMembers(io::Printer* printer) const;
void GenerateBuilderMembers(io::Printer* printer) const;
void GenerateInitializationCode(io::Printer* printer) const;
+ void GenerateBuilderClearCode(io::Printer* printer) const;
void GenerateMergingCode(io::Printer* printer) const;
void GenerateBuildingCode(io::Printer* printer) const;
void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateParsingDoneCode(io::Printer* printer) const;
void GenerateSerializationCode(io::Printer* printer) const;
void GenerateSerializedSizeCode(io::Printer* printer) const;
+ void GenerateFieldBuilderInitializationCode(io::Printer* printer) const;
+ void GenerateEqualsCode(io::Printer* printer) const;
+ void GenerateHashCode(io::Printer* printer) const;
string GetBoxedType() const;
- private:
+ protected:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ const int messageBitIndex_;
+ const int builderBitIndex_;
+ Context* context_;
+ ClassNameResolver* name_resolver_;
+
+ void PrintNestedBuilderCondition(io::Printer* printer,
+ const char* regular_case, const char* nested_builder_case) const;
+ void PrintNestedBuilderFunction(io::Printer* printer,
+ const char* method_prototype, const char* regular_case,
+ const char* nested_builder_case,
+ const char* trailing_code) const;
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator);
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableMessageFieldGenerator);
};
-class RepeatedMessageFieldGenerator : public FieldGenerator {
+class ImmutableMessageOneofFieldGenerator
+ : public ImmutableMessageFieldGenerator {
public:
- explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor);
- ~RepeatedMessageFieldGenerator();
+ ImmutableMessageOneofFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~ImmutableMessageOneofFieldGenerator();
- // implements FieldGenerator ---------------------------------------
+ void GenerateMembers(io::Printer* printer) const;
+ void GenerateBuilderMembers(io::Printer* printer) const;
+ void GenerateBuildingCode(io::Printer* printer) const;
+ void GenerateMergingCode(io::Printer* printer) const;
+ void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateSerializationCode(io::Printer* printer) const;
+ void GenerateSerializedSizeCode(io::Printer* printer) const;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableMessageOneofFieldGenerator);
+};
+
+class RepeatedImmutableMessageFieldGenerator : public ImmutableFieldGenerator {
+ public:
+ explicit RepeatedImmutableMessageFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~RepeatedImmutableMessageFieldGenerator();
+
+ // implements ImmutableFieldGenerator ---------------------------------------
+ int GetNumBitsForMessage() const;
+ int GetNumBitsForBuilder() const;
+ void GenerateInterfaceMembers(io::Printer* printer) const;
void GenerateMembers(io::Printer* printer) const;
void GenerateBuilderMembers(io::Printer* printer) const;
void GenerateInitializationCode(io::Printer* printer) const;
+ void GenerateBuilderClearCode(io::Printer* printer) const;
void GenerateMergingCode(io::Printer* printer) const;
void GenerateBuildingCode(io::Printer* printer) const;
void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateParsingDoneCode(io::Printer* printer) const;
void GenerateSerializationCode(io::Printer* printer) const;
void GenerateSerializedSizeCode(io::Printer* printer) const;
+ void GenerateFieldBuilderInitializationCode(io::Printer* printer) const;
+ void GenerateEqualsCode(io::Printer* printer) const;
+ void GenerateHashCode(io::Printer* printer) const;
string GetBoxedType() const;
- private:
+ protected:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ const int messageBitIndex_;
+ const int builderBitIndex_;
+ Context* context_;
+ ClassNameResolver* name_resolver_;
+
+ void PrintNestedBuilderCondition(io::Printer* printer,
+ const char* regular_case, const char* nested_builder_case) const;
+ void PrintNestedBuilderFunction(io::Printer* printer,
+ const char* method_prototype, const char* regular_case,
+ const char* nested_builder_case,
+ const char* trailing_code) const;
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator);
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableMessageFieldGenerator);
};
} // namespace java
diff --git a/src/google/protobuf/compiler/java/java_name_resolver.cc b/src/google/protobuf/compiler/java/java_name_resolver.cc
new file mode 100644
index 0000000..0c363f9
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_name_resolver.cc
@@ -0,0 +1,266 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// 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_name_resolver.h>
+
+#include <map>
+#include <string>
+
+#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/stubs/substitute.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+namespace {
+// A suffix that will be appended to the file's outer class name if the name
+// conflicts with some other types defined in the file.
+const char* kOuterClassNameSuffix = "OuterClass";
+
+// Strip package name from a descriptor's full name.
+// For example:
+// Full name : foo.Bar.Baz
+// Package name: foo
+// After strip : Bar.Baz
+string StripPackageName(const string& full_name,
+ const FileDescriptor* file) {
+ if (file->package().empty()) {
+ return full_name;
+ } else {
+ // Strip package name
+ return full_name.substr(file->package().size() + 1);
+ }
+}
+
+// Get the name of a message's Java class without package name prefix.
+string ClassNameWithoutPackage(const Descriptor* descriptor,
+ bool immutable) {
+ return StripPackageName(descriptor->full_name(),
+ descriptor->file());
+}
+
+// Get the name of an enum's Java class without package name prefix.
+string ClassNameWithoutPackage(const EnumDescriptor* descriptor,
+ bool immutable) {
+ // Doesn't append "Mutable" for enum type's name.
+ const Descriptor* message_descriptor = descriptor->containing_type();
+ if (message_descriptor == NULL) {
+ return descriptor->name();
+ } else {
+ return ClassNameWithoutPackage(message_descriptor, immutable) +
+ "." + descriptor->name();
+ }
+}
+
+// Get the name of a service's Java class without package name prefix.
+string ClassNameWithoutPackage(const ServiceDescriptor* descriptor,
+ bool immutable) {
+ string full_name = StripPackageName(descriptor->full_name(),
+ descriptor->file());
+ // We don't allow nested service definitions.
+ GOOGLE_CHECK(full_name.find('.') == string::npos);
+ return full_name;
+}
+
+// Check whether a given message or its nested types has the given class name.
+bool MessageHasConflictingClassName(const Descriptor* message,
+ const string& classname) {
+ if (message->name() == classname) return true;
+ for (int i = 0; i < message->nested_type_count(); ++i) {
+ if (MessageHasConflictingClassName(message->nested_type(i), classname)) {
+ return true;
+ }
+ }
+ for (int i = 0; i < message->enum_type_count(); ++i) {
+ if (message->enum_type(i)->name() == classname) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+ClassNameResolver::ClassNameResolver() {
+}
+
+ClassNameResolver::~ClassNameResolver() {
+}
+
+string ClassNameResolver::GetFileDefaultImmutableClassName(
+ const FileDescriptor* file) {
+ string basename;
+ string::size_type last_slash = file->name().find_last_of('/');
+ if (last_slash == string::npos) {
+ basename = file->name();
+ } else {
+ basename = file->name().substr(last_slash + 1);
+ }
+ return UnderscoresToCamelCase(StripProto(basename), true);
+}
+
+string ClassNameResolver::GetFileImmutableClassName(
+ const FileDescriptor* file) {
+ string& class_name = file_immutable_outer_class_names_[file];
+ if (class_name.empty()) {
+ if (file->options().has_java_outer_classname()) {
+ class_name = file->options().java_outer_classname();
+ } else {
+ class_name = GetFileDefaultImmutableClassName(file);
+ if (HasConflictingClassName(file, class_name)) {
+ class_name += kOuterClassNameSuffix;
+ }
+ }
+ }
+ return class_name;
+}
+
+string ClassNameResolver::GetFileClassName(const FileDescriptor* file,
+ bool immutable) {
+ if (immutable) {
+ return GetFileImmutableClassName(file);
+ } else {
+ return "Mutable" + GetFileImmutableClassName(file);
+ }
+}
+
+// Check whether there is any type defined in the proto file that has
+// the given class name.
+bool ClassNameResolver::HasConflictingClassName(
+ const FileDescriptor* file, const string& classname) {
+ for (int i = 0; i < file->enum_type_count(); i++) {
+ if (file->enum_type(i)->name() == classname) {
+ return true;
+ }
+ }
+ for (int i = 0; i < file->service_count(); i++) {
+ if (file->service(i)->name() == classname) {
+ return true;
+ }
+ }
+ for (int i = 0; i < file->message_type_count(); i++) {
+ if (MessageHasConflictingClassName(file->message_type(i), classname)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+string ClassNameResolver::GetDescriptorClassName(
+ const FileDescriptor* descriptor) {
+ return GetFileImmutableClassName(descriptor);
+}
+
+string ClassNameResolver::GetClassName(const FileDescriptor* descriptor,
+ bool immutable) {
+ string result = FileJavaPackage(descriptor, immutable);
+ if (!result.empty()) result += '.';
+ result += GetFileClassName(descriptor, immutable);
+ return result;
+}
+
+// Get the full name of a Java class by prepending the Java package name
+// or outer class name.
+string ClassNameResolver::GetClassFullName(const string& name_without_package,
+ const FileDescriptor* file,
+ bool immutable,
+ bool multiple_files) {
+ string result;
+ if (multiple_files) {
+ result = FileJavaPackage(file, immutable);
+ } else {
+ result = GetClassName(file, immutable);
+ }
+ if (!result.empty()) {
+ result += '.';
+ }
+ result += name_without_package;
+ return result;
+}
+
+string ClassNameResolver::GetClassName(const Descriptor* descriptor,
+ bool immutable) {
+ return GetClassFullName(ClassNameWithoutPackage(descriptor, immutable),
+ descriptor->file(), immutable,
+ MultipleJavaFiles(descriptor->file(), immutable));
+}
+
+string ClassNameResolver::GetClassName(const EnumDescriptor* descriptor,
+ bool immutable) {
+ return GetClassFullName(ClassNameWithoutPackage(descriptor, immutable),
+ descriptor->file(), immutable,
+ MultipleJavaFiles(descriptor->file(), immutable));
+}
+
+string ClassNameResolver::GetClassName(const ServiceDescriptor* descriptor,
+ bool immutable) {
+ return GetClassFullName(ClassNameWithoutPackage(descriptor, immutable),
+ descriptor->file(), immutable,
+ MultipleJavaFiles(descriptor->file(), immutable));
+}
+
+// Get the Java Class style full name of a message.
+string ClassNameResolver::GetJavaClassFullName(
+ const string& name_without_package,
+ const FileDescriptor* file,
+ bool immutable) {
+ string result;
+ if (MultipleJavaFiles(file, immutable)) {
+ result = FileJavaPackage(file, immutable);
+ if (!result.empty()) result += '.';
+ } else {
+ result = GetClassName(file, immutable);
+ if (!result.empty()) result += '$';
+ }
+ result += StringReplace(name_without_package, ".", "$", true);
+ return result;
+}
+
+string ClassNameResolver::GetExtensionIdentifierName(
+ const FieldDescriptor* descriptor, bool immutable) {
+ return GetClassName(descriptor->containing_type(), immutable) + "." +
+ descriptor->name();
+}
+
+
+string ClassNameResolver::GetJavaImmutableClassName(
+ const Descriptor* descriptor) {
+ return GetJavaClassFullName(
+ ClassNameWithoutPackage(descriptor, true),
+ descriptor->file(), true);
+}
+
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/compiler/java/java_name_resolver.h b/src/google/protobuf/compiler/java/java_name_resolver.h
new file mode 100644
index 0000000..ab60b0a
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_name_resolver.h
@@ -0,0 +1,124 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// 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_NAME_RESOLVER_H__
+#define GOOGLE_PROTOBUF_COMPILER_JAVA_NAME_RESOLVER_H__
+
+#include <map>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+class Descriptor;
+class EnumDescriptor;
+class FieldDescriptor;
+class FileDescriptor;
+class ServiceDescriptor;
+
+namespace compiler {
+namespace java {
+
+// Used to get the Java class related names for a given descriptor. It caches
+// the results to avoid redundant calculation across multiple name queries.
+// Thread-safety note: This class is *not* thread-safe.
+class ClassNameResolver {
+ public:
+ ClassNameResolver();
+ ~ClassNameResolver();
+
+ // Gets the unqualified outer class name for the file.
+ string GetFileClassName(const FileDescriptor* file, bool immutable);
+ // Gets the unqualified immutable outer class name of a file.
+ string GetFileImmutableClassName(const FileDescriptor* file);
+ // Gets the unqualified default immutable outer class name of a file
+ // (converted from the proto file's name).
+ string GetFileDefaultImmutableClassName(const FileDescriptor* file);
+
+ // Check whether there is any type defined in the proto file that has
+ // the given class name.
+ bool HasConflictingClassName(const FileDescriptor* file,
+ const string& classname);
+
+ // Gets the name of the outer class that holds descriptor information.
+ // Descriptors are shared between immutable messages and mutable messages.
+ // Since both of them are generated optionally, the descriptors need to be
+ // put in another common place.
+ string GetDescriptorClassName(const FileDescriptor* file);
+
+ // Gets the fully-qualified class name corresponding to the given descriptor.
+ string GetClassName(const Descriptor* descriptor, bool immutable);
+ string GetClassName(const EnumDescriptor* descriptor, bool immutable);
+ string GetClassName(const ServiceDescriptor* descriptor, bool immutable);
+ string GetClassName(const FileDescriptor* descriptor, bool immutable);
+
+ template<class DescriptorType>
+ string GetImmutableClassName(const DescriptorType* descriptor) {
+ return GetClassName(descriptor, true);
+ }
+ template<class DescriptorType>
+ string GetMutableClassName(const DescriptorType* descriptor) {
+ return GetClassName(descriptor, false);
+ }
+
+ // Gets the fully qualified name of an extension identifier.
+ string GetExtensionIdentifierName(const FieldDescriptor* descriptor,
+ bool immutable);
+
+ // Gets the fully qualified name for generated classes in Java convention.
+ // Nested classes will be separated using '$' instead of '.'
+ // For example:
+ // com.package.OuterClass$OuterMessage$InnerMessage
+ string GetJavaImmutableClassName(const Descriptor* descriptor);
+ private:
+ // Get the full name of a Java class by prepending the Java package name
+ // or outer class name.
+ string GetClassFullName(const string& name_without_package,
+ const FileDescriptor* file,
+ bool immutable,
+ bool multiple_files);
+ // Get the Java Class style full name of a message.
+ string GetJavaClassFullName(
+ const string& name_without_package,
+ const FileDescriptor* file,
+ bool immutable);
+ // Caches the result to provide better performance.
+ map<const FileDescriptor*, string> file_immutable_outer_class_names_;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ClassNameResolver);
+};
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_NAME_RESOLVER_H__
diff --git a/src/google/protobuf/compiler/java/java_plugin_unittest.cc b/src/google/protobuf/compiler/java/java_plugin_unittest.cc
index cfe0188..c4d6995 100644
--- a/src/google/protobuf/compiler/java/java_plugin_unittest.cc
+++ b/src/google/protobuf/compiler/java/java_plugin_unittest.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -34,6 +34,8 @@
// It seemed like parameterizing it would add more complexity than it is
// worth.
+#include <memory>
+
#include <google/protobuf/compiler/java/java_generator.h>
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/io/zero_copy_stream.h>
@@ -56,21 +58,22 @@ class TestGenerator : public CodeGenerator {
virtual bool Generate(const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* context,
string* error) const {
- TryInsert("Test.java", "outer_class_scope", output_directory);
- TryInsert("Test.java", "class_scope:foo.Bar", output_directory);
- TryInsert("Test.java", "class_scope:foo.Bar.Baz", output_directory);
- TryInsert("Test.java", "builder_scope:foo.Bar", output_directory);
- TryInsert("Test.java", "builder_scope:foo.Bar.Baz", output_directory);
- TryInsert("Test.java", "enum_scope:foo.Qux", output_directory);
+ string filename = "Test.java";
+ TryInsert(filename, "outer_class_scope", context);
+ TryInsert(filename, "class_scope:foo.Bar", context);
+ TryInsert(filename, "class_scope:foo.Bar.Baz", context);
+ TryInsert(filename, "builder_scope:foo.Bar", context);
+ TryInsert(filename, "builder_scope:foo.Bar.Baz", context);
+ TryInsert(filename, "enum_scope:foo.Qux", context);
return true;
}
void TryInsert(const string& filename, const string& insertion_point,
- OutputDirectory* output_directory) const {
+ GeneratorContext* context) const {
scoped_ptr<io::ZeroCopyOutputStream> output(
- output_directory->OpenForInsert(filename, insertion_point));
+ context->OpenForInsert(filename, insertion_point));
io::Printer printer(output.get(), '$');
printer.Print("// inserted $name$\n", "name", insertion_point);
}
@@ -80,16 +83,16 @@ class TestGenerator : public CodeGenerator {
// not verify that they are correctly-placed; that would require actually
// compiling the output which is a bit more than I care to do for this test.
TEST(JavaPluginTest, PluginTest) {
- File::WriteStringToFileOrDie(
- "syntax = \"proto2\";\n"
- "package foo;\n"
- "option java_package = \"\";\n"
- "option java_outer_classname = \"Test\";\n"
- "message Bar {\n"
- " message Baz {}\n"
- "}\n"
- "enum Qux { BLAH = 1; }\n",
- TestTempDir() + "/test.proto");
+ GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/test.proto",
+ "syntax = \"proto2\";\n"
+ "package foo;\n"
+ "option java_package = \"\";\n"
+ "option java_outer_classname = \"Test\";\n"
+ "message Bar {\n"
+ " message Baz {}\n"
+ "}\n"
+ "enum Qux { BLAH = 1; }\n",
+ true));
google::protobuf::compiler::CommandLineInterface cli;
cli.SetInputsAreProtoPathRelative(true);
diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc
index f6179bf..6713d29 100644
--- a/src/google/protobuf/compiler/java/java_primitive_field.cc
+++ b/src/google/protobuf/compiler/java/java_primitive_field.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -35,9 +35,12 @@
#include <map>
#include <string>
-#include <google/protobuf/compiler/java/java_primitive_field.h>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/compiler/java/java_context.h>
+#include <google/protobuf/compiler/java/java_doc_comment.h>
#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/compiler/java/java_name_resolver.h>
+#include <google/protobuf/compiler/java/java_primitive_field.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
@@ -72,98 +75,25 @@ const char* PrimitiveTypeName(JavaType type) {
return NULL;
}
-bool IsReferenceType(JavaType type) {
- switch (type) {
- case JAVATYPE_INT : return false;
- case JAVATYPE_LONG : return false;
- case JAVATYPE_FLOAT : return false;
- case JAVATYPE_DOUBLE : return false;
- case JAVATYPE_BOOLEAN: return false;
- case JAVATYPE_STRING : return true;
- case JAVATYPE_BYTES : return true;
- case JAVATYPE_ENUM : return true;
- case JAVATYPE_MESSAGE: return true;
-
- // No default because we want the compiler to complain if any new
- // JavaTypes are added.
- }
-
- GOOGLE_LOG(FATAL) << "Can't get here.";
- return false;
-}
-
-const char* GetCapitalizedType(const FieldDescriptor* field) {
- switch (GetType(field)) {
- case FieldDescriptor::TYPE_INT32 : return "Int32" ;
- case FieldDescriptor::TYPE_UINT32 : return "UInt32" ;
- case FieldDescriptor::TYPE_SINT32 : return "SInt32" ;
- case FieldDescriptor::TYPE_FIXED32 : return "Fixed32" ;
- case FieldDescriptor::TYPE_SFIXED32: return "SFixed32";
- case FieldDescriptor::TYPE_INT64 : return "Int64" ;
- case FieldDescriptor::TYPE_UINT64 : return "UInt64" ;
- case FieldDescriptor::TYPE_SINT64 : return "SInt64" ;
- case FieldDescriptor::TYPE_FIXED64 : return "Fixed64" ;
- case FieldDescriptor::TYPE_SFIXED64: return "SFixed64";
- case FieldDescriptor::TYPE_FLOAT : return "Float" ;
- case FieldDescriptor::TYPE_DOUBLE : return "Double" ;
- case FieldDescriptor::TYPE_BOOL : return "Bool" ;
- case FieldDescriptor::TYPE_STRING : return "String" ;
- case FieldDescriptor::TYPE_BYTES : return "Bytes" ;
- case FieldDescriptor::TYPE_ENUM : return "Enum" ;
- case FieldDescriptor::TYPE_GROUP : return "Group" ;
- case FieldDescriptor::TYPE_MESSAGE : return "Message" ;
-
- // No default because we want the compiler to complain if any new
- // types are added.
- }
-
- GOOGLE_LOG(FATAL) << "Can't get here.";
- return NULL;
-}
-
-// For encodings with fixed sizes, returns that size in bytes. Otherwise
-// returns -1.
-int FixedSize(FieldDescriptor::Type type) {
- switch (type) {
- case FieldDescriptor::TYPE_INT32 : return -1;
- case FieldDescriptor::TYPE_INT64 : return -1;
- case FieldDescriptor::TYPE_UINT32 : return -1;
- case FieldDescriptor::TYPE_UINT64 : return -1;
- case FieldDescriptor::TYPE_SINT32 : return -1;
- case FieldDescriptor::TYPE_SINT64 : return -1;
- case FieldDescriptor::TYPE_FIXED32 : return WireFormatLite::kFixed32Size;
- case FieldDescriptor::TYPE_FIXED64 : return WireFormatLite::kFixed64Size;
- case FieldDescriptor::TYPE_SFIXED32: return WireFormatLite::kSFixed32Size;
- case FieldDescriptor::TYPE_SFIXED64: return WireFormatLite::kSFixed64Size;
- case FieldDescriptor::TYPE_FLOAT : return WireFormatLite::kFloatSize;
- case FieldDescriptor::TYPE_DOUBLE : return WireFormatLite::kDoubleSize;
-
- case FieldDescriptor::TYPE_BOOL : return WireFormatLite::kBoolSize;
- case FieldDescriptor::TYPE_ENUM : return -1;
-
- case FieldDescriptor::TYPE_STRING : return -1;
- case FieldDescriptor::TYPE_BYTES : return -1;
- case FieldDescriptor::TYPE_GROUP : return -1;
- case FieldDescriptor::TYPE_MESSAGE : return -1;
-
- // No default because we want the compiler to complain if any new
- // types are added.
- }
- GOOGLE_LOG(FATAL) << "Can't get here.";
- return -1;
-}
-
void SetPrimitiveVariables(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ const FieldGeneratorInfo* info,
+ ClassNameResolver* name_resolver,
map<string, string>* variables) {
- (*variables)["name"] =
- UnderscoresToCamelCase(descriptor);
- (*variables)["capitalized_name"] =
- UnderscoresToCapitalizedCamelCase(descriptor);
- (*variables)["number"] = SimpleItoa(descriptor->number());
+ SetCommonFieldVariables(descriptor, info, variables);
+
(*variables)["type"] = PrimitiveTypeName(GetJavaType(descriptor));
(*variables)["boxed_type"] = BoxedPrimitiveTypeName(GetJavaType(descriptor));
- (*variables)["default"] = DefaultValue(descriptor);
- (*variables)["capitalized_type"] = GetCapitalizedType(descriptor);
+ (*variables)["field_type"] = (*variables)["type"];
+ (*variables)["field_list_type"] = "java.util.List<" +
+ (*variables)["boxed_type"] + ">";
+ (*variables)["empty_list"] = "java.util.Collections.emptyList()";
+ (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver);
+ (*variables)["default_init"] = IsDefaultValueJavaDefault(descriptor) ?
+ "" : ("= " + ImmutableDefaultValue(descriptor, name_resolver));
+ (*variables)["capitalized_type"] =
+ GetCapitalizedType(descriptor, /* immutable = */ true);
(*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor));
(*variables)["tag_size"] = SimpleItoa(
WireFormat::TagSize(descriptor->number(), GetType(descriptor)));
@@ -175,129 +105,517 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor,
} else {
(*variables)["null_check"] = "";
}
+ // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported
+ // by the proto compiler
+ (*variables)["deprecation"] = descriptor->options().deprecated()
+ ? "@java.lang.Deprecated " : "";
int fixed_size = FixedSize(GetType(descriptor));
if (fixed_size != -1) {
(*variables)["fixed_size"] = SimpleItoa(fixed_size);
}
+ (*variables)["on_changed"] =
+ HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : "";
+
+ if (SupportFieldPresence(descriptor->file())) {
+ // For singular messages and builders, one bit is used for the hasField bit.
+ (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex);
+ (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex);
+
+ // Note that these have a trailing ";".
+ (*variables)["set_has_field_bit_message"] =
+ GenerateSetBit(messageBitIndex) + ";";
+ (*variables)["set_has_field_bit_builder"] =
+ GenerateSetBit(builderBitIndex) + ";";
+ (*variables)["clear_has_field_bit_builder"] =
+ GenerateClearBit(builderBitIndex) + ";";
+
+ (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex);
+ } else {
+ (*variables)["set_has_field_bit_message"] = "";
+ (*variables)["set_has_field_bit_builder"] = "";
+ (*variables)["clear_has_field_bit_builder"] = "";
+
+ if (descriptor->type() == FieldDescriptor::TYPE_BYTES) {
+ (*variables)["is_field_present_message"] =
+ "!" + (*variables)["name"] + "_.isEmpty()";
+ } else {
+ (*variables)["is_field_present_message"] =
+ (*variables)["name"] + "_ != " + (*variables)["default"];
+ }
+ }
+
+ // For repated builders, one bit is used for whether the array is immutable.
+ (*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex);
+ (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex);
+ (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex);
+
+ // For repeated fields, one bit is used for whether the array is immutable
+ // in the parsing constructor.
+ (*variables)["get_mutable_bit_parser"] =
+ GenerateGetBitMutableLocal(builderBitIndex);
+ (*variables)["set_mutable_bit_parser"] =
+ GenerateSetBitMutableLocal(builderBitIndex);
+
+ (*variables)["get_has_field_bit_from_local"] =
+ GenerateGetBitFromLocal(builderBitIndex);
+ (*variables)["set_has_field_bit_to_local"] =
+ GenerateSetBitToLocal(messageBitIndex);
}
+
} // namespace
// ===================================================================
-PrimitiveFieldGenerator::
-PrimitiveFieldGenerator(const FieldDescriptor* descriptor)
- : descriptor_(descriptor) {
- SetPrimitiveVariables(descriptor, &variables_);
+ImmutablePrimitiveFieldGenerator::
+ImmutablePrimitiveFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : descriptor_(descriptor), messageBitIndex_(messageBitIndex),
+ builderBitIndex_(builderBitIndex), context_(context),
+ name_resolver_(context->GetNameResolver()) {
+ SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex,
+ context->GetFieldGeneratorInfo(descriptor),
+ name_resolver_, &variables_);
}
-PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {}
+ImmutablePrimitiveFieldGenerator::~ImmutablePrimitiveFieldGenerator() {}
+
+int ImmutablePrimitiveFieldGenerator::GetNumBitsForMessage() const {
+ return 1;
+}
+
+int ImmutablePrimitiveFieldGenerator::GetNumBitsForBuilder() const {
+ return 1;
+}
-void PrimitiveFieldGenerator::
+void ImmutablePrimitiveFieldGenerator::
+GenerateInterfaceMembers(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$boolean has$capitalized_name$();\n");
+ }
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$$type$ get$capitalized_name$();\n");
+}
+
+void ImmutablePrimitiveFieldGenerator::
GenerateMembers(io::Printer* printer) const {
printer->Print(variables_,
- "private boolean has$capitalized_name$;\n"
- "private $type$ $name$_ = $default$;\n"
- "public boolean has$capitalized_name$() { return has$capitalized_name$; }\n"
- "public $type$ get$capitalized_name$() { return $name$_; }\n");
+ "private $field_type$ $name$_;\n");
+ PrintExtraFieldInfo(variables_, printer);
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $get_has_field_bit_message$;\n"
+ "}\n");
+ }
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " return $name$_;\n"
+ "}\n");
}
-void PrimitiveFieldGenerator::
+void ImmutablePrimitiveFieldGenerator::
GenerateBuilderMembers(io::Printer* printer) const {
printer->Print(variables_,
- "public boolean has$capitalized_name$() {\n"
- " return result.has$capitalized_name$();\n"
- "}\n"
- "public $type$ get$capitalized_name$() {\n"
- " return result.get$capitalized_name$();\n"
- "}\n"
- "public Builder set$capitalized_name$($type$ value) {\n"
+ "private $field_type$ $name$_ $default_init$;\n");
+
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $get_has_field_bit_builder$;\n"
+ "}\n");
+ }
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " return $name$_;\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder set$capitalized_name$($type$ value) {\n"
"$null_check$"
- " result.has$capitalized_name$ = true;\n"
- " result.$name$_ = value;\n"
+ " $set_has_field_bit_builder$\n"
+ " $name$_ = value;\n"
+ " $on_changed$\n"
" return this;\n"
- "}\n"
- "public Builder clear$capitalized_name$() {\n"
- " result.has$capitalized_name$ = false;\n");
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder clear$capitalized_name$() {\n"
+ " $clear_has_field_bit_builder$\n");
JavaType type = GetJavaType(descriptor_);
if (type == JAVATYPE_STRING || type == JAVATYPE_BYTES) {
// The default value is not a simple literal so we want to avoid executing
// it multiple times. Instead, get the default out of the default instance.
printer->Print(variables_,
- " result.$name$_ = getDefaultInstance().get$capitalized_name$();\n");
+ " $name$_ = getDefaultInstance().get$capitalized_name$();\n");
} else {
printer->Print(variables_,
- " result.$name$_ = $default$;\n");
+ " $name$_ = $default$;\n");
}
printer->Print(variables_,
+ " $on_changed$\n"
" return this;\n"
"}\n");
}
-void PrimitiveFieldGenerator::
+void ImmutablePrimitiveFieldGenerator::
+GenerateFieldBuilderInitializationCode(io::Printer* printer) const {
+ // noop for primitives
+}
+
+void ImmutablePrimitiveFieldGenerator::
GenerateInitializationCode(io::Printer* printer) const {
- // Initialized inline.
+ printer->Print(variables_, "$name$_ = $default$;\n");
}
-void PrimitiveFieldGenerator::
-GenerateMergingCode(io::Printer* printer) const {
+void ImmutablePrimitiveFieldGenerator::
+GenerateBuilderClearCode(io::Printer* printer) const {
printer->Print(variables_,
- "if (other.has$capitalized_name$()) {\n"
- " set$capitalized_name$(other.get$capitalized_name$());\n"
- "}\n");
+ "$name$_ = $default$;\n"
+ "$clear_has_field_bit_builder$\n");
+}
+
+void ImmutablePrimitiveFieldGenerator::
+GenerateMergingCode(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ printer->Print(variables_,
+ "if (other.has$capitalized_name$()) {\n"
+ " set$capitalized_name$(other.get$capitalized_name$());\n"
+ "}\n");
+ } else {
+ printer->Print(variables_,
+ "if (other.get$capitalized_name$() != $default$) {\n"
+ " set$capitalized_name$(other.get$capitalized_name$());\n"
+ "}\n");
+ }
}
-void PrimitiveFieldGenerator::
+void ImmutablePrimitiveFieldGenerator::
GenerateBuildingCode(io::Printer* printer) const {
- // Nothing to do here for primitive types.
+ if (SupportFieldPresence(descriptor_->file())) {
+ printer->Print(variables_,
+ "if ($get_has_field_bit_from_local$) {\n"
+ " $set_has_field_bit_to_local$;\n"
+ "}\n");
+ }
+ printer->Print(variables_,
+ "result.$name$_ = $name$_;\n");
}
-void PrimitiveFieldGenerator::
+void ImmutablePrimitiveFieldGenerator::
GenerateParsingCode(io::Printer* printer) const {
printer->Print(variables_,
- "set$capitalized_name$(input.read$capitalized_type$());\n");
+ "$set_has_field_bit_message$\n"
+ "$name$_ = input.read$capitalized_type$();\n");
+}
+
+void ImmutablePrimitiveFieldGenerator::
+GenerateParsingDoneCode(io::Printer* printer) const {
+ // noop for primitives.
}
-void PrimitiveFieldGenerator::
+void ImmutablePrimitiveFieldGenerator::
GenerateSerializationCode(io::Printer* printer) const {
printer->Print(variables_,
- "if (has$capitalized_name$()) {\n"
- " output.write$capitalized_type$($number$, get$capitalized_name$());\n"
+ "if ($is_field_present_message$) {\n"
+ " output.write$capitalized_type$($number$, $name$_);\n"
"}\n");
}
-void PrimitiveFieldGenerator::
+void ImmutablePrimitiveFieldGenerator::
GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print(variables_,
- "if (has$capitalized_name$()) {\n"
+ "if ($is_field_present_message$) {\n"
" size += com.google.protobuf.CodedOutputStream\n"
- " .compute$capitalized_type$Size($number$, get$capitalized_name$());\n"
+ " .compute$capitalized_type$Size($number$, $name$_);\n"
"}\n");
}
-string PrimitiveFieldGenerator::GetBoxedType() const {
+void ImmutablePrimitiveFieldGenerator::
+GenerateEqualsCode(io::Printer* printer) const {
+ switch (GetJavaType(descriptor_)) {
+ case JAVATYPE_INT:
+ case JAVATYPE_LONG:
+ case JAVATYPE_BOOLEAN:
+ printer->Print(variables_,
+ "result = result && (get$capitalized_name$()\n"
+ " == other.get$capitalized_name$());\n");
+ break;
+
+ case JAVATYPE_FLOAT:
+ printer->Print(variables_,
+ "result = result && (\n"
+ " java.lang.Float.floatToIntBits(get$capitalized_name$())\n"
+ " == java.lang.Float.floatToIntBits(\n"
+ " other.get$capitalized_name$()));\n");
+ break;
+
+ case JAVATYPE_DOUBLE:
+ printer->Print(variables_,
+ "result = result && (\n"
+ " java.lang.Double.doubleToLongBits(get$capitalized_name$())\n"
+ " == java.lang.Double.doubleToLongBits(\n"
+ " other.get$capitalized_name$()));\n");
+ break;
+
+ case JAVATYPE_STRING:
+ case JAVATYPE_BYTES:
+ printer->Print(variables_,
+ "result = result && get$capitalized_name$()\n"
+ " .equals(other.get$capitalized_name$());\n");
+ break;
+
+ case JAVATYPE_ENUM:
+ case JAVATYPE_MESSAGE:
+ default:
+ GOOGLE_LOG(FATAL) << "Can't get here.";
+ break;
+ }
+}
+
+void ImmutablePrimitiveFieldGenerator::
+GenerateHashCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "hash = (37 * hash) + $constant_name$;\n");
+ switch (GetJavaType(descriptor_)) {
+ case JAVATYPE_INT:
+ printer->Print(variables_,
+ "hash = (53 * hash) + get$capitalized_name$();\n");
+ break;
+
+ case JAVATYPE_LONG:
+ printer->Print(variables_,
+ "hash = (53 * hash) + com.google.protobuf.Internal.hashLong(\n"
+ " get$capitalized_name$());\n");
+ break;
+
+ case JAVATYPE_BOOLEAN:
+ printer->Print(variables_,
+ "hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(\n"
+ " get$capitalized_name$());\n");
+ break;
+
+ case JAVATYPE_FLOAT:
+ printer->Print(variables_,
+ "hash = (53 * hash) + java.lang.Float.floatToIntBits(\n"
+ " get$capitalized_name$());\n");
+ break;
+
+ case JAVATYPE_DOUBLE:
+ printer->Print(variables_,
+ "hash = (53 * hash) + com.google.protobuf.Internal.hashLong(\n"
+ " java.lang.Double.doubleToLongBits(get$capitalized_name$()));\n");
+ break;
+
+ case JAVATYPE_STRING:
+ case JAVATYPE_BYTES:
+ printer->Print(variables_,
+ "hash = (53 * hash) + get$capitalized_name$().hashCode();\n");
+ break;
+
+ case JAVATYPE_ENUM:
+ case JAVATYPE_MESSAGE:
+ default:
+ GOOGLE_LOG(FATAL) << "Can't get here.";
+ break;
+ }
+}
+
+string ImmutablePrimitiveFieldGenerator::GetBoxedType() const {
return BoxedPrimitiveTypeName(GetJavaType(descriptor_));
}
// ===================================================================
-RepeatedPrimitiveFieldGenerator::
-RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor)
- : descriptor_(descriptor) {
- SetPrimitiveVariables(descriptor, &variables_);
+ImmutablePrimitiveOneofFieldGenerator::
+ImmutablePrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : ImmutablePrimitiveFieldGenerator(
+ descriptor, messageBitIndex, builderBitIndex, context) {
+ const OneofGeneratorInfo* info =
+ context->GetOneofGeneratorInfo(descriptor->containing_oneof());
+ SetCommonOneofVariables(descriptor, info, &variables_);
}
-RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {}
+ImmutablePrimitiveOneofFieldGenerator::
+~ImmutablePrimitiveOneofFieldGenerator() {}
-void RepeatedPrimitiveFieldGenerator::
+void ImmutablePrimitiveOneofFieldGenerator::
GenerateMembers(io::Printer* printer) const {
+ PrintExtraFieldInfo(variables_, printer);
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $has_oneof_case_message$;\n"
+ "}\n");
+ }
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " if ($has_oneof_case_message$) {\n"
+ " return ($boxed_type$) $oneof_name$_;\n"
+ " }\n"
+ " return $default$;\n"
+ "}\n");
+}
+
+
+void ImmutablePrimitiveOneofFieldGenerator::
+GenerateBuilderMembers(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $has_oneof_case_message$;\n"
+ "}\n");
+ }
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$() {\n"
+ " if ($has_oneof_case_message$) {\n"
+ " return ($boxed_type$) $oneof_name$_;\n"
+ " }\n"
+ " return $default$;\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
printer->Print(variables_,
- "private java.util.List<$boxed_type$> $name$_ =\n"
- " java.util.Collections.emptyList();\n"
- "public java.util.List<$boxed_type$> get$capitalized_name$List() {\n"
+ "$deprecation$public Builder set$capitalized_name$($type$ value) {\n"
+ "$null_check$"
+ " $set_oneof_case_message$;\n"
+ " $oneof_name$_ = value;\n"
+ " $on_changed$\n"
+ " return this;\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder clear$capitalized_name$() {\n"
+ " if ($has_oneof_case_message$) {\n"
+ " $clear_oneof_case_message$;\n"
+ " $oneof_name$_ = null;\n"
+ " $on_changed$\n"
+ " }\n"
+ " return this;\n"
+ "}\n");
+}
+
+void ImmutablePrimitiveOneofFieldGenerator::
+GenerateBuildingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n"
+ " result.$oneof_name$_ = $oneof_name$_;\n"
+ "}\n");
+}
+
+void ImmutablePrimitiveOneofFieldGenerator::
+GenerateMergingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "set$capitalized_name$(other.get$capitalized_name$());\n");
+}
+
+void ImmutablePrimitiveOneofFieldGenerator::
+GenerateParsingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "$set_oneof_case_message$;\n"
+ "$oneof_name$_ = input.read$capitalized_type$();\n");
+}
+
+void ImmutablePrimitiveOneofFieldGenerator::
+GenerateSerializationCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n"
+ " output.write$capitalized_type$(\n"
+ " $number$, ($type$)(($boxed_type$) $oneof_name$_));\n"
+ "}\n");
+}
+
+void ImmutablePrimitiveOneofFieldGenerator::
+GenerateSerializedSizeCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n"
+ " size += com.google.protobuf.CodedOutputStream\n"
+ " .compute$capitalized_type$Size(\n"
+ " $number$, ($type$)(($boxed_type$) $oneof_name$_));\n"
+ "}\n");
+}
+
+// ===================================================================
+
+RepeatedImmutablePrimitiveFieldGenerator::
+RepeatedImmutablePrimitiveFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : descriptor_(descriptor), messageBitIndex_(messageBitIndex),
+ builderBitIndex_(builderBitIndex), context_(context),
+ name_resolver_(context->GetNameResolver()) {
+ SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex,
+ context->GetFieldGeneratorInfo(descriptor),
+ name_resolver_, &variables_);
+}
+
+RepeatedImmutablePrimitiveFieldGenerator::
+~RepeatedImmutablePrimitiveFieldGenerator() {}
+
+int RepeatedImmutablePrimitiveFieldGenerator::GetNumBitsForMessage() const {
+ return 0;
+}
+
+int RepeatedImmutablePrimitiveFieldGenerator::GetNumBitsForBuilder() const {
+ return 1;
+}
+
+void RepeatedImmutablePrimitiveFieldGenerator::
+GenerateInterfaceMembers(io::Printer* printer) const {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$java.util.List<$boxed_type$> get$capitalized_name$List();\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$int get$capitalized_name$Count();\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$$type$ get$capitalized_name$(int index);\n");
+}
+
+
+void RepeatedImmutablePrimitiveFieldGenerator::
+GenerateMembers(io::Printer* printer) const {
+ printer->Print(variables_,
+ "private $field_list_type$ $name$_;\n");
+ PrintExtraFieldInfo(variables_, printer);
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.util.List<$boxed_type$>\n"
+ " get$capitalized_name$List() {\n"
" return $name$_;\n" // note: unmodifiable list
- "}\n"
- "public int get$capitalized_name$Count() { return $name$_.size(); }\n"
- "public $type$ get$capitalized_name$(int index) {\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public int get$capitalized_name$Count() {\n"
+ " return $name$_.size();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$(int index) {\n"
" return $name$_.get(index);\n"
"}\n");
@@ -308,92 +626,170 @@ GenerateMembers(io::Printer* printer) const {
}
}
-void RepeatedPrimitiveFieldGenerator::
+void RepeatedImmutablePrimitiveFieldGenerator::
GenerateBuilderMembers(io::Printer* printer) const {
+ // One field is the list and the bit field keeps track of whether the
+ // list is immutable. If it's immutable, the invariant is that it must
+ // either an instance of Collections.emptyList() or it's an ArrayList
+ // wrapped in a Collections.unmodifiableList() wrapper and nobody else has
+ // a refererence to the underlying ArrayList. This invariant allows us to
+ // share instances of lists between protocol buffers avoiding expensive
+ // memory allocations. Note, immutable is a strong guarantee here -- not
+ // just that the list cannot be modified via the reference but that the
+ // list can never be modified.
+ printer->Print(variables_,
+ "private $field_list_type$ $name$_ = $empty_list$;\n");
+
printer->Print(variables_,
+ "private void ensure$capitalized_name$IsMutable() {\n"
+ " if (!$get_mutable_bit_builder$) {\n"
+ " $name$_ = new java.util.ArrayList<$boxed_type$>($name$_);\n"
+ " $set_mutable_bit_builder$;\n"
+ " }\n"
+ "}\n");
+
// Note: We return an unmodifiable list because otherwise the caller
// could hold on to the returned list and modify it after the message
// has been built, thus mutating the message which is supposed to be
// immutable.
- "public java.util.List<$boxed_type$> get$capitalized_name$List() {\n"
- " return java.util.Collections.unmodifiableList(result.$name$_);\n"
- "}\n"
- "public int get$capitalized_name$Count() {\n"
- " return result.get$capitalized_name$Count();\n"
- "}\n"
- "public $type$ get$capitalized_name$(int index) {\n"
- " return result.get$capitalized_name$(index);\n"
- "}\n"
- "public Builder set$capitalized_name$(int index, $type$ value) {\n"
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.util.List<$boxed_type$>\n"
+ " get$capitalized_name$List() {\n"
+ " return java.util.Collections.unmodifiableList($name$_);\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public int get$capitalized_name$Count() {\n"
+ " return $name$_.size();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public $type$ get$capitalized_name$(int index) {\n"
+ " return $name$_.get(index);\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " int index, $type$ value) {\n"
"$null_check$"
- " result.$name$_.set(index, value);\n"
+ " ensure$capitalized_name$IsMutable();\n"
+ " $name$_.set(index, value);\n"
+ " $on_changed$\n"
" return this;\n"
- "}\n"
- "public Builder add$capitalized_name$($type$ value) {\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder add$capitalized_name$($type$ value) {\n"
"$null_check$"
- " if (result.$name$_.isEmpty()) {\n"
- " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n"
- " }\n"
- " result.$name$_.add(value);\n"
+ " ensure$capitalized_name$IsMutable();\n"
+ " $name$_.add(value);\n"
+ " $on_changed$\n"
" return this;\n"
- "}\n"
- "public Builder addAll$capitalized_name$(\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder addAll$capitalized_name$(\n"
" java.lang.Iterable<? extends $boxed_type$> values) {\n"
- " if (result.$name$_.isEmpty()) {\n"
- " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n"
- " }\n"
- " super.addAll(values, result.$name$_);\n"
+ " ensure$capitalized_name$IsMutable();\n"
+ " com.google.protobuf.AbstractMessageLite.Builder.addAll(\n"
+ " values, $name$_);\n"
+ " $on_changed$\n"
" return this;\n"
- "}\n"
- "public Builder clear$capitalized_name$() {\n"
- " result.$name$_ = java.util.Collections.emptyList();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder clear$capitalized_name$() {\n"
+ " $name$_ = $empty_list$;\n"
+ " $clear_mutable_bit_builder$;\n"
+ " $on_changed$\n"
" return this;\n"
"}\n");
}
-void RepeatedPrimitiveFieldGenerator::
+void RepeatedImmutablePrimitiveFieldGenerator::
+GenerateFieldBuilderInitializationCode(io::Printer* printer) const {
+ // noop for primitives
+}
+
+void RepeatedImmutablePrimitiveFieldGenerator::
GenerateInitializationCode(io::Printer* printer) const {
- // Initialized inline.
+ printer->Print(variables_, "$name$_ = $empty_list$;\n");
}
-void RepeatedPrimitiveFieldGenerator::
+void RepeatedImmutablePrimitiveFieldGenerator::
+GenerateBuilderClearCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "$name$_ = $empty_list$;\n"
+ "$clear_mutable_bit_builder$;\n");
+}
+
+void RepeatedImmutablePrimitiveFieldGenerator::
GenerateMergingCode(io::Printer* printer) const {
+ // The code below does two optimizations:
+ // 1. If the other list is empty, there's nothing to do. This ensures we
+ // don't allocate a new array if we already have an immutable one.
+ // 2. If the other list is non-empty and our current list is empty, we can
+ // reuse the other list which is guaranteed to be immutable.
printer->Print(variables_,
"if (!other.$name$_.isEmpty()) {\n"
- " if (result.$name$_.isEmpty()) {\n"
- " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n"
+ " if ($name$_.isEmpty()) {\n"
+ " $name$_ = other.$name$_;\n"
+ " $clear_mutable_bit_builder$;\n"
+ " } else {\n"
+ " ensure$capitalized_name$IsMutable();\n"
+ " $name$_.addAll(other.$name$_);\n"
" }\n"
- " result.$name$_.addAll(other.$name$_);\n"
+ " $on_changed$\n"
"}\n");
}
-void RepeatedPrimitiveFieldGenerator::
+void RepeatedImmutablePrimitiveFieldGenerator::
GenerateBuildingCode(io::Printer* printer) const {
+ // The code below ensures that the result has an immutable list. If our
+ // list is immutable, we can just reuse it. If not, we make it immutable.
printer->Print(variables_,
- "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n"
- " result.$name$_ =\n"
- " java.util.Collections.unmodifiableList(result.$name$_);\n"
- "}\n");
+ "if ($get_mutable_bit_builder$) {\n"
+ " $name$_ = java.util.Collections.unmodifiableList($name$_);\n"
+ " $clear_mutable_bit_builder$;\n"
+ "}\n"
+ "result.$name$_ = $name$_;\n");
}
-void RepeatedPrimitiveFieldGenerator::
+void RepeatedImmutablePrimitiveFieldGenerator::
GenerateParsingCode(io::Printer* printer) const {
printer->Print(variables_,
- "add$capitalized_name$(input.read$capitalized_type$());\n");
+ "if (!$get_mutable_bit_parser$) {\n"
+ " $name$_ = new java.util.ArrayList<$boxed_type$>();\n"
+ " $set_mutable_bit_parser$;\n"
+ "}\n"
+ "$name$_.add(input.read$capitalized_type$());\n");
}
-void RepeatedPrimitiveFieldGenerator::
+void RepeatedImmutablePrimitiveFieldGenerator::
GenerateParsingCodeFromPacked(io::Printer* printer) const {
printer->Print(variables_,
"int length = input.readRawVarint32();\n"
"int limit = input.pushLimit(length);\n"
+ "if (!$get_mutable_bit_parser$ && input.getBytesUntilLimit() > 0) {\n"
+ " $name$_ = new java.util.ArrayList<$boxed_type$>();\n"
+ " $set_mutable_bit_parser$;\n"
+ "}\n"
"while (input.getBytesUntilLimit() > 0) {\n"
- " add$capitalized_name$(input.read$capitalized_type$());\n"
+ " $name$_.add(input.read$capitalized_type$());\n"
"}\n"
"input.popLimit(limit);\n");
}
-void RepeatedPrimitiveFieldGenerator::
+void RepeatedImmutablePrimitiveFieldGenerator::
+GenerateParsingDoneCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($get_mutable_bit_parser$) {\n"
+ " $name$_ = java.util.Collections.unmodifiableList($name$_);\n"
+ "}\n");
+}
+
+void RepeatedImmutablePrimitiveFieldGenerator::
GenerateSerializationCode(io::Printer* printer) const {
if (descriptor_->options().packed()) {
printer->Print(variables_,
@@ -401,18 +797,18 @@ GenerateSerializationCode(io::Printer* printer) const {
" output.writeRawVarint32($tag$);\n"
" output.writeRawVarint32($name$MemoizedSerializedSize);\n"
"}\n"
- "for ($type$ element : get$capitalized_name$List()) {\n"
- " output.write$capitalized_type$NoTag(element);\n"
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
+ " output.write$capitalized_type$NoTag($name$_.get(i));\n"
"}\n");
} else {
printer->Print(variables_,
- "for ($type$ element : get$capitalized_name$List()) {\n"
- " output.write$capitalized_type$($number$, element);\n"
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
+ " output.write$capitalized_type$($number$, $name$_.get(i));\n"
"}\n");
}
}
-void RepeatedPrimitiveFieldGenerator::
+void RepeatedImmutablePrimitiveFieldGenerator::
GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print(variables_,
"{\n"
@@ -421,9 +817,9 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
if (FixedSize(GetType(descriptor_)) == -1) {
printer->Print(variables_,
- "for ($type$ element : get$capitalized_name$List()) {\n"
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
" dataSize += com.google.protobuf.CodedOutputStream\n"
- " .compute$capitalized_type$SizeNoTag(element);\n"
+ " .compute$capitalized_type$SizeNoTag($name$_.get(i));\n"
"}\n");
} else {
printer->Print(variables_,
@@ -455,7 +851,23 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print("}\n");
}
-string RepeatedPrimitiveFieldGenerator::GetBoxedType() const {
+void RepeatedImmutablePrimitiveFieldGenerator::
+GenerateEqualsCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "result = result && get$capitalized_name$List()\n"
+ " .equals(other.get$capitalized_name$List());\n");
+}
+
+void RepeatedImmutablePrimitiveFieldGenerator::
+GenerateHashCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (get$capitalized_name$Count() > 0) {\n"
+ " hash = (37 * hash) + $constant_name$;\n"
+ " hash = (53 * hash) + get$capitalized_name$List().hashCode();\n"
+ "}\n");
+}
+
+string RepeatedImmutablePrimitiveFieldGenerator::GetBoxedType() const {
return BoxedPrimitiveTypeName(GetJavaType(descriptor_));
}
diff --git a/src/google/protobuf/compiler/java/java_primitive_field.h b/src/google/protobuf/compiler/java/java_primitive_field.h
index 4e482a0..d0cd12d 100644
--- a/src/google/protobuf/compiler/java/java_primitive_field.h
+++ b/src/google/protobuf/compiler/java/java_primitive_field.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -41,56 +41,115 @@
namespace google {
namespace protobuf {
+ namespace compiler {
+ namespace java {
+ class Context; // context.h
+ class ClassNameResolver; // name_resolver.h
+ }
+ }
+}
+
+namespace protobuf {
namespace compiler {
namespace java {
-class PrimitiveFieldGenerator : public FieldGenerator {
+class ImmutablePrimitiveFieldGenerator : public ImmutableFieldGenerator {
public:
- explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor);
- ~PrimitiveFieldGenerator();
-
- // implements FieldGenerator ---------------------------------------
+ explicit ImmutablePrimitiveFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~ImmutablePrimitiveFieldGenerator();
+
+ // implements ImmutableFieldGenerator ---------------------------------------
+ int GetNumBitsForMessage() const;
+ int GetNumBitsForBuilder() const;
+ void GenerateInterfaceMembers(io::Printer* printer) const;
void GenerateMembers(io::Printer* printer) const;
void GenerateBuilderMembers(io::Printer* printer) const;
void GenerateInitializationCode(io::Printer* printer) const;
+ void GenerateBuilderClearCode(io::Printer* printer) const;
void GenerateMergingCode(io::Printer* printer) const;
void GenerateBuildingCode(io::Printer* printer) const;
void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateParsingDoneCode(io::Printer* printer) const;
void GenerateSerializationCode(io::Printer* printer) const;
void GenerateSerializedSizeCode(io::Printer* printer) const;
+ void GenerateFieldBuilderInitializationCode(io::Printer* printer) const;
+ void GenerateEqualsCode(io::Printer* printer) const;
+ void GenerateHashCode(io::Printer* printer) const;
string GetBoxedType() const;
- private:
+ protected:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ const int messageBitIndex_;
+ const int builderBitIndex_;
+ Context* context_;
+ ClassNameResolver* name_resolver_;
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator);
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutablePrimitiveFieldGenerator);
};
-class RepeatedPrimitiveFieldGenerator : public FieldGenerator {
+class ImmutablePrimitiveOneofFieldGenerator
+ : public ImmutablePrimitiveFieldGenerator {
public:
- explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor);
- ~RepeatedPrimitiveFieldGenerator();
+ ImmutablePrimitiveOneofFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~ImmutablePrimitiveOneofFieldGenerator();
- // implements FieldGenerator ---------------------------------------
+ void GenerateMembers(io::Printer* printer) const;
+ void GenerateBuilderMembers(io::Printer* printer) const;
+ void GenerateBuildingCode(io::Printer* printer) const;
+ void GenerateMergingCode(io::Printer* printer) const;
+ void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateSerializationCode(io::Printer* printer) const;
+ void GenerateSerializedSizeCode(io::Printer* printer) const;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutablePrimitiveOneofFieldGenerator);
+};
+
+class RepeatedImmutablePrimitiveFieldGenerator
+ : public ImmutableFieldGenerator {
+ public:
+ explicit RepeatedImmutablePrimitiveFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ virtual ~RepeatedImmutablePrimitiveFieldGenerator();
+
+ // implements ImmutableFieldGenerator ---------------------------------------
+ int GetNumBitsForMessage() const;
+ int GetNumBitsForBuilder() const;
+ void GenerateInterfaceMembers(io::Printer* printer) const;
void GenerateMembers(io::Printer* printer) const;
void GenerateBuilderMembers(io::Printer* printer) const;
void GenerateInitializationCode(io::Printer* printer) const;
+ void GenerateBuilderClearCode(io::Printer* printer) const;
void GenerateMergingCode(io::Printer* printer) const;
void GenerateBuildingCode(io::Printer* printer) const;
void GenerateParsingCode(io::Printer* printer) const;
void GenerateParsingCodeFromPacked(io::Printer* printer) const;
+ void GenerateParsingDoneCode(io::Printer* printer) const;
void GenerateSerializationCode(io::Printer* printer) const;
void GenerateSerializedSizeCode(io::Printer* printer) const;
+ void GenerateFieldBuilderInitializationCode(io::Printer* printer) const;
+ void GenerateEqualsCode(io::Printer* printer) const;
+ void GenerateHashCode(io::Printer* printer) const;
string GetBoxedType() const;
private:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ const int messageBitIndex_;
+ const int builderBitIndex_;
+ Context* context_;
+ ClassNameResolver* name_resolver_;
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPrimitiveFieldGenerator);
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutablePrimitiveFieldGenerator);
};
} // namespace java
diff --git a/src/google/protobuf/compiler/java/java_service.cc b/src/google/protobuf/compiler/java/java_service.cc
index 5545bf7..7baead1 100644
--- a/src/google/protobuf/compiler/java/java_service.cc
+++ b/src/google/protobuf/compiler/java/java_service.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -33,7 +33,11 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/java/java_service.h>
+
+#include <google/protobuf/compiler/java/java_context.h>
+#include <google/protobuf/compiler/java/java_doc_comment.h>
#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/compiler/java/java_name_resolver.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/stubs/strutil.h>
@@ -48,8 +52,18 @@ ServiceGenerator::ServiceGenerator(const ServiceDescriptor* descriptor)
ServiceGenerator::~ServiceGenerator() {}
-void ServiceGenerator::Generate(io::Printer* printer) {
- bool is_own_file = descriptor_->file()->options().java_multiple_files();
+// ===================================================================
+ImmutableServiceGenerator::ImmutableServiceGenerator(
+ const ServiceDescriptor* descriptor, Context* context)
+ : ServiceGenerator(descriptor), context_(context),
+ name_resolver_(context->GetNameResolver()) {}
+
+ImmutableServiceGenerator::~ImmutableServiceGenerator() {}
+
+void ImmutableServiceGenerator::Generate(io::Printer* printer) {
+ bool is_own_file =
+ MultipleJavaFiles(descriptor_->file(), /* immutable = */ true);
+ WriteServiceDocComment(printer, descriptor_);
printer->Print(
"public $static$ abstract class $classname$\n"
" implements com.google.protobuf.Service {\n",
@@ -75,7 +89,7 @@ void ServiceGenerator::Generate(io::Printer* printer) {
" getDescriptor() {\n"
" return $file$.getDescriptor().getServices().get($index$);\n"
"}\n",
- "file", ClassName(descriptor_->file()),
+ "file", name_resolver_->GetImmutableClassName(descriptor_->file()),
"index", SimpleItoa(descriptor_->index()));
GenerateGetDescriptorForType(printer);
@@ -86,11 +100,18 @@ void ServiceGenerator::Generate(io::Printer* printer) {
GenerateStub(printer);
GenerateBlockingStub(printer);
+ // Add an insertion point.
+ printer->Print(
+ "\n"
+ "// @@protoc_insertion_point(class_scope:$full_name$)\n",
+ "full_name", descriptor_->full_name());
+
printer->Outdent();
printer->Print("}\n\n");
}
-void ServiceGenerator::GenerateGetDescriptorForType(io::Printer* printer) {
+void ImmutableServiceGenerator::GenerateGetDescriptorForType(
+ io::Printer* printer) {
printer->Print(
"public final com.google.protobuf.Descriptors.ServiceDescriptor\n"
" getDescriptorForType() {\n"
@@ -98,7 +119,7 @@ void ServiceGenerator::GenerateGetDescriptorForType(io::Printer* printer) {
"}\n");
}
-void ServiceGenerator::GenerateInterface(io::Printer* printer) {
+void ImmutableServiceGenerator::GenerateInterface(io::Printer* printer) {
printer->Print("public interface Interface {\n");
printer->Indent();
GenerateAbstractMethods(printer);
@@ -106,7 +127,7 @@ void ServiceGenerator::GenerateInterface(io::Printer* printer) {
printer->Print("}\n\n");
}
-void ServiceGenerator::GenerateNewReflectiveServiceMethod(
+void ImmutableServiceGenerator::GenerateNewReflectiveServiceMethod(
io::Printer* printer) {
printer->Print(
"public static com.google.protobuf.Service newReflectiveService(\n"
@@ -118,7 +139,7 @@ void ServiceGenerator::GenerateNewReflectiveServiceMethod(
for (int i = 0; i < descriptor_->method_count(); i++) {
const MethodDescriptor* method = descriptor_->method(i);
- printer->Print("@Override\n");
+ printer->Print("@java.lang.Override\n");
GenerateMethodSignature(printer, method, IS_CONCRETE);
printer->Print(
" {\n"
@@ -133,7 +154,7 @@ void ServiceGenerator::GenerateNewReflectiveServiceMethod(
printer->Print("}\n\n");
}
-void ServiceGenerator::GenerateNewReflectiveBlockingServiceMethod(
+void ImmutableServiceGenerator::GenerateNewReflectiveBlockingServiceMethod(
io::Printer* printer) {
printer->Print(
"public static com.google.protobuf.BlockingService\n"
@@ -154,15 +175,16 @@ void ServiceGenerator::GenerateNewReflectiveBlockingServiceMethod(
printer->Print("}\n\n");
}
-void ServiceGenerator::GenerateAbstractMethods(io::Printer* printer) {
+void ImmutableServiceGenerator::GenerateAbstractMethods(io::Printer* printer) {
for (int i = 0; i < descriptor_->method_count(); i++) {
const MethodDescriptor* method = descriptor_->method(i);
+ WriteMethodDocComment(printer, method);
GenerateMethodSignature(printer, method, IS_ABSTRACT);
printer->Print(";\n\n");
}
}
-void ServiceGenerator::GenerateCallMethod(io::Printer* printer) {
+void ImmutableServiceGenerator::GenerateCallMethod(io::Printer* printer) {
printer->Print(
"\n"
"public final void callMethod(\n"
@@ -185,8 +207,10 @@ void ServiceGenerator::GenerateCallMethod(io::Printer* printer) {
map<string, string> vars;
vars["index"] = SimpleItoa(i);
vars["method"] = UnderscoresToCamelCase(method);
- vars["input"] = ClassName(method->input_type());
- vars["output"] = ClassName(method->output_type());
+ vars["input"] = name_resolver_->GetImmutableClassName(
+ method->input_type());
+ vars["output"] = name_resolver_->GetImmutableClassName(
+ method->output_type());
printer->Print(vars,
"case $index$:\n"
" this.$method$(controller, ($input$)request,\n"
@@ -208,7 +232,8 @@ void ServiceGenerator::GenerateCallMethod(io::Printer* printer) {
"\n");
}
-void ServiceGenerator::GenerateCallBlockingMethod(io::Printer* printer) {
+void ImmutableServiceGenerator::GenerateCallBlockingMethod(
+ io::Printer* printer) {
printer->Print(
"\n"
"public final com.google.protobuf.Message callBlockingMethod(\n"
@@ -230,8 +255,10 @@ void ServiceGenerator::GenerateCallBlockingMethod(io::Printer* printer) {
map<string, string> vars;
vars["index"] = SimpleItoa(i);
vars["method"] = UnderscoresToCamelCase(method);
- vars["input"] = ClassName(method->input_type());
- vars["output"] = ClassName(method->output_type());
+ vars["input"] = name_resolver_->GetImmutableClassName(
+ method->input_type());
+ vars["output"] = name_resolver_->GetImmutableClassName(
+ method->output_type());
printer->Print(vars,
"case $index$:\n"
" return impl.$method$(controller, ($input$)request);\n");
@@ -250,7 +277,7 @@ void ServiceGenerator::GenerateCallBlockingMethod(io::Printer* printer) {
"\n");
}
-void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which,
+void ImmutableServiceGenerator::GenerateGetPrototype(RequestOrResponse which,
io::Printer* printer) {
/*
* TODO(cpovirk): The exception message says "Service.foo" when it may be
@@ -274,7 +301,7 @@ void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which,
const MethodDescriptor* method = descriptor_->method(i);
map<string, string> vars;
vars["index"] = SimpleItoa(i);
- vars["type"] = ClassName(
+ vars["type"] = name_resolver_->GetImmutableClassName(
(which == REQUEST) ? method->input_type() : method->output_type());
printer->Print(vars,
"case $index$:\n"
@@ -294,7 +321,7 @@ void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which,
"\n");
}
-void ServiceGenerator::GenerateStub(io::Printer* printer) {
+void ImmutableServiceGenerator::GenerateStub(io::Printer* printer) {
printer->Print(
"public static Stub newStub(\n"
" com.google.protobuf.RpcChannel channel) {\n"
@@ -303,7 +330,7 @@ void ServiceGenerator::GenerateStub(io::Printer* printer) {
"\n"
"public static final class Stub extends $classname$ implements Interface {"
"\n",
- "classname", ClassName(descriptor_));
+ "classname", name_resolver_->GetImmutableClassName(descriptor_));
printer->Indent();
printer->Print(
@@ -326,7 +353,8 @@ void ServiceGenerator::GenerateStub(io::Printer* printer) {
map<string, string> vars;
vars["index"] = SimpleItoa(i);
- vars["output"] = ClassName(method->output_type());
+ vars["output"] = name_resolver_->GetImmutableClassName(
+ method->output_type());
printer->Print(vars,
"channel.callMethod(\n"
" getDescriptor().getMethods().get($index$),\n"
@@ -348,7 +376,7 @@ void ServiceGenerator::GenerateStub(io::Printer* printer) {
"\n");
}
-void ServiceGenerator::GenerateBlockingStub(io::Printer* printer) {
+void ImmutableServiceGenerator::GenerateBlockingStub(io::Printer* printer) {
printer->Print(
"public static BlockingInterface newBlockingStub(\n"
" com.google.protobuf.BlockingRpcChannel channel) {\n"
@@ -390,7 +418,8 @@ void ServiceGenerator::GenerateBlockingStub(io::Printer* printer) {
map<string, string> vars;
vars["index"] = SimpleItoa(i);
- vars["output"] = ClassName(method->output_type());
+ vars["output"] = name_resolver_->GetImmutableClassName(
+ method->output_type());
printer->Print(vars,
"return ($output$) channel.callBlockingMethod(\n"
" getDescriptor().getMethods().get($index$),\n"
@@ -408,13 +437,13 @@ void ServiceGenerator::GenerateBlockingStub(io::Printer* printer) {
printer->Print("}\n");
}
-void ServiceGenerator::GenerateMethodSignature(io::Printer* printer,
+void ImmutableServiceGenerator::GenerateMethodSignature(io::Printer* printer,
const MethodDescriptor* method,
IsAbstract is_abstract) {
map<string, string> vars;
vars["name"] = UnderscoresToCamelCase(method);
- vars["input"] = ClassName(method->input_type());
- vars["output"] = ClassName(method->output_type());
+ vars["input"] = name_resolver_->GetImmutableClassName(method->input_type());
+ vars["output"] = name_resolver_->GetImmutableClassName(method->output_type());
vars["abstract"] = (is_abstract == IS_ABSTRACT) ? "abstract" : "";
printer->Print(vars,
"public $abstract$ void $name$(\n"
@@ -423,13 +452,13 @@ void ServiceGenerator::GenerateMethodSignature(io::Printer* printer,
" com.google.protobuf.RpcCallback<$output$> done)");
}
-void ServiceGenerator::GenerateBlockingMethodSignature(
+void ImmutableServiceGenerator::GenerateBlockingMethodSignature(
io::Printer* printer,
const MethodDescriptor* method) {
map<string, string> vars;
vars["method"] = UnderscoresToCamelCase(method);
- vars["input"] = ClassName(method->input_type());
- vars["output"] = ClassName(method->output_type());
+ vars["input"] = name_resolver_->GetImmutableClassName(method->input_type());
+ vars["output"] = name_resolver_->GetImmutableClassName(method->output_type());
printer->Print(vars,
"\n"
"public $output$ $method$(\n"
diff --git a/src/google/protobuf/compiler/java/java_service.h b/src/google/protobuf/compiler/java/java_service.h
index e07eebf..6707e82 100644
--- a/src/google/protobuf/compiler/java/java_service.h
+++ b/src/google/protobuf/compiler/java/java_service.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -40,8 +40,14 @@
namespace google {
namespace protobuf {
+ namespace compiler {
+ namespace java {
+ class Context; // context.h
+ class ClassNameResolver; // name_resolver.h
+ }
+ }
namespace io {
- class Printer; // printer.h
+ class Printer; // printer.h
}
}
@@ -52,9 +58,27 @@ namespace java {
class ServiceGenerator {
public:
explicit ServiceGenerator(const ServiceDescriptor* descriptor);
- ~ServiceGenerator();
+ virtual ~ServiceGenerator();
+
+ virtual void Generate(io::Printer* printer) = 0;
+
+ enum RequestOrResponse { REQUEST, RESPONSE };
+ enum IsAbstract { IS_ABSTRACT, IS_CONCRETE };
+
+ protected:
+ const ServiceDescriptor* descriptor_;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceGenerator);
+};
- void Generate(io::Printer* printer);
+class ImmutableServiceGenerator : public ServiceGenerator {
+ public:
+ explicit ImmutableServiceGenerator(const ServiceDescriptor* descriptor,
+ Context* context);
+ virtual ~ImmutableServiceGenerator();
+
+ virtual void Generate(io::Printer* printer);
private:
@@ -80,7 +104,6 @@ class ServiceGenerator {
void GenerateCallBlockingMethod(io::Printer* printer);
// Generate the implementations of Service.get{Request,Response}Prototype().
- enum RequestOrResponse { REQUEST, RESPONSE };
void GenerateGetPrototype(RequestOrResponse which, io::Printer* printer);
// Generate a stub implementation of the service.
@@ -88,7 +111,6 @@ class ServiceGenerator {
// Generate a method signature, possibly abstract, without body or trailing
// semicolon.
- enum IsAbstract { IS_ABSTRACT, IS_CONCRETE };
void GenerateMethodSignature(io::Printer* printer,
const MethodDescriptor* method,
IsAbstract is_abstract);
@@ -100,9 +122,9 @@ class ServiceGenerator {
void GenerateBlockingMethodSignature(io::Printer* printer,
const MethodDescriptor* method);
- const ServiceDescriptor* descriptor_;
-
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceGenerator);
+ Context* context_;
+ ClassNameResolver* name_resolver_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableServiceGenerator);
};
} // namespace java
diff --git a/src/google/protobuf/compiler/java/java_shared_code_generator.cc b/src/google/protobuf/compiler/java/java_shared_code_generator.cc
new file mode 100644
index 0000000..ea77f79
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_shared_code_generator.cc
@@ -0,0 +1,201 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: xiaofeng@google.com (Feng Xiao)
+
+#include <google/protobuf/compiler/java/java_shared_code_generator.h>
+
+#include <memory>
+
+#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/compiler/java/java_name_resolver.h>
+#include <google/protobuf/compiler/code_generator.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/stubs/strutil.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+SharedCodeGenerator::SharedCodeGenerator(const FileDescriptor* file)
+ : name_resolver_(new ClassNameResolver), file_(file) {
+}
+
+SharedCodeGenerator::~SharedCodeGenerator() {
+}
+
+void SharedCodeGenerator::Generate(GeneratorContext* context,
+ vector<string>* file_list) {
+ string java_package = FileJavaPackage(file_);
+ string package_dir = JavaPackageToDir(java_package);
+
+ if (HasDescriptorMethods(file_)) {
+ // Generate descriptors.
+ string classname = name_resolver_->GetDescriptorClassName(file_);
+ string filename = package_dir + classname + ".java";
+ file_list->push_back(filename);
+ scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
+ scoped_ptr<io::Printer> printer(new io::Printer(output.get(), '$'));
+
+ printer->Print(
+ "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
+ "// source: $filename$\n"
+ "\n",
+ "filename", file_->name());
+ if (!java_package.empty()) {
+ printer->Print(
+ "package $package$;\n"
+ "\n",
+ "package", java_package);
+ }
+ printer->Print(
+ "public final class $classname$ {\n"
+ " public static com.google.protobuf.Descriptors.FileDescriptor\n"
+ " descriptor;\n"
+ " static {\n",
+ "classname", classname);
+ printer->Indent();
+ printer->Indent();
+ GenerateDescriptors(printer.get());
+ printer->Outdent();
+ printer->Outdent();
+ printer->Print(
+ " }\n"
+ "}\n");
+
+ printer.reset();
+ output.reset();
+ }
+}
+
+
+void SharedCodeGenerator::GenerateDescriptors(io::Printer* printer) {
+ // Embed the descriptor. We simply serialize the entire FileDescriptorProto
+ // and embed it as a string literal, which is parsed and built into real
+ // descriptors at initialization time. We unfortunately have to put it in
+ // a string literal, not a byte array, because apparently using a literal
+ // byte array causes the Java compiler to generate *instructions* to
+ // initialize each and every byte of the array, e.g. as if you typed:
+ // b[0] = 123; b[1] = 456; b[2] = 789;
+ // This makes huge bytecode files and can easily hit the compiler's internal
+ // code size limits (error "code to large"). String literals are apparently
+ // embedded raw, which is what we want.
+ FileDescriptorProto file_proto;
+ file_->CopyTo(&file_proto);
+
+
+ string file_data;
+ file_proto.SerializeToString(&file_data);
+
+ printer->Print(
+ "java.lang.String[] descriptorData = {\n");
+ printer->Indent();
+
+ // Only write 40 bytes per line.
+ static const int kBytesPerLine = 40;
+ for (int i = 0; i < file_data.size(); i += kBytesPerLine) {
+ if (i > 0) {
+ // Every 400 lines, start a new string literal, in order to avoid the
+ // 64k length limit.
+ if (i % 400 == 0) {
+ printer->Print(",\n");
+ } else {
+ printer->Print(" +\n");
+ }
+ }
+ printer->Print("\"$data$\"",
+ "data", CEscape(file_data.substr(i, kBytesPerLine)));
+ }
+
+ printer->Outdent();
+ printer->Print("\n};\n");
+
+ // -----------------------------------------------------------------
+ // Create the InternalDescriptorAssigner.
+
+ printer->Print(
+ "com.google.protobuf.Descriptors.FileDescriptor."
+ "InternalDescriptorAssigner assigner =\n"
+ " new com.google.protobuf.Descriptors.FileDescriptor."
+ " InternalDescriptorAssigner() {\n"
+ " public com.google.protobuf.ExtensionRegistry assignDescriptors(\n"
+ " com.google.protobuf.Descriptors.FileDescriptor root) {\n"
+ " descriptor = root;\n"
+ // Custom options will be handled when immutable messages' outer class is
+ // loaded. Here we just return null and let custom options be unknown
+ // fields.
+ " return null;\n"
+ " }\n"
+ " };\n");
+
+ // -----------------------------------------------------------------
+ // Find out all dependencies.
+ vector<pair<string, string> > dependencies;
+ for (int i = 0; i < file_->dependency_count(); i++) {
+ if (ShouldIncludeDependency(file_->dependency(i))) {
+ string filename = file_->dependency(i)->name();
+ string classname = FileJavaPackage(file_->dependency(i)) + "." +
+ name_resolver_->GetDescriptorClassName(
+ file_->dependency(i));
+ dependencies.push_back(make_pair(filename, classname));
+ }
+ }
+
+ // -----------------------------------------------------------------
+ // Invoke internalBuildGeneratedFileFrom() to build the file.
+ printer->Print(
+ "com.google.protobuf.Descriptors.FileDescriptor\n"
+ " .internalBuildGeneratedFileFrom(descriptorData,\n"
+ " new com.google.protobuf.Descriptors.FileDescriptor[] {\n");
+
+ for (int i = 0; i < dependencies.size(); i++) {
+ const string& dependency = dependencies[i].second;
+ printer->Print(
+ " $dependency$.getDescriptor(),\n",
+ "dependency", dependency);
+ }
+
+ printer->Print(
+ " }, assigner);\n");
+}
+
+bool SharedCodeGenerator::ShouldIncludeDependency(
+ const FileDescriptor* descriptor) {
+ return true;
+}
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/compiler/java/java_shared_code_generator.h b/src/google/protobuf/compiler/java/java_shared_code_generator.h
new file mode 100644
index 0000000..1bb2f3d
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_shared_code_generator.h
@@ -0,0 +1,90 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: xiaofeng@google.com (Feng Xiao)
+//
+// Generators that generate shared code between immutable API and mutable API.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_SHARED_CODE_GENERATOR_H__
+#define GOOGLE_PROTOBUF_COMPILER_JAVA_SHARED_CODE_GENERATOR_H__
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+ class FileDescriptor; // descriptor.h
+ namespace compiler {
+ class GeneratorContext; // code_generator.h
+ namespace java {
+ class ClassNameResolver; // name_resolver.h
+ }
+ }
+ namespace io {
+ class Printer; // printer.h
+ }
+}
+
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+// A generator that generates code that are shared between immutable API
+// and mutable API. Currently only descriptors are shared.
+class SharedCodeGenerator {
+ public:
+ explicit SharedCodeGenerator(const FileDescriptor* file);
+ ~SharedCodeGenerator();
+
+ void Generate(GeneratorContext* generator_context,
+ vector<string>* file_list);
+ void GenerateDescriptors(io::Printer* printer);
+
+ private:
+ // Returns whether the dependency should be included in the output file.
+ // Always returns true for opensource, but used internally at Google to help
+ // improve compatibility with version 1 of protocol buffers.
+ bool ShouldIncludeDependency(const FileDescriptor* descriptor);
+
+ scoped_ptr<ClassNameResolver> name_resolver_;
+ const FileDescriptor* file_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SharedCodeGenerator);
+};
+
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_SHARED_CODE_GENERATOR_H__
diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc
new file mode 100644
index 0000000..8889a74
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_string_field.cc
@@ -0,0 +1,1056 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: kenton@google.com (Kenton Varda)
+// Author: jonp@google.com (Jon Perlow)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <map>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/compiler/java/java_context.h>
+#include <google/protobuf/compiler/java/java_doc_comment.h>
+#include <google/protobuf/compiler/java/java_helpers.h>
+#include <google/protobuf/compiler/java/java_name_resolver.h>
+#include <google/protobuf/compiler/java/java_string_field.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/stubs/strutil.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+using internal::WireFormat;
+using internal::WireFormatLite;
+
+namespace {
+
+void SetPrimitiveVariables(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ const FieldGeneratorInfo* info,
+ ClassNameResolver* name_resolver,
+ map<string, string>* variables) {
+ SetCommonFieldVariables(descriptor, info, variables);
+
+ (*variables)["empty_list"] = "com.google.protobuf.LazyStringArrayList.EMPTY";
+
+ (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver);
+ (*variables)["default_init"] =
+ "= " + ImmutableDefaultValue(descriptor, name_resolver);
+ (*variables)["capitalized_type"] = "String";
+ (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor));
+ (*variables)["tag_size"] = SimpleItoa(
+ WireFormat::TagSize(descriptor->number(), GetType(descriptor)));
+ (*variables)["null_check"] =
+ " if (value == null) {\n"
+ " throw new NullPointerException();\n"
+ " }\n";
+
+ // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported
+ // by the proto compiler
+ (*variables)["deprecation"] = descriptor->options().deprecated()
+ ? "@java.lang.Deprecated " : "";
+ (*variables)["on_changed"] =
+ HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : "";
+
+ if (SupportFieldPresence(descriptor->file())) {
+ // For singular messages and builders, one bit is used for the hasField bit.
+ (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex);
+ (*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex);
+
+ // Note that these have a trailing ";".
+ (*variables)["set_has_field_bit_message"] =
+ GenerateSetBit(messageBitIndex) + ";";
+ (*variables)["set_has_field_bit_builder"] =
+ GenerateSetBit(builderBitIndex) + ";";
+ (*variables)["clear_has_field_bit_builder"] =
+ GenerateClearBit(builderBitIndex) + ";";
+
+ (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex);
+ } else {
+ (*variables)["set_has_field_bit_message"] = "";
+ (*variables)["set_has_field_bit_builder"] = "";
+ (*variables)["clear_has_field_bit_builder"] = "";
+
+ (*variables)["is_field_present_message"] =
+ "!get" + (*variables)["capitalized_name"] + "Bytes().isEmpty()";
+ }
+
+ // For repeated builders, one bit is used for whether the array is immutable.
+ (*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex);
+ (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex);
+ (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex);
+
+ // For repeated fields, one bit is used for whether the array is immutable
+ // in the parsing constructor.
+ (*variables)["get_mutable_bit_parser"] =
+ GenerateGetBitMutableLocal(builderBitIndex);
+ (*variables)["set_mutable_bit_parser"] =
+ GenerateSetBitMutableLocal(builderBitIndex);
+
+ (*variables)["get_has_field_bit_from_local"] =
+ GenerateGetBitFromLocal(builderBitIndex);
+ (*variables)["set_has_field_bit_to_local"] =
+ GenerateSetBitToLocal(messageBitIndex);
+}
+
+bool CheckUtf8(const FieldDescriptor* descriptor) {
+ return descriptor->file()->options().java_string_check_utf8();
+}
+
+} // namespace
+
+// ===================================================================
+
+ImmutableStringFieldGenerator::
+ImmutableStringFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : descriptor_(descriptor), messageBitIndex_(messageBitIndex),
+ builderBitIndex_(builderBitIndex), context_(context),
+ name_resolver_(context->GetNameResolver()) {
+ SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex,
+ context->GetFieldGeneratorInfo(descriptor),
+ name_resolver_, &variables_);
+}
+
+ImmutableStringFieldGenerator::~ImmutableStringFieldGenerator() {}
+
+int ImmutableStringFieldGenerator::GetNumBitsForMessage() const {
+ return 1;
+}
+
+int ImmutableStringFieldGenerator::GetNumBitsForBuilder() const {
+ return 1;
+}
+
+// A note about how strings are handled. This code used to just store a String
+// in the Message. This had two issues:
+//
+// 1. It wouldn't roundtrip byte arrays that were not vaid UTF-8 encoded
+// strings, but rather fields that were raw bytes incorrectly marked
+// as strings in the proto file. This is common because in the proto1
+// syntax, string was the way to indicate bytes and C++ engineers can
+// easily make this mistake without affecting the C++ API. By converting to
+// strings immediately, some java code might corrupt these byte arrays as
+// it passes through a java server even if the field was never accessed by
+// application code.
+//
+// 2. There's a performance hit to converting between bytes and strings and
+// it many cases, the field is never even read by the application code. This
+// avoids unnecessary conversions in the common use cases.
+//
+// So now, the field for String is maintained as an Object reference which can
+// either store a String or a ByteString. The code uses an instanceof check
+// to see which one it has and converts to the other one if needed. It remembers
+// the last value requested (in a thread safe manner) as this is most likely
+// the one needed next. The thread safety is such that if two threads both
+// convert the field because the changes made by each thread were not visible to
+// the other, they may cause a conversion to happen more times than would
+// otherwise be necessary. This was deemed better than adding synchronization
+// overhead. It will not cause any corruption issues or affect the behavior of
+// the API. The instanceof check is also highly optimized in the JVM and we
+// decided it was better to reduce the memory overhead by not having two
+// separate fields but rather use dynamic type checking.
+//
+// For single fields, the logic for this is done inside the generated code. For
+// repeated fields, the logic is done in LazyStringArrayList and
+// UnmodifiableLazyStringList.
+void ImmutableStringFieldGenerator::
+GenerateInterfaceMembers(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$boolean has$capitalized_name$();\n");
+ }
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$java.lang.String get$capitalized_name$();\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$com.google.protobuf.ByteString\n"
+ " get$capitalized_name$Bytes();\n");
+}
+
+void ImmutableStringFieldGenerator::
+GenerateMembers(io::Printer* printer) const {
+ printer->Print(variables_,
+ "private java.lang.Object $name$_;\n");
+ PrintExtraFieldInfo(variables_, printer);
+
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $get_has_field_bit_message$;\n"
+ "}\n");
+ }
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.lang.String get$capitalized_name$() {\n"
+ " java.lang.Object ref = $name$_;\n"
+ " if (ref instanceof java.lang.String) {\n"
+ " return (java.lang.String) ref;\n"
+ " } else {\n"
+ " com.google.protobuf.ByteString bs = \n"
+ " (com.google.protobuf.ByteString) ref;\n"
+ " java.lang.String s = bs.toStringUtf8();\n");
+ if (CheckUtf8(descriptor_)) {
+ printer->Print(variables_,
+ " $name$_ = s;\n");
+ } else {
+ printer->Print(variables_,
+ " if (bs.isValidUtf8()) {\n"
+ " $name$_ = s;\n"
+ " }\n");
+ }
+ printer->Print(variables_,
+ " return s;\n"
+ " }\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public com.google.protobuf.ByteString\n"
+ " get$capitalized_name$Bytes() {\n"
+ " java.lang.Object ref = $name$_;\n"
+ " if (ref instanceof java.lang.String) {\n"
+ " com.google.protobuf.ByteString b = \n"
+ " com.google.protobuf.ByteString.copyFromUtf8(\n"
+ " (java.lang.String) ref);\n"
+ " $name$_ = b;\n"
+ " return b;\n"
+ " } else {\n"
+ " return (com.google.protobuf.ByteString) ref;\n"
+ " }\n"
+ "}\n");
+}
+
+void ImmutableStringFieldGenerator::
+GenerateBuilderMembers(io::Printer* printer) const {
+ printer->Print(variables_,
+ "private java.lang.Object $name$_ $default_init$;\n");
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $get_has_field_bit_builder$;\n"
+ "}\n");
+ }
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.lang.String get$capitalized_name$() {\n"
+ " java.lang.Object ref = $name$_;\n"
+ " if (!(ref instanceof java.lang.String)) {\n"
+ " com.google.protobuf.ByteString bs =\n"
+ " (com.google.protobuf.ByteString) ref;\n"
+ " java.lang.String s = bs.toStringUtf8();\n");
+ if (CheckUtf8(descriptor_)) {
+ printer->Print(variables_,
+ " $name$_ = s;\n");
+ } else {
+ printer->Print(variables_,
+ " if (bs.isValidUtf8()) {\n"
+ " $name$_ = s;\n"
+ " }\n");
+ }
+ printer->Print(variables_,
+ " return s;\n"
+ " } else {\n"
+ " return (java.lang.String) ref;\n"
+ " }\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public com.google.protobuf.ByteString\n"
+ " get$capitalized_name$Bytes() {\n"
+ " java.lang.Object ref = $name$_;\n"
+ " if (ref instanceof String) {\n"
+ " com.google.protobuf.ByteString b = \n"
+ " com.google.protobuf.ByteString.copyFromUtf8(\n"
+ " (java.lang.String) ref);\n"
+ " $name$_ = b;\n"
+ " return b;\n"
+ " } else {\n"
+ " return (com.google.protobuf.ByteString) ref;\n"
+ " }\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " java.lang.String value) {\n"
+ "$null_check$"
+ " $set_has_field_bit_builder$\n"
+ " $name$_ = value;\n"
+ " $on_changed$\n"
+ " return this;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder clear$capitalized_name$() {\n"
+ " $clear_has_field_bit_builder$\n");
+ // The default value is not a simple literal so we want to avoid executing
+ // it multiple times. Instead, get the default out of the default instance.
+ printer->Print(variables_,
+ " $name$_ = getDefaultInstance().get$capitalized_name$();\n");
+ printer->Print(variables_,
+ " $on_changed$\n"
+ " return this;\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder set$capitalized_name$Bytes(\n"
+ " com.google.protobuf.ByteString value) {\n"
+ "$null_check$");
+ if (CheckUtf8(descriptor_)) {
+ printer->Print(variables_,
+ " checkByteStringIsUtf8(value);\n");
+ }
+ printer->Print(variables_,
+ " $set_has_field_bit_builder$\n"
+ " $name$_ = value;\n"
+ " $on_changed$\n"
+ " return this;\n"
+ "}\n");
+}
+
+void ImmutableStringFieldGenerator::
+GenerateFieldBuilderInitializationCode(io::Printer* printer) const {
+ // noop for primitives
+}
+
+void ImmutableStringFieldGenerator::
+GenerateInitializationCode(io::Printer* printer) const {
+ printer->Print(variables_, "$name$_ = $default$;\n");
+}
+
+void ImmutableStringFieldGenerator::
+GenerateBuilderClearCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "$name$_ = $default$;\n"
+ "$clear_has_field_bit_builder$\n");
+}
+
+void ImmutableStringFieldGenerator::
+GenerateMergingCode(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ // Allow a slight breach of abstraction here in order to avoid forcing
+ // all string fields to Strings when copying fields from a Message.
+ printer->Print(variables_,
+ "if (other.has$capitalized_name$()) {\n"
+ " $set_has_field_bit_builder$\n"
+ " $name$_ = other.$name$_;\n"
+ " $on_changed$\n"
+ "}\n");
+ } else {
+ printer->Print(variables_,
+ "if (!other.get$capitalized_name$().isEmpty()) {\n"
+ " $name$_ = other.$name$_;\n"
+ " $on_changed$\n"
+ "}\n");
+ }
+}
+
+void ImmutableStringFieldGenerator::
+GenerateBuildingCode(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ printer->Print(variables_,
+ "if ($get_has_field_bit_from_local$) {\n"
+ " $set_has_field_bit_to_local$;\n"
+ "}\n");
+ }
+ printer->Print(variables_,
+ "result.$name$_ = $name$_;\n");
+}
+
+void ImmutableStringFieldGenerator::
+GenerateParsingCode(io::Printer* printer) const {
+ if (CheckUtf8(descriptor_)) {
+ printer->Print(variables_,
+ "String s = input.readStringRequireUtf8();\n"
+ "$set_has_field_bit_message$\n"
+ "$name$_ = s;\n");
+ } else {
+ printer->Print(variables_,
+ "com.google.protobuf.ByteString bs = input.readBytes();\n"
+ "$set_has_field_bit_message$\n"
+ "$name$_ = bs;\n");
+ }
+}
+
+void ImmutableStringFieldGenerator::
+GenerateParsingDoneCode(io::Printer* printer) const {
+ // noop for strings.
+}
+
+void ImmutableStringFieldGenerator::
+GenerateSerializationCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($is_field_present_message$) {\n"
+ " output.writeBytes($number$, get$capitalized_name$Bytes());\n"
+ "}\n");
+}
+
+void ImmutableStringFieldGenerator::
+GenerateSerializedSizeCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($is_field_present_message$) {\n"
+ " size += com.google.protobuf.CodedOutputStream\n"
+ " .computeBytesSize($number$, get$capitalized_name$Bytes());\n"
+ "}\n");
+}
+
+void ImmutableStringFieldGenerator::
+GenerateEqualsCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "result = result && get$capitalized_name$()\n"
+ " .equals(other.get$capitalized_name$());\n");
+}
+
+void ImmutableStringFieldGenerator::
+GenerateHashCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "hash = (37 * hash) + $constant_name$;\n");
+ printer->Print(variables_,
+ "hash = (53 * hash) + get$capitalized_name$().hashCode();\n");
+}
+
+string ImmutableStringFieldGenerator::GetBoxedType() const {
+ return "java.lang.String";
+}
+
+// ===================================================================
+
+ImmutableStringOneofFieldGenerator::
+ImmutableStringOneofFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : ImmutableStringFieldGenerator(
+ descriptor, messageBitIndex, builderBitIndex, context) {
+ const OneofGeneratorInfo* info =
+ context->GetOneofGeneratorInfo(descriptor->containing_oneof());
+ SetCommonOneofVariables(descriptor, info, &variables_);
+}
+
+ImmutableStringOneofFieldGenerator::
+~ImmutableStringOneofFieldGenerator() {}
+
+void ImmutableStringOneofFieldGenerator::
+GenerateMembers(io::Printer* printer) const {
+ PrintExtraFieldInfo(variables_, printer);
+
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $has_oneof_case_message$;\n"
+ "}\n");
+ }
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.lang.String get$capitalized_name$() {\n"
+ " java.lang.Object ref $default_init$;\n"
+ " if ($has_oneof_case_message$) {\n"
+ " ref = $oneof_name$_;\n"
+ " }\n"
+ " if (ref instanceof java.lang.String) {\n"
+ " return (java.lang.String) ref;\n"
+ " } else {\n"
+ " com.google.protobuf.ByteString bs = \n"
+ " (com.google.protobuf.ByteString) ref;\n"
+ " java.lang.String s = bs.toStringUtf8();\n");
+ if (CheckUtf8(descriptor_)) {
+ printer->Print(variables_,
+ " if ($has_oneof_case_message$) {\n"
+ " $oneof_name$_ = s;\n"
+ " }\n");
+ } else {
+ printer->Print(variables_,
+ " if (bs.isValidUtf8() && ($has_oneof_case_message$)) {\n"
+ " $oneof_name$_ = s;\n"
+ " }\n");
+ }
+ printer->Print(variables_,
+ " return s;\n"
+ " }\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+
+ printer->Print(variables_,
+ "$deprecation$public com.google.protobuf.ByteString\n"
+ " get$capitalized_name$Bytes() {\n"
+ " java.lang.Object ref $default_init$;\n"
+ " if ($has_oneof_case_message$) {\n"
+ " ref = $oneof_name$_;\n"
+ " }\n"
+ " if (ref instanceof java.lang.String) {\n"
+ " com.google.protobuf.ByteString b = \n"
+ " com.google.protobuf.ByteString.copyFromUtf8(\n"
+ " (java.lang.String) ref);\n"
+ " if ($has_oneof_case_message$) {\n"
+ " $oneof_name$_ = b;\n"
+ " }\n"
+ " return b;\n"
+ " } else {\n"
+ " return (com.google.protobuf.ByteString) ref;\n"
+ " }\n"
+ "}\n");
+}
+
+void ImmutableStringOneofFieldGenerator::
+GenerateBuilderMembers(io::Printer* printer) const {
+ if (SupportFieldPresence(descriptor_->file())) {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public boolean has$capitalized_name$() {\n"
+ " return $has_oneof_case_message$;\n"
+ "}\n");
+ }
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.lang.String get$capitalized_name$() {\n"
+ " java.lang.Object ref $default_init$;\n"
+ " if ($has_oneof_case_message$) {\n"
+ " ref = $oneof_name$_;\n"
+ " }\n"
+ " if (!(ref instanceof java.lang.String)) {\n"
+ " com.google.protobuf.ByteString bs =\n"
+ " (com.google.protobuf.ByteString) ref;\n"
+ " java.lang.String s = bs.toStringUtf8();\n"
+ " if ($has_oneof_case_message$) {\n");
+ if (CheckUtf8(descriptor_)) {
+ printer->Print(variables_,
+ " $oneof_name$_ = s;\n");
+ } else {
+ printer->Print(variables_,
+ " if (bs.isValidUtf8()) {\n"
+ " $oneof_name$_ = s;\n"
+ " }\n");
+ }
+ printer->Print(variables_,
+ " }\n"
+ " return s;\n"
+ " } else {\n"
+ " return (java.lang.String) ref;\n"
+ " }\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public com.google.protobuf.ByteString\n"
+ " get$capitalized_name$Bytes() {\n"
+ " java.lang.Object ref $default_init$;\n"
+ " if ($has_oneof_case_message$) {\n"
+ " ref = $oneof_name$_;\n"
+ " }\n"
+ " if (ref instanceof String) {\n"
+ " com.google.protobuf.ByteString b = \n"
+ " com.google.protobuf.ByteString.copyFromUtf8(\n"
+ " (java.lang.String) ref);\n"
+ " if ($has_oneof_case_message$) {\n"
+ " $oneof_name$_ = b;\n"
+ " }\n"
+ " return b;\n"
+ " } else {\n"
+ " return (com.google.protobuf.ByteString) ref;\n"
+ " }\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " java.lang.String value) {\n"
+ "$null_check$"
+ " $set_oneof_case_message$;\n"
+ " $oneof_name$_ = value;\n"
+ " $on_changed$\n"
+ " return this;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder clear$capitalized_name$() {\n"
+ " if ($has_oneof_case_message$) {\n"
+ " $clear_oneof_case_message$;\n"
+ " $oneof_name$_ = null;\n"
+ " $on_changed$\n"
+ " }\n"
+ " return this;\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder set$capitalized_name$Bytes(\n"
+ " com.google.protobuf.ByteString value) {\n"
+ "$null_check$");
+ if (CheckUtf8(descriptor_)) {
+ printer->Print(variables_,
+ " checkByteStringIsUtf8(value);\n");
+ }
+ printer->Print(variables_,
+ " $set_oneof_case_message$;\n"
+ " $oneof_name$_ = value;\n"
+ " $on_changed$\n"
+ " return this;\n"
+ "}\n");
+}
+
+void ImmutableStringOneofFieldGenerator::
+GenerateMergingCode(io::Printer* printer) const {
+ // Allow a slight breach of abstraction here in order to avoid forcing
+ // all string fields to Strings when copying fields from a Message.
+ printer->Print(variables_,
+ "$set_oneof_case_message$;\n"
+ "$oneof_name$_ = other.$oneof_name$_;\n"
+ "$on_changed$\n");
+}
+
+void ImmutableStringOneofFieldGenerator::
+GenerateBuildingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n"
+ " result.$oneof_name$_ = $oneof_name$_;\n"
+ "}\n");
+}
+
+void ImmutableStringOneofFieldGenerator::
+GenerateParsingCode(io::Printer* printer) const {
+ if (CheckUtf8(descriptor_)) {
+ printer->Print(variables_,
+ "String s = input.readStringRequireUtf8();\n"
+ "$set_oneof_case_message$;\n"
+ "$oneof_name$_ = s;\n}\n");
+ } else {
+ printer->Print(variables_,
+ "com.google.protobuf.ByteString bs = input.readBytes();\n"
+ "$set_oneof_case_message$;\n"
+ "$oneof_name$_ = bs;\n");
+ }
+}
+
+void ImmutableStringOneofFieldGenerator::
+GenerateSerializationCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n"
+ " output.writeBytes($number$, get$capitalized_name$Bytes());\n"
+ "}\n");
+}
+
+void ImmutableStringOneofFieldGenerator::
+GenerateSerializedSizeCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($has_oneof_case_message$) {\n"
+ " size += com.google.protobuf.CodedOutputStream\n"
+ " .computeBytesSize($number$, get$capitalized_name$Bytes());\n"
+ "}\n");
+}
+
+// ===================================================================
+
+RepeatedImmutableStringFieldGenerator::
+RepeatedImmutableStringFieldGenerator(const FieldDescriptor* descriptor,
+ int messageBitIndex,
+ int builderBitIndex,
+ Context* context)
+ : descriptor_(descriptor), messageBitIndex_(messageBitIndex),
+ builderBitIndex_(builderBitIndex), context_(context),
+ name_resolver_(context->GetNameResolver()) {
+ SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex,
+ context->GetFieldGeneratorInfo(descriptor),
+ name_resolver_, &variables_);
+}
+
+RepeatedImmutableStringFieldGenerator::
+~RepeatedImmutableStringFieldGenerator() {}
+
+int RepeatedImmutableStringFieldGenerator::GetNumBitsForMessage() const {
+ return 0;
+}
+
+int RepeatedImmutableStringFieldGenerator::GetNumBitsForBuilder() const {
+ return 1;
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateInterfaceMembers(io::Printer* printer) const {
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$com.google.protobuf.ProtocolStringList\n"
+ " get$capitalized_name$List();\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$int get$capitalized_name$Count();\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$java.lang.String get$capitalized_name$(int index);\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$com.google.protobuf.ByteString\n"
+ " get$capitalized_name$Bytes(int index);\n");
+}
+
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateMembers(io::Printer* printer) const {
+ printer->Print(variables_,
+ "private com.google.protobuf.LazyStringList $name$_;\n");
+ PrintExtraFieldInfo(variables_, printer);
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public com.google.protobuf.ProtocolStringList\n"
+ " get$capitalized_name$List() {\n"
+ " return $name$_;\n" // note: unmodifiable list
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public int get$capitalized_name$Count() {\n"
+ " return $name$_.size();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.lang.String get$capitalized_name$(int index) {\n"
+ " return $name$_.get(index);\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public com.google.protobuf.ByteString\n"
+ " get$capitalized_name$Bytes(int index) {\n"
+ " return $name$_.getByteString(index);\n"
+ "}\n");
+
+ if (descriptor_->options().packed() &&
+ HasGeneratedMethods(descriptor_->containing_type())) {
+ printer->Print(variables_,
+ "private int $name$MemoizedSerializedSize = -1;\n");
+ }
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateBuilderMembers(io::Printer* printer) const {
+ // One field is the list and the bit field keeps track of whether the
+ // list is immutable. If it's immutable, the invariant is that it must
+ // either an instance of Collections.emptyList() or it's an ArrayList
+ // wrapped in a Collections.unmodifiableList() wrapper and nobody else has
+ // a refererence to the underlying ArrayList. This invariant allows us to
+ // share instances of lists between protocol buffers avoiding expensive
+ // memory allocations. Note, immutable is a strong guarantee here -- not
+ // just that the list cannot be modified via the reference but that the
+ // list can never be modified.
+ printer->Print(variables_,
+ "private com.google.protobuf.LazyStringList $name$_ = $empty_list$;\n");
+
+ printer->Print(variables_,
+ "private void ensure$capitalized_name$IsMutable() {\n"
+ " if (!$get_mutable_bit_builder$) {\n"
+ " $name$_ = new com.google.protobuf.LazyStringArrayList($name$_);\n"
+ " $set_mutable_bit_builder$;\n"
+ " }\n"
+ "}\n");
+
+ // Note: We return an unmodifiable list because otherwise the caller
+ // could hold on to the returned list and modify it after the message
+ // has been built, thus mutating the message which is supposed to be
+ // immutable.
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public com.google.protobuf.ProtocolStringList\n"
+ " get$capitalized_name$List() {\n"
+ " return $name$_.getUnmodifiableView();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public int get$capitalized_name$Count() {\n"
+ " return $name$_.size();\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public java.lang.String get$capitalized_name$(int index) {\n"
+ " return $name$_.get(index);\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public com.google.protobuf.ByteString\n"
+ " get$capitalized_name$Bytes(int index) {\n"
+ " return $name$_.getByteString(index);\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder set$capitalized_name$(\n"
+ " int index, java.lang.String value) {\n"
+ "$null_check$"
+ " ensure$capitalized_name$IsMutable();\n"
+ " $name$_.set(index, value);\n"
+ " $on_changed$\n"
+ " return this;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder add$capitalized_name$(\n"
+ " java.lang.String value) {\n"
+ "$null_check$"
+ " ensure$capitalized_name$IsMutable();\n"
+ " $name$_.add(value);\n"
+ " $on_changed$\n"
+ " return this;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder addAll$capitalized_name$(\n"
+ " java.lang.Iterable<java.lang.String> values) {\n"
+ " ensure$capitalized_name$IsMutable();\n"
+ " com.google.protobuf.AbstractMessageLite.Builder.addAll(\n"
+ " values, $name$_);\n"
+ " $on_changed$\n"
+ " return this;\n"
+ "}\n");
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder clear$capitalized_name$() {\n"
+ " $name$_ = $empty_list$;\n"
+ " $clear_mutable_bit_builder$;\n"
+ " $on_changed$\n"
+ " return this;\n"
+ "}\n");
+
+ WriteFieldDocComment(printer, descriptor_);
+ printer->Print(variables_,
+ "$deprecation$public Builder add$capitalized_name$Bytes(\n"
+ " com.google.protobuf.ByteString value) {\n"
+ "$null_check$");
+ if (CheckUtf8(descriptor_)) {
+ printer->Print(variables_,
+ " checkByteStringIsUtf8(value);\n");
+ }
+ printer->Print(variables_,
+ " ensure$capitalized_name$IsMutable();\n"
+ " $name$_.add(value);\n"
+ " $on_changed$\n"
+ " return this;\n"
+ "}\n");
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateFieldBuilderInitializationCode(io::Printer* printer) const {
+ // noop for primitives
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateInitializationCode(io::Printer* printer) const {
+ printer->Print(variables_, "$name$_ = $empty_list$;\n");
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateBuilderClearCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "$name$_ = $empty_list$;\n"
+ "$clear_mutable_bit_builder$;\n");
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateMergingCode(io::Printer* printer) const {
+ // The code below does two optimizations:
+ // 1. If the other list is empty, there's nothing to do. This ensures we
+ // don't allocate a new array if we already have an immutable one.
+ // 2. If the other list is non-empty and our current list is empty, we can
+ // reuse the other list which is guaranteed to be immutable.
+ printer->Print(variables_,
+ "if (!other.$name$_.isEmpty()) {\n"
+ " if ($name$_.isEmpty()) {\n"
+ " $name$_ = other.$name$_;\n"
+ " $clear_mutable_bit_builder$;\n"
+ " } else {\n"
+ " ensure$capitalized_name$IsMutable();\n"
+ " $name$_.addAll(other.$name$_);\n"
+ " }\n"
+ " $on_changed$\n"
+ "}\n");
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateBuildingCode(io::Printer* printer) const {
+ // The code below ensures that the result has an immutable list. If our
+ // list is immutable, we can just reuse it. If not, we make it immutable.
+
+ printer->Print(variables_,
+ "if ($get_mutable_bit_builder$) {\n"
+ " $name$_ = $name$_.getUnmodifiableView();\n"
+ " $clear_mutable_bit_builder$;\n"
+ "}\n"
+ "result.$name$_ = $name$_;\n");
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateParsingCode(io::Printer* printer) const {
+ if (CheckUtf8(descriptor_)) {
+ printer->Print(variables_,
+ "String s = input.readStringRequireUtf8();\n");
+ } else {
+ printer->Print(variables_,
+ "com.google.protobuf.ByteString bs = input.readBytes();\n");
+ }
+ printer->Print(variables_,
+ "if (!$get_mutable_bit_parser$) {\n"
+ " $name$_ = new com.google.protobuf.LazyStringArrayList();\n"
+ " $set_mutable_bit_parser$;\n"
+ "}\n");
+ if (CheckUtf8(descriptor_)) {
+ printer->Print(variables_,
+ "$name$_.add(s);\n");
+ } else {
+ printer->Print(variables_,
+ "$name$_.add(bs);\n");
+ }
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateParsingCodeFromPacked(io::Printer* printer) const {
+ printer->Print(variables_,
+ "int length = input.readRawVarint32();\n"
+ "int limit = input.pushLimit(length);\n"
+ "if (!$get_mutable_bit_parser$ && input.getBytesUntilLimit() > 0) {\n"
+ " $name$_ = new com.google.protobuf.LazyStringArrayList();\n"
+ " $set_mutable_bit_parser$;\n"
+ "}\n"
+ "while (input.getBytesUntilLimit() > 0) {\n");
+ if (CheckUtf8(descriptor_)) {
+ printer->Print(variables_,
+ " String s = input.readStringRequireUtf8();\n");
+ } else {
+ printer->Print(variables_,
+ " String s = input.readString();\n");
+ }
+ printer->Print(variables_,
+ " $name$.add(s);\n");
+ printer->Print(variables_,
+ "}\n"
+ "input.popLimit(limit);\n");
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateParsingDoneCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if ($get_mutable_bit_parser$) {\n"
+ " $name$_ = $name$_.getUnmodifiableView();\n"
+ "}\n");
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateSerializationCode(io::Printer* printer) const {
+ if (descriptor_->options().packed()) {
+ printer->Print(variables_,
+ "if (get$capitalized_name$List().size() > 0) {\n"
+ " output.writeRawVarint32($tag$);\n"
+ " output.writeRawVarint32($name$MemoizedSerializedSize);\n"
+ "}\n"
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
+ " output.write$capitalized_type$NoTag($name$_.get(i));\n"
+ "}\n");
+ } else {
+ printer->Print(variables_,
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
+ " output.writeBytes($number$, $name$_.getByteString(i));\n"
+ "}\n");
+ }
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateSerializedSizeCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "{\n"
+ " int dataSize = 0;\n");
+ printer->Indent();
+
+ printer->Print(variables_,
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
+ " dataSize += com.google.protobuf.CodedOutputStream\n"
+ " .computeBytesSizeNoTag($name$_.getByteString(i));\n"
+ "}\n");
+
+ printer->Print(
+ "size += dataSize;\n");
+
+ if (descriptor_->options().packed()) {
+ printer->Print(variables_,
+ "if (!get$capitalized_name$List().isEmpty()) {\n"
+ " size += $tag_size$;\n"
+ " size += com.google.protobuf.CodedOutputStream\n"
+ " .computeInt32SizeNoTag(dataSize);\n"
+ "}\n");
+ } else {
+ printer->Print(variables_,
+ "size += $tag_size$ * get$capitalized_name$List().size();\n");
+ }
+
+ // cache the data size for packed fields.
+ if (descriptor_->options().packed()) {
+ printer->Print(variables_,
+ "$name$MemoizedSerializedSize = dataSize;\n");
+ }
+
+ printer->Outdent();
+ printer->Print("}\n");
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateEqualsCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "result = result && get$capitalized_name$List()\n"
+ " .equals(other.get$capitalized_name$List());\n");
+}
+
+void RepeatedImmutableStringFieldGenerator::
+GenerateHashCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (get$capitalized_name$Count() > 0) {\n"
+ " hash = (37 * hash) + $constant_name$;\n"
+ " hash = (53 * hash) + get$capitalized_name$List().hashCode();\n"
+ "}\n");
+}
+
+string RepeatedImmutableStringFieldGenerator::GetBoxedType() const {
+ return "String";
+}
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/compiler/java/java_string_field.h b/src/google/protobuf/compiler/java/java_string_field.h
new file mode 100644
index 0000000..1ea44de
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_string_field.h
@@ -0,0 +1,160 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: kenton@google.com (Kenton Varda)
+// Author: jonp@google.com (Jon Perlow)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_STRING_FIELD_H__
+#define GOOGLE_PROTOBUF_COMPILER_JAVA_STRING_FIELD_H__
+
+#include <map>
+#include <string>
+#include <google/protobuf/compiler/java/java_field.h>
+
+namespace google {
+namespace protobuf {
+ namespace compiler {
+ namespace java {
+ class Context; // context.h
+ class ClassNameResolver; // name_resolver.h
+ }
+ }
+}
+
+namespace protobuf {
+namespace compiler {
+namespace java {
+
+class ImmutableStringFieldGenerator : public ImmutableFieldGenerator {
+ public:
+ explicit ImmutableStringFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~ImmutableStringFieldGenerator();
+
+ // implements ImmutableFieldGenerator ---------------------------------------
+ int GetNumBitsForMessage() const;
+ int GetNumBitsForBuilder() const;
+ void GenerateInterfaceMembers(io::Printer* printer) const;
+ void GenerateMembers(io::Printer* printer) const;
+ void GenerateBuilderMembers(io::Printer* printer) const;
+ void GenerateInitializationCode(io::Printer* printer) const;
+ void GenerateBuilderClearCode(io::Printer* printer) const;
+ void GenerateMergingCode(io::Printer* printer) const;
+ void GenerateBuildingCode(io::Printer* printer) const;
+ void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateParsingDoneCode(io::Printer* printer) const;
+ void GenerateSerializationCode(io::Printer* printer) const;
+ void GenerateSerializedSizeCode(io::Printer* printer) const;
+ void GenerateFieldBuilderInitializationCode(io::Printer* printer) const;
+ void GenerateEqualsCode(io::Printer* printer) const;
+ void GenerateHashCode(io::Printer* printer) const;
+
+ string GetBoxedType() const;
+
+ protected:
+ const FieldDescriptor* descriptor_;
+ map<string, string> variables_;
+ const int messageBitIndex_;
+ const int builderBitIndex_;
+ Context* context_;
+ ClassNameResolver* name_resolver_;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableStringFieldGenerator);
+};
+
+class ImmutableStringOneofFieldGenerator
+ : public ImmutableStringFieldGenerator {
+ public:
+ ImmutableStringOneofFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~ImmutableStringOneofFieldGenerator();
+
+ private:
+ void GenerateMembers(io::Printer* printer) const;
+ void GenerateBuilderMembers(io::Printer* printer) const;
+ void GenerateMergingCode(io::Printer* printer) const;
+ void GenerateBuildingCode(io::Printer* printer) const;
+ void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateSerializationCode(io::Printer* printer) const;
+ void GenerateSerializedSizeCode(io::Printer* printer) const;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableStringOneofFieldGenerator);
+};
+
+class RepeatedImmutableStringFieldGenerator : public ImmutableFieldGenerator {
+ public:
+ explicit RepeatedImmutableStringFieldGenerator(
+ const FieldDescriptor* descriptor, int messageBitIndex,
+ int builderBitIndex, Context* context);
+ ~RepeatedImmutableStringFieldGenerator();
+
+ // implements ImmutableFieldGenerator ---------------------------------------
+ int GetNumBitsForMessage() const;
+ int GetNumBitsForBuilder() const;
+ void GenerateInterfaceMembers(io::Printer* printer) const;
+ void GenerateMembers(io::Printer* printer) const;
+ void GenerateBuilderMembers(io::Printer* printer) const;
+ void GenerateInitializationCode(io::Printer* printer) const;
+ void GenerateBuilderClearCode(io::Printer* printer) const;
+ void GenerateMergingCode(io::Printer* printer) const;
+ void GenerateBuildingCode(io::Printer* printer) const;
+ void GenerateParsingCode(io::Printer* printer) const;
+ void GenerateParsingCodeFromPacked(io::Printer* printer) const;
+ void GenerateParsingDoneCode(io::Printer* printer) const;
+ void GenerateSerializationCode(io::Printer* printer) const;
+ void GenerateSerializedSizeCode(io::Printer* printer) const;
+ void GenerateFieldBuilderInitializationCode(io::Printer* printer) const;
+ void GenerateEqualsCode(io::Printer* printer) const;
+ void GenerateHashCode(io::Printer* printer) const;
+
+ string GetBoxedType() const;
+
+ private:
+ const FieldDescriptor* descriptor_;
+ map<string, string> variables_;
+ const int messageBitIndex_;
+ const int builderBitIndex_;
+ Context* context_;
+ ClassNameResolver* name_resolver_;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableStringFieldGenerator);
+};
+
+} // namespace java
+} // namespace compiler
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_STRING_FIELD_H__
diff --git a/src/google/protobuf/compiler/javamicro/javamicro_file.h b/src/google/protobuf/compiler/javamicro/javamicro_file.h
index 430172a..1804abf 100644
--- a/src/google/protobuf/compiler/javamicro/javamicro_file.h
+++ b/src/google/protobuf/compiler/javamicro/javamicro_file.h
@@ -38,6 +38,7 @@
#include <string>
#include <vector>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/javamicro/javamicro_params.h>
namespace google {
@@ -46,9 +47,6 @@ namespace protobuf {
namespace io {
class Printer; // printer.h
}
- namespace compiler {
- class OutputDirectory; // code_generator.h
- }
}
namespace protobuf {
diff --git a/src/google/protobuf/compiler/javanano/javanano_enum.cc b/src/google/protobuf/compiler/javanano/javanano_enum.cc
index f934b05..c6e8dfe 100644
--- a/src/google/protobuf/compiler/javanano/javanano_enum.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_enum.cc
@@ -73,13 +73,45 @@ void EnumGenerator::Generate(io::Printer* printer) {
"// enum $classname$\n",
"classname", descriptor_->name());
+ const string classname = RenameJavaKeywords(descriptor_->name());
+
// Start of container interface
+ // If generating intdefs, we use the container interface as the intdef if
+ // present. Otherwise, we just make an empty @interface parallel to the
+ // constants.
+ bool use_intdef = params_.generate_intdefs();
bool use_shell_class = params_.java_enum_style();
- if (use_shell_class) {
- printer->Print(
- "public interface $classname$ {\n",
- "classname", RenameJavaKeywords(descriptor_->name()));
+ if (use_intdef) {
+ // @IntDef annotation so tools can enforce correctness
+ // Annotations will be discarded by the compiler
+ printer->Print("@java.lang.annotation.Retention("
+ "java.lang.annotation.RetentionPolicy.SOURCE)\n"
+ "@android.support.annotation.IntDef({\n");
printer->Indent();
+ for (int i = 0; i < canonical_values_.size(); i++) {
+ const string constant_name =
+ RenameJavaKeywords(canonical_values_[i]->name());
+ if (use_shell_class) {
+ printer->Print("$classname$.$name$,\n",
+ "classname", classname,
+ "name", constant_name);
+ } else {
+ printer->Print("$name$,\n", "name", constant_name);
+ }
+ }
+ printer->Outdent();
+ printer->Print("})\n");
+ }
+ if (use_shell_class || use_intdef) {
+ printer->Print(
+ "public $at_for_intdef$interface $classname$ {\n",
+ "classname", classname,
+ "at_for_intdef", use_intdef ? "@" : "");
+ if (use_shell_class) {
+ printer->Indent();
+ } else {
+ printer->Print("}\n\n");
+ }
}
// Canonical values
diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
index 6a18834..7666db3 100644
--- a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
@@ -59,6 +59,7 @@ void SetEnumVariables(const Params& params,
RenameJavaKeywords(UnderscoresToCapitalizedCamelCase(descriptor));
(*variables)["number"] = SimpleItoa(descriptor->number());
if (params.use_reference_types_for_primitives()
+ && !params.reftypes_primitive_enums()
&& !descriptor->is_repeated()) {
(*variables)["type"] = "java.lang.Integer";
(*variables)["default"] = "null";
@@ -75,6 +76,10 @@ void SetEnumVariables(const Params& params,
internal::WireFormatLite::MakeTag(descriptor->number(),
internal::WireFormat::WireTypeForFieldType(descriptor->type())));
(*variables)["message_name"] = descriptor->containing_type()->name();
+ const EnumDescriptor* enum_type = descriptor->enum_type();
+ (*variables)["message_type_intdef"] = "@"
+ + ToJavaName(params, enum_type->name(), true,
+ enum_type->containing_type(), enum_type->file());
}
void LoadEnumValues(const Params& params,
@@ -115,8 +120,10 @@ EnumFieldGenerator::~EnumFieldGenerator() {}
void EnumFieldGenerator::
GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const {
- printer->Print(variables_,
- "public $type$ $name$;\n");
+ if (params_.generate_intdefs()) {
+ printer->Print(variables_, "$message_type_intdef$\n");
+ }
+ printer->Print(variables_, "public $type$ $name$;\n");
if (params_.generate_has()) {
printer->Print(variables_,
@@ -197,7 +204,8 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
}
void EnumFieldGenerator::GenerateEqualsCode(io::Printer* printer) const {
- if (params_.use_reference_types_for_primitives()) {
+ if (params_.use_reference_types_for_primitives()
+ && !params_.reftypes_primitive_enums()) {
printer->Print(variables_,
"if (this.$name$ == null) {\n"
" if (other.$name$ != null) {\n"
@@ -228,7 +236,8 @@ void EnumFieldGenerator::GenerateEqualsCode(io::Printer* printer) const {
void EnumFieldGenerator::GenerateHashCodeCode(io::Printer* printer) const {
printer->Print(
"result = 31 * result + ");
- if (params_.use_reference_types_for_primitives()) {
+ if (params_.use_reference_types_for_primitives()
+ && !params_.reftypes_primitive_enums()) {
printer->Print(variables_,
"(this.$name$ == null ? 0 : this.$name$)");
} else {
@@ -253,12 +262,22 @@ AccessorEnumFieldGenerator::~AccessorEnumFieldGenerator() {}
void AccessorEnumFieldGenerator::
GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const {
+ printer->Print(variables_, "private int $name$_;\n");
+ if (params_.generate_intdefs()) {
+ printer->Print(variables_, "$message_type_intdef$\n");
+ }
printer->Print(variables_,
- "private int $name$_;\n"
"public int get$capitalized_name$() {\n"
" return $name$_;\n"
"}\n"
- "public $message_name$ set$capitalized_name$(int value) {\n"
+ "public $message_name$ set$capitalized_name$(");
+ if (params_.generate_intdefs()) {
+ printer->Print(variables_,
+ "\n"
+ " $message_type_intdef$ ");
+ }
+ printer->Print(variables_,
+ "int value) {\n"
" $name$_ = value;\n"
" $set_has$;\n"
" return this;\n"
@@ -496,6 +515,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
}
void RepeatedEnumFieldGenerator::
+GenerateFixClonedCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (this.$name$ != null && this.$name$.length > 0) {\n"
+ " cloned.$name$ = this.$name$.clone();\n"
+ "}\n");
+}
+
+void RepeatedEnumFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_,
"if (!com.google.protobuf.nano.InternalNano.equals(\n"
diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.h b/src/google/protobuf/compiler/javanano/javanano_enum_field.h
index 55bf635..8622463 100644
--- a/src/google/protobuf/compiler/javanano/javanano_enum_field.h
+++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.h
@@ -106,6 +106,7 @@ class RepeatedEnumFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const;
+ void GenerateFixClonedCode(io::Printer* printer) const;
private:
void GenerateRepeatedDataSizeCode(io::Printer* printer) const;
diff --git a/src/google/protobuf/compiler/javanano/javanano_field.h b/src/google/protobuf/compiler/javanano/javanano_field.h
index 61fc621..e015259 100644
--- a/src/google/protobuf/compiler/javanano/javanano_field.h
+++ b/src/google/protobuf/compiler/javanano/javanano_field.h
@@ -82,6 +82,7 @@ class FieldGenerator {
virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0;
virtual void GenerateEqualsCode(io::Printer* printer) const = 0;
virtual void GenerateHashCodeCode(io::Printer* printer) const = 0;
+ virtual void GenerateFixClonedCode(io::Printer* printer) const {}
protected:
const Params& params_;
diff --git a/src/google/protobuf/compiler/javanano/javanano_file.h b/src/google/protobuf/compiler/javanano/javanano_file.h
index 9472721..cfcd7c6 100644
--- a/src/google/protobuf/compiler/javanano/javanano_file.h
+++ b/src/google/protobuf/compiler/javanano/javanano_file.h
@@ -38,6 +38,7 @@
#include <string>
#include <vector>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/javanano/javanano_params.h>
namespace google {
@@ -46,9 +47,6 @@ namespace protobuf {
namespace io {
class Printer; // printer.h
}
- namespace compiler {
- class OutputDirectory; // code_generator.h
- }
}
namespace protobuf {
diff --git a/src/google/protobuf/compiler/javanano/javanano_generator.cc b/src/google/protobuf/compiler/javanano/javanano_generator.cc
index 61ef2ca..99ebe12 100644
--- a/src/google/protobuf/compiler/javanano/javanano_generator.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_generator.cc
@@ -139,13 +139,25 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file,
params.set_java_enum_style(option_value == "java");
} else if (option_name == "optional_field_style") {
params.set_optional_field_accessors(option_value == "accessors");
- params.set_use_reference_types_for_primitives(option_value == "reftypes");
+ params.set_use_reference_types_for_primitives(option_value == "reftypes"
+ || option_value == "reftypes_compat_mode");
+ params.set_reftypes_primitive_enums(
+ option_value == "reftypes_compat_mode");
+ if (option_value == "reftypes_compat_mode") {
+ params.set_generate_clear(false);
+ }
} else if (option_name == "generate_equals") {
params.set_generate_equals(option_value == "true");
} else if (option_name == "ignore_services") {
params.set_ignore_services(option_value == "true");
} else if (option_name == "parcelable_messages") {
params.set_parcelable_messages(option_value == "true");
+ } else if (option_name == "generate_clone") {
+ params.set_generate_clone(option_value == "true");
+ } else if (option_name == "generate_intdefs") {
+ params.set_generate_intdefs(option_value == "true");
+ } else if (option_name == "generate_clear") {
+ params.set_generate_clear(option_value == "true");
} else {
*error = "Ignore unknown javanano generator option: " + option_name;
}
diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.cc b/src/google/protobuf/compiler/javanano/javanano_helpers.cc
index e8326a4..2149418 100644
--- a/src/google/protobuf/compiler/javanano/javanano_helpers.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_helpers.cc
@@ -336,14 +336,14 @@ string PrimitiveTypeName(JavaType type) {
case JAVATYPE_STRING : return "java.lang.String";
case JAVATYPE_BYTES : return "byte[]";
case JAVATYPE_ENUM : return "int";
- case JAVATYPE_MESSAGE: return NULL;
+ case JAVATYPE_MESSAGE: return "";
// No default because we want the compiler to complain if any new
// JavaTypes are added.
}
GOOGLE_LOG(FATAL) << "Can't get here.";
- return NULL;
+ return "";
}
string BoxedPrimitiveTypeName(JavaType type) {
@@ -356,14 +356,14 @@ string BoxedPrimitiveTypeName(JavaType type) {
case JAVATYPE_STRING : return "java.lang.String";
case JAVATYPE_BYTES : return "byte[]";
case JAVATYPE_ENUM : return "java.lang.Integer";
- case JAVATYPE_MESSAGE: return NULL;
+ case JAVATYPE_MESSAGE: return "";
// No default because we want the compiler to complain if any new
// JavaTypes are added.
}
GOOGLE_LOG(FATAL) << "Can't get here.";
- return NULL;
+ return "";
}
string EmptyArrayName(const Params& params, const FieldDescriptor* field) {
@@ -392,6 +392,10 @@ string DefaultValue(const Params& params, const FieldDescriptor* field) {
}
if (params.use_reference_types_for_primitives()) {
+ if (params.reftypes_primitive_enums()
+ && field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+ return "Integer.MIN_VALUE";
+ }
return "null";
}
diff --git a/src/google/protobuf/compiler/javanano/javanano_message.cc b/src/google/protobuf/compiler/javanano/javanano_message.cc
index 7a2b4a0..758c9e8 100644
--- a/src/google/protobuf/compiler/javanano/javanano_message.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_message.cc
@@ -134,21 +134,37 @@ void MessageGenerator::Generate(io::Printer* printer) {
}
if (params_.store_unknown_fields() && params_.parcelable_messages()) {
printer->Print(
- " com.google.protobuf.nano.android.ParcelableExtendableMessageNano<$classname$> {\n",
+ " com.google.protobuf.nano.android.ParcelableExtendableMessageNano<$classname$>",
"classname", descriptor_->name());
} else if (params_.store_unknown_fields()) {
printer->Print(
- " com.google.protobuf.nano.ExtendableMessageNano<$classname$> {\n",
+ " com.google.protobuf.nano.ExtendableMessageNano<$classname$>",
"classname", descriptor_->name());
} else if (params_.parcelable_messages()) {
printer->Print(
- " com.google.protobuf.nano.android.ParcelableMessageNano {\n");
+ " com.google.protobuf.nano.android.ParcelableMessageNano");
} else {
printer->Print(
- " com.google.protobuf.nano.MessageNano {\n");
+ " com.google.protobuf.nano.MessageNano");
+ }
+ if (params_.generate_clone()) {
+ printer->Print(" implements java.lang.Cloneable {\n");
+ } else {
+ printer->Print(" {\n");
}
printer->Indent();
+ if (params_.parcelable_messages()) {
+ printer->Print(
+ "\n"
+ "// Used by Parcelable\n"
+ "@SuppressWarnings({\"unused\"})\n"
+ "public static final android.os.Parcelable.Creator<$classname$> CREATOR =\n"
+ " new com.google.protobuf.nano.android.ParcelableMessageNanoCreator<\n"
+ " $classname$>($classname$.class);\n",
+ "classname", descriptor_->name());
+ }
+
// Nested types and extensions
for (int i = 0; i < descriptor_->extension_count(); i++) {
ExtensionGenerator(descriptor_->extension(i), params_).Generate(printer);
@@ -245,22 +261,34 @@ void MessageGenerator::Generate(io::Printer* printer) {
" _classInitialized = true;\n"
" }\n"
" }\n"
- " }\n"
- " clear();\n"
- "}\n");
+ " }\n");
+ if (params_.generate_clear()) {
+ printer->Print(" clear();\n");
+ }
+ printer->Print("}\n");
} else {
printer->Print(
"\n"
- "public $classname$() {\n"
- " clear();\n"
- "}\n",
+ "public $classname$() {\n",
"classname", descriptor_->name());
+ if (params_.generate_clear()) {
+ printer->Print(" clear();\n");
+ } else {
+ printer->Indent();
+ GenerateFieldInitializers(printer);
+ printer->Outdent();
+ }
+ printer->Print("}\n");
}
// Other methods in this class
GenerateClear(printer);
+ if (params_.generate_clone()) {
+ GenerateClone(printer);
+ }
+
if (params_.generate_equals()) {
GenerateEquals(printer);
GenerateHashCode(printer);
@@ -440,12 +468,24 @@ void MessageGenerator::GenerateSerializeOneField(
}
void MessageGenerator::GenerateClear(io::Printer* printer) {
+ if (!params_.generate_clear()) {
+ return;
+ }
printer->Print(
"\n"
"public $classname$ clear() {\n",
"classname", descriptor_->name());
printer->Indent();
+ GenerateFieldInitializers(printer);
+
+ printer->Outdent();
+ printer->Print(
+ " return this;\n"
+ "}\n");
+}
+
+void MessageGenerator::GenerateFieldInitializers(io::Printer* printer) {
// Clear bit fields.
int totalInts = (field_generators_.total_bits() + 31) / 32;
for (int i = 0; i < totalInts; i++) {
@@ -463,12 +503,34 @@ void MessageGenerator::GenerateClear(io::Printer* printer) {
if (params_.store_unknown_fields()) {
printer->Print("unknownFieldData = null;\n");
}
+ printer->Print("cachedSize = -1;\n");
+}
+
+void MessageGenerator::GenerateClone(io::Printer* printer) {
+ printer->Print(
+ "@Override\n"
+ "public $classname$ clone() {\n",
+ "classname", descriptor_->name());
+ printer->Indent();
+
+ printer->Print(
+ "$classname$ cloned;\n"
+ "try {\n"
+ " cloned = ($classname$) super.clone();\n"
+ "} catch (java.lang.CloneNotSupportedException e) {\n"
+ " throw new java.lang.AssertionError(e);\n"
+ "}\n",
+ "classname", descriptor_->name());
+
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ field_generators_.get(descriptor_->field(i)).GenerateFixClonedCode(printer);
+ }
printer->Outdent();
printer->Print(
- " cachedSize = -1;\n"
- " return this;\n"
- "}\n");
+ " return cloned;\n"
+ "}\n"
+ "\n");
}
void MessageGenerator::GenerateEquals(io::Printer* printer) {
@@ -501,7 +563,11 @@ void MessageGenerator::GenerateEquals(io::Printer* printer) {
if (params_.store_unknown_fields()) {
printer->Print(
- "return unknownFieldDataEquals(other);\n");
+ "if (unknownFieldData == null || unknownFieldData.isEmpty()) {\n"
+ " return other.unknownFieldData == null || other.unknownFieldData.isEmpty();\n"
+ "} else {\n"
+ " return unknownFieldData.equals(other.unknownFieldData);\n"
+ "}");
} else {
printer->Print(
"return true;\n");
@@ -523,6 +589,7 @@ void MessageGenerator::GenerateHashCode(io::Printer* printer) {
printer->Indent();
printer->Print("int result = 17;\n");
+ printer->Print("result = 31 * result + getClass().getName().hashCode();\n");
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
field_generators_.get(field).GenerateHashCodeCode(printer);
@@ -530,7 +597,9 @@ void MessageGenerator::GenerateHashCode(io::Printer* printer) {
if (params_.store_unknown_fields()) {
printer->Print(
- "result = 31 * result + unknownFieldDataHashCode();\n");
+ "result = 31 * result + \n"
+ " (unknownFieldData == null || unknownFieldData.isEmpty() ? 0 : \n"
+ " unknownFieldData.hashCode());\n");
}
printer->Print("return result;\n");
diff --git a/src/google/protobuf/compiler/javanano/javanano_message.h b/src/google/protobuf/compiler/javanano/javanano_message.h
index f87f84f..1bb6557 100644
--- a/src/google/protobuf/compiler/javanano/javanano_message.h
+++ b/src/google/protobuf/compiler/javanano/javanano_message.h
@@ -77,8 +77,10 @@ class MessageGenerator {
const FieldDescriptor* field);
void GenerateClear(io::Printer* printer);
+ void GenerateFieldInitializers(io::Printer* printer);
void GenerateEquals(io::Printer* printer);
void GenerateHashCode(io::Printer* printer);
+ void GenerateClone(io::Printer* printer);
const Params& params_;
const Descriptor* descriptor_;
diff --git a/src/google/protobuf/compiler/javanano/javanano_message_field.cc b/src/google/protobuf/compiler/javanano/javanano_message_field.cc
index a46081d..4752644 100644
--- a/src/google/protobuf/compiler/javanano/javanano_message_field.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_message_field.cc
@@ -127,6 +127,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
}
void MessageFieldGenerator::
+GenerateFixClonedCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (this.$name$ != null) {\n"
+ " cloned.$name$ = this.$name$.clone();\n"
+ "}\n");
+}
+
+void MessageFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$name$ == null) { \n"
@@ -238,6 +246,19 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
}
void RepeatedMessageFieldGenerator::
+GenerateFixClonedCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (this.$name$ != null && this.$name$.length > 0) {\n"
+ " cloned.$name$ = new $type$[this.$name$.length];\n"
+ " for (int i = 0; i < this.$name$.length; i++) {\n"
+ " if (this.$name$[i] != null) {\n"
+ " cloned.$name$[i] = this.$name$[i].clone();\n"
+ " }\n"
+ " }\n"
+ "}\n");
+}
+
+void RepeatedMessageFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_,
"if (!com.google.protobuf.nano.InternalNano.equals(\n"
diff --git a/src/google/protobuf/compiler/javanano/javanano_message_field.h b/src/google/protobuf/compiler/javanano/javanano_message_field.h
index e94a37b..1edff25 100644
--- a/src/google/protobuf/compiler/javanano/javanano_message_field.h
+++ b/src/google/protobuf/compiler/javanano/javanano_message_field.h
@@ -58,6 +58,7 @@ class MessageFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const;
+ void GenerateFixClonedCode(io::Printer* printer) const;
private:
const FieldDescriptor* descriptor_;
@@ -80,6 +81,7 @@ class RepeatedMessageFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const;
+ void GenerateFixClonedCode(io::Printer* printer) const;
private:
const FieldDescriptor* descriptor_;
diff --git a/src/google/protobuf/compiler/javanano/javanano_params.h b/src/google/protobuf/compiler/javanano/javanano_params.h
index d0626a2..e3b4bb9 100644
--- a/src/google/protobuf/compiler/javanano/javanano_params.h
+++ b/src/google/protobuf/compiler/javanano/javanano_params.h
@@ -64,6 +64,10 @@ class Params {
bool generate_equals_;
bool ignore_services_;
bool parcelable_messages_;
+ bool reftypes_primitive_enums_;
+ bool generate_clear_;
+ bool generate_clone_;
+ bool generate_intdefs_;
public:
Params(const string & base_name) :
@@ -77,7 +81,11 @@ class Params {
use_reference_types_for_primitives_(false),
generate_equals_(false),
ignore_services_(false),
- parcelable_messages_(false) {
+ parcelable_messages_(false),
+ reftypes_primitive_enums_(false),
+ generate_clear_(true),
+ generate_clone_(false),
+ generate_intdefs_(false) {
}
const string& base_name() const {
@@ -213,6 +221,34 @@ class Params {
bool parcelable_messages() const {
return parcelable_messages_;
}
+
+ void set_reftypes_primitive_enums(bool value) {
+ reftypes_primitive_enums_ = value;
+ }
+ bool reftypes_primitive_enums() const {
+ return reftypes_primitive_enums_;
+ }
+
+ void set_generate_clear(bool value) {
+ generate_clear_ = value;
+ }
+ bool generate_clear() const {
+ return generate_clear_;
+ }
+
+ void set_generate_clone(bool value) {
+ generate_clone_ = value;
+ }
+ bool generate_clone() const {
+ return generate_clone_;
+ }
+
+ void set_generate_intdefs(bool value) {
+ generate_intdefs_ = value;
+ }
+ bool generate_intdefs() const {
+ return generate_intdefs_;
+ }
};
} // namespace javanano
diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
index a3bc3a8..bda52c6 100644
--- a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
@@ -385,6 +385,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
}
}
+void RepeatedPrimitiveFieldGenerator::
+GenerateFixClonedCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (this.$name$ != null && this.$name$.length > 0) {\n"
+ " cloned.$name$ = this.$name$.clone();\n"
+ "}\n");
+}
+
void PrimitiveFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const {
// We define equality as serialized form equality. If generate_has(),
diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.h b/src/google/protobuf/compiler/javanano/javanano_primitive_field.h
index c04a19b..5ace0de 100644
--- a/src/google/protobuf/compiler/javanano/javanano_primitive_field.h
+++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.h
@@ -108,6 +108,7 @@ class RepeatedPrimitiveFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const;
+ void GenerateFixClonedCode(io::Printer* printer) const;
private:
void GenerateRepeatedDataSizeCode(io::Printer* printer) const;
diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc
index 8bd71b1..1fc60ce 100644
--- a/src/google/protobuf/compiler/main.cc
+++ b/src/google/protobuf/compiler/main.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -45,7 +45,7 @@ int main(int argc, char* argv[]) {
// Proto2 C++
google::protobuf::compiler::cpp::CppGenerator cpp_generator;
- cli.RegisterGenerator("--cpp_out", &cpp_generator,
+ cli.RegisterGenerator("--cpp_out", "--cpp_opt", &cpp_generator,
"Generate C++ header and source.");
// Proto2 Java
diff --git a/src/google/protobuf/compiler/mock_code_generator.cc b/src/google/protobuf/compiler/mock_code_generator.cc
index 83d5a4e..916b0cc 100644
--- a/src/google/protobuf/compiler/mock_code_generator.cc
+++ b/src/google/protobuf/compiler/mock_code_generator.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -32,19 +32,32 @@
#include <google/protobuf/compiler/mock_code_generator.h>
+#include <memory>
+
#include <google/protobuf/testing/file.h>
+#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
#include <gtest/gtest.h>
-#include <google/protobuf/stubs/stl_util-inl.h>
+#include <google/protobuf/stubs/stl_util.h>
namespace google {
namespace protobuf {
namespace compiler {
+// Returns the list of the names of files in all_files in the form of a
+// comma-separated string.
+string CommaSeparatedList(const vector<const FileDescriptor*> all_files) {
+ vector<string> names;
+ for (int i = 0; i < all_files.size(); i++) {
+ names.push_back(all_files[i]->name());
+ }
+ return Join(names, ",");
+}
+
static const char* kFirstInsertionPointName = "first_mock_insertion_point";
static const char* kSecondInsertionPointName = "second_mock_insertion_point";
static const char* kFirstInsertionPoint =
@@ -63,13 +76,14 @@ void MockCodeGenerator::ExpectGenerated(
const string& insertions,
const string& file,
const string& first_message_name,
+ const string& first_parsed_file_name,
const string& output_directory) {
string content;
- ASSERT_TRUE(File::ReadFileToString(
- output_directory + "/" + GetOutputFileName(name, file), &content));
+ GOOGLE_CHECK_OK(
+ File::GetContents(output_directory + "/" + GetOutputFileName(name, file),
+ &content, true));
- vector<string> lines;
- SplitStringUsing(content, "\n", &lines);
+ vector<string> lines = Split(content, "\n", true);
while (!lines.empty() && lines.back().empty()) {
lines.pop_back();
@@ -83,8 +97,9 @@ void MockCodeGenerator::ExpectGenerated(
SplitStringUsing(insertions, ",", &insertion_list);
}
- ASSERT_EQ(lines.size(), 3 + insertion_list.size() * 2);
- EXPECT_EQ(GetOutputFileContent(name, parameter, file, first_message_name),
+ EXPECT_EQ(lines.size(), 3 + insertion_list.size() * 2);
+ EXPECT_EQ(GetOutputFileContent(name, parameter, file,
+ first_parsed_file_name, first_message_name),
lines[0]);
EXPECT_EQ(kFirstInsertionPoint, lines[1 + insertion_list.size()]);
@@ -92,12 +107,12 @@ void MockCodeGenerator::ExpectGenerated(
for (int i = 0; i < insertion_list.size(); i++) {
EXPECT_EQ(GetOutputFileContent(insertion_list[i], "first_insert",
- file, first_message_name),
+ file, file, first_message_name),
lines[1 + i]);
// Second insertion point is indented, so the inserted text should
// automatically be indented too.
EXPECT_EQ(" " + GetOutputFileContent(insertion_list[i], "second_insert",
- file, first_message_name),
+ file, file, first_message_name),
lines[2 + insertion_list.size() + i]);
}
}
@@ -105,7 +120,7 @@ void MockCodeGenerator::ExpectGenerated(
bool MockCodeGenerator::Generate(
const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* context,
string* error) const {
for (int i = 0; i < file->message_type_count(); i++) {
if (HasPrefixString(file->message_type(i)->name(), "MockCodeGenerator_")) {
@@ -120,6 +135,15 @@ bool MockCodeGenerator::Generate(
} else if (command == "Abort") {
cerr << "Saw message type MockCodeGenerator_Abort." << endl;
abort();
+ } else if (command == "HasSourceCodeInfo") {
+ FileDescriptorProto file_descriptor_proto;
+ file->CopySourceCodeInfoTo(&file_descriptor_proto);
+ bool has_source_code_info =
+ file_descriptor_proto.has_source_code_info() &&
+ file_descriptor_proto.source_code_info().location_size() > 0;
+ cerr << "Saw message type MockCodeGenerator_HasSourceCodeInfo: "
+ << has_source_code_info << "." << endl;
+ abort();
} else {
GOOGLE_LOG(FATAL) << "Unknown MockCodeGenerator command: " << command;
}
@@ -133,12 +157,11 @@ bool MockCodeGenerator::Generate(
for (int i = 0; i < insert_into.size(); i++) {
{
- scoped_ptr<io::ZeroCopyOutputStream> output(
- output_directory->OpenForInsert(
- GetOutputFileName(insert_into[i], file),
- kFirstInsertionPointName));
+ scoped_ptr<io::ZeroCopyOutputStream> output(context->OpenForInsert(
+ GetOutputFileName(insert_into[i], file), kFirstInsertionPointName));
io::Printer printer(output.get(), '$');
- printer.PrintRaw(GetOutputFileContent(name_, "first_insert", file));
+ printer.PrintRaw(GetOutputFileContent(name_, "first_insert",
+ file, context));
if (printer.failed()) {
*error = "MockCodeGenerator detected write error.";
return false;
@@ -147,11 +170,11 @@ bool MockCodeGenerator::Generate(
{
scoped_ptr<io::ZeroCopyOutputStream> output(
- output_directory->OpenForInsert(
- GetOutputFileName(insert_into[i], file),
- kSecondInsertionPointName));
+ context->OpenForInsert(GetOutputFileName(insert_into[i], file),
+ kSecondInsertionPointName));
io::Printer printer(output.get(), '$');
- printer.PrintRaw(GetOutputFileContent(name_, "second_insert", file));
+ printer.PrintRaw(GetOutputFileContent(name_, "second_insert",
+ file, context));
if (printer.failed()) {
*error = "MockCodeGenerator detected write error.";
return false;
@@ -160,10 +183,11 @@ bool MockCodeGenerator::Generate(
}
} else {
scoped_ptr<io::ZeroCopyOutputStream> output(
- output_directory->Open(GetOutputFileName(name_, file)));
+ context->Open(GetOutputFileName(name_, file)));
io::Printer printer(output.get(), '$');
- printer.PrintRaw(GetOutputFileContent(name_, parameter, file));
+ printer.PrintRaw(GetOutputFileContent(name_, parameter,
+ file, context));
printer.PrintRaw(kFirstInsertionPoint);
printer.PrintRaw(kSecondInsertionPoint);
@@ -186,11 +210,16 @@ string MockCodeGenerator::GetOutputFileName(const string& generator_name,
return file + ".MockCodeGenerator." + generator_name;
}
-string MockCodeGenerator::GetOutputFileContent(const string& generator_name,
- const string& parameter,
- const FileDescriptor* file) {
+string MockCodeGenerator::GetOutputFileContent(
+ const string& generator_name,
+ const string& parameter,
+ const FileDescriptor* file,
+ GeneratorContext *context) {
+ vector<const FileDescriptor*> all_files;
+ context->ListParsedFiles(&all_files);
return GetOutputFileContent(
generator_name, parameter, file->name(),
+ CommaSeparatedList(all_files),
file->message_type_count() > 0 ?
file->message_type(0)->name() : "(none)");
}
@@ -199,9 +228,11 @@ string MockCodeGenerator::GetOutputFileContent(
const string& generator_name,
const string& parameter,
const string& file,
+ const string& parsed_file_list,
const string& first_message_name) {
- return strings::Substitute("$0: $1, $2, $3\n",
- generator_name, parameter, file, first_message_name);
+ return strings::Substitute("$0: $1, $2, $3, $4\n",
+ generator_name, parameter, file,
+ first_message_name, parsed_file_list);
}
} // namespace compiler
diff --git a/src/google/protobuf/compiler/mock_code_generator.h b/src/google/protobuf/compiler/mock_code_generator.h
index 01d69dd..8c8348d 100644
--- a/src/google/protobuf/compiler/mock_code_generator.h
+++ b/src/google/protobuf/compiler/mock_code_generator.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -59,6 +59,10 @@ namespace compiler {
// MockCodeGenerator_Exit." to stderr and then calls exit(123).
// MockCodeGenerator_Abort: Generate() prints "Saw message type
// MockCodeGenerator_Abort." to stderr and then calls abort().
+// MockCodeGenerator_HasSourceCodeInfo: Causes Generate() to abort after
+// printing "Saw message type MockCodeGenerator_HasSourceCodeInfo: FOO." to
+// stderr, where FOO is "1" if the supplied FileDescriptorProto has source
+// code info, and "0" otherwise.
class MockCodeGenerator : public CodeGenerator {
public:
MockCodeGenerator(const string& name);
@@ -69,11 +73,14 @@ class MockCodeGenerator : public CodeGenerator {
//
// |insertions| is a comma-separated list of names of MockCodeGenerators which
// should have inserted lines into this file.
+ // |parsed_file_list| is a comma-separated list of names of the files
+ // that are being compiled together in this run.
static void ExpectGenerated(const string& name,
const string& parameter,
const string& insertions,
const string& file,
const string& first_message_name,
+ const string& parsed_file_list,
const string& output_directory);
// Get the name of the file which would be written by the given generator.
@@ -86,7 +93,7 @@ class MockCodeGenerator : public CodeGenerator {
virtual bool Generate(const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* context,
string* error) const;
private:
@@ -94,10 +101,12 @@ class MockCodeGenerator : public CodeGenerator {
static string GetOutputFileContent(const string& generator_name,
const string& parameter,
- const FileDescriptor* file);
+ const FileDescriptor* file,
+ GeneratorContext *context);
static string GetOutputFileContent(const string& generator_name,
const string& parameter,
const string& file,
+ const string& parsed_file_list,
const string& first_message_name);
};
diff --git a/src/google/protobuf/compiler/package_info.h b/src/google/protobuf/compiler/package_info.h
index b897126..fb6b473 100644
--- a/src/google/protobuf/compiler/package_info.h
+++ b/src/google/protobuf/compiler/package_info.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc
index 758f70d..474a8f8 100644
--- a/src/google/protobuf/compiler/parser.cc
+++ b/src/google/protobuf/compiler/parser.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -46,7 +46,7 @@
#include <google/protobuf/io/tokenizer.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/strutil.h>
-#include <google/protobuf/stubs/map-util.h>
+#include <google/protobuf/stubs/map_util.h>
namespace google {
namespace protobuf {
@@ -174,6 +174,20 @@ bool Parser::ConsumeInteger(int* output, const char* error) {
}
}
+bool Parser::ConsumeSignedInteger(int* output, const char* error) {
+ bool is_negative = false;
+ uint64 max_value = kint32max;
+ if (TryConsume("-")) {
+ is_negative = true;
+ max_value += 1;
+ }
+ uint64 value = 0;
+ DO(ConsumeInteger64(max_value, &value, error));
+ if (is_negative) value *= -1;
+ *output = value;
+ return true;
+}
+
bool Parser::ConsumeInteger64(uint64 max_value, uint64* output,
const char* error) {
if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
@@ -237,6 +251,35 @@ bool Parser::ConsumeString(string* output, const char* error) {
}
}
+bool Parser::TryConsumeEndOfDeclaration(const char* text,
+ const LocationRecorder* location) {
+ if (LookingAt(text)) {
+ string leading, trailing;
+ input_->NextWithComments(&trailing, NULL, &leading);
+
+ // Save the leading comments for next time, and recall the leading comments
+ // from last time.
+ leading.swap(upcoming_doc_comments_);
+
+ if (location != NULL) {
+ location->AttachComments(&leading, &trailing);
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool Parser::ConsumeEndOfDeclaration(const char* text,
+ const LocationRecorder* location) {
+ if (TryConsumeEndOfDeclaration(text, location)) {
+ return true;
+ } else {
+ AddError("Expected \"" + string(text) + "\".");
+ return false;
+ }
+}
+
// -------------------------------------------------------------------
void Parser::AddError(int line, int column, const string& error) {
@@ -250,20 +293,87 @@ void Parser::AddError(const string& error) {
AddError(input_->current().line, input_->current().column, error);
}
-void Parser::RecordLocation(
- const Message* descriptor,
- DescriptorPool::ErrorCollector::ErrorLocation location,
- int line, int column) {
- if (source_location_table_ != NULL) {
- source_location_table_->Add(descriptor, location, line, column);
+// -------------------------------------------------------------------
+
+Parser::LocationRecorder::LocationRecorder(Parser* parser)
+ : parser_(parser),
+ location_(parser_->source_code_info_->add_location()) {
+ location_->add_span(parser_->input_->current().line);
+ location_->add_span(parser_->input_->current().column);
+}
+
+Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent) {
+ Init(parent);
+}
+
+Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent,
+ int path1) {
+ Init(parent);
+ AddPath(path1);
+}
+
+Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent,
+ int path1, int path2) {
+ Init(parent);
+ AddPath(path1);
+ AddPath(path2);
+}
+
+void Parser::LocationRecorder::Init(const LocationRecorder& parent) {
+ parser_ = parent.parser_;
+ location_ = parser_->source_code_info_->add_location();
+ location_->mutable_path()->CopyFrom(parent.location_->path());
+
+ location_->add_span(parser_->input_->current().line);
+ location_->add_span(parser_->input_->current().column);
+}
+
+Parser::LocationRecorder::~LocationRecorder() {
+ if (location_->span_size() <= 2) {
+ EndAt(parser_->input_->previous());
}
}
-void Parser::RecordLocation(
- const Message* descriptor,
+void Parser::LocationRecorder::AddPath(int path_component) {
+ location_->add_path(path_component);
+}
+
+void Parser::LocationRecorder::StartAt(const io::Tokenizer::Token& token) {
+ location_->set_span(0, token.line);
+ location_->set_span(1, token.column);
+}
+
+void Parser::LocationRecorder::StartAt(const LocationRecorder& other) {
+ location_->set_span(0, other.location_->span(0));
+ location_->set_span(1, other.location_->span(1));
+}
+
+void Parser::LocationRecorder::EndAt(const io::Tokenizer::Token& token) {
+ if (token.line != location_->span(0)) {
+ location_->add_span(token.line);
+ }
+ location_->add_span(token.end_column);
+}
+
+void Parser::LocationRecorder::RecordLegacyLocation(const Message* descriptor,
DescriptorPool::ErrorCollector::ErrorLocation location) {
- RecordLocation(descriptor, location,
- input_->current().line, input_->current().column);
+ if (parser_->source_location_table_ != NULL) {
+ parser_->source_location_table_->Add(
+ descriptor, location, location_->span(0), location_->span(1));
+ }
+}
+
+void Parser::LocationRecorder::AttachComments(
+ string* leading, string* trailing) const {
+ GOOGLE_CHECK(!location_->has_leading_comments());
+ GOOGLE_CHECK(!location_->has_trailing_comments());
+
+ if (!leading->empty()) {
+ location_->mutable_leading_comments()->swap(*leading);
+ }
+ if (!trailing->empty()) {
+ location_->mutable_trailing_comments()->swap(*trailing);
+ }
}
// -------------------------------------------------------------------
@@ -273,7 +383,7 @@ void Parser::SkipStatement() {
if (AtEnd()) {
return;
} else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) {
- if (TryConsume(";")) {
+ if (TryConsumeEndOfDeclaration(";", NULL)) {
return;
} else if (TryConsume("{")) {
SkipRestOfBlock();
@@ -291,7 +401,7 @@ void Parser::SkipRestOfBlock() {
if (AtEnd()) {
return;
} else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) {
- if (TryConsume("}")) {
+ if (TryConsumeEndOfDeclaration("}", NULL)) {
return;
} else if (TryConsume("{")) {
SkipRestOfBlock();
@@ -308,38 +418,51 @@ bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) {
had_errors_ = false;
syntax_identifier_.clear();
+ // Note that |file| could be NULL at this point if
+ // stop_after_syntax_identifier_ is true. So, we conservatively allocate
+ // SourceCodeInfo on the stack, then swap it into the FileDescriptorProto
+ // later on.
+ SourceCodeInfo source_code_info;
+ source_code_info_ = &source_code_info;
+
if (LookingAtType(io::Tokenizer::TYPE_START)) {
// Advance to first token.
- input_->Next();
+ input_->NextWithComments(NULL, NULL, &upcoming_doc_comments_);
}
- if (require_syntax_identifier_ || LookingAt("syntax")) {
- if (!ParseSyntaxIdentifier()) {
- // Don't attempt to parse the file if we didn't recognize the syntax
- // identifier.
- return false;
+ {
+ LocationRecorder root_location(this);
+
+ if (require_syntax_identifier_ || LookingAt("syntax")) {
+ if (!ParseSyntaxIdentifier()) {
+ // Don't attempt to parse the file if we didn't recognize the syntax
+ // identifier.
+ return false;
+ }
+ } else if (!stop_after_syntax_identifier_) {
+ syntax_identifier_ = "proto2";
}
- } else if (!stop_after_syntax_identifier_) {
- syntax_identifier_ = "proto2";
- }
- if (stop_after_syntax_identifier_) return !had_errors_;
+ if (stop_after_syntax_identifier_) return !had_errors_;
- // Repeatedly parse statements until we reach the end of the file.
- while (!AtEnd()) {
- if (!ParseTopLevelStatement(file)) {
- // This statement failed to parse. Skip it, but keep looping to parse
- // other statements.
- SkipStatement();
+ // Repeatedly parse statements until we reach the end of the file.
+ while (!AtEnd()) {
+ if (!ParseTopLevelStatement(file, root_location)) {
+ // This statement failed to parse. Skip it, but keep looping to parse
+ // other statements.
+ SkipStatement();
- if (LookingAt("}")) {
- AddError("Unmatched \"}\".");
- input_->Next();
+ if (LookingAt("}")) {
+ AddError("Unmatched \"}\".");
+ input_->NextWithComments(NULL, NULL, &upcoming_doc_comments_);
+ }
}
}
}
input_ = NULL;
+ source_code_info_ = NULL;
+ source_code_info.Swap(file->mutable_source_code_info());
return !had_errors_;
}
@@ -349,7 +472,7 @@ bool Parser::ParseSyntaxIdentifier() {
io::Tokenizer::Token syntax_token = input_->current();
string syntax;
DO(ConsumeString(&syntax, "Expected syntax identifier."));
- DO(Consume(";"));
+ DO(ConsumeEndOfDeclaration(";", NULL));
syntax_identifier_ = syntax;
@@ -363,25 +486,43 @@ bool Parser::ParseSyntaxIdentifier() {
return true;
}
-bool Parser::ParseTopLevelStatement(FileDescriptorProto* file) {
- if (TryConsume(";")) {
+bool Parser::ParseTopLevelStatement(FileDescriptorProto* file,
+ const LocationRecorder& root_location) {
+ if (TryConsumeEndOfDeclaration(";", NULL)) {
// empty statement; ignore
return true;
} else if (LookingAt("message")) {
- return ParseMessageDefinition(file->add_message_type());
+ LocationRecorder location(root_location,
+ FileDescriptorProto::kMessageTypeFieldNumber, file->message_type_size());
+ return ParseMessageDefinition(file->add_message_type(), location, file);
} else if (LookingAt("enum")) {
- return ParseEnumDefinition(file->add_enum_type());
+ LocationRecorder location(root_location,
+ FileDescriptorProto::kEnumTypeFieldNumber, file->enum_type_size());
+ return ParseEnumDefinition(file->add_enum_type(), location, file);
} else if (LookingAt("service")) {
- return ParseServiceDefinition(file->add_service());
+ LocationRecorder location(root_location,
+ FileDescriptorProto::kServiceFieldNumber, file->service_size());
+ return ParseServiceDefinition(file->add_service(), location, file);
} else if (LookingAt("extend")) {
+ LocationRecorder location(root_location,
+ FileDescriptorProto::kExtensionFieldNumber);
return ParseExtend(file->mutable_extension(),
- file->mutable_message_type());
+ file->mutable_message_type(),
+ root_location,
+ FileDescriptorProto::kMessageTypeFieldNumber,
+ location, file);
} else if (LookingAt("import")) {
- return ParseImport(file->add_dependency());
+ return ParseImport(file->mutable_dependency(),
+ file->mutable_public_dependency(),
+ file->mutable_weak_dependency(),
+ root_location, file);
} else if (LookingAt("package")) {
- return ParsePackage(file);
+ return ParsePackage(file, root_location, file);
} else if (LookingAt("option")) {
- return ParseOption(file->mutable_options());
+ LocationRecorder location(root_location,
+ FileDescriptorProto::kOptionsFieldNumber);
+ return ParseOption(file->mutable_options(), location, file,
+ OPTION_STATEMENT);
} else {
AddError("Expected top-level statement (e.g. \"message\").");
return false;
@@ -391,93 +532,234 @@ bool Parser::ParseTopLevelStatement(FileDescriptorProto* file) {
// -------------------------------------------------------------------
// Messages
-bool Parser::ParseMessageDefinition(DescriptorProto* message) {
+bool Parser::ParseMessageDefinition(
+ DescriptorProto* message,
+ const LocationRecorder& message_location,
+ const FileDescriptorProto* containing_file) {
DO(Consume("message"));
- RecordLocation(message, DescriptorPool::ErrorCollector::NAME);
- DO(ConsumeIdentifier(message->mutable_name(), "Expected message name."));
- DO(ParseMessageBlock(message));
+ {
+ LocationRecorder location(message_location,
+ DescriptorProto::kNameFieldNumber);
+ location.RecordLegacyLocation(
+ message, DescriptorPool::ErrorCollector::NAME);
+ DO(ConsumeIdentifier(message->mutable_name(), "Expected message name."));
+ }
+ DO(ParseMessageBlock(message, message_location, containing_file));
return true;
}
-bool Parser::ParseMessageBlock(DescriptorProto* message) {
- DO(Consume("{"));
+namespace {
+
+const int kMaxExtensionRangeSentinel = -1;
- while (!TryConsume("}")) {
+bool IsMessageSetWireFormatMessage(const DescriptorProto& message) {
+ const MessageOptions& options = message.options();
+ for (int i = 0; i < options.uninterpreted_option_size(); ++i) {
+ const UninterpretedOption& uninterpreted = options.uninterpreted_option(i);
+ if (uninterpreted.name_size() == 1 &&
+ uninterpreted.name(0).name_part() == "message_set_wire_format" &&
+ uninterpreted.identifier_value() == "true") {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Modifies any extension ranges that specified 'max' as the end of the
+// extension range, and sets them to the type-specific maximum. The actual max
+// tag number can only be determined after all options have been parsed.
+void AdjustExtensionRangesWithMaxEndNumber(DescriptorProto* message) {
+ const bool is_message_set = IsMessageSetWireFormatMessage(*message);
+ const int max_extension_number = is_message_set ?
+ kint32max :
+ FieldDescriptor::kMaxNumber + 1;
+ for (int i = 0; i < message->extension_range_size(); ++i) {
+ if (message->extension_range(i).end() == kMaxExtensionRangeSentinel) {
+ message->mutable_extension_range(i)->set_end(max_extension_number);
+ }
+ }
+}
+
+} // namespace
+
+bool Parser::ParseMessageBlock(DescriptorProto* message,
+ const LocationRecorder& message_location,
+ const FileDescriptorProto* containing_file) {
+ DO(ConsumeEndOfDeclaration("{", &message_location));
+
+ while (!TryConsumeEndOfDeclaration("}", NULL)) {
if (AtEnd()) {
AddError("Reached end of input in message definition (missing '}').");
return false;
}
- if (!ParseMessageStatement(message)) {
+ if (!ParseMessageStatement(message, message_location, containing_file)) {
// This statement failed to parse. Skip it, but keep looping to parse
// other statements.
SkipStatement();
}
}
+ if (message->extension_range_size() > 0) {
+ AdjustExtensionRangesWithMaxEndNumber(message);
+ }
return true;
}
-bool Parser::ParseMessageStatement(DescriptorProto* message) {
- if (TryConsume(";")) {
+bool Parser::ParseMessageStatement(DescriptorProto* message,
+ const LocationRecorder& message_location,
+ const FileDescriptorProto* containing_file) {
+ if (TryConsumeEndOfDeclaration(";", NULL)) {
// empty statement; ignore
return true;
} else if (LookingAt("message")) {
- return ParseMessageDefinition(message->add_nested_type());
+ LocationRecorder location(message_location,
+ DescriptorProto::kNestedTypeFieldNumber,
+ message->nested_type_size());
+ return ParseMessageDefinition(message->add_nested_type(), location,
+ containing_file);
} else if (LookingAt("enum")) {
- return ParseEnumDefinition(message->add_enum_type());
+ LocationRecorder location(message_location,
+ DescriptorProto::kEnumTypeFieldNumber,
+ message->enum_type_size());
+ return ParseEnumDefinition(message->add_enum_type(), location,
+ containing_file);
} else if (LookingAt("extensions")) {
- return ParseExtensions(message);
+ LocationRecorder location(message_location,
+ DescriptorProto::kExtensionRangeFieldNumber);
+ return ParseExtensions(message, location, containing_file);
} else if (LookingAt("extend")) {
+ LocationRecorder location(message_location,
+ DescriptorProto::kExtensionFieldNumber);
return ParseExtend(message->mutable_extension(),
- message->mutable_nested_type());
+ message->mutable_nested_type(),
+ message_location,
+ DescriptorProto::kNestedTypeFieldNumber,
+ location, containing_file);
} else if (LookingAt("option")) {
- return ParseOption(message->mutable_options());
+ LocationRecorder location(message_location,
+ DescriptorProto::kOptionsFieldNumber);
+ return ParseOption(message->mutable_options(), location,
+ containing_file, OPTION_STATEMENT);
+ } else if (LookingAt("oneof")) {
+ int oneof_index = message->oneof_decl_size();
+ LocationRecorder oneof_location(message_location,
+ DescriptorProto::kOneofDeclFieldNumber,
+ oneof_index);
+
+ return ParseOneof(message->add_oneof_decl(), message,
+ oneof_index, oneof_location, message_location,
+ containing_file);
} else {
+ LocationRecorder location(message_location,
+ DescriptorProto::kFieldFieldNumber,
+ message->field_size());
return ParseMessageField(message->add_field(),
- message->mutable_nested_type());
+ message->mutable_nested_type(),
+ message_location,
+ DescriptorProto::kNestedTypeFieldNumber,
+ location,
+ containing_file);
}
}
bool Parser::ParseMessageField(FieldDescriptorProto* field,
- RepeatedPtrField<DescriptorProto>* messages) {
- // Parse label and type.
- FieldDescriptorProto::Label label;
- DO(ParseLabel(&label));
- field->set_label(label);
-
- RecordLocation(field, DescriptorPool::ErrorCollector::TYPE);
- FieldDescriptorProto::Type type = FieldDescriptorProto::TYPE_INT32;
- string type_name;
- DO(ParseType(&type, &type_name));
- if (type_name.empty()) {
- field->set_type(type);
- } else {
- field->set_type_name(type_name);
+ RepeatedPtrField<DescriptorProto>* messages,
+ const LocationRecorder& parent_location,
+ int location_field_number_for_nested_type,
+ const LocationRecorder& field_location,
+ const FileDescriptorProto* containing_file) {
+ {
+ LocationRecorder location(field_location,
+ FieldDescriptorProto::kLabelFieldNumber);
+ FieldDescriptorProto::Label label;
+ DO(ParseLabel(&label, containing_file));
+ field->set_label(label);
+ }
+
+ return ParseMessageFieldNoLabel(field, messages, parent_location,
+ location_field_number_for_nested_type,
+ field_location,
+ containing_file);
+}
+
+bool Parser::ParseMessageFieldNoLabel(
+ FieldDescriptorProto* field,
+ RepeatedPtrField<DescriptorProto>* messages,
+ const LocationRecorder& parent_location,
+ int location_field_number_for_nested_type,
+ const LocationRecorder& field_location,
+ const FileDescriptorProto* containing_file) {
+ // Parse type.
+ {
+ LocationRecorder location(field_location); // add path later
+ location.RecordLegacyLocation(field, DescriptorPool::ErrorCollector::TYPE);
+
+ FieldDescriptorProto::Type type = FieldDescriptorProto::TYPE_INT32;
+ string type_name;
+ DO(ParseType(&type, &type_name));
+ if (type_name.empty()) {
+ location.AddPath(FieldDescriptorProto::kTypeFieldNumber);
+ field->set_type(type);
+ } else {
+ location.AddPath(FieldDescriptorProto::kTypeNameFieldNumber);
+ field->set_type_name(type_name);
+ }
}
// Parse name and '='.
- RecordLocation(field, DescriptorPool::ErrorCollector::NAME);
io::Tokenizer::Token name_token = input_->current();
- DO(ConsumeIdentifier(field->mutable_name(), "Expected field name."));
+ {
+ LocationRecorder location(field_location,
+ FieldDescriptorProto::kNameFieldNumber);
+ location.RecordLegacyLocation(field, DescriptorPool::ErrorCollector::NAME);
+ DO(ConsumeIdentifier(field->mutable_name(), "Expected field name."));
+ }
DO(Consume("=", "Missing field number."));
// Parse field number.
- RecordLocation(field, DescriptorPool::ErrorCollector::NUMBER);
- int number;
- DO(ConsumeInteger(&number, "Expected field number."));
- field->set_number(number);
+ {
+ LocationRecorder location(field_location,
+ FieldDescriptorProto::kNumberFieldNumber);
+ location.RecordLegacyLocation(
+ field, DescriptorPool::ErrorCollector::NUMBER);
+ int number;
+ DO(ConsumeInteger(&number, "Expected field number."));
+ field->set_number(number);
+ }
// Parse options.
- DO(ParseFieldOptions(field));
+ DO(ParseFieldOptions(field, field_location, containing_file));
// Deal with groups.
- if (type_name.empty() && type == FieldDescriptorProto::TYPE_GROUP) {
+ if (field->has_type() && field->type() == FieldDescriptorProto::TYPE_GROUP) {
+ // Awkward: Since a group declares both a message type and a field, we
+ // have to create overlapping locations.
+ LocationRecorder group_location(parent_location);
+ group_location.StartAt(field_location);
+ group_location.AddPath(location_field_number_for_nested_type);
+ group_location.AddPath(messages->size());
+
DescriptorProto* group = messages->Add();
group->set_name(field->name());
+
// Record name location to match the field name's location.
- RecordLocation(group, DescriptorPool::ErrorCollector::NAME,
- name_token.line, name_token.column);
+ {
+ LocationRecorder location(group_location,
+ DescriptorProto::kNameFieldNumber);
+ location.StartAt(name_token);
+ location.EndAt(name_token);
+ location.RecordLegacyLocation(
+ group, DescriptorPool::ErrorCollector::NAME);
+ }
+
+ // The field's type_name also comes from the name. Confusing!
+ {
+ LocationRecorder location(field_location,
+ FieldDescriptorProto::kTypeNameFieldNumber);
+ location.StartAt(name_token);
+ location.EndAt(name_token);
+ }
// As a hack for backwards-compatibility, we force the group name to start
// with a capital letter and lower-case the field name. New code should
@@ -490,27 +772,37 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field,
field->set_type_name(group->name());
if (LookingAt("{")) {
- DO(ParseMessageBlock(group));
+ DO(ParseMessageBlock(group, group_location, containing_file));
} else {
AddError("Missing group body.");
return false;
}
} else {
- DO(Consume(";"));
+ DO(ConsumeEndOfDeclaration(";", &field_location));
}
return true;
}
-bool Parser::ParseFieldOptions(FieldDescriptorProto* field) {
- if (!TryConsume("[")) return true;
+bool Parser::ParseFieldOptions(FieldDescriptorProto* field,
+ const LocationRecorder& field_location,
+ const FileDescriptorProto* containing_file) {
+ if (!LookingAt("[")) return true;
+
+ LocationRecorder location(field_location,
+ FieldDescriptorProto::kOptionsFieldNumber);
+
+ DO(Consume("["));
// Parse field options.
do {
if (LookingAt("default")) {
- DO(ParseDefaultAssignment(field));
+ // We intentionally pass field_location rather than location here, since
+ // the default value is not actually an option.
+ DO(ParseDefaultAssignment(field, field_location, containing_file));
} else {
- DO(ParseOptionAssignment(field->mutable_options()));
+ DO(ParseOption(field->mutable_options(), location,
+ containing_file, OPTION_ASSIGNMENT));
}
} while (TryConsume(","));
@@ -518,7 +810,10 @@ bool Parser::ParseFieldOptions(FieldDescriptorProto* field) {
return true;
}
-bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field) {
+bool Parser::ParseDefaultAssignment(
+ FieldDescriptorProto* field,
+ const LocationRecorder& field_location,
+ const FileDescriptorProto* containing_file) {
if (field->has_default_value()) {
AddError("Already set option \"default\".");
field->clear_default_value();
@@ -527,13 +822,24 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field) {
DO(Consume("default"));
DO(Consume("="));
- RecordLocation(field, DescriptorPool::ErrorCollector::DEFAULT_VALUE);
+ LocationRecorder location(field_location,
+ FieldDescriptorProto::kDefaultValueFieldNumber);
+ location.RecordLegacyLocation(
+ field, DescriptorPool::ErrorCollector::DEFAULT_VALUE);
string* default_value = field->mutable_default_value();
if (!field->has_type()) {
// The field has a type name, but we don't know if it is a message or an
- // enum yet. Assume an enum for now.
- DO(ConsumeIdentifier(default_value, "Expected identifier."));
+ // enum yet. (If it were a primitive type, |field| would have a type set
+ // already.) In this case, simply take the current string as the default
+ // value; we will catch the error later if it is not a valid enum value.
+ // (N.B. that we do not check whether the current token is an identifier:
+ // doing so throws strange errors when the user mistypes a primitive
+ // typename and we assume it's an enum. E.g.: "optional int foo = 1 [default
+ // = 42]". In such a case the fundamental error is really that "int" is not
+ // a type, not that "42" is not an identifier. See b/12533582.)
+ *default_value = input_->current().text;
+ input_->Next();
return true;
}
@@ -559,7 +865,8 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field) {
}
// Parse the integer to verify that it is not out-of-range.
uint64 value;
- DO(ConsumeInteger64(max_value, &value, "Expected integer."));
+ DO(ConsumeInteger64(max_value, &value,
+ "Expected integer for field default value."));
// And stringify it again.
default_value->append(SimpleItoa(value));
break;
@@ -581,7 +888,8 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field) {
}
// Parse the integer to verify that it is not out-of-range.
uint64 value;
- DO(ConsumeInteger64(max_value, &value, "Expected integer."));
+ DO(ConsumeInteger64(max_value, &value,
+ "Expected integer for field default value."));
// And stringify it again.
default_value->append(SimpleItoa(value));
break;
@@ -613,7 +921,11 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field) {
break;
case FieldDescriptorProto::TYPE_STRING:
- DO(ConsumeString(default_value, "Expected string."));
+ // Note: When file opton java_string_check_utf8 is true, if a
+ // non-string representation (eg byte[]) is later supported, it must
+ // be checked for UTF-8-ness.
+ DO(ConsumeString(default_value, "Expected string for field default "
+ "value."));
break;
case FieldDescriptorProto::TYPE_BYTES:
@@ -622,7 +934,8 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field) {
break;
case FieldDescriptorProto::TYPE_ENUM:
- DO(ConsumeIdentifier(default_value, "Expected identifier."));
+ DO(ConsumeIdentifier(default_value, "Expected enum identifier for field "
+ "default value."));
break;
case FieldDescriptorProto::TYPE_MESSAGE:
@@ -634,26 +947,36 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field) {
return true;
}
-bool Parser::ParseOptionNamePart(UninterpretedOption* uninterpreted_option) {
+bool Parser::ParseOptionNamePart(UninterpretedOption* uninterpreted_option,
+ const LocationRecorder& part_location,
+ const FileDescriptorProto* containing_file) {
UninterpretedOption::NamePart* name = uninterpreted_option->add_name();
string identifier; // We parse identifiers into this string.
if (LookingAt("(")) { // This is an extension.
DO(Consume("("));
- // An extension name consists of dot-separated identifiers, and may begin
- // with a dot.
- if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
- DO(ConsumeIdentifier(&identifier, "Expected identifier."));
- name->mutable_name_part()->append(identifier);
- }
- while (LookingAt(".")) {
- DO(Consume("."));
- name->mutable_name_part()->append(".");
- DO(ConsumeIdentifier(&identifier, "Expected identifier."));
- name->mutable_name_part()->append(identifier);
+
+ {
+ LocationRecorder location(
+ part_location, UninterpretedOption::NamePart::kNamePartFieldNumber);
+ // An extension name consists of dot-separated identifiers, and may begin
+ // with a dot.
+ if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
+ DO(ConsumeIdentifier(&identifier, "Expected identifier."));
+ name->mutable_name_part()->append(identifier);
+ }
+ while (LookingAt(".")) {
+ DO(Consume("."));
+ name->mutable_name_part()->append(".");
+ DO(ConsumeIdentifier(&identifier, "Expected identifier."));
+ name->mutable_name_part()->append(identifier);
+ }
}
+
DO(Consume(")"));
name->set_is_extension(true);
} else { // This is a regular field.
+ LocationRecorder location(
+ part_location, UninterpretedOption::NamePart::kNamePartFieldNumber);
DO(ConsumeIdentifier(&identifier, "Expected identifier."));
name->mutable_name_part()->append(identifier);
name->set_is_extension(false);
@@ -661,116 +984,214 @@ bool Parser::ParseOptionNamePart(UninterpretedOption* uninterpreted_option) {
return true;
}
+bool Parser::ParseUninterpretedBlock(string* value) {
+ // Note that enclosing braces are not added to *value.
+ // We do NOT use ConsumeEndOfStatement for this brace because it's delimiting
+ // an expression, not a block of statements.
+ DO(Consume("{"));
+ int brace_depth = 1;
+ while (!AtEnd()) {
+ if (LookingAt("{")) {
+ brace_depth++;
+ } else if (LookingAt("}")) {
+ brace_depth--;
+ if (brace_depth == 0) {
+ input_->Next();
+ return true;
+ }
+ }
+ // TODO(sanjay): Interpret line/column numbers to preserve formatting
+ if (!value->empty()) value->push_back(' ');
+ value->append(input_->current().text);
+ input_->Next();
+ }
+ AddError("Unexpected end of stream while parsing aggregate value.");
+ return false;
+}
+
// We don't interpret the option here. Instead we store it in an
// UninterpretedOption, to be interpreted later.
-bool Parser::ParseOptionAssignment(Message* options) {
+bool Parser::ParseOption(Message* options,
+ const LocationRecorder& options_location,
+ const FileDescriptorProto* containing_file,
+ OptionStyle style) {
// Create an entry in the uninterpreted_option field.
const FieldDescriptor* uninterpreted_option_field = options->GetDescriptor()->
FindFieldByName("uninterpreted_option");
GOOGLE_CHECK(uninterpreted_option_field != NULL)
<< "No field named \"uninterpreted_option\" in the Options proto.";
+ const Reflection* reflection = options->GetReflection();
+
+ LocationRecorder location(
+ options_location, uninterpreted_option_field->number(),
+ reflection->FieldSize(*options, uninterpreted_option_field));
+
+ if (style == OPTION_STATEMENT) {
+ DO(Consume("option"));
+ }
+
UninterpretedOption* uninterpreted_option = down_cast<UninterpretedOption*>(
options->GetReflection()->AddMessage(options,
uninterpreted_option_field));
// Parse dot-separated name.
- RecordLocation(uninterpreted_option,
- DescriptorPool::ErrorCollector::OPTION_NAME);
-
- DO(ParseOptionNamePart(uninterpreted_option));
+ {
+ LocationRecorder name_location(location,
+ UninterpretedOption::kNameFieldNumber);
+ name_location.RecordLegacyLocation(
+ uninterpreted_option, DescriptorPool::ErrorCollector::OPTION_NAME);
+
+ {
+ LocationRecorder part_location(name_location,
+ uninterpreted_option->name_size());
+ DO(ParseOptionNamePart(uninterpreted_option, part_location,
+ containing_file));
+ }
- while (LookingAt(".")) {
- DO(Consume("."));
- DO(ParseOptionNamePart(uninterpreted_option));
+ while (LookingAt(".")) {
+ DO(Consume("."));
+ LocationRecorder part_location(name_location,
+ uninterpreted_option->name_size());
+ DO(ParseOptionNamePart(uninterpreted_option, part_location,
+ containing_file));
+ }
}
DO(Consume("="));
- RecordLocation(uninterpreted_option,
- DescriptorPool::ErrorCollector::OPTION_VALUE);
+ {
+ LocationRecorder value_location(location);
+ value_location.RecordLegacyLocation(
+ uninterpreted_option, DescriptorPool::ErrorCollector::OPTION_VALUE);
- // All values are a single token, except for negative numbers, which consist
- // of a single '-' symbol, followed by a positive number.
- bool is_negative = TryConsume("-");
+ // All values are a single token, except for negative numbers, which consist
+ // of a single '-' symbol, followed by a positive number.
+ bool is_negative = TryConsume("-");
- switch (input_->current().type) {
- case io::Tokenizer::TYPE_START:
- GOOGLE_LOG(FATAL) << "Trying to read value before any tokens have been read.";
- return false;
-
- case io::Tokenizer::TYPE_END:
- AddError("Unexpected end of stream while parsing option value.");
- return false;
+ switch (input_->current().type) {
+ case io::Tokenizer::TYPE_START:
+ GOOGLE_LOG(FATAL) << "Trying to read value before any tokens have been read.";
+ return false;
- case io::Tokenizer::TYPE_IDENTIFIER: {
- if (is_negative) {
- AddError("Invalid '-' symbol before identifier.");
+ case io::Tokenizer::TYPE_END:
+ AddError("Unexpected end of stream while parsing option value.");
return false;
+
+ case io::Tokenizer::TYPE_IDENTIFIER: {
+ value_location.AddPath(
+ UninterpretedOption::kIdentifierValueFieldNumber);
+ if (is_negative) {
+ AddError("Invalid '-' symbol before identifier.");
+ return false;
+ }
+ string value;
+ DO(ConsumeIdentifier(&value, "Expected identifier."));
+ uninterpreted_option->set_identifier_value(value);
+ break;
}
- string value;
- DO(ConsumeIdentifier(&value, "Expected identifier."));
- uninterpreted_option->set_identifier_value(value);
- break;
- }
- case io::Tokenizer::TYPE_INTEGER: {
- uint64 value;
- uint64 max_value =
- is_negative ? static_cast<uint64>(kint64max) + 1 : kuint64max;
- DO(ConsumeInteger64(max_value, &value, "Expected integer."));
- if (is_negative) {
- uninterpreted_option->set_negative_int_value(-value);
- } else {
- uninterpreted_option->set_positive_int_value(value);
+ case io::Tokenizer::TYPE_INTEGER: {
+ uint64 value;
+ uint64 max_value =
+ is_negative ? static_cast<uint64>(kint64max) + 1 : kuint64max;
+ DO(ConsumeInteger64(max_value, &value, "Expected integer."));
+ if (is_negative) {
+ value_location.AddPath(
+ UninterpretedOption::kNegativeIntValueFieldNumber);
+ uninterpreted_option->set_negative_int_value(
+ -static_cast<int64>(value));
+ } else {
+ value_location.AddPath(
+ UninterpretedOption::kPositiveIntValueFieldNumber);
+ uninterpreted_option->set_positive_int_value(value);
+ }
+ break;
}
- break;
- }
- case io::Tokenizer::TYPE_FLOAT: {
- double value;
- DO(ConsumeNumber(&value, "Expected number."));
- uninterpreted_option->set_double_value(is_negative ? -value : value);
- break;
- }
+ case io::Tokenizer::TYPE_FLOAT: {
+ value_location.AddPath(UninterpretedOption::kDoubleValueFieldNumber);
+ double value;
+ DO(ConsumeNumber(&value, "Expected number."));
+ uninterpreted_option->set_double_value(is_negative ? -value : value);
+ break;
+ }
- case io::Tokenizer::TYPE_STRING: {
- if (is_negative) {
- AddError("Invalid '-' symbol before string.");
- return false;
+ case io::Tokenizer::TYPE_STRING: {
+ value_location.AddPath(UninterpretedOption::kStringValueFieldNumber);
+ if (is_negative) {
+ AddError("Invalid '-' symbol before string.");
+ return false;
+ }
+ string value;
+ DO(ConsumeString(&value, "Expected string."));
+ uninterpreted_option->set_string_value(value);
+ break;
}
- string value;
- DO(ConsumeString(&value, "Expected string."));
- uninterpreted_option->set_string_value(value);
- break;
+
+ case io::Tokenizer::TYPE_SYMBOL:
+ if (LookingAt("{")) {
+ value_location.AddPath(
+ UninterpretedOption::kAggregateValueFieldNumber);
+ DO(ParseUninterpretedBlock(
+ uninterpreted_option->mutable_aggregate_value()));
+ } else {
+ AddError("Expected option value.");
+ return false;
+ }
+ break;
}
+ }
- case io::Tokenizer::TYPE_SYMBOL:
- AddError("Expected option value.");
- return false;
+ if (style == OPTION_STATEMENT) {
+ DO(ConsumeEndOfDeclaration(";", &location));
}
+
return true;
}
-bool Parser::ParseExtensions(DescriptorProto* message) {
+bool Parser::ParseExtensions(DescriptorProto* message,
+ const LocationRecorder& extensions_location,
+ const FileDescriptorProto* containing_file) {
// Parse the declaration.
DO(Consume("extensions"));
do {
+ // Note that kExtensionRangeFieldNumber was already pushed by the parent.
+ LocationRecorder location(extensions_location,
+ message->extension_range_size());
+
DescriptorProto::ExtensionRange* range = message->add_extension_range();
- RecordLocation(range, DescriptorPool::ErrorCollector::NUMBER);
+ location.RecordLegacyLocation(
+ range, DescriptorPool::ErrorCollector::NUMBER);
int start, end;
- DO(ConsumeInteger(&start, "Expected field number range."));
+ io::Tokenizer::Token start_token;
+
+ {
+ LocationRecorder start_location(
+ location, DescriptorProto::ExtensionRange::kStartFieldNumber);
+ start_token = input_->current();
+ DO(ConsumeInteger(&start, "Expected field number range."));
+ }
if (TryConsume("to")) {
+ LocationRecorder end_location(
+ location, DescriptorProto::ExtensionRange::kEndFieldNumber);
if (TryConsume("max")) {
- end = FieldDescriptor::kMaxNumber;
+ // Set to the sentinel value - 1 since we increment the value below.
+ // The actual value of the end of the range should be set with
+ // AdjustExtensionRangesWithMaxEndNumber.
+ end = kMaxExtensionRangeSentinel - 1;
} else {
DO(ConsumeInteger(&end, "Expected integer."));
}
} else {
+ LocationRecorder end_location(
+ location, DescriptorProto::ExtensionRange::kEndFieldNumber);
+ end_location.StartAt(start_token);
+ end_location.EndAt(start_token);
end = start;
}
@@ -782,24 +1203,26 @@ bool Parser::ParseExtensions(DescriptorProto* message) {
range->set_end(end);
} while (TryConsume(","));
- DO(Consume(";"));
+ DO(ConsumeEndOfDeclaration(";", &extensions_location));
return true;
}
bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
- RepeatedPtrField<DescriptorProto>* messages) {
+ RepeatedPtrField<DescriptorProto>* messages,
+ const LocationRecorder& parent_location,
+ int location_field_number_for_nested_type,
+ const LocationRecorder& extend_location,
+ const FileDescriptorProto* containing_file) {
DO(Consume("extend"));
- // We expect to see at least one extension field defined in the extend block.
- // We need to create it now so we can record the extendee's location.
- FieldDescriptorProto* first_field = extensions->Add();
-
// Parse the extendee type.
- RecordLocation(first_field, DescriptorPool::ErrorCollector::EXTENDEE);
- DO(ParseUserDefinedType(first_field->mutable_extendee()));
+ io::Tokenizer::Token extendee_start = input_->current();
+ string extendee;
+ DO(ParseUserDefinedType(&extendee));
+ io::Tokenizer::Token extendee_end = input_->previous();
// Parse the block.
- DO(Consume("{"));
+ DO(ConsumeEndOfDeclaration("{", &extend_location));
bool is_first = true;
@@ -809,21 +1232,92 @@ bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
return false;
}
- FieldDescriptorProto* field;
- if (is_first) {
- field = first_field;
- is_first = false;
- } else {
- field = extensions->Add();
- field->set_extendee(first_field->extendee());
+ // Note that kExtensionFieldNumber was already pushed by the parent.
+ LocationRecorder location(extend_location, extensions->size());
+
+ FieldDescriptorProto* field = extensions->Add();
+
+ {
+ LocationRecorder extendee_location(
+ location, FieldDescriptorProto::kExtendeeFieldNumber);
+ extendee_location.StartAt(extendee_start);
+ extendee_location.EndAt(extendee_end);
+
+ if (is_first) {
+ extendee_location.RecordLegacyLocation(
+ field, DescriptorPool::ErrorCollector::EXTENDEE);
+ is_first = false;
+ }
}
- if (!ParseMessageField(field, messages)) {
+ field->set_extendee(extendee);
+
+ if (!ParseMessageField(field, messages, parent_location,
+ location_field_number_for_nested_type,
+ location,
+ containing_file)) {
// This statement failed to parse. Skip it, but keep looping to parse
// other statements.
SkipStatement();
}
- } while(!TryConsume("}"));
+ } while (!TryConsumeEndOfDeclaration("}", NULL));
+
+ return true;
+}
+
+bool Parser::ParseOneof(OneofDescriptorProto* oneof_decl,
+ DescriptorProto* containing_type,
+ int oneof_index,
+ const LocationRecorder& oneof_location,
+ const LocationRecorder& containing_type_location,
+ const FileDescriptorProto* containing_file) {
+ DO(Consume("oneof"));
+
+ {
+ LocationRecorder name_location(oneof_location,
+ OneofDescriptorProto::kNameFieldNumber);
+ DO(ConsumeIdentifier(oneof_decl->mutable_name(), "Expected oneof name."));
+ }
+
+ DO(ConsumeEndOfDeclaration("{", &oneof_location));
+
+ do {
+ if (AtEnd()) {
+ AddError("Reached end of input in oneof definition (missing '}').");
+ return false;
+ }
+
+ // Print a nice error if the user accidentally tries to place a label
+ // on an individual member of a oneof.
+ if (LookingAt("required") ||
+ LookingAt("optional") ||
+ LookingAt("repeated")) {
+ AddError("Fields in oneofs must not have labels (required / optional "
+ "/ repeated).");
+ // We can continue parsing here because we understand what the user
+ // meant. The error report will still make parsing fail overall.
+ input_->Next();
+ }
+
+ LocationRecorder field_location(containing_type_location,
+ DescriptorProto::kFieldFieldNumber,
+ containing_type->field_size());
+
+ FieldDescriptorProto* field = containing_type->add_field();
+ field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+ field->set_oneof_index(oneof_index);
+
+ if (!ParseMessageFieldNoLabel(field,
+ containing_type->mutable_nested_type(),
+ containing_type_location,
+ DescriptorProto::kNestedTypeFieldNumber,
+ field_location,
+ containing_file)) {
+ // This statement failed to parse. Skip it, but keep looping to parse
+ // other statements.
+ SkipStatement();
+ }
+ } while (!TryConsumeEndOfDeclaration("}", NULL));
return true;
}
@@ -831,24 +1325,35 @@ bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
// -------------------------------------------------------------------
// Enums
-bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type) {
+bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type,
+ const LocationRecorder& enum_location,
+ const FileDescriptorProto* containing_file) {
DO(Consume("enum"));
- RecordLocation(enum_type, DescriptorPool::ErrorCollector::NAME);
- DO(ConsumeIdentifier(enum_type->mutable_name(), "Expected enum name."));
- DO(ParseEnumBlock(enum_type));
+
+ {
+ LocationRecorder location(enum_location,
+ EnumDescriptorProto::kNameFieldNumber);
+ location.RecordLegacyLocation(
+ enum_type, DescriptorPool::ErrorCollector::NAME);
+ DO(ConsumeIdentifier(enum_type->mutable_name(), "Expected enum name."));
+ }
+
+ DO(ParseEnumBlock(enum_type, enum_location, containing_file));
return true;
}
-bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type) {
- DO(Consume("{"));
+bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type,
+ const LocationRecorder& enum_location,
+ const FileDescriptorProto* containing_file) {
+ DO(ConsumeEndOfDeclaration("{", &enum_location));
- while (!TryConsume("}")) {
+ while (!TryConsumeEndOfDeclaration("}", NULL)) {
if (AtEnd()) {
AddError("Reached end of input in enum definition (missing '}').");
return false;
}
- if (!ParseEnumStatement(enum_type)) {
+ if (!ParseEnumStatement(enum_type, enum_location, containing_file)) {
// This statement failed to parse. Skip it, but keep looping to parse
// other statements.
SkipStatement();
@@ -858,41 +1363,73 @@ bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type) {
return true;
}
-bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type) {
- if (TryConsume(";")) {
+bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type,
+ const LocationRecorder& enum_location,
+ const FileDescriptorProto* containing_file) {
+ if (TryConsumeEndOfDeclaration(";", NULL)) {
// empty statement; ignore
return true;
} else if (LookingAt("option")) {
- return ParseOption(enum_type->mutable_options());
+ LocationRecorder location(enum_location,
+ EnumDescriptorProto::kOptionsFieldNumber);
+ return ParseOption(enum_type->mutable_options(), location,
+ containing_file, OPTION_STATEMENT);
} else {
- return ParseEnumConstant(enum_type->add_value());
+ LocationRecorder location(enum_location,
+ EnumDescriptorProto::kValueFieldNumber, enum_type->value_size());
+ return ParseEnumConstant(enum_type->add_value(), location, containing_file);
}
}
-bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value) {
- RecordLocation(enum_value, DescriptorPool::ErrorCollector::NAME);
- DO(ConsumeIdentifier(enum_value->mutable_name(),
- "Expected enum constant name."));
+bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value,
+ const LocationRecorder& enum_value_location,
+ const FileDescriptorProto* containing_file) {
+ // Parse name.
+ {
+ LocationRecorder location(enum_value_location,
+ EnumValueDescriptorProto::kNameFieldNumber);
+ location.RecordLegacyLocation(
+ enum_value, DescriptorPool::ErrorCollector::NAME);
+ DO(ConsumeIdentifier(enum_value->mutable_name(),
+ "Expected enum constant name."));
+ }
+
DO(Consume("=", "Missing numeric value for enum constant."));
- bool is_negative = TryConsume("-");
- int number;
- DO(ConsumeInteger(&number, "Expected integer."));
- if (is_negative) number *= -1;
- enum_value->set_number(number);
+ // Parse value.
+ {
+ LocationRecorder location(
+ enum_value_location, EnumValueDescriptorProto::kNumberFieldNumber);
+ location.RecordLegacyLocation(
+ enum_value, DescriptorPool::ErrorCollector::NUMBER);
- DO(ParseEnumConstantOptions(enum_value));
+ int number;
+ DO(ConsumeSignedInteger(&number, "Expected integer."));
+ enum_value->set_number(number);
+ }
- DO(Consume(";"));
+ DO(ParseEnumConstantOptions(enum_value, enum_value_location,
+ containing_file));
+
+ DO(ConsumeEndOfDeclaration(";", &enum_value_location));
return true;
}
-bool Parser::ParseEnumConstantOptions(EnumValueDescriptorProto* value) {
- if (!TryConsume("[")) return true;
+bool Parser::ParseEnumConstantOptions(
+ EnumValueDescriptorProto* value,
+ const LocationRecorder& enum_value_location,
+ const FileDescriptorProto* containing_file) {
+ if (!LookingAt("[")) return true;
+
+ LocationRecorder location(
+ enum_value_location, EnumValueDescriptorProto::kOptionsFieldNumber);
+
+ DO(Consume("["));
do {
- DO(ParseOptionAssignment(value->mutable_options()));
+ DO(ParseOption(value->mutable_options(), location,
+ containing_file, OPTION_ASSIGNMENT));
} while (TryConsume(","));
DO(Consume("]"));
@@ -902,24 +1439,36 @@ bool Parser::ParseEnumConstantOptions(EnumValueDescriptorProto* value) {
// -------------------------------------------------------------------
// Services
-bool Parser::ParseServiceDefinition(ServiceDescriptorProto* service) {
+bool Parser::ParseServiceDefinition(
+ ServiceDescriptorProto* service,
+ const LocationRecorder& service_location,
+ const FileDescriptorProto* containing_file) {
DO(Consume("service"));
- RecordLocation(service, DescriptorPool::ErrorCollector::NAME);
- DO(ConsumeIdentifier(service->mutable_name(), "Expected service name."));
- DO(ParseServiceBlock(service));
+
+ {
+ LocationRecorder location(service_location,
+ ServiceDescriptorProto::kNameFieldNumber);
+ location.RecordLegacyLocation(
+ service, DescriptorPool::ErrorCollector::NAME);
+ DO(ConsumeIdentifier(service->mutable_name(), "Expected service name."));
+ }
+
+ DO(ParseServiceBlock(service, service_location, containing_file));
return true;
}
-bool Parser::ParseServiceBlock(ServiceDescriptorProto* service) {
- DO(Consume("{"));
+bool Parser::ParseServiceBlock(ServiceDescriptorProto* service,
+ const LocationRecorder& service_location,
+ const FileDescriptorProto* containing_file) {
+ DO(ConsumeEndOfDeclaration("{", &service_location));
- while (!TryConsume("}")) {
+ while (!TryConsumeEndOfDeclaration("}", NULL)) {
if (AtEnd()) {
AddError("Reached end of input in service definition (missing '}').");
return false;
}
- if (!ParseServiceStatement(service)) {
+ if (!ParseServiceStatement(service, service_location, containing_file)) {
// This statement failed to parse. Skip it, but keep looping to parse
// other statements.
SkipStatement();
@@ -929,55 +1478,98 @@ bool Parser::ParseServiceBlock(ServiceDescriptorProto* service) {
return true;
}
-bool Parser::ParseServiceStatement(ServiceDescriptorProto* service) {
- if (TryConsume(";")) {
+bool Parser::ParseServiceStatement(ServiceDescriptorProto* service,
+ const LocationRecorder& service_location,
+ const FileDescriptorProto* containing_file) {
+ if (TryConsumeEndOfDeclaration(";", NULL)) {
// empty statement; ignore
return true;
} else if (LookingAt("option")) {
- return ParseOption(service->mutable_options());
+ LocationRecorder location(
+ service_location, ServiceDescriptorProto::kOptionsFieldNumber);
+ return ParseOption(service->mutable_options(), location,
+ containing_file, OPTION_STATEMENT);
} else {
- return ParseServiceMethod(service->add_method());
+ LocationRecorder location(service_location,
+ ServiceDescriptorProto::kMethodFieldNumber, service->method_size());
+ return ParseServiceMethod(service->add_method(), location, containing_file);
}
}
-bool Parser::ParseServiceMethod(MethodDescriptorProto* method) {
+bool Parser::ParseServiceMethod(MethodDescriptorProto* method,
+ const LocationRecorder& method_location,
+ const FileDescriptorProto* containing_file) {
DO(Consume("rpc"));
- RecordLocation(method, DescriptorPool::ErrorCollector::NAME);
- DO(ConsumeIdentifier(method->mutable_name(), "Expected method name."));
+
+ {
+ LocationRecorder location(method_location,
+ MethodDescriptorProto::kNameFieldNumber);
+ location.RecordLegacyLocation(
+ method, DescriptorPool::ErrorCollector::NAME);
+ DO(ConsumeIdentifier(method->mutable_name(), "Expected method name."));
+ }
// Parse input type.
DO(Consume("("));
- RecordLocation(method, DescriptorPool::ErrorCollector::INPUT_TYPE);
- DO(ParseUserDefinedType(method->mutable_input_type()));
+ {
+ LocationRecorder location(method_location,
+ MethodDescriptorProto::kInputTypeFieldNumber);
+ location.RecordLegacyLocation(
+ method, DescriptorPool::ErrorCollector::INPUT_TYPE);
+ DO(ParseUserDefinedType(method->mutable_input_type()));
+ }
DO(Consume(")"));
// Parse output type.
DO(Consume("returns"));
DO(Consume("("));
- RecordLocation(method, DescriptorPool::ErrorCollector::OUTPUT_TYPE);
- DO(ParseUserDefinedType(method->mutable_output_type()));
+ {
+ LocationRecorder location(method_location,
+ MethodDescriptorProto::kOutputTypeFieldNumber);
+ location.RecordLegacyLocation(
+ method, DescriptorPool::ErrorCollector::OUTPUT_TYPE);
+ DO(ParseUserDefinedType(method->mutable_output_type()));
+ }
DO(Consume(")"));
- if (TryConsume("{")) {
+ if (LookingAt("{")) {
// Options!
- while (!TryConsume("}")) {
- if (AtEnd()) {
- AddError("Reached end of input in method options (missing '}').");
- return false;
- }
+ DO(ParseOptions(method_location,
+ containing_file,
+ MethodDescriptorProto::kOptionsFieldNumber,
+ method->mutable_options()));
+ } else {
+ DO(ConsumeEndOfDeclaration(";", &method_location));
+ }
- if (TryConsume(";")) {
- // empty statement; ignore
- } else {
- if (!ParseOption(method->mutable_options())) {
- // This statement failed to parse. Skip it, but keep looping to
- // parse other statements.
- SkipStatement();
- }
+ return true;
+}
+
+
+bool Parser::ParseOptions(const LocationRecorder& parent_location,
+ const FileDescriptorProto* containing_file,
+ const int optionsFieldNumber,
+ Message* mutable_options) {
+ // Options!
+ ConsumeEndOfDeclaration("{", &parent_location);
+ while (!TryConsumeEndOfDeclaration("}", NULL)) {
+ if (AtEnd()) {
+ AddError("Reached end of input in method options (missing '}').");
+ return false;
+ }
+
+ if (TryConsumeEndOfDeclaration(";", NULL)) {
+ // empty statement; ignore
+ } else {
+ LocationRecorder location(parent_location,
+ optionsFieldNumber);
+ if (!ParseOption(mutable_options, location, containing_file,
+ OPTION_STATEMENT)) {
+ // This statement failed to parse. Skip it, but keep looping to
+ // parse other statements.
+ SkipStatement();
}
}
- } else {
- DO(Consume(";"));
}
return true;
@@ -985,7 +1577,8 @@ bool Parser::ParseServiceMethod(MethodDescriptorProto* method) {
// -------------------------------------------------------------------
-bool Parser::ParseLabel(FieldDescriptorProto::Label* label) {
+bool Parser::ParseLabel(FieldDescriptorProto::Label* label,
+ const FileDescriptorProto* containing_file) {
if (TryConsume("optional")) {
*label = FieldDescriptorProto::LABEL_OPTIONAL;
return true;
@@ -1053,7 +1646,9 @@ bool Parser::ParseUserDefinedType(string* type_name) {
// ===================================================================
-bool Parser::ParsePackage(FileDescriptorProto* file) {
+bool Parser::ParsePackage(FileDescriptorProto* file,
+ const LocationRecorder& root_location,
+ const FileDescriptorProto* containing_file) {
if (file->has_package()) {
AddError("Multiple package definitions.");
// Don't append the new package to the old one. Just replace it. Not
@@ -1063,32 +1658,57 @@ bool Parser::ParsePackage(FileDescriptorProto* file) {
DO(Consume("package"));
- RecordLocation(file, DescriptorPool::ErrorCollector::NAME);
+ {
+ LocationRecorder location(root_location,
+ FileDescriptorProto::kPackageFieldNumber);
+ location.RecordLegacyLocation(file, DescriptorPool::ErrorCollector::NAME);
- while (true) {
- string identifier;
- DO(ConsumeIdentifier(&identifier, "Expected identifier."));
- file->mutable_package()->append(identifier);
- if (!TryConsume(".")) break;
- file->mutable_package()->append(".");
+ while (true) {
+ string identifier;
+ DO(ConsumeIdentifier(&identifier, "Expected identifier."));
+ file->mutable_package()->append(identifier);
+ if (!TryConsume(".")) break;
+ file->mutable_package()->append(".");
+ }
+
+ location.EndAt(input_->previous());
+
+ DO(ConsumeEndOfDeclaration(";", &location));
}
- DO(Consume(";"));
return true;
}
-bool Parser::ParseImport(string* import_filename) {
+bool Parser::ParseImport(RepeatedPtrField<string>* dependency,
+ RepeatedField<int32>* public_dependency,
+ RepeatedField<int32>* weak_dependency,
+ const LocationRecorder& root_location,
+ const FileDescriptorProto* containing_file) {
DO(Consume("import"));
- DO(ConsumeString(import_filename,
- "Expected a string naming the file to import."));
- DO(Consume(";"));
- return true;
-}
+ if (LookingAt("public")) {
+ LocationRecorder location(
+ root_location, FileDescriptorProto::kPublicDependencyFieldNumber,
+ public_dependency->size());
+ DO(Consume("public"));
+ *public_dependency->Add() = dependency->size();
+ } else if (LookingAt("weak")) {
+ LocationRecorder location(
+ root_location, FileDescriptorProto::kWeakDependencyFieldNumber,
+ weak_dependency->size());
+ DO(Consume("weak"));
+ *weak_dependency->Add() = dependency->size();
+ }
+ {
+ LocationRecorder location(root_location,
+ FileDescriptorProto::kDependencyFieldNumber,
+ dependency->size());
+ DO(ConsumeString(dependency->Add(),
+ "Expected a string naming the file to import."));
+
+ location.EndAt(input_->previous());
-bool Parser::ParseOption(Message* options) {
- DO(Consume("option"));
- DO(ParseOptionAssignment(options));
- DO(Consume(";"));
+ DO(ConsumeEndOfDeclaration(";", &location));
+ }
return true;
}
diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h
index 72c96d0..d0a2359 100644
--- a/src/google/protobuf/compiler/parser.h
+++ b/src/google/protobuf/compiler/parser.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -40,7 +40,6 @@
#include <map>
#include <string>
#include <utility>
-#include <google/protobuf/stubs/common.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/repeated_field.h>
@@ -74,6 +73,9 @@ class LIBPROTOBUF_EXPORT Parser {
// Optional fetaures:
+ // DEPRECATED: New code should use the SourceCodeInfo embedded in the
+ // FileDescriptorProto.
+ //
// Requests that locations of certain definitions be recorded to the given
// SourceLocationTable while parsing. This can be used to look up exact line
// and column numbers for errors reported by DescriptorPool during validation.
@@ -82,7 +84,7 @@ class LIBPROTOBUF_EXPORT Parser {
source_location_table_ = location_table;
}
- // Requsets that errors be recorded to the given ErrorCollector while
+ // Requests that errors be recorded to the given ErrorCollector while
// parsing. Set to NULL (the default) to discard error messages.
void RecordErrorsTo(io::ErrorCollector* error_collector) {
error_collector_ = error_collector;
@@ -113,6 +115,8 @@ class LIBPROTOBUF_EXPORT Parser {
}
private:
+ class LocationRecorder;
+
// =================================================================
// Error recovery helpers
@@ -161,6 +165,8 @@ class LIBPROTOBUF_EXPORT Parser {
bool ConsumeIdentifier(string* output, const char* error);
// Consume an integer and store its value in "output".
bool ConsumeInteger(int* output, const char* error);
+ // Consume a signed integer and store its value in "output".
+ bool ConsumeSignedInteger(int* output, const char* error);
// Consume a 64-bit integer and store its value in "output". If the value
// is greater than max_value, an error will be reported.
bool ConsumeInteger64(uint64 max_value, uint64* output, const char* error);
@@ -170,6 +176,20 @@ class LIBPROTOBUF_EXPORT Parser {
// Consume a string literal and store its (unescaped) value in "output".
bool ConsumeString(string* output, const char* error);
+ // Consume a token representing the end of the statement. Comments between
+ // this token and the next will be harvested for documentation. The given
+ // LocationRecorder should refer to the declaration that was just parsed;
+ // it will be populated with these comments.
+ //
+ // TODO(kenton): The LocationRecorder is const because historically locations
+ // have been passed around by const reference, for no particularly good
+ // reason. We should probably go through and change them all to mutable
+ // pointer to make this more intuitive.
+ bool TryConsumeEndOfDeclaration(const char* text,
+ const LocationRecorder* location);
+ bool ConsumeEndOfDeclaration(const char* text,
+ const LocationRecorder* location);
+
// -----------------------------------------------------------------
// Error logging helpers
@@ -180,16 +200,67 @@ class LIBPROTOBUF_EXPORT Parser {
// of the current token.
void AddError(const string& error);
- // Record the given line and column and associate it with this descriptor
- // in the SourceLocationTable.
- void RecordLocation(const Message* descriptor,
- DescriptorPool::ErrorCollector::ErrorLocation location,
- int line, int column);
-
- // Record the current line and column and associate it with this descriptor
- // in the SourceLocationTable.
- void RecordLocation(const Message* descriptor,
- DescriptorPool::ErrorCollector::ErrorLocation location);
+ // Records a location in the SourceCodeInfo.location table (see
+ // descriptor.proto). We use RAII to ensure that the start and end locations
+ // are recorded -- the constructor records the start location and the
+ // destructor records the end location. Since the parser is
+ // recursive-descent, this works out beautifully.
+ class LIBPROTOBUF_EXPORT LocationRecorder {
+ public:
+ // Construct the file's "root" location.
+ LocationRecorder(Parser* parser);
+
+ // Construct a location that represents a declaration nested within the
+ // given parent. E.g. a field's location is nested within the location
+ // for a message type. The parent's path will be copied, so you should
+ // call AddPath() only to add the path components leading from the parent
+ // to the child (as opposed to leading from the root to the child).
+ LocationRecorder(const LocationRecorder& parent);
+
+ // Convenience constructors that call AddPath() one or two times.
+ LocationRecorder(const LocationRecorder& parent, int path1);
+ LocationRecorder(const LocationRecorder& parent, int path1, int path2);
+
+ ~LocationRecorder();
+
+ // Add a path component. See SourceCodeInfo.Location.path in
+ // descriptor.proto.
+ void AddPath(int path_component);
+
+ // By default the location is considered to start at the current token at
+ // the time the LocationRecorder is created. StartAt() sets the start
+ // location to the given token instead.
+ void StartAt(const io::Tokenizer::Token& token);
+
+ // Start at the same location as some other LocationRecorder.
+ void StartAt(const LocationRecorder& other);
+
+ // By default the location is considered to end at the previous token at
+ // the time the LocationRecorder is destroyed. EndAt() sets the end
+ // location to the given token instead.
+ void EndAt(const io::Tokenizer::Token& token);
+
+ // Records the start point of this location to the SourceLocationTable that
+ // was passed to RecordSourceLocationsTo(), if any. SourceLocationTable
+ // is an older way of keeping track of source locations which is still
+ // used in some places.
+ void RecordLegacyLocation(const Message* descriptor,
+ DescriptorPool::ErrorCollector::ErrorLocation location);
+
+ // Attaches leading and trailing comments to the location. The two strings
+ // will be swapped into place, so after this is called *leading and
+ // *trailing will be empty.
+ //
+ // TODO(kenton): See comment on TryConsumeEndOfDeclaration(), above, for
+ // why this is const.
+ void AttachComments(string* leading, string* trailing) const;
+
+ private:
+ Parser* parser_;
+ SourceCodeInfo::Location* location_;
+
+ void Init(const LocationRecorder& parent);
+ };
// =================================================================
// Parsers for various language constructs
@@ -210,54 +281,131 @@ class LIBPROTOBUF_EXPORT Parser {
// makes logic much simpler for the caller.
// Parse a top-level message, enum, service, etc.
- bool ParseTopLevelStatement(FileDescriptorProto* file);
+ bool ParseTopLevelStatement(FileDescriptorProto* file,
+ const LocationRecorder& root_location);
// Parse various language high-level language construrcts.
- bool ParseMessageDefinition(DescriptorProto* message);
- bool ParseEnumDefinition(EnumDescriptorProto* enum_type);
- bool ParseServiceDefinition(ServiceDescriptorProto* service);
- bool ParsePackage(FileDescriptorProto* file);
- bool ParseImport(string* import_filename);
- bool ParseOption(Message* options);
+ bool ParseMessageDefinition(DescriptorProto* message,
+ const LocationRecorder& message_location,
+ const FileDescriptorProto* containing_file);
+ bool ParseEnumDefinition(EnumDescriptorProto* enum_type,
+ const LocationRecorder& enum_location,
+ const FileDescriptorProto* containing_file);
+ bool ParseServiceDefinition(ServiceDescriptorProto* service,
+ const LocationRecorder& service_location,
+ const FileDescriptorProto* containing_file);
+ bool ParsePackage(FileDescriptorProto* file,
+ const LocationRecorder& root_location,
+ const FileDescriptorProto* containing_file);
+ bool ParseImport(RepeatedPtrField<string>* dependency,
+ RepeatedField<int32>* public_dependency,
+ RepeatedField<int32>* weak_dependency,
+ const LocationRecorder& root_location,
+ const FileDescriptorProto* containing_file);
+ bool ParseOption(Message* options,
+ const LocationRecorder& options_location,
+ const FileDescriptorProto* containing_file);
// These methods parse the contents of a message, enum, or service type and
// add them to the given object. They consume the entire block including
// the beginning and ending brace.
- bool ParseMessageBlock(DescriptorProto* message);
- bool ParseEnumBlock(EnumDescriptorProto* enum_type);
- bool ParseServiceBlock(ServiceDescriptorProto* service);
+ bool ParseMessageBlock(DescriptorProto* message,
+ const LocationRecorder& message_location,
+ const FileDescriptorProto* containing_file);
+ bool ParseEnumBlock(EnumDescriptorProto* enum_type,
+ const LocationRecorder& enum_location,
+ const FileDescriptorProto* containing_file);
+ bool ParseServiceBlock(ServiceDescriptorProto* service,
+ const LocationRecorder& service_location,
+ const FileDescriptorProto* containing_file);
// Parse one statement within a message, enum, or service block, inclunding
// final semicolon.
- bool ParseMessageStatement(DescriptorProto* message);
- bool ParseEnumStatement(EnumDescriptorProto* message);
- bool ParseServiceStatement(ServiceDescriptorProto* message);
+ bool ParseMessageStatement(DescriptorProto* message,
+ const LocationRecorder& message_location,
+ const FileDescriptorProto* containing_file);
+ bool ParseEnumStatement(EnumDescriptorProto* message,
+ const LocationRecorder& enum_location,
+ const FileDescriptorProto* containing_file);
+ bool ParseServiceStatement(ServiceDescriptorProto* message,
+ const LocationRecorder& service_location,
+ const FileDescriptorProto* containing_file);
// Parse a field of a message. If the field is a group, its type will be
// added to "messages".
+ //
+ // parent_location and location_field_number_for_nested_type are needed when
+ // parsing groups -- we need to generate a nested message type within the
+ // parent and record its location accordingly. Since the parent could be
+ // either a FileDescriptorProto or a DescriptorProto, we must pass in the
+ // correct field number to use.
bool ParseMessageField(FieldDescriptorProto* field,
- RepeatedPtrField<DescriptorProto>* messages);
+ RepeatedPtrField<DescriptorProto>* messages,
+ const LocationRecorder& parent_location,
+ int location_field_number_for_nested_type,
+ const LocationRecorder& field_location,
+ const FileDescriptorProto* containing_file);
+
+ // Like ParseMessageField() but expects the label has already been filled in
+ // by the caller.
+ bool ParseMessageFieldNoLabel(FieldDescriptorProto* field,
+ RepeatedPtrField<DescriptorProto>* messages,
+ const LocationRecorder& parent_location,
+ int location_field_number_for_nested_type,
+ const LocationRecorder& field_location,
+ const FileDescriptorProto* containing_file);
// Parse an "extensions" declaration.
- bool ParseExtensions(DescriptorProto* message);
+ bool ParseExtensions(DescriptorProto* message,
+ const LocationRecorder& extensions_location,
+ const FileDescriptorProto* containing_file);
- // Parse an "extend" declaration.
+ // Parse an "extend" declaration. (See also comments for
+ // ParseMessageField().)
bool ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
- RepeatedPtrField<DescriptorProto>* messages);
+ RepeatedPtrField<DescriptorProto>* messages,
+ const LocationRecorder& parent_location,
+ int location_field_number_for_nested_type,
+ const LocationRecorder& extend_location,
+ const FileDescriptorProto* containing_file);
+
+ // Parse a "oneof" declaration. The caller is responsible for setting
+ // oneof_decl->label() since it will have had to parse the label before it
+ // knew it was parsing a oneof.
+ bool ParseOneof(OneofDescriptorProto* oneof_decl,
+ DescriptorProto* containing_type,
+ int oneof_index,
+ const LocationRecorder& oneof_location,
+ const LocationRecorder& containing_type_location,
+ const FileDescriptorProto* containing_file);
// Parse a single enum value within an enum block.
- bool ParseEnumConstant(EnumValueDescriptorProto* enum_value);
+ bool ParseEnumConstant(EnumValueDescriptorProto* enum_value,
+ const LocationRecorder& enum_value_location,
+ const FileDescriptorProto* containing_file);
// Parse enum constant options, i.e. the list in square brackets at the end
// of the enum constant value definition.
- bool ParseEnumConstantOptions(EnumValueDescriptorProto* value);
+ bool ParseEnumConstantOptions(EnumValueDescriptorProto* value,
+ const LocationRecorder& enum_value_location,
+ const FileDescriptorProto* containing_file);
// Parse a single method within a service definition.
- bool ParseServiceMethod(MethodDescriptorProto* method);
+ bool ParseServiceMethod(MethodDescriptorProto* method,
+ const LocationRecorder& method_location,
+ const FileDescriptorProto* containing_file);
+
+
+ // Parse options of a single method or stream.
+ bool ParseOptions(const LocationRecorder& parent_location,
+ const FileDescriptorProto* containing_file,
+ const int optionsFieldNumber,
+ Message* mutable_options);
// Parse "required", "optional", or "repeated" and fill in "label"
// with the value.
- bool ParseLabel(FieldDescriptorProto::Label* label);
+ bool ParseLabel(FieldDescriptorProto::Label* label,
+ const FileDescriptorProto* containing_file);
// Parse a type name and fill in "type" (if it is a primitive) or
// "type_name" (if it is not) with the type parsed.
@@ -269,39 +417,75 @@ class LIBPROTOBUF_EXPORT Parser {
// Parses field options, i.e. the stuff in square brackets at the end
// of a field definition. Also parses default value.
- bool ParseFieldOptions(FieldDescriptorProto* field);
+ bool ParseFieldOptions(FieldDescriptorProto* field,
+ const LocationRecorder& field_location,
+ const FileDescriptorProto* containing_file);
// Parse the "default" option. This needs special handling because its
// type is the field's type.
- bool ParseDefaultAssignment(FieldDescriptorProto* field);
+ bool ParseDefaultAssignment(FieldDescriptorProto* field,
+ const LocationRecorder& field_location,
+ const FileDescriptorProto* containing_file);
+
+ enum OptionStyle {
+ OPTION_ASSIGNMENT, // just "name = value"
+ OPTION_STATEMENT // "option name = value;"
+ };
// Parse a single option name/value pair, e.g. "ctype = CORD". The name
// identifies a field of the given Message, and the value of that field
// is set to the parsed value.
- bool ParseOptionAssignment(Message* options);
+ bool ParseOption(Message* options,
+ const LocationRecorder& options_location,
+ const FileDescriptorProto* containing_file,
+ OptionStyle style);
// Parses a single part of a multipart option name. A multipart name consists
// of names separated by dots. Each name is either an identifier or a series
// of identifiers separated by dots and enclosed in parentheses. E.g.,
// "foo.(bar.baz).qux".
- bool ParseOptionNamePart(UninterpretedOption* uninterpreted_option);
+ bool ParseOptionNamePart(UninterpretedOption* uninterpreted_option,
+ const LocationRecorder& part_location,
+ const FileDescriptorProto* containing_file);
+
+ // Parses a string surrounded by balanced braces. Strips off the outer
+ // braces and stores the enclosed string in *value.
+ // E.g.,
+ // { foo } *value gets 'foo'
+ // { foo { bar: box } } *value gets 'foo { bar: box }'
+ // {} *value gets ''
+ //
+ // REQUIRES: LookingAt("{")
+ // When finished successfully, we are looking at the first token past
+ // the ending brace.
+ bool ParseUninterpretedBlock(string* value);
// =================================================================
io::Tokenizer* input_;
io::ErrorCollector* error_collector_;
- SourceLocationTable* source_location_table_;
+ SourceCodeInfo* source_code_info_;
+ SourceLocationTable* source_location_table_; // legacy
bool had_errors_;
bool require_syntax_identifier_;
bool stop_after_syntax_identifier_;
string syntax_identifier_;
+ // Leading doc comments for the next declaration. These are not complete
+ // yet; use ConsumeEndOfDeclaration() to get the complete comments.
+ string upcoming_doc_comments_;
+
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Parser);
};
// A table mapping (descriptor, ErrorLocation) pairs -- as reported by
// DescriptorPool when validating descriptors -- to line and column numbers
// within the original source code.
+//
+// This is semi-obsolete: FileDescriptorProto.source_code_info now contains
+// far more complete information about source locations. However, as of this
+// writing you still need to use SourceLocationTable when integrating with
+// DescriptorPool.
class LIBPROTOBUF_EXPORT SourceLocationTable {
public:
SourceLocationTable();
diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc
index e2262b8..9ec29a4 100644
--- a/src/google/protobuf/compiler/parser_unittest.cc
+++ b/src/google/protobuf/compiler/parser_unittest.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -32,8 +32,10 @@
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
+#include <memory>
#include <vector>
#include <algorithm>
+#include <map>
#include <google/protobuf/compiler/parser.h>
@@ -43,8 +45,10 @@
#include <google/protobuf/wire_format.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/unittest.pb.h>
+#include <google/protobuf/unittest_custom_options.pb.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
+#include <google/protobuf/stubs/map_util.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
@@ -118,6 +122,9 @@ class ParserTest : public testing::Test {
EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type);
ASSERT_EQ("", error_collector_.text_);
+ // We don't cover SourceCodeInfo in these tests.
+ actual.clear_source_code_info();
+
// Parse the ASCII representation in order to canonicalize it. We could
// just compare directly to actual.DebugString(), but that would require
// that the caller precisely match the formatting that DebugString()
@@ -430,6 +437,69 @@ TEST_F(ParseMessageTest, FieldOptions) {
"}");
}
+TEST_F(ParseMessageTest, Oneof) {
+ ExpectParsesTo(
+ "message TestMessage {\n"
+ " oneof foo {\n"
+ " int32 a = 1;\n"
+ " string b = 2;\n"
+ " TestMessage c = 3;\n"
+ " group D = 4 { optional int32 i = 5; }\n"
+ " }\n"
+ "}\n",
+
+ "message_type {"
+ " name: \"TestMessage\""
+ " field { name:\"a\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 "
+ " oneof_index:0 }"
+ " field { name:\"b\" label:LABEL_OPTIONAL type:TYPE_STRING number:2 "
+ " oneof_index:0 }"
+ " field { name:\"c\" label:LABEL_OPTIONAL type_name:\"TestMessage\" "
+ " number:3 oneof_index:0 }"
+ " field { name:\"d\" label:LABEL_OPTIONAL type:TYPE_GROUP "
+ " type_name:\"D\" number:4 oneof_index:0 }"
+ " oneof_decl {"
+ " name: \"foo\""
+ " }"
+ " nested_type {"
+ " name: \"D\""
+ " field { name:\"i\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 }"
+ " }"
+ "}");
+}
+
+TEST_F(ParseMessageTest, MultipleOneofs) {
+ ExpectParsesTo(
+ "message TestMessage {\n"
+ " oneof foo {\n"
+ " int32 a = 1;\n"
+ " string b = 2;\n"
+ " }\n"
+ " oneof bar {\n"
+ " int32 c = 3;\n"
+ " string d = 4;\n"
+ " }\n"
+ "}\n",
+
+ "message_type {"
+ " name: \"TestMessage\""
+ " field { name:\"a\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 "
+ " oneof_index:0 }"
+ " field { name:\"b\" label:LABEL_OPTIONAL type:TYPE_STRING number:2 "
+ " oneof_index:0 }"
+ " field { name:\"c\" label:LABEL_OPTIONAL type:TYPE_INT32 number:3 "
+ " oneof_index:1 }"
+ " field { name:\"d\" label:LABEL_OPTIONAL type:TYPE_STRING number:4 "
+ " oneof_index:1 }"
+ " oneof_decl {"
+ " name: \"foo\""
+ " }"
+ " oneof_decl {"
+ " name: \"bar\""
+ " }"
+ "}");
+}
+
TEST_F(ParseMessageTest, Group) {
ExpectParsesTo(
"message TestMessage {\n"
@@ -504,6 +574,31 @@ TEST_F(ParseMessageTest, CompoundExtensionRange) {
"}");
}
+TEST_F(ParseMessageTest, LargerMaxForMessageSetWireFormatMessages) {
+ // Messages using the message_set_wire_format option can accept larger
+ // extension numbers, as the numbers are not encoded as int32 field values
+ // rather than tags.
+ ExpectParsesTo(
+ "message TestMessage {\n"
+ " extensions 4 to max;\n"
+ " option message_set_wire_format = true;\n"
+ "}\n",
+
+ "message_type {"
+ " name: \"TestMessage\""
+ " extension_range { start:4 end: 0x7fffffff }"
+ " options {\n"
+ " uninterpreted_option { \n"
+ " name {\n"
+ " name_part: \"message_set_wire_format\"\n"
+ " is_extension: false\n"
+ " }\n"
+ " identifier_value: \"true\"\n"
+ " }\n"
+ " }\n"
+ "}");
+}
+
TEST_F(ParseMessageTest, Extensions) {
ExpectParsesTo(
"extend Extendee1 { optional int32 foo = 12; }\n"
@@ -544,6 +639,7 @@ TEST_F(ParseMessageTest, MultipleExtensionsOneExtendee) {
" type_name:\"TestMessage\" extendee: \"Extendee1\" }");
}
+
// ===================================================================
typedef ParserTest ParseEnumTest;
@@ -566,6 +662,10 @@ TEST_F(ParseEnumTest, Values) {
" FOO = 13;\n"
" BAR = -10;\n"
" BAZ = 500;\n"
+ " HEX_MAX = 0x7FFFFFFF;\n"
+ " HEX_MIN = -0x80000000;\n"
+ " INT_MAX = 2147483647;\n"
+ " INT_MIN = -2147483648;\n"
"}\n",
"enum_type {"
@@ -573,6 +673,10 @@ TEST_F(ParseEnumTest, Values) {
" value { name:\"FOO\" number:13 }"
" value { name:\"BAR\" number:-10 }"
" value { name:\"BAZ\" number:500 }"
+ " value { name:\"HEX_MAX\" number:2147483647 }"
+ " value { name:\"HEX_MIN\" number:-2147483648 }"
+ " value { name:\"INT_MAX\" number:2147483647 }"
+ " value { name:\"INT_MIN\" number:-2147483648 }"
"}");
}
@@ -626,7 +730,7 @@ TEST_F(ParseServiceTest, SimpleService) {
"}");
}
-TEST_F(ParseServiceTest, Methods) {
+TEST_F(ParseServiceTest, MethodsAndStreams) {
ExpectParsesTo(
"service TestService {\n"
" rpc Foo(In1) returns (Out1);\n"
@@ -642,6 +746,8 @@ TEST_F(ParseServiceTest, Methods) {
"}");
}
+
+
// ===================================================================
// imports and packages
@@ -663,6 +769,20 @@ TEST_F(ParseMiscTest, ParseMultipleImports) {
"dependency: \"baz.proto\"");
}
+TEST_F(ParseMiscTest, ParsePublicImports) {
+ ExpectParsesTo(
+ "import \"foo.proto\";\n"
+ "import public \"bar.proto\";\n"
+ "import \"baz.proto\";\n"
+ "import public \"qux.proto\";\n",
+ "dependency: \"foo.proto\""
+ "dependency: \"bar.proto\""
+ "dependency: \"baz.proto\""
+ "dependency: \"qux.proto\""
+ "public_dependency: 1 "
+ "public_dependency: 3 ");
+}
+
TEST_F(ParseMiscTest, ParsePackage) {
ExpectParsesTo(
"package foo.bar.baz;\n",
@@ -817,7 +937,7 @@ TEST_F(ParseErrorTest, DefaultValueTypeMismatch) {
"message TestMessage {\n"
" optional uint32 foo = 1 [default=true];\n"
"}\n",
- "1:35: Expected integer.\n");
+ "1:35: Expected integer for field default value.\n");
}
TEST_F(ParseErrorTest, DefaultValueNotBoolean) {
@@ -833,7 +953,7 @@ TEST_F(ParseErrorTest, DefaultValueNotString) {
"message TestMessage {\n"
" optional string foo = 1 [default=1];\n"
"}\n",
- "1:35: Expected string.\n");
+ "1:35: Expected string for field default value.\n");
}
TEST_F(ParseErrorTest, DefaultValueUnsignedNegative) {
@@ -862,12 +982,26 @@ TEST_F(ParseErrorTest, DefaultValueTooLarge) {
"6:36: Integer out of range.\n");
}
+TEST_F(ParseErrorTest, EnumValueOutOfRange) {
+ ExpectHasErrors(
+ "enum TestEnum {\n"
+ " HEX_TOO_BIG = 0x80000000;\n"
+ " HEX_TOO_SMALL = -0x80000001;\n"
+ " INT_TOO_BIG = 2147483648;\n"
+ " INT_TOO_SMALL = -2147483649;\n"
+ "}\n",
+ "1:19: Integer out of range.\n"
+ "2:19: Integer out of range.\n"
+ "3:19: Integer out of range.\n"
+ "4:19: Integer out of range.\n");
+}
+
TEST_F(ParseErrorTest, DefaultValueMissing) {
ExpectHasErrors(
"message TestMessage {\n"
" optional uint32 foo = 1 [default=];\n"
"}\n",
- "1:35: Expected integer.\n");
+ "1:35: Expected integer for field default value.\n");
}
TEST_F(ParseErrorTest, DefaultValueForGroup) {
@@ -886,6 +1020,27 @@ TEST_F(ParseErrorTest, DuplicateDefaultValue) {
"1:37: Already set option \"default\".\n");
}
+TEST_F(ParseErrorTest, MissingOneofName) {
+ ExpectHasErrors(
+ "message TestMessage {\n"
+ " oneof {\n"
+ " int32 bar = 1;\n"
+ " }\n"
+ "}\n",
+ "1:8: Expected oneof name.\n");
+}
+
+TEST_F(ParseErrorTest, LabelInOneof) {
+ ExpectHasErrors(
+ "message TestMessage {\n"
+ " oneof foo {\n"
+ " optional int32 bar = 1;\n"
+ " }\n"
+ "}\n",
+ "2:4: Fields in oneofs must not have labels (required / optional "
+ "/ repeated).\n");
+}
+
TEST_F(ParseErrorTest, GroupNotCapitalized) {
ExpectHasErrors(
"message TestMessage {\n"
@@ -930,6 +1085,12 @@ TEST_F(ParseErrorTest, MultipleParseErrors) {
"3:25: Expected \";\".\n");
}
+TEST_F(ParseErrorTest, EofInAggregateValue) {
+ ExpectHasErrors(
+ "option (fileopt) = { i:100\n",
+ "1:0: Unexpected end of stream while parsing aggregate value.\n");
+}
+
// -------------------------------------------------------------------
// Enum errors
@@ -965,6 +1126,7 @@ TEST_F(ParseErrorTest, ServiceMethodPrimitiveParams) {
"1:26: Expected message type.\n");
}
+
TEST_F(ParseErrorTest, EofInMethodOptions) {
ExpectHasErrors(
"service TestService {\n"
@@ -973,6 +1135,7 @@ TEST_F(ParseErrorTest, EofInMethodOptions) {
"1:29: Reached end of input in service definition (missing '}').\n");
}
+
TEST_F(ParseErrorTest, PrimitiveMethodInput) {
ExpectHasErrors(
"service TestService {\n"
@@ -981,6 +1144,7 @@ TEST_F(ParseErrorTest, PrimitiveMethodInput) {
"1:10: Expected message type.\n");
}
+
TEST_F(ParseErrorTest, MethodOptionTypeError) {
// This used to cause an infinite loop.
ExpectHasErrors(
@@ -991,6 +1155,7 @@ TEST_F(ParseErrorTest, MethodOptionTypeError) {
"2:45: Expected \"=\".\n");
}
+
// -------------------------------------------------------------------
// Import and package errors
@@ -1146,6 +1311,7 @@ TEST_F(ParserValidationErrorTest, MethodNameError) {
"3:6: \"Bar\" is already defined in \"Foo\".\n");
}
+
TEST_F(ParserValidationErrorTest, MethodInputTypeError) {
ExpectHasValidationErrors(
"message Baz {}\n"
@@ -1155,6 +1321,7 @@ TEST_F(ParserValidationErrorTest, MethodInputTypeError) {
"2:10: \"Qux\" is not defined.\n");
}
+
TEST_F(ParserValidationErrorTest, MethodOutputTypeError) {
ExpectHasValidationErrors(
"message Baz {}\n"
@@ -1164,17 +1331,94 @@ TEST_F(ParserValidationErrorTest, MethodOutputTypeError) {
"2:23: \"Qux\" is not defined.\n");
}
+
+TEST_F(ParserValidationErrorTest, ResovledUndefinedError) {
+ // Create another file which defines symbol ".base.bar".
+ FileDescriptorProto other_file;
+ other_file.set_name("base.proto");
+ other_file.set_package("base");
+ other_file.add_message_type()->set_name("bar");
+ EXPECT_TRUE(pool_.BuildFile(other_file) != NULL);
+
+ // Define "foo.base" and try "base.bar".
+ // "base.bar" is resolved to "foo.base.bar" which is not defined.
+ ExpectHasValidationErrors(
+ "package foo.base;\n"
+ "import \"base.proto\";\n"
+ "message qux {\n"
+ " optional base.bar baz = 1;\n"
+ " optional .base.bar quz = 2;\n"
+ "}\n",
+ "3:11: \"base.bar\" is resolved to \"foo.base.bar\","
+ " which is not defined. The innermost scope is searched first "
+ "in name resolution. Consider using a leading '.'(i.e., \".base.bar\")"
+ " to start from the outermost scope.\n");
+}
+
+TEST_F(ParserValidationErrorTest, ResovledUndefinedOptionError) {
+ // Build descriptor message in test pool
+ FileDescriptorProto descriptor_proto;
+ DescriptorProto::descriptor()->file()->CopyTo(&descriptor_proto);
+ ASSERT_TRUE(pool_.BuildFile(descriptor_proto) != NULL);
+
+ // base2.proto:
+ // package baz
+ // import google/protobuf/descriptor.proto
+ // message Bar { optional int32 foo = 1; }
+ // extend FileOptions { optional Bar bar = 7672757; }
+ FileDescriptorProto other_file;
+ other_file.set_name("base2.proto");
+ other_file.set_package("baz");
+ other_file.add_dependency();
+ other_file.set_dependency(0, descriptor_proto.name());
+
+ DescriptorProto* message(other_file.add_message_type());
+ message->set_name("Bar");
+ FieldDescriptorProto* field(message->add_field());
+ field->set_name("foo");
+ field->set_number(1);
+ field->set_label(FieldDescriptorProto_Label_LABEL_OPTIONAL);
+ field->set_type(FieldDescriptorProto_Type_TYPE_INT32);
+
+ FieldDescriptorProto* extension(other_file.add_extension());
+ extension->set_name("bar");
+ extension->set_number(7672757);
+ extension->set_label(FieldDescriptorProto_Label_LABEL_OPTIONAL);
+ extension->set_type(FieldDescriptorProto_Type_TYPE_MESSAGE);
+ extension->set_type_name("Bar");
+ extension->set_extendee("google.protobuf.FileOptions");
+
+ EXPECT_TRUE(pool_.BuildFile(other_file) != NULL);
+
+ // qux.proto:
+ // package qux.baz
+ // option (baz.bar).foo = 1;
+ //
+ // Although "baz.bar" is already defined, the lookup code will try
+ // "qux.baz.bar", since it's the match from the innermost scope,
+ // which will cause a symbol not defined error.
+ ExpectHasValidationErrors(
+ "package qux.baz;\n"
+ "import \"base2.proto\";\n"
+ "option (baz.bar).foo = 1;\n",
+ "2:7: Option \"(baz.bar)\" is resolved to \"(qux.baz.bar)\","
+ " which is not defined. The innermost scope is searched first "
+ "in name resolution. Consider using a leading '.'(i.e., \"(.baz.bar)\")"
+ " to start from the outermost scope.\n");
+}
+
// ===================================================================
// Test that the output from FileDescriptor::DebugString() (and all other
// descriptor types) is parseable, and results in the same Descriptor
-// definitions again afoter parsing (not, however, that the order of messages
+// definitions again afoter parsing (note, however, that the order of messages
// cannot be guaranteed to be the same)
typedef ParserTest ParseDecriptorDebugTest;
class CompareDescriptorNames {
public:
- bool operator()(const DescriptorProto* left, const DescriptorProto* right) {
+ bool operator()(const DescriptorProto* left,
+ const DescriptorProto* right) const {
return left->name() < right->name();
}
};
@@ -1218,13 +1462,19 @@ TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) {
FileDescriptorProto parsed;
parser_->Parse(input_.get(), &parsed);
EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type);
- ASSERT_EQ("", error_collector_.text_);
+ ASSERT_EQ("", error_collector_.text_)
+ << "Failed to parse:\n" << debug_string;
// We now have a FileDescriptorProto, but to compare with the expected we
// need to link to a FileDecriptor, then output back to a proto. We'll
// also need to give it the same name as the original.
parsed.set_name("google/protobuf/unittest.proto");
// We need the imported dependency before we can build our parsed proto
+ const FileDescriptor* public_import =
+ protobuf_unittest_import::PublicImportMessage::descriptor()->file();
+ FileDescriptorProto public_import_proto;
+ public_import->CopyTo(&public_import_proto);
+ ASSERT_TRUE(pool_.BuildFile(public_import_proto) != NULL);
const FileDescriptor* import =
protobuf_unittest_import::ImportMessage::descriptor()->file();
FileDescriptorProto import_proto;
@@ -1232,6 +1482,8 @@ TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) {
ASSERT_TRUE(pool_.BuildFile(import_proto) != NULL);
const FileDescriptor* actual = pool_.BuildFile(parsed);
parsed.Clear();
+ ASSERT_TRUE(actual != NULL)
+ << "Failed to validate:\n" << debug_string;
actual->CopyTo(&parsed);
ASSERT_TRUE(actual != NULL);
@@ -1247,6 +1499,1110 @@ TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) {
EXPECT_EQ(expected.DebugString(), parsed.DebugString());
}
+TEST_F(ParseDecriptorDebugTest, TestCustomOptions) {
+ const FileDescriptor* original_file =
+ protobuf_unittest::AggregateMessage::descriptor()->file();
+ FileDescriptorProto expected;
+ original_file->CopyTo(&expected);
+
+ string debug_string = original_file->DebugString();
+
+ // Parse the debug string
+ SetupParser(debug_string.c_str());
+ FileDescriptorProto parsed;
+ parser_->Parse(input_.get(), &parsed);
+ EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type);
+ ASSERT_EQ("", error_collector_.text_);
+
+ // We now have a FileDescriptorProto, but to compare with the expected we
+ // need to link to a FileDecriptor, then output back to a proto. We'll
+ // also need to give it the same name as the original.
+ parsed.set_name(original_file->name());
+
+ // unittest_custom_options.proto depends on descriptor.proto.
+ const FileDescriptor* import = FileDescriptorProto::descriptor()->file();
+ FileDescriptorProto import_proto;
+ import->CopyTo(&import_proto);
+ ASSERT_TRUE(pool_.BuildFile(import_proto) != NULL);
+ const FileDescriptor* actual = pool_.BuildFile(parsed);
+ ASSERT_TRUE(actual != NULL);
+ parsed.Clear();
+ actual->CopyTo(&parsed);
+
+ // The messages might be in different orders, making them hard to compare.
+ // So, sort the messages in the descriptor protos (including nested messages,
+ // recursively).
+ SortMessages(&expected);
+ SortMessages(&parsed);
+
+ EXPECT_EQ(expected.DebugString(), parsed.DebugString());
+}
+
+// ===================================================================
+// SourceCodeInfo tests.
+
+// Follows a path -- as defined by SourceCodeInfo.Location.path -- from a
+// message to a particular sub-field.
+// * If the target is itself a message, sets *output_message to point at it,
+// *output_field to NULL, and *output_index to -1.
+// * Otherwise, if the target is an element of a repeated field, sets
+// *output_message to the containing message, *output_field to the descriptor
+// of the field, and *output_index to the index of the element.
+// * Otherwise, the target is a field (possibly a repeated field, but not any
+// one element). Sets *output_message to the containing message,
+// *output_field to the descriptor of the field, and *output_index to -1.
+// Returns true if the path was valid, false otherwise. A gTest failure is
+// recorded before returning false.
+bool FollowPath(const Message& root,
+ const int* path_begin, const int* path_end,
+ const Message** output_message,
+ const FieldDescriptor** output_field,
+ int* output_index) {
+ if (path_begin == path_end) {
+ // Path refers to this whole message.
+ *output_message = &root;
+ *output_field = NULL;
+ *output_index = -1;
+ return true;
+ }
+
+ const Descriptor* descriptor = root.GetDescriptor();
+ const Reflection* reflection = root.GetReflection();
+
+ const FieldDescriptor* field = descriptor->FindFieldByNumber(*path_begin);
+
+ if (field == NULL) {
+ ADD_FAILURE() << descriptor->name() << " has no field number: "
+ << *path_begin;
+ return false;
+ }
+
+ ++path_begin;
+
+ if (field->is_repeated()) {
+ if (path_begin == path_end) {
+ // Path refers to the whole repeated field.
+ *output_message = &root;
+ *output_field = field;
+ *output_index = -1;
+ return true;
+ }
+
+ int index = *path_begin++;
+ int size = reflection->FieldSize(root, field);
+
+ if (index >= size) {
+ ADD_FAILURE() << descriptor->name() << "." << field->name()
+ << " has size " << size << ", but path contained index: "
+ << index;
+ return false;
+ }
+
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ // Descend into child message.
+ const Message& child = reflection->GetRepeatedMessage(root, field, index);
+ return FollowPath(child, path_begin, path_end,
+ output_message, output_field, output_index);
+ } else if (path_begin == path_end) {
+ // Path refers to this element.
+ *output_message = &root;
+ *output_field = field;
+ *output_index = index;
+ return true;
+ } else {
+ ADD_FAILURE() << descriptor->name() << "." << field->name()
+ << " is not a message; cannot descend into it.";
+ return false;
+ }
+ } else {
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ const Message& child = reflection->GetMessage(root, field);
+ return FollowPath(child, path_begin, path_end,
+ output_message, output_field, output_index);
+ } else if (path_begin == path_end) {
+ // Path refers to this field.
+ *output_message = &root;
+ *output_field = field;
+ *output_index = -1;
+ return true;
+ } else {
+ ADD_FAILURE() << descriptor->name() << "." << field->name()
+ << " is not a message; cannot descend into it.";
+ return false;
+ }
+ }
+}
+
+// Check if two spans are equal.
+bool CompareSpans(const RepeatedField<int>& span1,
+ const RepeatedField<int>& span2) {
+ if (span1.size() != span2.size()) return false;
+ for (int i = 0; i < span1.size(); i++) {
+ if (span1.Get(i) != span2.Get(i)) return false;
+ }
+ return true;
+}
+
+// Test fixture for source info tests, which check that source locations are
+// recorded correctly in FileDescriptorProto.source_code_info.location.
+class SourceInfoTest : public ParserTest {
+ protected:
+ // The parsed file (initialized by Parse()).
+ FileDescriptorProto file_;
+
+ // Parse the given text as a .proto file and populate the spans_ map with
+ // all the source location spans in its SourceCodeInfo table.
+ bool Parse(const char* text) {
+ ExtractMarkers(text);
+ SetupParser(text_without_markers_.c_str());
+ if (!parser_->Parse(input_.get(), &file_)) {
+ return false;
+ }
+
+ const SourceCodeInfo& source_info = file_.source_code_info();
+ for (int i = 0; i < source_info.location_size(); i++) {
+ const SourceCodeInfo::Location& location = source_info.location(i);
+ const Message* descriptor_proto = NULL;
+ const FieldDescriptor* field = NULL;
+ int index = 0;
+ if (!FollowPath(file_, location.path().begin(), location.path().end(),
+ &descriptor_proto, &field, &index)) {
+ return false;
+ }
+
+ spans_.insert(make_pair(SpanKey(*descriptor_proto, field, index),
+ &location));
+ }
+
+ return true;
+ }
+
+ virtual void TearDown() {
+ EXPECT_TRUE(spans_.empty())
+ << "Forgot to call HasSpan() for:\n"
+ << spans_.begin()->second->DebugString();
+ }
+
+ // -----------------------------------------------------------------
+ // HasSpan() checks that the span of source code delimited by the given
+ // tags (comments) correspond via the SourceCodeInfo table to the given
+ // part of the FileDescriptorProto. (If unclear, look at the actual tests;
+ // it should quickly become obvious.)
+
+ bool HasSpan(char start_marker, char end_marker,
+ const Message& descriptor_proto) {
+ return HasSpanWithComment(
+ start_marker, end_marker, descriptor_proto, NULL, -1, NULL, NULL);
+ }
+
+ bool HasSpanWithComment(char start_marker, char end_marker,
+ const Message& descriptor_proto,
+ const char* expected_leading_comments,
+ const char* expected_trailing_comments) {
+ return HasSpanWithComment(
+ start_marker, end_marker, descriptor_proto, NULL, -1,
+ expected_leading_comments, expected_trailing_comments);
+ }
+
+ bool HasSpan(char start_marker, char end_marker,
+ const Message& descriptor_proto, const string& field_name) {
+ return HasSpan(start_marker, end_marker, descriptor_proto, field_name, -1);
+ }
+
+ bool HasSpan(char start_marker, char end_marker,
+ const Message& descriptor_proto, const string& field_name,
+ int index) {
+ return HasSpan(start_marker, end_marker, descriptor_proto,
+ field_name, index, NULL, NULL);
+ }
+
+ bool HasSpan(char start_marker, char end_marker,
+ const Message& descriptor_proto,
+ const string& field_name, int index,
+ const char* expected_leading_comments,
+ const char* expected_trailing_comments) {
+ const FieldDescriptor* field =
+ descriptor_proto.GetDescriptor()->FindFieldByName(field_name);
+ if (field == NULL) {
+ ADD_FAILURE() << descriptor_proto.GetDescriptor()->name()
+ << " has no such field: " << field_name;
+ return false;
+ }
+
+ return HasSpanWithComment(
+ start_marker, end_marker, descriptor_proto, field, index,
+ expected_leading_comments, expected_trailing_comments);
+ }
+
+ bool HasSpan(const Message& descriptor_proto) {
+ return HasSpanWithComment(
+ '\0', '\0', descriptor_proto, NULL, -1, NULL, NULL);
+ }
+
+ bool HasSpan(const Message& descriptor_proto, const string& field_name) {
+ return HasSpan('\0', '\0', descriptor_proto, field_name, -1);
+ }
+
+ bool HasSpan(const Message& descriptor_proto, const string& field_name,
+ int index) {
+ return HasSpan('\0', '\0', descriptor_proto, field_name, index);
+ }
+
+ bool HasSpanWithComment(char start_marker, char end_marker,
+ const Message& descriptor_proto,
+ const FieldDescriptor* field, int index,
+ const char* expected_leading_comments,
+ const char* expected_trailing_comments) {
+ pair<SpanMap::iterator, SpanMap::iterator> range =
+ spans_.equal_range(SpanKey(descriptor_proto, field, index));
+
+ if (start_marker == '\0') {
+ if (range.first == range.second) {
+ return false;
+ } else {
+ spans_.erase(range.first);
+ return true;
+ }
+ } else {
+ pair<int, int> start_pos = FindOrDie(markers_, start_marker);
+ pair<int, int> end_pos = FindOrDie(markers_, end_marker);
+
+ RepeatedField<int> expected_span;
+ expected_span.Add(start_pos.first);
+ expected_span.Add(start_pos.second);
+ if (end_pos.first != start_pos.first) {
+ expected_span.Add(end_pos.first);
+ }
+ expected_span.Add(end_pos.second);
+
+ for (SpanMap::iterator iter = range.first; iter != range.second; ++iter) {
+ if (CompareSpans(expected_span, iter->second->span())) {
+ if (expected_leading_comments == NULL) {
+ EXPECT_FALSE(iter->second->has_leading_comments());
+ } else {
+ EXPECT_TRUE(iter->second->has_leading_comments());
+ EXPECT_EQ(expected_leading_comments,
+ iter->second->leading_comments());
+ }
+ if (expected_trailing_comments == NULL) {
+ EXPECT_FALSE(iter->second->has_trailing_comments());
+ } else {
+ EXPECT_TRUE(iter->second->has_trailing_comments());
+ EXPECT_EQ(expected_trailing_comments,
+ iter->second->trailing_comments());
+ }
+
+ spans_.erase(iter);
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ private:
+ struct SpanKey {
+ const Message* descriptor_proto;
+ const FieldDescriptor* field;
+ int index;
+
+ inline SpanKey() {}
+ inline SpanKey(const Message& descriptor_proto_param,
+ const FieldDescriptor* field_param,
+ int index_param)
+ : descriptor_proto(&descriptor_proto_param), field(field_param),
+ index(index_param) {}
+
+ inline bool operator<(const SpanKey& other) const {
+ if (descriptor_proto < other.descriptor_proto) return true;
+ if (descriptor_proto > other.descriptor_proto) return false;
+ if (field < other.field) return true;
+ if (field > other.field) return false;
+ return index < other.index;
+ }
+ };
+
+ typedef multimap<SpanKey, const SourceCodeInfo::Location*> SpanMap;
+ SpanMap spans_;
+ map<char, pair<int, int> > markers_;
+ string text_without_markers_;
+
+ void ExtractMarkers(const char* text) {
+ markers_.clear();
+ text_without_markers_.clear();
+ int line = 0;
+ int column = 0;
+ while (*text != '\0') {
+ if (*text == '$') {
+ ++text;
+ GOOGLE_CHECK_NE('\0', *text);
+ if (*text == '$') {
+ text_without_markers_ += '$';
+ ++column;
+ } else {
+ markers_[*text] = make_pair(line, column);
+ ++text;
+ GOOGLE_CHECK_EQ('$', *text);
+ }
+ } else if (*text == '\n') {
+ ++line;
+ column = 0;
+ text_without_markers_ += *text;
+ } else {
+ text_without_markers_ += *text;
+ ++column;
+ }
+ ++text;
+ }
+ }
+};
+
+TEST_F(SourceInfoTest, BasicFileDecls) {
+ EXPECT_TRUE(Parse(
+ "$a$syntax = \"proto2\";\n"
+ "package $b$foo.bar$c$;\n"
+ "import $d$\"baz.proto\"$e$;\n"
+ "import $f$\"qux.proto\"$g$;$h$\n"
+ "\n"
+ "// comment ignored\n"));
+
+ EXPECT_TRUE(HasSpan('a', 'h', file_));
+ EXPECT_TRUE(HasSpan('b', 'c', file_, "package"));
+ EXPECT_TRUE(HasSpan('d', 'e', file_, "dependency", 0));
+ EXPECT_TRUE(HasSpan('f', 'g', file_, "dependency", 1));
+}
+
+TEST_F(SourceInfoTest, Messages) {
+ EXPECT_TRUE(Parse(
+ "$a$message $b$Foo$c$ {}$d$\n"
+ "$e$message $f$Bar$g$ {}$h$\n"));
+
+ EXPECT_TRUE(HasSpan('a', 'd', file_.message_type(0)));
+ EXPECT_TRUE(HasSpan('b', 'c', file_.message_type(0), "name"));
+ EXPECT_TRUE(HasSpan('e', 'h', file_.message_type(1)));
+ EXPECT_TRUE(HasSpan('f', 'g', file_.message_type(1), "name"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+}
+
+TEST_F(SourceInfoTest, Fields) {
+ EXPECT_TRUE(Parse(
+ "message Foo {\n"
+ " $a$optional$b$ $c$int32$d$ $e$bar$f$ = $g$1$h$;$i$\n"
+ " $j$repeated$k$ $l$X.Y$m$ $n$baz$o$ = $p$2$q$;$r$\n"
+ "}\n"));
+
+ const FieldDescriptorProto& field1 = file_.message_type(0).field(0);
+ const FieldDescriptorProto& field2 = file_.message_type(0).field(1);
+
+ EXPECT_TRUE(HasSpan('a', 'i', field1));
+ EXPECT_TRUE(HasSpan('a', 'b', field1, "label"));
+ EXPECT_TRUE(HasSpan('c', 'd', field1, "type"));
+ EXPECT_TRUE(HasSpan('e', 'f', field1, "name"));
+ EXPECT_TRUE(HasSpan('g', 'h', field1, "number"));
+
+ EXPECT_TRUE(HasSpan('j', 'r', field2));
+ EXPECT_TRUE(HasSpan('j', 'k', field2, "label"));
+ EXPECT_TRUE(HasSpan('l', 'm', field2, "type_name"));
+ EXPECT_TRUE(HasSpan('n', 'o', field2, "name"));
+ EXPECT_TRUE(HasSpan('p', 'q', field2, "number"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.message_type(0)));
+ EXPECT_TRUE(HasSpan(file_.message_type(0), "name"));
+}
+
+TEST_F(SourceInfoTest, Extensions) {
+ EXPECT_TRUE(Parse(
+ "$a$extend $b$Foo$c$ {\n"
+ " $d$optional$e$ int32 bar = 1;$f$\n"
+ " $g$repeated$h$ X.Y baz = 2;$i$\n"
+ "}$j$\n"
+ "$k$extend $l$Bar$m$ {\n"
+ " $n$optional int32 qux = 1;$o$\n"
+ "}$p$\n"));
+
+ const FieldDescriptorProto& field1 = file_.extension(0);
+ const FieldDescriptorProto& field2 = file_.extension(1);
+ const FieldDescriptorProto& field3 = file_.extension(2);
+
+ EXPECT_TRUE(HasSpan('a', 'j', file_, "extension"));
+ EXPECT_TRUE(HasSpan('k', 'p', file_, "extension"));
+
+ EXPECT_TRUE(HasSpan('d', 'f', field1));
+ EXPECT_TRUE(HasSpan('d', 'e', field1, "label"));
+ EXPECT_TRUE(HasSpan('b', 'c', field1, "extendee"));
+
+ EXPECT_TRUE(HasSpan('g', 'i', field2));
+ EXPECT_TRUE(HasSpan('g', 'h', field2, "label"));
+ EXPECT_TRUE(HasSpan('b', 'c', field2, "extendee"));
+
+ EXPECT_TRUE(HasSpan('n', 'o', field3));
+ EXPECT_TRUE(HasSpan('l', 'm', field3, "extendee"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(field1, "type"));
+ EXPECT_TRUE(HasSpan(field1, "name"));
+ EXPECT_TRUE(HasSpan(field1, "number"));
+ EXPECT_TRUE(HasSpan(field2, "type_name"));
+ EXPECT_TRUE(HasSpan(field2, "name"));
+ EXPECT_TRUE(HasSpan(field2, "number"));
+ EXPECT_TRUE(HasSpan(field3, "label"));
+ EXPECT_TRUE(HasSpan(field3, "type"));
+ EXPECT_TRUE(HasSpan(field3, "name"));
+ EXPECT_TRUE(HasSpan(field3, "number"));
+}
+
+TEST_F(SourceInfoTest, NestedExtensions) {
+ EXPECT_TRUE(Parse(
+ "message Message {\n"
+ " $a$extend $b$Foo$c$ {\n"
+ " $d$optional$e$ int32 bar = 1;$f$\n"
+ " $g$repeated$h$ X.Y baz = 2;$i$\n"
+ " }$j$\n"
+ " $k$extend $l$Bar$m$ {\n"
+ " $n$optional int32 qux = 1;$o$\n"
+ " }$p$\n"
+ "}\n"));
+
+ const FieldDescriptorProto& field1 = file_.message_type(0).extension(0);
+ const FieldDescriptorProto& field2 = file_.message_type(0).extension(1);
+ const FieldDescriptorProto& field3 = file_.message_type(0).extension(2);
+
+ EXPECT_TRUE(HasSpan('a', 'j', file_.message_type(0), "extension"));
+ EXPECT_TRUE(HasSpan('k', 'p', file_.message_type(0), "extension"));
+
+ EXPECT_TRUE(HasSpan('d', 'f', field1));
+ EXPECT_TRUE(HasSpan('d', 'e', field1, "label"));
+ EXPECT_TRUE(HasSpan('b', 'c', field1, "extendee"));
+
+ EXPECT_TRUE(HasSpan('g', 'i', field2));
+ EXPECT_TRUE(HasSpan('g', 'h', field2, "label"));
+ EXPECT_TRUE(HasSpan('b', 'c', field2, "extendee"));
+
+ EXPECT_TRUE(HasSpan('n', 'o', field3));
+ EXPECT_TRUE(HasSpan('l', 'm', field3, "extendee"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.message_type(0)));
+ EXPECT_TRUE(HasSpan(file_.message_type(0), "name"));
+ EXPECT_TRUE(HasSpan(field1, "type"));
+ EXPECT_TRUE(HasSpan(field1, "name"));
+ EXPECT_TRUE(HasSpan(field1, "number"));
+ EXPECT_TRUE(HasSpan(field2, "type_name"));
+ EXPECT_TRUE(HasSpan(field2, "name"));
+ EXPECT_TRUE(HasSpan(field2, "number"));
+ EXPECT_TRUE(HasSpan(field3, "label"));
+ EXPECT_TRUE(HasSpan(field3, "type"));
+ EXPECT_TRUE(HasSpan(field3, "name"));
+ EXPECT_TRUE(HasSpan(field3, "number"));
+}
+
+TEST_F(SourceInfoTest, ExtensionRanges) {
+ EXPECT_TRUE(Parse(
+ "message Message {\n"
+ " $a$extensions $b$1$c$ to $d$4$e$, $f$6$g$;$h$\n"
+ " $i$extensions $j$8$k$ to $l$max$m$;$n$\n"
+ "}\n"));
+
+ const DescriptorProto::ExtensionRange& range1 =
+ file_.message_type(0).extension_range(0);
+ const DescriptorProto::ExtensionRange& range2 =
+ file_.message_type(0).extension_range(1);
+ const DescriptorProto::ExtensionRange& range3 =
+ file_.message_type(0).extension_range(2);
+
+ EXPECT_TRUE(HasSpan('a', 'h', file_.message_type(0), "extension_range"));
+ EXPECT_TRUE(HasSpan('i', 'n', file_.message_type(0), "extension_range"));
+
+ EXPECT_TRUE(HasSpan('b', 'e', range1));
+ EXPECT_TRUE(HasSpan('b', 'c', range1, "start"));
+ EXPECT_TRUE(HasSpan('d', 'e', range1, "end"));
+
+ EXPECT_TRUE(HasSpan('f', 'g', range2));
+ EXPECT_TRUE(HasSpan('f', 'g', range2, "start"));
+ EXPECT_TRUE(HasSpan('f', 'g', range2, "end"));
+
+ EXPECT_TRUE(HasSpan('j', 'm', range3));
+ EXPECT_TRUE(HasSpan('j', 'k', range3, "start"));
+ EXPECT_TRUE(HasSpan('l', 'm', range3, "end"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.message_type(0)));
+ EXPECT_TRUE(HasSpan(file_.message_type(0), "name"));
+}
+
+TEST_F(SourceInfoTest, Oneofs) {
+ EXPECT_TRUE(Parse(
+ "message Foo {\n"
+ " $a$oneof $c$foo$d$ {\n"
+ " $e$int32$f$ $g$a$h$ = $i$1$j$;$k$\n"
+ " }$r$\n"
+ "}\n"));
+
+ const OneofDescriptorProto& oneof_decl = file_.message_type(0).oneof_decl(0);
+ const FieldDescriptorProto& field = file_.message_type(0).field(0);
+
+ EXPECT_TRUE(HasSpan('a', 'r', oneof_decl));
+ EXPECT_TRUE(HasSpan('c', 'd', oneof_decl, "name"));
+
+ EXPECT_TRUE(HasSpan('e', 'k', field));
+ EXPECT_TRUE(HasSpan('e', 'f', field, "type"));
+ EXPECT_TRUE(HasSpan('g', 'h', field, "name"));
+ EXPECT_TRUE(HasSpan('i', 'j', field, "number"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.message_type(0)));
+ EXPECT_TRUE(HasSpan(file_.message_type(0), "name"));
+}
+
+TEST_F(SourceInfoTest, NestedMessages) {
+ EXPECT_TRUE(Parse(
+ "message Foo {\n"
+ " $a$message $b$Bar$c$ {\n"
+ " $d$message $e$Baz$f$ {}$g$\n"
+ " }$h$\n"
+ " $i$message $j$Qux$k$ {}$l$\n"
+ "}\n"));
+
+ const DescriptorProto& bar = file_.message_type(0).nested_type(0);
+ const DescriptorProto& baz = bar.nested_type(0);
+ const DescriptorProto& qux = file_.message_type(0).nested_type(1);
+
+ EXPECT_TRUE(HasSpan('a', 'h', bar));
+ EXPECT_TRUE(HasSpan('b', 'c', bar, "name"));
+ EXPECT_TRUE(HasSpan('d', 'g', baz));
+ EXPECT_TRUE(HasSpan('e', 'f', baz, "name"));
+ EXPECT_TRUE(HasSpan('i', 'l', qux));
+ EXPECT_TRUE(HasSpan('j', 'k', qux, "name"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.message_type(0)));
+ EXPECT_TRUE(HasSpan(file_.message_type(0), "name"));
+}
+
+TEST_F(SourceInfoTest, Groups) {
+ EXPECT_TRUE(Parse(
+ "message Foo {\n"
+ " message Bar {}\n"
+ " $a$optional$b$ $c$group$d$ $e$Baz$f$ = $g$1$h$ {\n"
+ " $i$message Qux {}$j$\n"
+ " }$k$\n"
+ "}\n"));
+
+ const DescriptorProto& bar = file_.message_type(0).nested_type(0);
+ const DescriptorProto& baz = file_.message_type(0).nested_type(1);
+ const DescriptorProto& qux = baz.nested_type(0);
+ const FieldDescriptorProto& field = file_.message_type(0).field(0);
+
+ EXPECT_TRUE(HasSpan('a', 'k', field));
+ EXPECT_TRUE(HasSpan('a', 'b', field, "label"));
+ EXPECT_TRUE(HasSpan('c', 'd', field, "type"));
+ EXPECT_TRUE(HasSpan('e', 'f', field, "name"));
+ EXPECT_TRUE(HasSpan('e', 'f', field, "type_name"));
+ EXPECT_TRUE(HasSpan('g', 'h', field, "number"));
+
+ EXPECT_TRUE(HasSpan('a', 'k', baz));
+ EXPECT_TRUE(HasSpan('e', 'f', baz, "name"));
+ EXPECT_TRUE(HasSpan('i', 'j', qux));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.message_type(0)));
+ EXPECT_TRUE(HasSpan(file_.message_type(0), "name"));
+ EXPECT_TRUE(HasSpan(bar));
+ EXPECT_TRUE(HasSpan(bar, "name"));
+ EXPECT_TRUE(HasSpan(qux, "name"));
+}
+
+TEST_F(SourceInfoTest, Enums) {
+ EXPECT_TRUE(Parse(
+ "$a$enum $b$Foo$c$ {}$d$\n"
+ "$e$enum $f$Bar$g$ {}$h$\n"));
+
+ EXPECT_TRUE(HasSpan('a', 'd', file_.enum_type(0)));
+ EXPECT_TRUE(HasSpan('b', 'c', file_.enum_type(0), "name"));
+ EXPECT_TRUE(HasSpan('e', 'h', file_.enum_type(1)));
+ EXPECT_TRUE(HasSpan('f', 'g', file_.enum_type(1), "name"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+}
+
+TEST_F(SourceInfoTest, EnumValues) {
+ EXPECT_TRUE(Parse(
+ "enum Foo {\n"
+ " $a$BAR$b$ = $c$1$d$;$e$\n"
+ " $f$BAZ$g$ = $h$2$i$;$j$\n"
+ "}"));
+
+ const EnumValueDescriptorProto& bar = file_.enum_type(0).value(0);
+ const EnumValueDescriptorProto& baz = file_.enum_type(0).value(1);
+
+ EXPECT_TRUE(HasSpan('a', 'e', bar));
+ EXPECT_TRUE(HasSpan('a', 'b', bar, "name"));
+ EXPECT_TRUE(HasSpan('c', 'd', bar, "number"));
+ EXPECT_TRUE(HasSpan('f', 'j', baz));
+ EXPECT_TRUE(HasSpan('f', 'g', baz, "name"));
+ EXPECT_TRUE(HasSpan('h', 'i', baz, "number"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.enum_type(0)));
+ EXPECT_TRUE(HasSpan(file_.enum_type(0), "name"));
+}
+
+TEST_F(SourceInfoTest, NestedEnums) {
+ EXPECT_TRUE(Parse(
+ "message Foo {\n"
+ " $a$enum $b$Bar$c$ {}$d$\n"
+ " $e$enum $f$Baz$g$ {}$h$\n"
+ "}\n"));
+
+ const EnumDescriptorProto& bar = file_.message_type(0).enum_type(0);
+ const EnumDescriptorProto& baz = file_.message_type(0).enum_type(1);
+
+ EXPECT_TRUE(HasSpan('a', 'd', bar));
+ EXPECT_TRUE(HasSpan('b', 'c', bar, "name"));
+ EXPECT_TRUE(HasSpan('e', 'h', baz));
+ EXPECT_TRUE(HasSpan('f', 'g', baz, "name"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.message_type(0)));
+ EXPECT_TRUE(HasSpan(file_.message_type(0), "name"));
+}
+
+TEST_F(SourceInfoTest, Services) {
+ EXPECT_TRUE(Parse(
+ "$a$service $b$Foo$c$ {}$d$\n"
+ "$e$service $f$Bar$g$ {}$h$\n"));
+
+ EXPECT_TRUE(HasSpan('a', 'd', file_.service(0)));
+ EXPECT_TRUE(HasSpan('b', 'c', file_.service(0), "name"));
+ EXPECT_TRUE(HasSpan('e', 'h', file_.service(1)));
+ EXPECT_TRUE(HasSpan('f', 'g', file_.service(1), "name"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+}
+
+TEST_F(SourceInfoTest, MethodsAndStreams) {
+ EXPECT_TRUE(Parse(
+ "service Foo {\n"
+ " $a$rpc $b$Bar$c$($d$X$e$) returns($f$Y$g$);$h$"
+ " $i$rpc $j$Baz$k$($l$Z$m$) returns($n$W$o$);$p$"
+ "}"));
+
+ const MethodDescriptorProto& bar = file_.service(0).method(0);
+ const MethodDescriptorProto& baz = file_.service(0).method(1);
+
+ EXPECT_TRUE(HasSpan('a', 'h', bar));
+ EXPECT_TRUE(HasSpan('b', 'c', bar, "name"));
+ EXPECT_TRUE(HasSpan('d', 'e', bar, "input_type"));
+ EXPECT_TRUE(HasSpan('f', 'g', bar, "output_type"));
+
+ EXPECT_TRUE(HasSpan('i', 'p', baz));
+ EXPECT_TRUE(HasSpan('j', 'k', baz, "name"));
+ EXPECT_TRUE(HasSpan('l', 'm', baz, "input_type"));
+ EXPECT_TRUE(HasSpan('n', 'o', baz, "output_type"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.service(0)));
+ EXPECT_TRUE(HasSpan(file_.service(0), "name"));
+}
+
+
+TEST_F(SourceInfoTest, Options) {
+ EXPECT_TRUE(Parse(
+ "$a$option $b$foo$c$.$d$($e$bar.baz$f$)$g$ = "
+ "$h$123$i$;$j$\n"
+ "$k$option qux = $l$-123$m$;$n$\n"
+ "$o$option corge = $p$abc$q$;$r$\n"
+ "$s$option grault = $t$'blah'$u$;$v$\n"
+ "$w$option garply = $x${ yadda yadda }$y$;$z$\n"
+ "$0$option waldo = $1$123.0$2$;$3$\n"
+ ));
+
+ const UninterpretedOption& option1 = file_.options().uninterpreted_option(0);
+ const UninterpretedOption& option2 = file_.options().uninterpreted_option(1);
+ const UninterpretedOption& option3 = file_.options().uninterpreted_option(2);
+ const UninterpretedOption& option4 = file_.options().uninterpreted_option(3);
+ const UninterpretedOption& option5 = file_.options().uninterpreted_option(4);
+ const UninterpretedOption& option6 = file_.options().uninterpreted_option(5);
+
+ EXPECT_TRUE(HasSpan('a', 'j', file_.options()));
+ EXPECT_TRUE(HasSpan('a', 'j', option1));
+ EXPECT_TRUE(HasSpan('b', 'g', option1, "name"));
+ EXPECT_TRUE(HasSpan('b', 'c', option1.name(0)));
+ EXPECT_TRUE(HasSpan('b', 'c', option1.name(0), "name_part"));
+ EXPECT_TRUE(HasSpan('d', 'g', option1.name(1)));
+ EXPECT_TRUE(HasSpan('e', 'f', option1.name(1), "name_part"));
+ EXPECT_TRUE(HasSpan('h', 'i', option1, "positive_int_value"));
+
+ EXPECT_TRUE(HasSpan('k', 'n', file_.options()));
+ EXPECT_TRUE(HasSpan('l', 'm', option2, "negative_int_value"));
+
+ EXPECT_TRUE(HasSpan('o', 'r', file_.options()));
+ EXPECT_TRUE(HasSpan('p', 'q', option3, "identifier_value"));
+
+ EXPECT_TRUE(HasSpan('s', 'v', file_.options()));
+ EXPECT_TRUE(HasSpan('t', 'u', option4, "string_value"));
+
+ EXPECT_TRUE(HasSpan('w', 'z', file_.options()));
+ EXPECT_TRUE(HasSpan('x', 'y', option5, "aggregate_value"));
+
+ EXPECT_TRUE(HasSpan('0', '3', file_.options()));
+ EXPECT_TRUE(HasSpan('1', '2', option6, "double_value"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(option2));
+ EXPECT_TRUE(HasSpan(option3));
+ EXPECT_TRUE(HasSpan(option4));
+ EXPECT_TRUE(HasSpan(option5));
+ EXPECT_TRUE(HasSpan(option6));
+ EXPECT_TRUE(HasSpan(option2, "name"));
+ EXPECT_TRUE(HasSpan(option3, "name"));
+ EXPECT_TRUE(HasSpan(option4, "name"));
+ EXPECT_TRUE(HasSpan(option5, "name"));
+ EXPECT_TRUE(HasSpan(option6, "name"));
+ EXPECT_TRUE(HasSpan(option2.name(0)));
+ EXPECT_TRUE(HasSpan(option3.name(0)));
+ EXPECT_TRUE(HasSpan(option4.name(0)));
+ EXPECT_TRUE(HasSpan(option5.name(0)));
+ EXPECT_TRUE(HasSpan(option6.name(0)));
+ EXPECT_TRUE(HasSpan(option2.name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(option3.name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(option4.name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(option5.name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(option6.name(0), "name_part"));
+}
+
+TEST_F(SourceInfoTest, ScopedOptions) {
+ EXPECT_TRUE(Parse(
+ "message Foo {\n"
+ " $a$option mopt = 1;$b$\n"
+ "}\n"
+ "enum Bar {\n"
+ " $c$option eopt = 1;$d$\n"
+ "}\n"
+ "service Baz {\n"
+ " $e$option sopt = 1;$f$\n"
+ " rpc M(X) returns(Y) {\n"
+ " $g$option mopt = 1;$h$\n"
+ " }\n"
+ "}\n"));
+
+ EXPECT_TRUE(HasSpan('a', 'b', file_.message_type(0).options()));
+ EXPECT_TRUE(HasSpan('c', 'd', file_.enum_type(0).options()));
+ EXPECT_TRUE(HasSpan('e', 'f', file_.service(0).options()));
+ EXPECT_TRUE(HasSpan('g', 'h', file_.service(0).method(0).options()));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.message_type(0)));
+ EXPECT_TRUE(HasSpan(file_.message_type(0), "name"));
+ EXPECT_TRUE(HasSpan(file_.message_type(0).options()
+ .uninterpreted_option(0)));
+ EXPECT_TRUE(HasSpan(file_.message_type(0).options()
+ .uninterpreted_option(0), "name"));
+ EXPECT_TRUE(HasSpan(file_.message_type(0).options()
+ .uninterpreted_option(0).name(0)));
+ EXPECT_TRUE(HasSpan(file_.message_type(0).options()
+ .uninterpreted_option(0).name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(file_.message_type(0).options()
+ .uninterpreted_option(0), "positive_int_value"));
+ EXPECT_TRUE(HasSpan(file_.enum_type(0)));
+ EXPECT_TRUE(HasSpan(file_.enum_type(0), "name"));
+ EXPECT_TRUE(HasSpan(file_.enum_type(0).options()
+ .uninterpreted_option(0)));
+ EXPECT_TRUE(HasSpan(file_.enum_type(0).options()
+ .uninterpreted_option(0), "name"));
+ EXPECT_TRUE(HasSpan(file_.enum_type(0).options()
+ .uninterpreted_option(0).name(0)));
+ EXPECT_TRUE(HasSpan(file_.enum_type(0).options()
+ .uninterpreted_option(0).name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(file_.enum_type(0).options()
+ .uninterpreted_option(0), "positive_int_value"));
+ EXPECT_TRUE(HasSpan(file_.service(0)));
+ EXPECT_TRUE(HasSpan(file_.service(0), "name"));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(0)));
+ EXPECT_TRUE(HasSpan(file_.service(0).options()
+ .uninterpreted_option(0)));
+ EXPECT_TRUE(HasSpan(file_.service(0).options()
+ .uninterpreted_option(0), "name"));
+ EXPECT_TRUE(HasSpan(file_.service(0).options()
+ .uninterpreted_option(0).name(0)));
+ EXPECT_TRUE(HasSpan(file_.service(0).options()
+ .uninterpreted_option(0).name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(file_.service(0).options()
+ .uninterpreted_option(0), "positive_int_value"));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(0), "name"));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(0), "input_type"));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(0), "output_type"));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(0).options()
+ .uninterpreted_option(0)));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(0).options()
+ .uninterpreted_option(0), "name"));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(0).options()
+ .uninterpreted_option(0).name(0)));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(0).options()
+ .uninterpreted_option(0).name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(0).options()
+ .uninterpreted_option(0), "positive_int_value"));
+}
+
+TEST_F(SourceInfoTest, FieldOptions) {
+ // The actual "name = value" pairs are parsed by the same code as for
+ // top-level options so we won't re-test that -- just make sure that the
+ // syntax used for field options is understood.
+ EXPECT_TRUE(Parse(
+ "message Foo {"
+ " optional int32 bar = 1 "
+ "$a$[default=$b$123$c$,$d$opt1=123$e$,"
+ "$f$opt2='hi'$g$]$h$;"
+ "}\n"
+ ));
+
+ const FieldDescriptorProto& field = file_.message_type(0).field(0);
+ const UninterpretedOption& option1 = field.options().uninterpreted_option(0);
+ const UninterpretedOption& option2 = field.options().uninterpreted_option(1);
+
+ EXPECT_TRUE(HasSpan('a', 'h', field.options()));
+ EXPECT_TRUE(HasSpan('b', 'c', field, "default_value"));
+ EXPECT_TRUE(HasSpan('d', 'e', option1));
+ EXPECT_TRUE(HasSpan('f', 'g', option2));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.message_type(0)));
+ EXPECT_TRUE(HasSpan(file_.message_type(0), "name"));
+ EXPECT_TRUE(HasSpan(field));
+ EXPECT_TRUE(HasSpan(field, "label"));
+ EXPECT_TRUE(HasSpan(field, "type"));
+ EXPECT_TRUE(HasSpan(field, "name"));
+ EXPECT_TRUE(HasSpan(field, "number"));
+ EXPECT_TRUE(HasSpan(option1, "name"));
+ EXPECT_TRUE(HasSpan(option2, "name"));
+ EXPECT_TRUE(HasSpan(option1.name(0)));
+ EXPECT_TRUE(HasSpan(option2.name(0)));
+ EXPECT_TRUE(HasSpan(option1.name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(option2.name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(option1, "positive_int_value"));
+ EXPECT_TRUE(HasSpan(option2, "string_value"));
+}
+
+TEST_F(SourceInfoTest, EnumValueOptions) {
+ // The actual "name = value" pairs are parsed by the same code as for
+ // top-level options so we won't re-test that -- just make sure that the
+ // syntax used for enum options is understood.
+ EXPECT_TRUE(Parse(
+ "enum Foo {"
+ " BAR = 1 $a$[$b$opt1=123$c$,$d$opt2='hi'$e$]$f$;"
+ "}\n"
+ ));
+
+ const EnumValueDescriptorProto& value = file_.enum_type(0).value(0);
+ const UninterpretedOption& option1 = value.options().uninterpreted_option(0);
+ const UninterpretedOption& option2 = value.options().uninterpreted_option(1);
+
+ EXPECT_TRUE(HasSpan('a', 'f', value.options()));
+ EXPECT_TRUE(HasSpan('b', 'c', option1));
+ EXPECT_TRUE(HasSpan('d', 'e', option2));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.enum_type(0)));
+ EXPECT_TRUE(HasSpan(file_.enum_type(0), "name"));
+ EXPECT_TRUE(HasSpan(value));
+ EXPECT_TRUE(HasSpan(value, "name"));
+ EXPECT_TRUE(HasSpan(value, "number"));
+ EXPECT_TRUE(HasSpan(option1, "name"));
+ EXPECT_TRUE(HasSpan(option2, "name"));
+ EXPECT_TRUE(HasSpan(option1.name(0)));
+ EXPECT_TRUE(HasSpan(option2.name(0)));
+ EXPECT_TRUE(HasSpan(option1.name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(option2.name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(option1, "positive_int_value"));
+ EXPECT_TRUE(HasSpan(option2, "string_value"));
+}
+
+TEST_F(SourceInfoTest, DocComments) {
+ EXPECT_TRUE(Parse(
+ "// Foo leading\n"
+ "// line 2\n"
+ "$a$message Foo {\n"
+ " // Foo trailing\n"
+ " // line 2\n"
+ "\n"
+ " // ignored\n"
+ "\n"
+ " // bar leading\n"
+ " $b$optional int32 bar = 1;$c$\n"
+ " // bar trailing\n"
+ "}$d$\n"
+ "// ignored\n"
+ ));
+
+ const DescriptorProto& foo = file_.message_type(0);
+ const FieldDescriptorProto& bar = foo.field(0);
+
+ EXPECT_TRUE(HasSpanWithComment('a', 'd', foo,
+ " Foo leading\n line 2\n",
+ " Foo trailing\n line 2\n"));
+ EXPECT_TRUE(HasSpanWithComment('b', 'c', bar,
+ " bar leading\n",
+ " bar trailing\n"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(foo, "name"));
+ EXPECT_TRUE(HasSpan(bar, "label"));
+ EXPECT_TRUE(HasSpan(bar, "type"));
+ EXPECT_TRUE(HasSpan(bar, "name"));
+ EXPECT_TRUE(HasSpan(bar, "number"));
+}
+
+TEST_F(SourceInfoTest, DocComments2) {
+ EXPECT_TRUE(Parse(
+ "// ignored\n"
+ "syntax = \"proto2\";\n"
+ "// Foo leading\n"
+ "// line 2\n"
+ "$a$message Foo {\n"
+ " /* Foo trailing\n"
+ " * line 2 */\n"
+ " // ignored\n"
+ " /* bar leading\n"
+ " */"
+ " $b$optional int32 bar = 1;$c$ // bar trailing\n"
+ " // ignored\n"
+ "}$d$\n"
+ "// ignored\n"
+ "\n"
+ "// option leading\n"
+ "$e$option baz = 123;$f$\n"
+ "// option trailing\n"
+ ));
+
+ const DescriptorProto& foo = file_.message_type(0);
+ const FieldDescriptorProto& bar = foo.field(0);
+ const UninterpretedOption& baz = file_.options().uninterpreted_option(0);
+
+ EXPECT_TRUE(HasSpanWithComment('a', 'd', foo,
+ " Foo leading\n line 2\n",
+ " Foo trailing\n line 2 "));
+ EXPECT_TRUE(HasSpanWithComment('b', 'c', bar,
+ " bar leading\n",
+ " bar trailing\n"));
+ EXPECT_TRUE(HasSpanWithComment('e', 'f', baz,
+ " option leading\n",
+ " option trailing\n"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(foo, "name"));
+ EXPECT_TRUE(HasSpan(bar, "label"));
+ EXPECT_TRUE(HasSpan(bar, "type"));
+ EXPECT_TRUE(HasSpan(bar, "name"));
+ EXPECT_TRUE(HasSpan(bar, "number"));
+ EXPECT_TRUE(HasSpan(file_.options()));
+ EXPECT_TRUE(HasSpan(baz, "name"));
+ EXPECT_TRUE(HasSpan(baz.name(0)));
+ EXPECT_TRUE(HasSpan(baz.name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(baz, "positive_int_value"));
+}
+
+TEST_F(SourceInfoTest, DocComments3) {
+ EXPECT_TRUE(Parse(
+ "$a$message Foo {\n"
+ " // bar leading\n"
+ " $b$optional int32 bar = 1 [(baz.qux) = {}];$c$\n"
+ " // bar trailing\n"
+ "}$d$\n"
+ "// ignored\n"
+ ));
+
+ const DescriptorProto& foo = file_.message_type(0);
+ const FieldDescriptorProto& bar = foo.field(0);
+
+ EXPECT_TRUE(HasSpanWithComment('b', 'c', bar,
+ " bar leading\n",
+ " bar trailing\n"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(foo));
+ EXPECT_TRUE(HasSpan(foo, "name"));
+ EXPECT_TRUE(HasSpan(bar, "label"));
+ EXPECT_TRUE(HasSpan(bar, "type"));
+ EXPECT_TRUE(HasSpan(bar, "name"));
+ EXPECT_TRUE(HasSpan(bar, "number"));
+ EXPECT_TRUE(HasSpan(bar.options()));
+ EXPECT_TRUE(HasSpan(bar.options().uninterpreted_option(0)));
+ EXPECT_TRUE(HasSpan(bar.options().uninterpreted_option(0), "name"));
+ EXPECT_TRUE(HasSpan(bar.options().uninterpreted_option(0).name(0)));
+ EXPECT_TRUE(HasSpan(
+ bar.options().uninterpreted_option(0).name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(
+ bar.options().uninterpreted_option(0), "aggregate_value"));
+}
+
+TEST_F(SourceInfoTest, DocCommentsOneof) {
+ EXPECT_TRUE(Parse(
+ "// ignored\n"
+ "syntax = \"proto2\";\n"
+ "// Foo leading\n"
+ "$a$message Foo {\n"
+ " /* Foo trailing\n"
+ " */\n"
+ " // ignored\n"
+ " /* bar leading\n"
+ " * line 2 */\n"
+ " $b$oneof bar {\n"
+ " /* bar trailing\n"
+ " * line 2 */\n"
+ " // ignored\n"
+ " /* bar_int leading\n"
+ " */\n"
+ " $c$int32 bar_int = 1;$d$ // bar_int trailing\n"
+ " // ignored\n"
+ " }$e$\n"
+ "}$f$\n"));
+
+ const DescriptorProto& foo = file_.message_type(0);
+ const OneofDescriptorProto& bar = foo.oneof_decl(0);
+ const FieldDescriptorProto& bar_int = foo.field(0);
+
+ EXPECT_TRUE(HasSpanWithComment('a', 'f', foo,
+ " Foo leading\n",
+ " Foo trailing\n"));
+ EXPECT_TRUE(HasSpanWithComment('b', 'e', bar,
+ " bar leading\n line 2 ",
+ " bar trailing\n line 2 "));
+ EXPECT_TRUE(HasSpanWithComment('c', 'd', bar_int,
+ " bar_int leading\n",
+ " bar_int trailing\n"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(foo, "name"));
+ EXPECT_TRUE(HasSpan(bar, "name"));
+ EXPECT_TRUE(HasSpan(bar_int, "type"));
+ EXPECT_TRUE(HasSpan(bar_int, "name"));
+ EXPECT_TRUE(HasSpan(bar_int, "number"));
+}
+
// ===================================================================
} // anonymous namespace
diff --git a/src/google/protobuf/compiler/plugin.cc b/src/google/protobuf/compiler/plugin.cc
index a4aedaf..9011a6b 100644
--- a/src/google/protobuf/compiler/plugin.cc
+++ b/src/google/protobuf/compiler/plugin.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -59,13 +59,15 @@ namespace google {
namespace protobuf {
namespace compiler {
-class GeneratorResponseOutputDirectory : public OutputDirectory {
+class GeneratorResponseContext : public GeneratorContext {
public:
- GeneratorResponseOutputDirectory(CodeGeneratorResponse* response)
- : response_(response) {}
- virtual ~GeneratorResponseOutputDirectory() {}
+ GeneratorResponseContext(CodeGeneratorResponse* response,
+ const vector<const FileDescriptor*>& parsed_files)
+ : response_(response),
+ parsed_files_(parsed_files) {}
+ virtual ~GeneratorResponseContext() {}
- // implements OutputDirectory --------------------------------------
+ // implements GeneratorContext --------------------------------------
virtual io::ZeroCopyOutputStream* Open(const string& filename) {
CodeGeneratorResponse::File* file = response_->add_file();
@@ -81,8 +83,13 @@ class GeneratorResponseOutputDirectory : public OutputDirectory {
return new io::StringOutputStream(file->mutable_content());
}
+ void ListParsedFiles(vector<const FileDescriptor*>* output) {
+ *output = parsed_files_;
+ }
+
private:
CodeGeneratorResponse* response_;
+ const vector<const FileDescriptor*>& parsed_files_;
};
int PluginMain(int argc, char* argv[], const CodeGenerator* generator) {
@@ -112,22 +119,26 @@ int PluginMain(int argc, char* argv[], const CodeGenerator* generator) {
}
}
- CodeGeneratorResponse response;
- GeneratorResponseOutputDirectory output_directory(&response);
-
+ vector<const FileDescriptor*> parsed_files;
for (int i = 0; i < request.file_to_generate_size(); i++) {
- const FileDescriptor* file =
- pool.FindFileByName(request.file_to_generate(i));
- if (file == NULL) {
+ parsed_files.push_back(pool.FindFileByName(request.file_to_generate(i)));
+ if (parsed_files.back() == NULL) {
cerr << argv[0] << ": protoc asked plugin to generate a file but "
"did not provide a descriptor for the file: "
<< request.file_to_generate(i) << endl;
return 1;
}
+ }
+
+ CodeGeneratorResponse response;
+ GeneratorResponseContext context(&response, parsed_files);
+
+ for (int i = 0; i < parsed_files.size(); i++) {
+ const FileDescriptor* file = parsed_files[i];
string error;
bool succeeded = generator->Generate(
- file, request.parameter(), &output_directory, &error);
+ file, request.parameter(), &context, &error);
if (!succeeded && error.empty()) {
error = "Code generator returned false but provided no error "
diff --git a/src/google/protobuf/compiler/plugin.h b/src/google/protobuf/compiler/plugin.h
index 7c40333..679f9bd 100644
--- a/src/google/protobuf/compiler/plugin.h
+++ b/src/google/protobuf/compiler/plugin.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -56,7 +56,6 @@
#define GOOGLE_PROTOBUF_COMPILER_PLUGIN_H__
#include <google/protobuf/stubs/common.h>
-
namespace google {
namespace protobuf {
namespace compiler {
diff --git a/src/google/protobuf/compiler/plugin.pb.cc b/src/google/protobuf/compiler/plugin.pb.cc
index 6b0dd55..b5cd01b 100644
--- a/src/google/protobuf/compiler/plugin.pb.cc
+++ b/src/google/protobuf/compiler/plugin.pb.cc
@@ -1,11 +1,17 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: google/protobuf/compiler/plugin.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
#include "google/protobuf/compiler/plugin.pb.h"
+
+#include <algorithm>
+
+#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
#include <google/protobuf/reflection_ops.h>
#include <google/protobuf/wire_format.h>
// @@protoc_insertion_point(includes)
@@ -133,7 +139,9 @@ void protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto() {
"atorResponse\022\r\n\005error\030\001 \001(\t\022B\n\004file\030\017 \003("
"\01324.google.protobuf.compiler.CodeGenerat"
"orResponse.File\032>\n\004File\022\014\n\004name\030\001 \001(\t\022\027\n"
- "\017insertion_point\030\002 \001(\t\022\017\n\007content\030\017 \001(\t", 399);
+ "\017insertion_point\030\002 \001(\t\022\017\n\007content\030\017 \001(\tB"
+ ",\n\034com.google.protobuf.compilerB\014PluginP"
+ "rotos", 445);
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"google/protobuf/compiler/plugin.proto", &protobuf_RegisterTypes);
CodeGeneratorRequest::default_instance_ = new CodeGeneratorRequest();
@@ -152,10 +160,8 @@ struct StaticDescriptorInitializer_google_2fprotobuf_2fcompiler_2fplugin_2eproto
}
} static_descriptor_initializer_google_2fprotobuf_2fcompiler_2fplugin_2eproto_;
-
// ===================================================================
-const ::std::string CodeGeneratorRequest::_default_parameter_;
#ifndef _MSC_VER
const int CodeGeneratorRequest::kFileToGenerateFieldNumber;
const int CodeGeneratorRequest::kParameterFieldNumber;
@@ -165,6 +171,7 @@ const int CodeGeneratorRequest::kProtoFileFieldNumber;
CodeGeneratorRequest::CodeGeneratorRequest()
: ::google::protobuf::Message() {
SharedCtor();
+ // @@protoc_insertion_point(constructor:google.protobuf.compiler.CodeGeneratorRequest)
}
void CodeGeneratorRequest::InitAsDefaultInstance() {
@@ -174,20 +181,23 @@ CodeGeneratorRequest::CodeGeneratorRequest(const CodeGeneratorRequest& from)
: ::google::protobuf::Message() {
SharedCtor();
MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:google.protobuf.compiler.CodeGeneratorRequest)
}
void CodeGeneratorRequest::SharedCtor() {
+ ::google::protobuf::internal::GetEmptyString();
_cached_size_ = 0;
- parameter_ = const_cast< ::std::string*>(&_default_parameter_);
+ parameter_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
::memset(_has_bits_, 0, sizeof(_has_bits_));
}
CodeGeneratorRequest::~CodeGeneratorRequest() {
+ // @@protoc_insertion_point(destructor:google.protobuf.compiler.CodeGeneratorRequest)
SharedDtor();
}
void CodeGeneratorRequest::SharedDtor() {
- if (parameter_ != &_default_parameter_) {
+ if (parameter_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
delete parameter_;
}
if (this != default_instance_) {
@@ -205,7 +215,8 @@ const ::google::protobuf::Descriptor* CodeGeneratorRequest::descriptor() {
}
const CodeGeneratorRequest& CodeGeneratorRequest::default_instance() {
- if (default_instance_ == NULL) protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); return *default_instance_;
+ if (default_instance_ == NULL) protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto();
+ return *default_instance_;
}
CodeGeneratorRequest* CodeGeneratorRequest::default_instance_ = NULL;
@@ -215,11 +226,9 @@ CodeGeneratorRequest* CodeGeneratorRequest::New() const {
}
void CodeGeneratorRequest::Clear() {
- if (_has_bits_[1 / 32] & (0xffu << (1 % 32))) {
- if (_has_bit(1)) {
- if (parameter_ != &_default_parameter_) {
- parameter_->clear();
- }
+ if (has_parameter()) {
+ if (parameter_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ parameter_->clear();
}
}
file_to_generate_.Clear();
@@ -230,65 +239,70 @@ void CodeGeneratorRequest::Clear() {
bool CodeGeneratorRequest::MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input) {
-#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
::google::protobuf::uint32 tag;
- while ((tag = input->ReadTag()) != 0) {
+ // @@protoc_insertion_point(parse_start:google.protobuf.compiler.CodeGeneratorRequest)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
// repeated string file_to_generate = 1;
case 1: {
- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+ if (tag == 10) {
parse_file_to_generate:
DO_(::google::protobuf::internal::WireFormatLite::ReadString(
input, this->add_file_to_generate()));
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
- this->file_to_generate(0).data(), this->file_to_generate(0).length(),
- ::google::protobuf::internal::WireFormat::PARSE);
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+ this->file_to_generate(this->file_to_generate_size() - 1).data(),
+ this->file_to_generate(this->file_to_generate_size() - 1).length(),
+ ::google::protobuf::internal::WireFormat::PARSE,
+ "file_to_generate");
} else {
- goto handle_uninterpreted;
+ goto handle_unusual;
}
if (input->ExpectTag(10)) goto parse_file_to_generate;
if (input->ExpectTag(18)) goto parse_parameter;
break;
}
-
+
// optional string parameter = 2;
case 2: {
- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+ if (tag == 18) {
parse_parameter:
DO_(::google::protobuf::internal::WireFormatLite::ReadString(
input, this->mutable_parameter()));
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->parameter().data(), this->parameter().length(),
- ::google::protobuf::internal::WireFormat::PARSE);
+ ::google::protobuf::internal::WireFormat::PARSE,
+ "parameter");
} else {
- goto handle_uninterpreted;
+ goto handle_unusual;
}
if (input->ExpectTag(122)) goto parse_proto_file;
break;
}
-
+
// repeated .google.protobuf.FileDescriptorProto proto_file = 15;
case 15: {
- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+ if (tag == 122) {
parse_proto_file:
DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
input, add_proto_file()));
} else {
- goto handle_uninterpreted;
+ goto handle_unusual;
}
if (input->ExpectTag(122)) goto parse_proto_file;
- if (input->ExpectAtEnd()) return true;
+ if (input->ExpectAtEnd()) goto success;
break;
}
-
+
default: {
- handle_uninterpreted:
- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
- return true;
+ goto success;
}
DO_(::google::protobuf::internal::WireFormat::SkipField(
input, tag, mutable_unknown_fields()));
@@ -296,80 +310,93 @@ bool CodeGeneratorRequest::MergePartialFromCodedStream(
}
}
}
+success:
+ // @@protoc_insertion_point(parse_success:google.protobuf.compiler.CodeGeneratorRequest)
return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:google.protobuf.compiler.CodeGeneratorRequest)
+ return false;
#undef DO_
}
void CodeGeneratorRequest::SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:google.protobuf.compiler.CodeGeneratorRequest)
// repeated string file_to_generate = 1;
for (int i = 0; i < this->file_to_generate_size(); i++) {
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->file_to_generate(i).data(), this->file_to_generate(i).length(),
- ::google::protobuf::internal::WireFormat::SERIALIZE);
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "file_to_generate");
::google::protobuf::internal::WireFormatLite::WriteString(
1, this->file_to_generate(i), output);
}
-
+
// optional string parameter = 2;
- if (_has_bit(1)) {
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ if (has_parameter()) {
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->parameter().data(), this->parameter().length(),
- ::google::protobuf::internal::WireFormat::SERIALIZE);
- ::google::protobuf::internal::WireFormatLite::WriteString(
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "parameter");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
2, this->parameter(), output);
}
-
+
// repeated .google.protobuf.FileDescriptorProto proto_file = 15;
for (int i = 0; i < this->proto_file_size(); i++) {
::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray(
15, this->proto_file(i), output);
}
-
+
if (!unknown_fields().empty()) {
::google::protobuf::internal::WireFormat::SerializeUnknownFields(
unknown_fields(), output);
}
+ // @@protoc_insertion_point(serialize_end:google.protobuf.compiler.CodeGeneratorRequest)
}
::google::protobuf::uint8* CodeGeneratorRequest::SerializeWithCachedSizesToArray(
::google::protobuf::uint8* target) const {
+ // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.compiler.CodeGeneratorRequest)
// repeated string file_to_generate = 1;
for (int i = 0; i < this->file_to_generate_size(); i++) {
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->file_to_generate(i).data(), this->file_to_generate(i).length(),
- ::google::protobuf::internal::WireFormat::SERIALIZE);
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "file_to_generate");
target = ::google::protobuf::internal::WireFormatLite::
WriteStringToArray(1, this->file_to_generate(i), target);
}
-
+
// optional string parameter = 2;
- if (_has_bit(1)) {
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ if (has_parameter()) {
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->parameter().data(), this->parameter().length(),
- ::google::protobuf::internal::WireFormat::SERIALIZE);
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "parameter");
target =
::google::protobuf::internal::WireFormatLite::WriteStringToArray(
2, this->parameter(), target);
}
-
+
// repeated .google.protobuf.FileDescriptorProto proto_file = 15;
for (int i = 0; i < this->proto_file_size(); i++) {
target = ::google::protobuf::internal::WireFormatLite::
WriteMessageNoVirtualToArray(
15, this->proto_file(i), target);
}
-
+
if (!unknown_fields().empty()) {
target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
unknown_fields(), target);
}
+ // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.compiler.CodeGeneratorRequest)
return target;
}
int CodeGeneratorRequest::ByteSize() const {
int total_size = 0;
-
+
if (_has_bits_[1 / 32] & (0xffu << (1 % 32))) {
// optional string parameter = 2;
if (has_parameter()) {
@@ -377,7 +404,7 @@ int CodeGeneratorRequest::ByteSize() const {
::google::protobuf::internal::WireFormatLite::StringSize(
this->parameter());
}
-
+
}
// repeated string file_to_generate = 1;
total_size += 1 * this->file_to_generate_size();
@@ -385,7 +412,7 @@ int CodeGeneratorRequest::ByteSize() const {
total_size += ::google::protobuf::internal::WireFormatLite::StringSize(
this->file_to_generate(i));
}
-
+
// repeated .google.protobuf.FileDescriptorProto proto_file = 15;
total_size += 1 * this->proto_file_size();
for (int i = 0; i < this->proto_file_size(); i++) {
@@ -393,7 +420,7 @@ int CodeGeneratorRequest::ByteSize() const {
::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
this->proto_file(i));
}
-
+
if (!unknown_fields().empty()) {
total_size +=
::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
@@ -422,7 +449,7 @@ void CodeGeneratorRequest::MergeFrom(const CodeGeneratorRequest& from) {
file_to_generate_.MergeFrom(from.file_to_generate_);
proto_file_.MergeFrom(from.proto_file_);
if (from._has_bits_[1 / 32] & (0xffu << (1 % 32))) {
- if (from._has_bit(1)) {
+ if (from.has_parameter()) {
set_parameter(from.parameter());
}
}
@@ -442,10 +469,8 @@ void CodeGeneratorRequest::CopyFrom(const CodeGeneratorRequest& from) {
}
bool CodeGeneratorRequest::IsInitialized() const {
-
- for (int i = 0; i < proto_file_size(); i++) {
- if (!this->proto_file(i).IsInitialized()) return false;
- }
+
+ if (!::google::protobuf::internal::AllAreInitialized(this->proto_file())) return false;
return true;
}
@@ -471,9 +496,6 @@ void CodeGeneratorRequest::Swap(CodeGeneratorRequest* other) {
// ===================================================================
-const ::std::string CodeGeneratorResponse_File::_default_name_;
-const ::std::string CodeGeneratorResponse_File::_default_insertion_point_;
-const ::std::string CodeGeneratorResponse_File::_default_content_;
#ifndef _MSC_VER
const int CodeGeneratorResponse_File::kNameFieldNumber;
const int CodeGeneratorResponse_File::kInsertionPointFieldNumber;
@@ -483,6 +505,7 @@ const int CodeGeneratorResponse_File::kContentFieldNumber;
CodeGeneratorResponse_File::CodeGeneratorResponse_File()
: ::google::protobuf::Message() {
SharedCtor();
+ // @@protoc_insertion_point(constructor:google.protobuf.compiler.CodeGeneratorResponse.File)
}
void CodeGeneratorResponse_File::InitAsDefaultInstance() {
@@ -492,28 +515,31 @@ CodeGeneratorResponse_File::CodeGeneratorResponse_File(const CodeGeneratorRespon
: ::google::protobuf::Message() {
SharedCtor();
MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:google.protobuf.compiler.CodeGeneratorResponse.File)
}
void CodeGeneratorResponse_File::SharedCtor() {
+ ::google::protobuf::internal::GetEmptyString();
_cached_size_ = 0;
- name_ = const_cast< ::std::string*>(&_default_name_);
- insertion_point_ = const_cast< ::std::string*>(&_default_insertion_point_);
- content_ = const_cast< ::std::string*>(&_default_content_);
+ name_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ insertion_point_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
::memset(_has_bits_, 0, sizeof(_has_bits_));
}
CodeGeneratorResponse_File::~CodeGeneratorResponse_File() {
+ // @@protoc_insertion_point(destructor:google.protobuf.compiler.CodeGeneratorResponse.File)
SharedDtor();
}
void CodeGeneratorResponse_File::SharedDtor() {
- if (name_ != &_default_name_) {
+ if (name_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
delete name_;
}
- if (insertion_point_ != &_default_insertion_point_) {
+ if (insertion_point_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
delete insertion_point_;
}
- if (content_ != &_default_content_) {
+ if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
delete content_;
}
if (this != default_instance_) {
@@ -531,7 +557,8 @@ const ::google::protobuf::Descriptor* CodeGeneratorResponse_File::descriptor() {
}
const CodeGeneratorResponse_File& CodeGeneratorResponse_File::default_instance() {
- if (default_instance_ == NULL) protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); return *default_instance_;
+ if (default_instance_ == NULL) protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto();
+ return *default_instance_;
}
CodeGeneratorResponse_File* CodeGeneratorResponse_File::default_instance_ = NULL;
@@ -541,19 +568,19 @@ CodeGeneratorResponse_File* CodeGeneratorResponse_File::New() const {
}
void CodeGeneratorResponse_File::Clear() {
- if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
- if (_has_bit(0)) {
- if (name_ != &_default_name_) {
+ if (_has_bits_[0 / 32] & 7) {
+ if (has_name()) {
+ if (name_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
name_->clear();
}
}
- if (_has_bit(1)) {
- if (insertion_point_ != &_default_insertion_point_) {
+ if (has_insertion_point()) {
+ if (insertion_point_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
insertion_point_->clear();
}
}
- if (_has_bit(2)) {
- if (content_ != &_default_content_) {
+ if (has_content()) {
+ if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
content_->clear();
}
}
@@ -564,65 +591,70 @@ void CodeGeneratorResponse_File::Clear() {
bool CodeGeneratorResponse_File::MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input) {
-#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
::google::protobuf::uint32 tag;
- while ((tag = input->ReadTag()) != 0) {
+ // @@protoc_insertion_point(parse_start:google.protobuf.compiler.CodeGeneratorResponse.File)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
// optional string name = 1;
case 1: {
- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+ if (tag == 10) {
DO_(::google::protobuf::internal::WireFormatLite::ReadString(
input, this->mutable_name()));
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->name().data(), this->name().length(),
- ::google::protobuf::internal::WireFormat::PARSE);
+ ::google::protobuf::internal::WireFormat::PARSE,
+ "name");
} else {
- goto handle_uninterpreted;
+ goto handle_unusual;
}
if (input->ExpectTag(18)) goto parse_insertion_point;
break;
}
-
+
// optional string insertion_point = 2;
case 2: {
- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+ if (tag == 18) {
parse_insertion_point:
DO_(::google::protobuf::internal::WireFormatLite::ReadString(
input, this->mutable_insertion_point()));
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->insertion_point().data(), this->insertion_point().length(),
- ::google::protobuf::internal::WireFormat::PARSE);
+ ::google::protobuf::internal::WireFormat::PARSE,
+ "insertion_point");
} else {
- goto handle_uninterpreted;
+ goto handle_unusual;
}
if (input->ExpectTag(122)) goto parse_content;
break;
}
-
+
// optional string content = 15;
case 15: {
- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+ if (tag == 122) {
parse_content:
DO_(::google::protobuf::internal::WireFormatLite::ReadString(
input, this->mutable_content()));
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->content().data(), this->content().length(),
- ::google::protobuf::internal::WireFormat::PARSE);
+ ::google::protobuf::internal::WireFormat::PARSE,
+ "content");
} else {
- goto handle_uninterpreted;
+ goto handle_unusual;
}
- if (input->ExpectAtEnd()) return true;
+ if (input->ExpectAtEnd()) goto success;
break;
}
-
+
default: {
- handle_uninterpreted:
- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
- return true;
+ goto success;
}
DO_(::google::protobuf::internal::WireFormat::SkipField(
input, tag, mutable_unknown_fields()));
@@ -630,87 +662,102 @@ bool CodeGeneratorResponse_File::MergePartialFromCodedStream(
}
}
}
+success:
+ // @@protoc_insertion_point(parse_success:google.protobuf.compiler.CodeGeneratorResponse.File)
return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:google.protobuf.compiler.CodeGeneratorResponse.File)
+ return false;
#undef DO_
}
void CodeGeneratorResponse_File::SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:google.protobuf.compiler.CodeGeneratorResponse.File)
// optional string name = 1;
- if (_has_bit(0)) {
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ if (has_name()) {
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->name().data(), this->name().length(),
- ::google::protobuf::internal::WireFormat::SERIALIZE);
- ::google::protobuf::internal::WireFormatLite::WriteString(
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "name");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
1, this->name(), output);
}
-
+
// optional string insertion_point = 2;
- if (_has_bit(1)) {
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ if (has_insertion_point()) {
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->insertion_point().data(), this->insertion_point().length(),
- ::google::protobuf::internal::WireFormat::SERIALIZE);
- ::google::protobuf::internal::WireFormatLite::WriteString(
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "insertion_point");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
2, this->insertion_point(), output);
}
-
+
// optional string content = 15;
- if (_has_bit(2)) {
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ if (has_content()) {
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->content().data(), this->content().length(),
- ::google::protobuf::internal::WireFormat::SERIALIZE);
- ::google::protobuf::internal::WireFormatLite::WriteString(
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "content");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
15, this->content(), output);
}
-
+
if (!unknown_fields().empty()) {
::google::protobuf::internal::WireFormat::SerializeUnknownFields(
unknown_fields(), output);
}
+ // @@protoc_insertion_point(serialize_end:google.protobuf.compiler.CodeGeneratorResponse.File)
}
::google::protobuf::uint8* CodeGeneratorResponse_File::SerializeWithCachedSizesToArray(
::google::protobuf::uint8* target) const {
+ // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.compiler.CodeGeneratorResponse.File)
// optional string name = 1;
- if (_has_bit(0)) {
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ if (has_name()) {
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->name().data(), this->name().length(),
- ::google::protobuf::internal::WireFormat::SERIALIZE);
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "name");
target =
::google::protobuf::internal::WireFormatLite::WriteStringToArray(
1, this->name(), target);
}
-
+
// optional string insertion_point = 2;
- if (_has_bit(1)) {
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ if (has_insertion_point()) {
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->insertion_point().data(), this->insertion_point().length(),
- ::google::protobuf::internal::WireFormat::SERIALIZE);
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "insertion_point");
target =
::google::protobuf::internal::WireFormatLite::WriteStringToArray(
2, this->insertion_point(), target);
}
-
+
// optional string content = 15;
- if (_has_bit(2)) {
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ if (has_content()) {
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->content().data(), this->content().length(),
- ::google::protobuf::internal::WireFormat::SERIALIZE);
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "content");
target =
::google::protobuf::internal::WireFormatLite::WriteStringToArray(
15, this->content(), target);
}
-
+
if (!unknown_fields().empty()) {
target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
unknown_fields(), target);
}
+ // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.compiler.CodeGeneratorResponse.File)
return target;
}
int CodeGeneratorResponse_File::ByteSize() const {
int total_size = 0;
-
+
if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
// optional string name = 1;
if (has_name()) {
@@ -718,21 +765,21 @@ int CodeGeneratorResponse_File::ByteSize() const {
::google::protobuf::internal::WireFormatLite::StringSize(
this->name());
}
-
+
// optional string insertion_point = 2;
if (has_insertion_point()) {
total_size += 1 +
::google::protobuf::internal::WireFormatLite::StringSize(
this->insertion_point());
}
-
+
// optional string content = 15;
if (has_content()) {
total_size += 1 +
::google::protobuf::internal::WireFormatLite::StringSize(
this->content());
}
-
+
}
if (!unknown_fields().empty()) {
total_size +=
@@ -760,13 +807,13 @@ void CodeGeneratorResponse_File::MergeFrom(const ::google::protobuf::Message& fr
void CodeGeneratorResponse_File::MergeFrom(const CodeGeneratorResponse_File& from) {
GOOGLE_CHECK_NE(&from, this);
if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
- if (from._has_bit(0)) {
+ if (from.has_name()) {
set_name(from.name());
}
- if (from._has_bit(1)) {
+ if (from.has_insertion_point()) {
set_insertion_point(from.insertion_point());
}
- if (from._has_bit(2)) {
+ if (from.has_content()) {
set_content(from.content());
}
}
@@ -786,7 +833,7 @@ void CodeGeneratorResponse_File::CopyFrom(const CodeGeneratorResponse_File& from
}
bool CodeGeneratorResponse_File::IsInitialized() const {
-
+
return true;
}
@@ -812,7 +859,6 @@ void CodeGeneratorResponse_File::Swap(CodeGeneratorResponse_File* other) {
// -------------------------------------------------------------------
-const ::std::string CodeGeneratorResponse::_default_error_;
#ifndef _MSC_VER
const int CodeGeneratorResponse::kErrorFieldNumber;
const int CodeGeneratorResponse::kFileFieldNumber;
@@ -821,6 +867,7 @@ const int CodeGeneratorResponse::kFileFieldNumber;
CodeGeneratorResponse::CodeGeneratorResponse()
: ::google::protobuf::Message() {
SharedCtor();
+ // @@protoc_insertion_point(constructor:google.protobuf.compiler.CodeGeneratorResponse)
}
void CodeGeneratorResponse::InitAsDefaultInstance() {
@@ -830,20 +877,23 @@ CodeGeneratorResponse::CodeGeneratorResponse(const CodeGeneratorResponse& from)
: ::google::protobuf::Message() {
SharedCtor();
MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:google.protobuf.compiler.CodeGeneratorResponse)
}
void CodeGeneratorResponse::SharedCtor() {
+ ::google::protobuf::internal::GetEmptyString();
_cached_size_ = 0;
- error_ = const_cast< ::std::string*>(&_default_error_);
+ error_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
::memset(_has_bits_, 0, sizeof(_has_bits_));
}
CodeGeneratorResponse::~CodeGeneratorResponse() {
+ // @@protoc_insertion_point(destructor:google.protobuf.compiler.CodeGeneratorResponse)
SharedDtor();
}
void CodeGeneratorResponse::SharedDtor() {
- if (error_ != &_default_error_) {
+ if (error_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
delete error_;
}
if (this != default_instance_) {
@@ -861,7 +911,8 @@ const ::google::protobuf::Descriptor* CodeGeneratorResponse::descriptor() {
}
const CodeGeneratorResponse& CodeGeneratorResponse::default_instance() {
- if (default_instance_ == NULL) protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto(); return *default_instance_;
+ if (default_instance_ == NULL) protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto();
+ return *default_instance_;
}
CodeGeneratorResponse* CodeGeneratorResponse::default_instance_ = NULL;
@@ -871,11 +922,9 @@ CodeGeneratorResponse* CodeGeneratorResponse::New() const {
}
void CodeGeneratorResponse::Clear() {
- if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
- if (_has_bit(0)) {
- if (error_ != &_default_error_) {
- error_->clear();
- }
+ if (has_error()) {
+ if (error_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ error_->clear();
}
}
file_.Clear();
@@ -885,46 +934,50 @@ void CodeGeneratorResponse::Clear() {
bool CodeGeneratorResponse::MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input) {
-#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
::google::protobuf::uint32 tag;
- while ((tag = input->ReadTag()) != 0) {
+ // @@protoc_insertion_point(parse_start:google.protobuf.compiler.CodeGeneratorResponse)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
// optional string error = 1;
case 1: {
- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+ if (tag == 10) {
DO_(::google::protobuf::internal::WireFormatLite::ReadString(
input, this->mutable_error()));
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->error().data(), this->error().length(),
- ::google::protobuf::internal::WireFormat::PARSE);
+ ::google::protobuf::internal::WireFormat::PARSE,
+ "error");
} else {
- goto handle_uninterpreted;
+ goto handle_unusual;
}
if (input->ExpectTag(122)) goto parse_file;
break;
}
-
+
// repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15;
case 15: {
- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+ if (tag == 122) {
parse_file:
DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
input, add_file()));
} else {
- goto handle_uninterpreted;
+ goto handle_unusual;
}
if (input->ExpectTag(122)) goto parse_file;
- if (input->ExpectAtEnd()) return true;
+ if (input->ExpectAtEnd()) goto success;
break;
}
-
+
default: {
- handle_uninterpreted:
- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
- return true;
+ goto success;
}
DO_(::google::protobuf::internal::WireFormat::SkipField(
input, tag, mutable_unknown_fields()));
@@ -932,62 +985,73 @@ bool CodeGeneratorResponse::MergePartialFromCodedStream(
}
}
}
+success:
+ // @@protoc_insertion_point(parse_success:google.protobuf.compiler.CodeGeneratorResponse)
return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:google.protobuf.compiler.CodeGeneratorResponse)
+ return false;
#undef DO_
}
void CodeGeneratorResponse::SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:google.protobuf.compiler.CodeGeneratorResponse)
// optional string error = 1;
- if (_has_bit(0)) {
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ if (has_error()) {
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->error().data(), this->error().length(),
- ::google::protobuf::internal::WireFormat::SERIALIZE);
- ::google::protobuf::internal::WireFormatLite::WriteString(
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "error");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
1, this->error(), output);
}
-
+
// repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15;
for (int i = 0; i < this->file_size(); i++) {
::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray(
15, this->file(i), output);
}
-
+
if (!unknown_fields().empty()) {
::google::protobuf::internal::WireFormat::SerializeUnknownFields(
unknown_fields(), output);
}
+ // @@protoc_insertion_point(serialize_end:google.protobuf.compiler.CodeGeneratorResponse)
}
::google::protobuf::uint8* CodeGeneratorResponse::SerializeWithCachedSizesToArray(
::google::protobuf::uint8* target) const {
+ // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.compiler.CodeGeneratorResponse)
// optional string error = 1;
- if (_has_bit(0)) {
- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+ if (has_error()) {
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
this->error().data(), this->error().length(),
- ::google::protobuf::internal::WireFormat::SERIALIZE);
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "error");
target =
::google::protobuf::internal::WireFormatLite::WriteStringToArray(
1, this->error(), target);
}
-
+
// repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15;
for (int i = 0; i < this->file_size(); i++) {
target = ::google::protobuf::internal::WireFormatLite::
WriteMessageNoVirtualToArray(
15, this->file(i), target);
}
-
+
if (!unknown_fields().empty()) {
target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
unknown_fields(), target);
}
+ // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.compiler.CodeGeneratorResponse)
return target;
}
int CodeGeneratorResponse::ByteSize() const {
int total_size = 0;
-
+
if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
// optional string error = 1;
if (has_error()) {
@@ -995,7 +1059,7 @@ int CodeGeneratorResponse::ByteSize() const {
::google::protobuf::internal::WireFormatLite::StringSize(
this->error());
}
-
+
}
// repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15;
total_size += 1 * this->file_size();
@@ -1004,7 +1068,7 @@ int CodeGeneratorResponse::ByteSize() const {
::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
this->file(i));
}
-
+
if (!unknown_fields().empty()) {
total_size +=
::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
@@ -1032,7 +1096,7 @@ void CodeGeneratorResponse::MergeFrom(const CodeGeneratorResponse& from) {
GOOGLE_CHECK_NE(&from, this);
file_.MergeFrom(from.file_);
if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
- if (from._has_bit(0)) {
+ if (from.has_error()) {
set_error(from.error());
}
}
@@ -1052,7 +1116,7 @@ void CodeGeneratorResponse::CopyFrom(const CodeGeneratorResponse& from) {
}
bool CodeGeneratorResponse::IsInitialized() const {
-
+
return true;
}
diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h
index f8f8053..65634da 100644
--- a/src/google/protobuf/compiler/plugin.pb.h
+++ b/src/google/protobuf/compiler/plugin.pb.h
@@ -8,21 +8,22 @@
#include <google/protobuf/stubs/common.h>
-#if GOOGLE_PROTOBUF_VERSION < 2003000
+#if GOOGLE_PROTOBUF_VERSION < 2006000
#error This file was generated by a newer version of protoc which is
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 2003000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
+#if 2006001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
#endif
#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/message.h>
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/extension_set.h>
-#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/unknown_field_set.h>
#include "google/protobuf/descriptor.pb.h"
// @@protoc_insertion_point(includes)
@@ -45,29 +46,29 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message
public:
CodeGeneratorRequest();
virtual ~CodeGeneratorRequest();
-
+
CodeGeneratorRequest(const CodeGeneratorRequest& from);
-
+
inline CodeGeneratorRequest& operator=(const CodeGeneratorRequest& from) {
CopyFrom(from);
return *this;
}
-
+
inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
return _unknown_fields_;
}
-
+
inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
return &_unknown_fields_;
}
-
+
static const ::google::protobuf::Descriptor* descriptor();
static const CodeGeneratorRequest& default_instance();
-
+
void Swap(CodeGeneratorRequest* other);
-
+
// implements Message ----------------------------------------------
-
+
CodeGeneratorRequest* New() const;
void CopyFrom(const ::google::protobuf::Message& from);
void MergeFrom(const ::google::protobuf::Message& from);
@@ -75,7 +76,7 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message
void MergeFrom(const CodeGeneratorRequest& from);
void Clear();
bool IsInitialized() const;
-
+
int ByteSize() const;
bool MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input);
@@ -88,13 +89,12 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message
void SharedDtor();
void SetCachedSize(int size) const;
public:
-
::google::protobuf::Metadata GetMetadata() const;
-
+
// nested types ----------------------------------------------------
-
+
// accessors -------------------------------------------------------
-
+
// repeated string file_to_generate = 1;
inline int file_to_generate_size() const;
inline void clear_file_to_generate();
@@ -110,7 +110,7 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message
inline void add_file_to_generate(const char* value, size_t size);
inline const ::google::protobuf::RepeatedPtrField< ::std::string>& file_to_generate() const;
inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_file_to_generate();
-
+
// optional string parameter = 2;
inline bool has_parameter() const;
inline void clear_parameter();
@@ -120,7 +120,9 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message
inline void set_parameter(const char* value);
inline void set_parameter(const char* value, size_t size);
inline ::std::string* mutable_parameter();
-
+ inline ::std::string* release_parameter();
+ inline void set_allocated_parameter(::std::string* parameter);
+
// repeated .google.protobuf.FileDescriptorProto proto_file = 15;
inline int proto_file_size() const;
inline void clear_proto_file();
@@ -132,33 +134,23 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message
proto_file() const;
inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >*
mutable_proto_file();
-
+
// @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorRequest)
private:
+ inline void set_has_parameter();
+ inline void clear_has_parameter();
+
::google::protobuf::UnknownFieldSet _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
mutable int _cached_size_;
-
::google::protobuf::RepeatedPtrField< ::std::string> file_to_generate_;
::std::string* parameter_;
- static const ::std::string _default_parameter_;
::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto > proto_file_;
friend void LIBPROTOC_EXPORT protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto();
friend void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto();
friend void protobuf_ShutdownFile_google_2fprotobuf_2fcompiler_2fplugin_2eproto();
-
- ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32];
-
- // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
- inline bool _has_bit(int index) const {
- return (_has_bits_[index / 32] & (1u << (index % 32))) != 0;
- }
- inline void _set_bit(int index) {
- _has_bits_[index / 32] |= (1u << (index % 32));
- }
- inline void _clear_bit(int index) {
- _has_bits_[index / 32] &= ~(1u << (index % 32));
- }
-
+
void InitAsDefaultInstance();
static CodeGeneratorRequest* default_instance_;
};
@@ -168,29 +160,29 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M
public:
CodeGeneratorResponse_File();
virtual ~CodeGeneratorResponse_File();
-
+
CodeGeneratorResponse_File(const CodeGeneratorResponse_File& from);
-
+
inline CodeGeneratorResponse_File& operator=(const CodeGeneratorResponse_File& from) {
CopyFrom(from);
return *this;
}
-
+
inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
return _unknown_fields_;
}
-
+
inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
return &_unknown_fields_;
}
-
+
static const ::google::protobuf::Descriptor* descriptor();
static const CodeGeneratorResponse_File& default_instance();
-
+
void Swap(CodeGeneratorResponse_File* other);
-
+
// implements Message ----------------------------------------------
-
+
CodeGeneratorResponse_File* New() const;
void CopyFrom(const ::google::protobuf::Message& from);
void MergeFrom(const ::google::protobuf::Message& from);
@@ -198,7 +190,7 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M
void MergeFrom(const CodeGeneratorResponse_File& from);
void Clear();
bool IsInitialized() const;
-
+
int ByteSize() const;
bool MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input);
@@ -211,13 +203,12 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M
void SharedDtor();
void SetCachedSize(int size) const;
public:
-
::google::protobuf::Metadata GetMetadata() const;
-
+
// nested types ----------------------------------------------------
-
+
// accessors -------------------------------------------------------
-
+
// optional string name = 1;
inline bool has_name() const;
inline void clear_name();
@@ -227,7 +218,9 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M
inline void set_name(const char* value);
inline void set_name(const char* value, size_t size);
inline ::std::string* mutable_name();
-
+ inline ::std::string* release_name();
+ inline void set_allocated_name(::std::string* name);
+
// optional string insertion_point = 2;
inline bool has_insertion_point() const;
inline void clear_insertion_point();
@@ -237,7 +230,9 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M
inline void set_insertion_point(const char* value);
inline void set_insertion_point(const char* value, size_t size);
inline ::std::string* mutable_insertion_point();
-
+ inline ::std::string* release_insertion_point();
+ inline void set_allocated_insertion_point(::std::string* insertion_point);
+
// optional string content = 15;
inline bool has_content() const;
inline void clear_content();
@@ -247,35 +242,29 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse_File : public ::google::protobuf::M
inline void set_content(const char* value);
inline void set_content(const char* value, size_t size);
inline ::std::string* mutable_content();
-
+ inline ::std::string* release_content();
+ inline void set_allocated_content(::std::string* content);
+
// @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorResponse.File)
private:
+ inline void set_has_name();
+ inline void clear_has_name();
+ inline void set_has_insertion_point();
+ inline void clear_has_insertion_point();
+ inline void set_has_content();
+ inline void clear_has_content();
+
::google::protobuf::UnknownFieldSet _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
mutable int _cached_size_;
-
::std::string* name_;
- static const ::std::string _default_name_;
::std::string* insertion_point_;
- static const ::std::string _default_insertion_point_;
::std::string* content_;
- static const ::std::string _default_content_;
friend void LIBPROTOC_EXPORT protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto();
friend void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto();
friend void protobuf_ShutdownFile_google_2fprotobuf_2fcompiler_2fplugin_2eproto();
-
- ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32];
-
- // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
- inline bool _has_bit(int index) const {
- return (_has_bits_[index / 32] & (1u << (index % 32))) != 0;
- }
- inline void _set_bit(int index) {
- _has_bits_[index / 32] |= (1u << (index % 32));
- }
- inline void _clear_bit(int index) {
- _has_bits_[index / 32] &= ~(1u << (index % 32));
- }
-
+
void InitAsDefaultInstance();
static CodeGeneratorResponse_File* default_instance_;
};
@@ -285,29 +274,29 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag
public:
CodeGeneratorResponse();
virtual ~CodeGeneratorResponse();
-
+
CodeGeneratorResponse(const CodeGeneratorResponse& from);
-
+
inline CodeGeneratorResponse& operator=(const CodeGeneratorResponse& from) {
CopyFrom(from);
return *this;
}
-
+
inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
return _unknown_fields_;
}
-
+
inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
return &_unknown_fields_;
}
-
+
static const ::google::protobuf::Descriptor* descriptor();
static const CodeGeneratorResponse& default_instance();
-
+
void Swap(CodeGeneratorResponse* other);
-
+
// implements Message ----------------------------------------------
-
+
CodeGeneratorResponse* New() const;
void CopyFrom(const ::google::protobuf::Message& from);
void MergeFrom(const ::google::protobuf::Message& from);
@@ -315,7 +304,7 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag
void MergeFrom(const CodeGeneratorResponse& from);
void Clear();
bool IsInitialized() const;
-
+
int ByteSize() const;
bool MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input);
@@ -328,15 +317,14 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag
void SharedDtor();
void SetCachedSize(int size) const;
public:
-
::google::protobuf::Metadata GetMetadata() const;
-
+
// nested types ----------------------------------------------------
-
+
typedef CodeGeneratorResponse_File File;
-
+
// accessors -------------------------------------------------------
-
+
// optional string error = 1;
inline bool has_error() const;
inline void clear_error();
@@ -346,7 +334,9 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag
inline void set_error(const char* value);
inline void set_error(const char* value, size_t size);
inline ::std::string* mutable_error();
-
+ inline ::std::string* release_error();
+ inline void set_allocated_error(::std::string* error);
+
// repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15;
inline int file_size() const;
inline void clear_file();
@@ -358,32 +348,22 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag
file() const;
inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >*
mutable_file();
-
+
// @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorResponse)
private:
+ inline void set_has_error();
+ inline void clear_has_error();
+
::google::protobuf::UnknownFieldSet _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
mutable int _cached_size_;
-
::std::string* error_;
- static const ::std::string _default_error_;
::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File > file_;
friend void LIBPROTOC_EXPORT protobuf_AddDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto();
friend void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto();
friend void protobuf_ShutdownFile_google_2fprotobuf_2fcompiler_2fplugin_2eproto();
-
- ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32];
-
- // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
- inline bool _has_bit(int index) const {
- return (_has_bits_[index / 32] & (1u << (index % 32))) != 0;
- }
- inline void _set_bit(int index) {
- _has_bits_[index / 32] |= (1u << (index % 32));
- }
- inline void _clear_bit(int index) {
- _has_bits_[index / 32] &= ~(1u << (index % 32));
- }
-
+
void InitAsDefaultInstance();
static CodeGeneratorResponse* default_instance_;
};
@@ -402,83 +382,127 @@ inline void CodeGeneratorRequest::clear_file_to_generate() {
file_to_generate_.Clear();
}
inline const ::std::string& CodeGeneratorRequest::file_to_generate(int index) const {
+ // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate)
return file_to_generate_.Get(index);
}
inline ::std::string* CodeGeneratorRequest::mutable_file_to_generate(int index) {
+ // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate)
return file_to_generate_.Mutable(index);
}
inline void CodeGeneratorRequest::set_file_to_generate(int index, const ::std::string& value) {
+ // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate)
file_to_generate_.Mutable(index)->assign(value);
}
inline void CodeGeneratorRequest::set_file_to_generate(int index, const char* value) {
file_to_generate_.Mutable(index)->assign(value);
+ // @@protoc_insertion_point(field_set_char:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate)
}
inline void CodeGeneratorRequest::set_file_to_generate(int index, const char* value, size_t size) {
file_to_generate_.Mutable(index)->assign(
reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate)
}
inline ::std::string* CodeGeneratorRequest::add_file_to_generate() {
return file_to_generate_.Add();
}
inline void CodeGeneratorRequest::add_file_to_generate(const ::std::string& value) {
file_to_generate_.Add()->assign(value);
+ // @@protoc_insertion_point(field_add:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate)
}
inline void CodeGeneratorRequest::add_file_to_generate(const char* value) {
file_to_generate_.Add()->assign(value);
+ // @@protoc_insertion_point(field_add_char:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate)
}
inline void CodeGeneratorRequest::add_file_to_generate(const char* value, size_t size) {
file_to_generate_.Add()->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_add_pointer:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate)
}
inline const ::google::protobuf::RepeatedPtrField< ::std::string>&
CodeGeneratorRequest::file_to_generate() const {
+ // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate)
return file_to_generate_;
}
inline ::google::protobuf::RepeatedPtrField< ::std::string>*
CodeGeneratorRequest::mutable_file_to_generate() {
+ // @@protoc_insertion_point(field_mutable_list:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate)
return &file_to_generate_;
}
// optional string parameter = 2;
inline bool CodeGeneratorRequest::has_parameter() const {
- return _has_bit(1);
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void CodeGeneratorRequest::set_has_parameter() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void CodeGeneratorRequest::clear_has_parameter() {
+ _has_bits_[0] &= ~0x00000002u;
}
inline void CodeGeneratorRequest::clear_parameter() {
- if (parameter_ != &_default_parameter_) {
+ if (parameter_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
parameter_->clear();
}
- _clear_bit(1);
+ clear_has_parameter();
}
inline const ::std::string& CodeGeneratorRequest::parameter() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorRequest.parameter)
return *parameter_;
}
inline void CodeGeneratorRequest::set_parameter(const ::std::string& value) {
- _set_bit(1);
- if (parameter_ == &_default_parameter_) {
+ set_has_parameter();
+ if (parameter_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
parameter_ = new ::std::string;
}
parameter_->assign(value);
+ // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorRequest.parameter)
}
inline void CodeGeneratorRequest::set_parameter(const char* value) {
- _set_bit(1);
- if (parameter_ == &_default_parameter_) {
+ set_has_parameter();
+ if (parameter_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
parameter_ = new ::std::string;
}
parameter_->assign(value);
+ // @@protoc_insertion_point(field_set_char:google.protobuf.compiler.CodeGeneratorRequest.parameter)
}
inline void CodeGeneratorRequest::set_parameter(const char* value, size_t size) {
- _set_bit(1);
- if (parameter_ == &_default_parameter_) {
+ set_has_parameter();
+ if (parameter_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
parameter_ = new ::std::string;
}
parameter_->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorRequest.parameter)
}
inline ::std::string* CodeGeneratorRequest::mutable_parameter() {
- _set_bit(1);
- if (parameter_ == &_default_parameter_) {
+ set_has_parameter();
+ if (parameter_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
parameter_ = new ::std::string;
}
+ // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorRequest.parameter)
return parameter_;
}
+inline ::std::string* CodeGeneratorRequest::release_parameter() {
+ clear_has_parameter();
+ if (parameter_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ return NULL;
+ } else {
+ ::std::string* temp = parameter_;
+ parameter_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return temp;
+ }
+}
+inline void CodeGeneratorRequest::set_allocated_parameter(::std::string* parameter) {
+ if (parameter_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete parameter_;
+ }
+ if (parameter) {
+ set_has_parameter();
+ parameter_ = parameter;
+ } else {
+ clear_has_parameter();
+ parameter_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ }
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.compiler.CodeGeneratorRequest.parameter)
+}
// repeated .google.protobuf.FileDescriptorProto proto_file = 15;
inline int CodeGeneratorRequest::proto_file_size() const {
@@ -488,20 +512,25 @@ inline void CodeGeneratorRequest::clear_proto_file() {
proto_file_.Clear();
}
inline const ::google::protobuf::FileDescriptorProto& CodeGeneratorRequest::proto_file(int index) const {
+ // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorRequest.proto_file)
return proto_file_.Get(index);
}
inline ::google::protobuf::FileDescriptorProto* CodeGeneratorRequest::mutable_proto_file(int index) {
+ // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorRequest.proto_file)
return proto_file_.Mutable(index);
}
inline ::google::protobuf::FileDescriptorProto* CodeGeneratorRequest::add_proto_file() {
+ // @@protoc_insertion_point(field_add:google.protobuf.compiler.CodeGeneratorRequest.proto_file)
return proto_file_.Add();
}
inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >&
CodeGeneratorRequest::proto_file() const {
+ // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorRequest.proto_file)
return proto_file_;
}
inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >*
CodeGeneratorRequest::mutable_proto_file() {
+ // @@protoc_insertion_point(field_mutable_list:google.protobuf.compiler.CodeGeneratorRequest.proto_file)
return &proto_file_;
}
@@ -511,129 +540,231 @@ CodeGeneratorRequest::mutable_proto_file() {
// optional string name = 1;
inline bool CodeGeneratorResponse_File::has_name() const {
- return _has_bit(0);
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void CodeGeneratorResponse_File::set_has_name() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void CodeGeneratorResponse_File::clear_has_name() {
+ _has_bits_[0] &= ~0x00000001u;
}
inline void CodeGeneratorResponse_File::clear_name() {
- if (name_ != &_default_name_) {
+ if (name_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
name_->clear();
}
- _clear_bit(0);
+ clear_has_name();
}
inline const ::std::string& CodeGeneratorResponse_File::name() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorResponse.File.name)
return *name_;
}
inline void CodeGeneratorResponse_File::set_name(const ::std::string& value) {
- _set_bit(0);
- if (name_ == &_default_name_) {
+ set_has_name();
+ if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
name_ = new ::std::string;
}
name_->assign(value);
+ // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorResponse.File.name)
}
inline void CodeGeneratorResponse_File::set_name(const char* value) {
- _set_bit(0);
- if (name_ == &_default_name_) {
+ set_has_name();
+ if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
name_ = new ::std::string;
}
name_->assign(value);
+ // @@protoc_insertion_point(field_set_char:google.protobuf.compiler.CodeGeneratorResponse.File.name)
}
inline void CodeGeneratorResponse_File::set_name(const char* value, size_t size) {
- _set_bit(0);
- if (name_ == &_default_name_) {
+ set_has_name();
+ if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
name_ = new ::std::string;
}
name_->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorResponse.File.name)
}
inline ::std::string* CodeGeneratorResponse_File::mutable_name() {
- _set_bit(0);
- if (name_ == &_default_name_) {
+ set_has_name();
+ if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
name_ = new ::std::string;
}
+ // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.File.name)
return name_;
}
+inline ::std::string* CodeGeneratorResponse_File::release_name() {
+ clear_has_name();
+ if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ return NULL;
+ } else {
+ ::std::string* temp = name_;
+ name_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return temp;
+ }
+}
+inline void CodeGeneratorResponse_File::set_allocated_name(::std::string* name) {
+ if (name_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete name_;
+ }
+ if (name) {
+ set_has_name();
+ name_ = name;
+ } else {
+ clear_has_name();
+ name_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ }
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.compiler.CodeGeneratorResponse.File.name)
+}
// optional string insertion_point = 2;
inline bool CodeGeneratorResponse_File::has_insertion_point() const {
- return _has_bit(1);
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void CodeGeneratorResponse_File::set_has_insertion_point() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void CodeGeneratorResponse_File::clear_has_insertion_point() {
+ _has_bits_[0] &= ~0x00000002u;
}
inline void CodeGeneratorResponse_File::clear_insertion_point() {
- if (insertion_point_ != &_default_insertion_point_) {
+ if (insertion_point_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
insertion_point_->clear();
}
- _clear_bit(1);
+ clear_has_insertion_point();
}
inline const ::std::string& CodeGeneratorResponse_File::insertion_point() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point)
return *insertion_point_;
}
inline void CodeGeneratorResponse_File::set_insertion_point(const ::std::string& value) {
- _set_bit(1);
- if (insertion_point_ == &_default_insertion_point_) {
+ set_has_insertion_point();
+ if (insertion_point_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
insertion_point_ = new ::std::string;
}
insertion_point_->assign(value);
+ // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point)
}
inline void CodeGeneratorResponse_File::set_insertion_point(const char* value) {
- _set_bit(1);
- if (insertion_point_ == &_default_insertion_point_) {
+ set_has_insertion_point();
+ if (insertion_point_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
insertion_point_ = new ::std::string;
}
insertion_point_->assign(value);
+ // @@protoc_insertion_point(field_set_char:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point)
}
inline void CodeGeneratorResponse_File::set_insertion_point(const char* value, size_t size) {
- _set_bit(1);
- if (insertion_point_ == &_default_insertion_point_) {
+ set_has_insertion_point();
+ if (insertion_point_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
insertion_point_ = new ::std::string;
}
insertion_point_->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point)
}
inline ::std::string* CodeGeneratorResponse_File::mutable_insertion_point() {
- _set_bit(1);
- if (insertion_point_ == &_default_insertion_point_) {
+ set_has_insertion_point();
+ if (insertion_point_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
insertion_point_ = new ::std::string;
}
+ // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point)
return insertion_point_;
}
+inline ::std::string* CodeGeneratorResponse_File::release_insertion_point() {
+ clear_has_insertion_point();
+ if (insertion_point_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ return NULL;
+ } else {
+ ::std::string* temp = insertion_point_;
+ insertion_point_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return temp;
+ }
+}
+inline void CodeGeneratorResponse_File::set_allocated_insertion_point(::std::string* insertion_point) {
+ if (insertion_point_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete insertion_point_;
+ }
+ if (insertion_point) {
+ set_has_insertion_point();
+ insertion_point_ = insertion_point;
+ } else {
+ clear_has_insertion_point();
+ insertion_point_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ }
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point)
+}
// optional string content = 15;
inline bool CodeGeneratorResponse_File::has_content() const {
- return _has_bit(2);
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void CodeGeneratorResponse_File::set_has_content() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void CodeGeneratorResponse_File::clear_has_content() {
+ _has_bits_[0] &= ~0x00000004u;
}
inline void CodeGeneratorResponse_File::clear_content() {
- if (content_ != &_default_content_) {
+ if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
content_->clear();
}
- _clear_bit(2);
+ clear_has_content();
}
inline const ::std::string& CodeGeneratorResponse_File::content() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorResponse.File.content)
return *content_;
}
inline void CodeGeneratorResponse_File::set_content(const ::std::string& value) {
- _set_bit(2);
- if (content_ == &_default_content_) {
+ set_has_content();
+ if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
content_ = new ::std::string;
}
content_->assign(value);
+ // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorResponse.File.content)
}
inline void CodeGeneratorResponse_File::set_content(const char* value) {
- _set_bit(2);
- if (content_ == &_default_content_) {
+ set_has_content();
+ if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
content_ = new ::std::string;
}
content_->assign(value);
+ // @@protoc_insertion_point(field_set_char:google.protobuf.compiler.CodeGeneratorResponse.File.content)
}
inline void CodeGeneratorResponse_File::set_content(const char* value, size_t size) {
- _set_bit(2);
- if (content_ == &_default_content_) {
+ set_has_content();
+ if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
content_ = new ::std::string;
}
content_->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorResponse.File.content)
}
inline ::std::string* CodeGeneratorResponse_File::mutable_content() {
- _set_bit(2);
- if (content_ == &_default_content_) {
+ set_has_content();
+ if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
content_ = new ::std::string;
}
+ // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.File.content)
return content_;
}
+inline ::std::string* CodeGeneratorResponse_File::release_content() {
+ clear_has_content();
+ if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ return NULL;
+ } else {
+ ::std::string* temp = content_;
+ content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return temp;
+ }
+}
+inline void CodeGeneratorResponse_File::set_allocated_content(::std::string* content) {
+ if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete content_;
+ }
+ if (content) {
+ set_has_content();
+ content_ = content;
+ } else {
+ clear_has_content();
+ content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ }
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.compiler.CodeGeneratorResponse.File.content)
+}
// -------------------------------------------------------------------
@@ -641,45 +772,79 @@ inline ::std::string* CodeGeneratorResponse_File::mutable_content() {
// optional string error = 1;
inline bool CodeGeneratorResponse::has_error() const {
- return _has_bit(0);
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void CodeGeneratorResponse::set_has_error() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void CodeGeneratorResponse::clear_has_error() {
+ _has_bits_[0] &= ~0x00000001u;
}
inline void CodeGeneratorResponse::clear_error() {
- if (error_ != &_default_error_) {
+ if (error_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
error_->clear();
}
- _clear_bit(0);
+ clear_has_error();
}
inline const ::std::string& CodeGeneratorResponse::error() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorResponse.error)
return *error_;
}
inline void CodeGeneratorResponse::set_error(const ::std::string& value) {
- _set_bit(0);
- if (error_ == &_default_error_) {
+ set_has_error();
+ if (error_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
error_ = new ::std::string;
}
error_->assign(value);
+ // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorResponse.error)
}
inline void CodeGeneratorResponse::set_error(const char* value) {
- _set_bit(0);
- if (error_ == &_default_error_) {
+ set_has_error();
+ if (error_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
error_ = new ::std::string;
}
error_->assign(value);
+ // @@protoc_insertion_point(field_set_char:google.protobuf.compiler.CodeGeneratorResponse.error)
}
inline void CodeGeneratorResponse::set_error(const char* value, size_t size) {
- _set_bit(0);
- if (error_ == &_default_error_) {
+ set_has_error();
+ if (error_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
error_ = new ::std::string;
}
error_->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorResponse.error)
}
inline ::std::string* CodeGeneratorResponse::mutable_error() {
- _set_bit(0);
- if (error_ == &_default_error_) {
+ set_has_error();
+ if (error_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
error_ = new ::std::string;
}
+ // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.error)
return error_;
}
+inline ::std::string* CodeGeneratorResponse::release_error() {
+ clear_has_error();
+ if (error_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ return NULL;
+ } else {
+ ::std::string* temp = error_;
+ error_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return temp;
+ }
+}
+inline void CodeGeneratorResponse::set_allocated_error(::std::string* error) {
+ if (error_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete error_;
+ }
+ if (error) {
+ set_has_error();
+ error_ = error;
+ } else {
+ clear_has_error();
+ error_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ }
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.compiler.CodeGeneratorResponse.error)
+}
// repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15;
inline int CodeGeneratorResponse::file_size() const {
@@ -689,20 +854,25 @@ inline void CodeGeneratorResponse::clear_file() {
file_.Clear();
}
inline const ::google::protobuf::compiler::CodeGeneratorResponse_File& CodeGeneratorResponse::file(int index) const {
+ // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorResponse.file)
return file_.Get(index);
}
inline ::google::protobuf::compiler::CodeGeneratorResponse_File* CodeGeneratorResponse::mutable_file(int index) {
+ // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.file)
return file_.Mutable(index);
}
inline ::google::protobuf::compiler::CodeGeneratorResponse_File* CodeGeneratorResponse::add_file() {
+ // @@protoc_insertion_point(field_add:google.protobuf.compiler.CodeGeneratorResponse.file)
return file_.Add();
}
inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >&
CodeGeneratorResponse::file() const {
+ // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorResponse.file)
return file_;
}
inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >*
CodeGeneratorResponse::mutable_file() {
+ // @@protoc_insertion_point(field_mutable_list:google.protobuf.compiler.CodeGeneratorResponse.file)
return &file_;
}
diff --git a/src/google/protobuf/compiler/plugin.proto b/src/google/protobuf/compiler/plugin.proto
index 4e928b0..b65379d 100644
--- a/src/google/protobuf/compiler/plugin.proto
+++ b/src/google/protobuf/compiler/plugin.proto
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -45,6 +45,8 @@
// flag "--${NAME}_out" is passed to protoc.
package google.protobuf.compiler;
+option java_package = "com.google.protobuf.compiler";
+option java_outer_classname = "PluginProtos";
import "google/protobuf/descriptor.proto";
@@ -131,7 +133,7 @@ message CodeGeneratorResponse {
// in order to work correctly in that context.
//
// The code generator that generates the initial file and the one which
- // inserts into it must both run as part of a single invocatino of protoc.
+ // inserts into it must both run as part of a single invocation of protoc.
// Code generators are executed in the order in which they appear on the
// command line.
//
diff --git a/src/google/protobuf/compiler/python/python_generator.cc b/src/google/protobuf/compiler/python/python_generator.cc
index fae83a3..15e05da 100644
--- a/src/google/protobuf/compiler/python/python_generator.cc
+++ b/src/google/protobuf/compiler/python/python_generator.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -28,6 +28,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//#PY25 compatible generated code for GAE.
+// Copyright 2007 Google Inc. All Rights Reserved.
// Author: robinson@google.com (Will Robinson)
//
// This module outputs pure-Python protocol message classes that will
@@ -45,6 +47,7 @@
#include <limits>
#include <map>
#include <utility>
+#include <memory>
#include <string>
#include <vector>
@@ -52,6 +55,7 @@
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringprintf.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream.h>
@@ -106,6 +110,12 @@ string NamePrefixedWithNestedTypes(const DescriptorT& descriptor,
const char kDescriptorKey[] = "DESCRIPTOR";
+// Does the file have top-level enums?
+inline bool HasTopLevelEnums(const FileDescriptor *file) {
+ return file->enum_type_count() > 0;
+}
+
+
// Should we generate generic services for this file?
inline bool HasGenericServices(const FileDescriptor *file) {
return file->service_count() > 0 &&
@@ -120,13 +130,23 @@ void PrintTopBoilerplate(
// TODO(robinson): Allow parameterization of Python version?
printer->Print(
"# Generated by the protocol buffer compiler. DO NOT EDIT!\n"
- "\n"
- "from google.protobuf import descriptor\n"
- "from google.protobuf import message\n"
- "from google.protobuf import reflection\n");
+ "# source: $filename$\n"
+ "\nimport sys\n_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))" //##PY25
+ "\n",
+ "filename", file->name());
+ if (HasTopLevelEnums(file)) {
+ printer->Print(
+ "from google.protobuf.internal import enum_type_wrapper\n");
+ }
+ printer->Print(
+ "from google.protobuf import descriptor as _descriptor\n"
+ "from google.protobuf import message as _message\n"
+ "from google.protobuf import reflection as _reflection\n"
+ "from google.protobuf import symbol_database as "
+ "_symbol_database\n");
if (HasGenericServices(file)) {
printer->Print(
- "from google.protobuf import service\n"
+ "from google.protobuf import service as _service\n"
"from google.protobuf import service_reflection\n");
}
@@ -136,7 +156,8 @@ void PrintTopBoilerplate(
"from google.protobuf import descriptor_pb2\n");
}
printer->Print(
- "# @@protoc_insertion_point(imports)\n");
+ "# @@protoc_insertion_point(imports)\n\n"
+ "_sym_db = _symbol_database.Default()\n");
printer->Print("\n\n");
}
@@ -202,12 +223,12 @@ string StringifyDefaultValue(const FieldDescriptor& field) {
case FieldDescriptor::CPPTYPE_ENUM:
return SimpleItoa(field.default_value_enum()->number());
case FieldDescriptor::CPPTYPE_STRING:
- if (field.type() == FieldDescriptor::TYPE_STRING) {
- return "unicode(\"" + CEscape(field.default_value_string()) +
- "\", \"utf-8\")";
- } else {
- return "\"" + CEscape(field.default_value_string()) + "\"";
- }
+//##!PY25 return "b\"" + CEscape(field.default_value_string()) +
+//##!PY25 (field.type() != FieldDescriptor::TYPE_STRING ? "\"" :
+//##!PY25 "\".decode('utf-8')");
+ return "_b(\"" + CEscape(field.default_value_string()) + //##PY25
+ (field.type() != FieldDescriptor::TYPE_STRING ? "\")" : //##PY25
+ "\").decode('utf-8')"); //##PY25
case FieldDescriptor::CPPTYPE_MESSAGE:
return "None";
}
@@ -230,7 +251,7 @@ Generator::~Generator() {
bool Generator::Generate(const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* context,
string* error) const {
// Completely serialize all Generate() calls on this instance. The
@@ -252,26 +273,29 @@ bool Generator::Generate(const FileDescriptor* file,
fdp.SerializeToString(&file_descriptor_serialized_);
- scoped_ptr<io::ZeroCopyOutputStream> output(output_directory->Open(filename));
+ scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
GOOGLE_CHECK(output.get());
io::Printer printer(output.get(), '$');
printer_ = &printer;
PrintTopBoilerplate(printer_, file_, GeneratingDescriptorProto());
+ PrintImports();
PrintFileDescriptor();
PrintTopLevelEnums();
PrintTopLevelExtensions();
PrintAllNestedEnumsInFile();
PrintMessageDescriptors();
- // We have to print the imports after the descriptors, so that mutually
- // recursive protos in separate files can successfully reference each other.
- PrintImports();
FixForeignFieldsInDescriptors();
PrintMessages();
// We have to fix up the extensions after the message classes themselves,
// since they need to call static RegisterExtension() methods on these
// classes.
FixForeignFieldsInExtensions();
+ // Descriptor options may have custom extensions. These custom options
+ // can only be successfully parsed after we register corresponding
+ // extensions. Therefore we parse all options again here to recognize
+ // custom options that may be unknown when we define the descriptors.
+ FixAllDescriptorOptions();
if (HasGenericServices(file)) {
PrintServices();
}
@@ -290,6 +314,13 @@ void Generator::PrintImports() const {
module_name);
}
printer_->Print("\n");
+
+ // Print public imports.
+ for (int i = 0; i < file_->public_dependency_count(); ++i) {
+ string module_name = ModuleName(file_->public_dependency(i)->name());
+ printer_->Print("from $module$ import *\n", "module", module_name);
+ }
+ printer_->Print("\n");
}
// Prints the single file descriptor for this file.
@@ -299,20 +330,31 @@ void Generator::PrintFileDescriptor() const {
m["name"] = file_->name();
m["package"] = file_->package();
const char file_descriptor_template[] =
- "$descriptor_name$ = descriptor.FileDescriptor(\n"
+ "$descriptor_name$ = _descriptor.FileDescriptor(\n"
" name='$name$',\n"
" package='$package$',\n";
printer_->Print(m, file_descriptor_template);
printer_->Indent();
printer_->Print(
- "serialized_pb='$value$'",
+//##!PY25 "serialized_pb=b'$value$'\n",
+ "serialized_pb=_b('$value$')\n", //##PY25
"value", strings::CHexEscape(file_descriptor_serialized_));
+ if (file_->dependency_count() != 0) {
+ printer_->Print(",\ndependencies=[");
+ for (int i = 0; i < file_->dependency_count(); ++i) {
+ string module_name = ModuleName(file_->dependency(i)->name());
+ printer_->Print("$module_name$.DESCRIPTOR,", "module_name", module_name);
+ }
+ printer_->Print("]");
+ }
// TODO(falk): Also print options and fix the message_type, enum_type,
// service and extension later in the generation.
printer_->Outdent();
printer_->Print(")\n");
+ printer_->Print("_sym_db.RegisterFileDescriptor($name$)\n", "name",
+ kDescriptorKey);
printer_->Print("\n");
}
@@ -323,6 +365,11 @@ void Generator::PrintTopLevelEnums() const {
for (int i = 0; i < file_->enum_type_count(); ++i) {
const EnumDescriptor& enum_descriptor = *file_->enum_type(i);
PrintEnum(enum_descriptor);
+ printer_->Print("$name$ = "
+ "enum_type_wrapper.EnumTypeWrapper($descriptor_name$)",
+ "name", enum_descriptor.name(),
+ "descriptor_name",
+ ModuleLevelDescriptorName(enum_descriptor));
printer_->Print("\n");
for (int j = 0; j < enum_descriptor.value_count(); ++j) {
@@ -352,12 +399,14 @@ void Generator::PrintAllNestedEnumsInFile() const {
// enum_descriptor.
void Generator::PrintEnum(const EnumDescriptor& enum_descriptor) const {
map<string, string> m;
- m["descriptor_name"] = ModuleLevelDescriptorName(enum_descriptor);
+ string module_level_descriptor_name =
+ ModuleLevelDescriptorName(enum_descriptor);
+ m["descriptor_name"] = module_level_descriptor_name;
m["name"] = enum_descriptor.name();
m["full_name"] = enum_descriptor.full_name();
m["file"] = kDescriptorKey;
const char enum_descriptor_template[] =
- "$descriptor_name$ = descriptor.EnumDescriptor(\n"
+ "$descriptor_name$ = _descriptor.EnumDescriptor(\n"
" name='$name$',\n"
" full_name='$full_name$',\n"
" filename=None,\n"
@@ -377,11 +426,13 @@ void Generator::PrintEnum(const EnumDescriptor& enum_descriptor) const {
printer_->Print("containing_type=None,\n");
printer_->Print("options=$options_value$,\n",
"options_value",
- OptionsValue("EnumOptions", CEscape(options_string)));
+ OptionsValue("EnumOptions", options_string));
EnumDescriptorProto edp;
PrintSerializedPbInterval(enum_descriptor, edp);
printer_->Outdent();
printer_->Print(")\n");
+ printer_->Print("_sym_db.RegisterEnumDescriptor($name$)\n", "name",
+ module_level_descriptor_name);
printer_->Print("\n");
}
@@ -438,7 +489,7 @@ void Generator::PrintServiceDescriptor(
descriptor.options().SerializeToString(&options_string);
printer_->Print(
- "$service_name$ = descriptor.ServiceDescriptor(\n",
+ "$service_name$ = _descriptor.ServiceDescriptor(\n",
"service_name", service_name);
printer_->Indent();
map<string, string> m;
@@ -461,7 +512,6 @@ void Generator::PrintServiceDescriptor(
printer_->Print("methods=[\n");
for (int i = 0; i < descriptor.method_count(); ++i) {
const MethodDescriptor* method = descriptor.method(i);
- string options_string;
method->options().SerializeToString(&options_string);
m.clear();
@@ -472,7 +522,7 @@ void Generator::PrintServiceDescriptor(
m["input_type"] = ModuleLevelDescriptorName(*(method->input_type()));
m["output_type"] = ModuleLevelDescriptorName(*(method->output_type()));
m["options_value"] = OptionsValue("MethodOptions", options_string);
- printer_->Print("descriptor.MethodDescriptor(\n");
+ printer_->Print("_descriptor.MethodDescriptor(\n");
printer_->Indent();
printer_->Print(
m,
@@ -493,27 +543,36 @@ void Generator::PrintServiceDescriptor(
void Generator::PrintServiceClass(const ServiceDescriptor& descriptor) const {
// Print the service.
- printer_->Print("class $class_name$(service.Service):\n",
+ printer_->Print("$class_name$ = service_reflection.GeneratedServiceType("
+ "'$class_name$', (_service.Service,), dict(\n",
"class_name", descriptor.name());
printer_->Indent();
printer_->Print(
- "__metaclass__ = service_reflection.GeneratedServiceType\n"
- "$descriptor_key$ = $descriptor_name$\n",
+ "$descriptor_key$ = $descriptor_name$,\n",
"descriptor_key", kDescriptorKey,
"descriptor_name", ModuleLevelServiceDescriptorName(descriptor));
+ printer_->Print(
+ "__module__ = '$module_name$'\n",
+ "module_name", ModuleName(file_->name()));
+ printer_->Print("))\n\n");
printer_->Outdent();
}
void Generator::PrintServiceStub(const ServiceDescriptor& descriptor) const {
// Print the service stub.
- printer_->Print("class $class_name$_Stub($class_name$):\n",
+ printer_->Print("$class_name$_Stub = "
+ "service_reflection.GeneratedServiceStubType("
+ "'$class_name$_Stub', ($class_name$,), dict(\n",
"class_name", descriptor.name());
printer_->Indent();
printer_->Print(
- "__metaclass__ = service_reflection.GeneratedServiceStubType\n"
- "$descriptor_key$ = $descriptor_name$\n",
+ "$descriptor_key$ = $descriptor_name$,\n",
"descriptor_key", kDescriptorKey,
"descriptor_name", ModuleLevelServiceDescriptorName(descriptor));
+ printer_->Print(
+ "__module__ = '$module_name$'\n",
+ "module_name", ModuleName(file_->name()));
+ printer_->Print("))\n\n");
printer_->Outdent();
}
@@ -525,7 +584,7 @@ void Generator::PrintDescriptor(const Descriptor& message_descriptor) const {
PrintNestedDescriptors(message_descriptor);
printer_->Print("\n");
- printer_->Print("$descriptor_name$ = descriptor.Descriptor(\n",
+ printer_->Print("$descriptor_name$ = _descriptor.Descriptor(\n",
"descriptor_name",
ModuleLevelDescriptorName(message_descriptor));
printer_->Indent();
@@ -583,7 +642,22 @@ void Generator::PrintDescriptor(const Descriptor& message_descriptor) const {
"end", SimpleItoa(range->end));
}
printer_->Print("],\n");
-
+ printer_->Print("oneofs=[\n");
+ printer_->Indent();
+ for (int i = 0; i < message_descriptor.oneof_decl_count(); ++i) {
+ const OneofDescriptor* desc = message_descriptor.oneof_decl(i);
+ map<string, string> m;
+ m["name"] = desc->name();
+ m["full_name"] = desc->full_name();
+ m["index"] = SimpleItoa(desc->index());
+ printer_->Print(
+ m,
+ "_descriptor.OneofDescriptor(\n"
+ " name='$name$', full_name='$full_name$',\n"
+ " index=$index$, containing_type=None, fields=[]),\n");
+ }
+ printer_->Outdent();
+ printer_->Print("],\n");
// Serialization of proto
DescriptorProto edp;
PrintSerializedPbInterval(message_descriptor, edp);
@@ -606,7 +680,12 @@ void Generator::PrintNestedDescriptors(
// Prints all messages in |file|.
void Generator::PrintMessages() const {
for (int i = 0; i < file_->message_type_count(); ++i) {
- PrintMessage(*file_->message_type(i));
+ vector<string> to_register;
+ PrintMessage(*file_->message_type(i), "", &to_register);
+ for (int j = 0; j < to_register.size(); ++j) {
+ printer_->Print("_sym_db.RegisterMessage($name$)\n", "name",
+ to_register[j]);
+ }
printer_->Print("\n");
}
}
@@ -618,33 +697,40 @@ void Generator::PrintMessages() const {
// reflection.py will use to construct the meat of the class itself.
//
// Mutually recursive with PrintNestedMessages().
-void Generator::PrintMessage(
- const Descriptor& message_descriptor) const {
- printer_->Print("class $name$(message.Message):\n", "name",
- message_descriptor.name());
+// Collect nested message names to_register for the symbol_database.
+void Generator::PrintMessage(const Descriptor& message_descriptor,
+ const string& prefix,
+ vector<string>* to_register) const {
+ string qualified_name(prefix + message_descriptor.name());
+ to_register->push_back(qualified_name);
+ printer_->Print(
+ "$name$ = _reflection.GeneratedProtocolMessageType('$name$', "
+ "(_message.Message,), dict(\n",
+ "name", message_descriptor.name());
printer_->Indent();
- printer_->Print("__metaclass__ = reflection.GeneratedProtocolMessageType\n");
- PrintNestedMessages(message_descriptor);
+
+ PrintNestedMessages(message_descriptor, qualified_name + ".", to_register);
map<string, string> m;
m["descriptor_key"] = kDescriptorKey;
m["descriptor_name"] = ModuleLevelDescriptorName(message_descriptor);
- printer_->Print(m, "$descriptor_key$ = $descriptor_name$\n");
-
- printer_->Print(
- "\n"
- "# @@protoc_insertion_point(class_scope:$full_name$)\n",
- "full_name", message_descriptor.full_name());
-
+ printer_->Print(m, "$descriptor_key$ = $descriptor_name$,\n");
+ printer_->Print("__module__ = '$module_name$'\n",
+ "module_name", ModuleName(file_->name()));
+ printer_->Print("# @@protoc_insertion_point(class_scope:$full_name$)\n",
+ "full_name", message_descriptor.full_name());
+ printer_->Print("))\n");
printer_->Outdent();
}
// Prints all nested messages within |containing_descriptor|.
// Mutually recursive with PrintMessage().
-void Generator::PrintNestedMessages(
- const Descriptor& containing_descriptor) const {
+void Generator::PrintNestedMessages(const Descriptor& containing_descriptor,
+ const string& prefix,
+ vector<string>* to_register) const {
for (int i = 0; i < containing_descriptor.nested_type_count(); ++i) {
printer_->Print("\n");
- PrintMessage(*containing_descriptor.nested_type(i));
+ PrintMessage(*containing_descriptor.nested_type(i), prefix, to_register);
+ printer_->Print(",\n");
}
}
@@ -672,6 +758,57 @@ void Generator::FixForeignFieldsInDescriptor(
const EnumDescriptor& enum_descriptor = *descriptor.enum_type(i);
FixContainingTypeInDescriptor(enum_descriptor, &descriptor);
}
+ for (int i = 0; i < descriptor.oneof_decl_count(); ++i) {
+ map<string, string> m;
+ const OneofDescriptor* oneof = descriptor.oneof_decl(i);
+ m["descriptor_name"] = ModuleLevelDescriptorName(descriptor);
+ m["oneof_name"] = oneof->name();
+ for (int j = 0; j < oneof->field_count(); ++j) {
+ m["field_name"] = oneof->field(j)->name();
+ printer_->Print(
+ m,
+ "$descriptor_name$.oneofs_by_name['$oneof_name$'].fields.append(\n"
+ " $descriptor_name$.fields_by_name['$field_name$'])\n");
+ printer_->Print(
+ m,
+ "$descriptor_name$.fields_by_name['$field_name$'].containing_oneof = "
+ "$descriptor_name$.oneofs_by_name['$oneof_name$']\n");
+ }
+ }
+}
+
+void Generator::AddMessageToFileDescriptor(const Descriptor& descriptor) const {
+ map<string, string> m;
+ m["descriptor_name"] = kDescriptorKey;
+ m["message_name"] = descriptor.name();
+ m["message_descriptor_name"] = ModuleLevelDescriptorName(descriptor);
+ const char file_descriptor_template[] =
+ "$descriptor_name$.message_types_by_name['$message_name$'] = "
+ "$message_descriptor_name$\n";
+ printer_->Print(m, file_descriptor_template);
+}
+
+void Generator::AddEnumToFileDescriptor(
+ const EnumDescriptor& descriptor) const {
+ map<string, string> m;
+ m["descriptor_name"] = kDescriptorKey;
+ m["enum_name"] = descriptor.name();
+ m["enum_descriptor_name"] = ModuleLevelDescriptorName(descriptor);
+ const char file_descriptor_template[] =
+ "$descriptor_name$.enum_types_by_name['$enum_name$'] = "
+ "$enum_descriptor_name$\n";
+ printer_->Print(m, file_descriptor_template);
+}
+
+void Generator::AddExtensionToFileDescriptor(
+ const FieldDescriptor& descriptor) const {
+ map<string, string> m;
+ m["descriptor_name"] = kDescriptorKey;
+ m["field_name"] = descriptor.name();
+ const char file_descriptor_template[] =
+ "$descriptor_name$.extensions_by_name['$field_name$'] = "
+ "$field_name$\n";
+ printer_->Print(m, file_descriptor_template);
}
// Sets any necessary message_type and enum_type attributes
@@ -738,7 +875,7 @@ void Generator::FixContainingTypeInDescriptor(
const string parent_name = ModuleLevelDescriptorName(
*containing_descriptor);
printer_->Print(
- "$nested_name$.containing_type = $parent_name$;\n",
+ "$nested_name$.containing_type = $parent_name$\n",
"nested_name", nested_name,
"parent_name", parent_name);
}
@@ -752,6 +889,15 @@ void Generator::FixForeignFieldsInDescriptors() const {
for (int i = 0; i < file_->message_type_count(); ++i) {
FixForeignFieldsInDescriptor(*file_->message_type(i), NULL);
}
+ for (int i = 0; i < file_->message_type_count(); ++i) {
+ AddMessageToFileDescriptor(*file_->message_type(i));
+ }
+ for (int i = 0; i < file_->enum_type_count(); ++i) {
+ AddEnumToFileDescriptor(*file_->enum_type(i));
+ }
+ for (int i = 0; i < file_->extension_count(); ++i) {
+ AddExtensionToFileDescriptor(*file_->extension(i));
+ }
printer_->Print("\n");
}
@@ -767,6 +913,7 @@ void Generator::FixForeignFieldsInExtensions() const {
for (int i = 0; i < file_->message_type_count(); ++i) {
FixForeignFieldsInNestedExtensions(*file_->message_type(i));
}
+ printer_->Print("\n");
}
void Generator::FixForeignFieldsInExtension(
@@ -817,20 +964,24 @@ void Generator::PrintEnumValueDescriptor(
m["options"] = OptionsValue("EnumValueOptions", options_string);
printer_->Print(
m,
- "descriptor.EnumValueDescriptor(\n"
+ "_descriptor.EnumValueDescriptor(\n"
" name='$name$', index=$index$, number=$number$,\n"
" options=$options$,\n"
" type=None)");
}
+// Returns a Python expression that calls descriptor._ParseOptions using
+// the given descriptor class name and serialized options protobuf string.
string Generator::OptionsValue(
const string& class_name, const string& serialized_options) const {
if (serialized_options.length() == 0 || GeneratingDescriptorProto()) {
return "None";
} else {
string full_class_name = "descriptor_pb2." + class_name;
- return "descriptor._ParseOptions(" + full_class_name + "(), '"
- + CEscape(serialized_options)+ "')";
+//##!PY25 return "_descriptor._ParseOptions(" + full_class_name + "(), b'"
+//##!PY25 + CEscape(serialized_options)+ "')";
+ return "_descriptor._ParseOptions(" + full_class_name + "(), _b('" //##PY25
+ + CEscape(serialized_options)+ "'))"; //##PY25
}
}
@@ -855,7 +1006,7 @@ void Generator::PrintFieldDescriptor(
// these fields in correctly after all referenced descriptors have been
// defined and/or imported (see FixForeignFieldsInDescriptors()).
const char field_descriptor_decl[] =
- "descriptor.FieldDescriptor(\n"
+ "_descriptor.FieldDescriptor(\n"
" name='$name$', full_name='$full_name$', index=$index$,\n"
" number=$number$, type=$type$, cpp_type=$cpp_type$, label=$label$,\n"
" has_default_value=$has_default_value$, default_value=$default_value$,\n"
@@ -986,6 +1137,125 @@ void Generator::PrintSerializedPbInterval(
"serialized_end", SimpleItoa(offset + sp.size()));
}
+namespace {
+void PrintDescriptorOptionsFixingCode(const string& descriptor,
+ const string& options,
+ io::Printer* printer) {
+ // TODO(xiaofeng): I have added a method _SetOptions() to DescriptorBase
+ // in proto2 python runtime but it couldn't be used here because appengine
+ // uses a snapshot version of the library in which the new method is not
+ // yet present. After appengine has synced their runtime library, the code
+ // below should be cleaned up to use _SetOptions().
+ printer->Print(
+ "$descriptor$.has_options = True\n"
+ "$descriptor$._options = $options$\n",
+ "descriptor", descriptor, "options", options);
+}
+} // namespace
+
+// Prints expressions that set the options field of all descriptors.
+void Generator::FixAllDescriptorOptions() const {
+ // Prints an expression that sets the file descriptor's options.
+ string file_options = OptionsValue(
+ "FileOptions", file_->options().SerializeAsString());
+ if (file_options != "None") {
+ PrintDescriptorOptionsFixingCode(kDescriptorKey, file_options, printer_);
+ }
+ // Prints expressions that set the options for all top level enums.
+ for (int i = 0; i < file_->enum_type_count(); ++i) {
+ const EnumDescriptor& enum_descriptor = *file_->enum_type(i);
+ FixOptionsForEnum(enum_descriptor);
+ }
+ // Prints expressions that set the options for all top level extensions.
+ for (int i = 0; i < file_->extension_count(); ++i) {
+ const FieldDescriptor& field = *file_->extension(i);
+ FixOptionsForField(field);
+ }
+ // Prints expressions that set the options for all messages, nested enums,
+ // nested extensions and message fields.
+ for (int i = 0; i < file_->message_type_count(); ++i) {
+ FixOptionsForMessage(*file_->message_type(i));
+ }
+}
+
+// Prints expressions that set the options for an enum descriptor and its
+// value descriptors.
+void Generator::FixOptionsForEnum(const EnumDescriptor& enum_descriptor) const {
+ string descriptor_name = ModuleLevelDescriptorName(enum_descriptor);
+ string enum_options = OptionsValue(
+ "EnumOptions", enum_descriptor.options().SerializeAsString());
+ if (enum_options != "None") {
+ PrintDescriptorOptionsFixingCode(descriptor_name, enum_options, printer_);
+ }
+ for (int i = 0; i < enum_descriptor.value_count(); ++i) {
+ const EnumValueDescriptor& value_descriptor = *enum_descriptor.value(i);
+ string value_options = OptionsValue(
+ "EnumValueOptions", value_descriptor.options().SerializeAsString());
+ if (value_options != "None") {
+ PrintDescriptorOptionsFixingCode(
+ StringPrintf("%s.values_by_name[\"%s\"]", descriptor_name.c_str(),
+ value_descriptor.name().c_str()),
+ value_options, printer_);
+ }
+ }
+}
+
+// Prints expressions that set the options for field descriptors (including
+// extensions).
+void Generator::FixOptionsForField(
+ const FieldDescriptor& field) const {
+ string field_options = OptionsValue(
+ "FieldOptions", field.options().SerializeAsString());
+ if (field_options != "None") {
+ string field_name;
+ if (field.is_extension()) {
+ if (field.extension_scope() == NULL) {
+ // Top level extensions.
+ field_name = field.name();
+ } else {
+ field_name = FieldReferencingExpression(
+ field.extension_scope(), field, "extensions_by_name");
+ }
+ } else {
+ field_name = FieldReferencingExpression(
+ field.containing_type(), field, "fields_by_name");
+ }
+ PrintDescriptorOptionsFixingCode(field_name, field_options, printer_);
+ }
+}
+
+// Prints expressions that set the options for a message and all its inner
+// types (nested messages, nested enums, extensions, fields).
+void Generator::FixOptionsForMessage(const Descriptor& descriptor) const {
+ // Nested messages.
+ for (int i = 0; i < descriptor.nested_type_count(); ++i) {
+ FixOptionsForMessage(*descriptor.nested_type(i));
+ }
+ // Enums.
+ for (int i = 0; i < descriptor.enum_type_count(); ++i) {
+ FixOptionsForEnum(*descriptor.enum_type(i));
+ }
+ // Fields.
+ for (int i = 0; i < descriptor.field_count(); ++i) {
+ const FieldDescriptor& field = *descriptor.field(i);
+ FixOptionsForField(field);
+ }
+ // Extensions.
+ for (int i = 0; i < descriptor.extension_count(); ++i) {
+ const FieldDescriptor& field = *descriptor.extension(i);
+ FixOptionsForField(field);
+ }
+ // Message option for this message.
+ string message_options = OptionsValue(
+ "MessageOptions", descriptor.options().SerializeAsString());
+ if (message_options != "None") {
+ string descriptor_name = ModuleLevelDescriptorName(descriptor);
+ PrintDescriptorOptionsFixingCode(descriptor_name,
+ message_options,
+ printer_);
+ }
+}
+
} // namespace python
} // namespace compiler
} // namespace protobuf
diff --git a/src/google/protobuf/compiler/python/python_generator.h b/src/google/protobuf/compiler/python/python_generator.h
index 43c2087..f86e9ea 100644
--- a/src/google/protobuf/compiler/python/python_generator.h
+++ b/src/google/protobuf/compiler/python/python_generator.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -66,7 +66,7 @@ class LIBPROTOC_EXPORT Generator : public CodeGenerator {
// CodeGenerator methods.
virtual bool Generate(const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* generator_context,
string* error) const;
private:
@@ -94,8 +94,11 @@ class LIBPROTOC_EXPORT Generator : public CodeGenerator {
void PrintNestedDescriptors(const Descriptor& containing_descriptor) const;
void PrintMessages() const;
- void PrintMessage(const Descriptor& message_descriptor) const;
- void PrintNestedMessages(const Descriptor& containing_descriptor) const;
+ void PrintMessage(const Descriptor& message_descriptor, const string& prefix,
+ vector<string>* to_register) const;
+ void PrintNestedMessages(const Descriptor& containing_descriptor,
+ const string& prefix,
+ vector<string>* to_register) const;
void FixForeignFieldsInDescriptors() const;
void FixForeignFieldsInDescriptor(
@@ -104,6 +107,9 @@ class LIBPROTOC_EXPORT Generator : public CodeGenerator {
void FixForeignFieldsInField(const Descriptor* containing_type,
const FieldDescriptor& field,
const string& python_dict_name) const;
+ void AddMessageToFileDescriptor(const Descriptor& descriptor) const;
+ void AddEnumToFileDescriptor(const EnumDescriptor& descriptor) const;
+ void AddExtensionToFileDescriptor(const FieldDescriptor& descriptor) const;
string FieldReferencingExpression(const Descriptor* containing_type,
const FieldDescriptor& field,
const string& python_dict_name) const;
@@ -137,6 +143,11 @@ class LIBPROTOC_EXPORT Generator : public CodeGenerator {
void PrintSerializedPbInterval(
const DescriptorT& descriptor, DescriptorProtoT& proto) const;
+ void FixAllDescriptorOptions() const;
+ void FixOptionsForField(const FieldDescriptor& field) const;
+ void FixOptionsForEnum(const EnumDescriptor& descriptor) const;
+ void FixOptionsForMessage(const Descriptor& descriptor) const;
+
// Very coarse-grained lock to ensure that Generate() is reentrant.
// Guards file_, printer_ and file_descriptor_serialized_.
mutable Mutex mutex_;
diff --git a/src/google/protobuf/compiler/python/python_plugin_unittest.cc b/src/google/protobuf/compiler/python/python_plugin_unittest.cc
index fde8876..09dbc65 100644
--- a/src/google/protobuf/compiler/python/python_plugin_unittest.cc
+++ b/src/google/protobuf/compiler/python/python_plugin_unittest.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -34,6 +34,8 @@
// It seemed like parameterizing it would add more complexity than it is
// worth.
+#include <memory>
+
#include <google/protobuf/compiler/python/python_generator.h>
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/io/zero_copy_stream.h>
@@ -56,19 +58,19 @@ class TestGenerator : public CodeGenerator {
virtual bool Generate(const FileDescriptor* file,
const string& parameter,
- OutputDirectory* output_directory,
+ GeneratorContext* context,
string* error) const {
- TryInsert("test_pb2.py", "imports", output_directory);
- TryInsert("test_pb2.py", "module_scope", output_directory);
- TryInsert("test_pb2.py", "class_scope:foo.Bar", output_directory);
- TryInsert("test_pb2.py", "class_scope:foo.Bar.Baz", output_directory);
+ TryInsert("test_pb2.py", "imports", context);
+ TryInsert("test_pb2.py", "module_scope", context);
+ TryInsert("test_pb2.py", "class_scope:foo.Bar", context);
+ TryInsert("test_pb2.py", "class_scope:foo.Bar.Baz", context);
return true;
}
void TryInsert(const string& filename, const string& insertion_point,
- OutputDirectory* output_directory) const {
+ GeneratorContext* context) const {
scoped_ptr<io::ZeroCopyOutputStream> output(
- output_directory->OpenForInsert(filename, insertion_point));
+ context->OpenForInsert(filename, insertion_point));
io::Printer printer(output.get(), '$');
printer.Print("// inserted $name$\n", "name", insertion_point);
}
@@ -78,13 +80,13 @@ class TestGenerator : public CodeGenerator {
// not verify that they are correctly-placed; that would require actually
// compiling the output which is a bit more than I care to do for this test.
TEST(PythonPluginTest, PluginTest) {
- File::WriteStringToFileOrDie(
- "syntax = \"proto2\";\n"
- "package foo;\n"
- "message Bar {\n"
- " message Baz {}\n"
- "}\n",
- TestTempDir() + "/test.proto");
+ GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/test.proto",
+ "syntax = \"proto2\";\n"
+ "package foo;\n"
+ "message Bar {\n"
+ " message Baz {}\n"
+ "}\n",
+ true));
google::protobuf::compiler::CommandLineInterface cli;
cli.SetInputsAreProtoPathRelative(true);
diff --git a/src/google/protobuf/compiler/subprocess.cc b/src/google/protobuf/compiler/subprocess.cc
index de46a3e..61ae438 100644
--- a/src/google/protobuf/compiler/subprocess.cc
+++ b/src/google/protobuf/compiler/subprocess.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -32,13 +32,16 @@
#include <google/protobuf/compiler/subprocess.h>
+#include <algorithm>
+#include <iostream>
+
#ifndef _WIN32
#include <errno.h>
+#include <sys/select.h>
#include <sys/wait.h>
#include <signal.h>
#endif
-#include <algorithm>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/message.h>
#include <google/protobuf/stubs/substitute.h>
@@ -96,7 +99,7 @@ void Subprocess::Start(const string& program, SearchMode search_mode) {
}
// Setup STARTUPINFO to redirect handles.
- STARTUPINFO startup_info;
+ STARTUPINFOA startup_info;
ZeroMemory(&startup_info, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
startup_info.dwFlags = STARTF_USESTDHANDLES;
@@ -115,16 +118,16 @@ void Subprocess::Start(const string& program, SearchMode search_mode) {
// Create the process.
PROCESS_INFORMATION process_info;
- if (CreateProcess((search_mode == SEARCH_PATH) ? NULL : program.c_str(),
- (search_mode == SEARCH_PATH) ? name_copy : NULL,
- NULL, // process security attributes
- NULL, // thread security attributes
- TRUE, // inherit handles?
- 0, // obscure creation flags
- NULL, // environment (inherit from parent)
- NULL, // current directory (inherit from parent)
- &startup_info,
- &process_info)) {
+ if (CreateProcessA((search_mode == SEARCH_PATH) ? NULL : program.c_str(),
+ (search_mode == SEARCH_PATH) ? name_copy : NULL,
+ NULL, // process security attributes
+ NULL, // thread security attributes
+ TRUE, // inherit handles?
+ 0, // obscure creation flags
+ NULL, // environment (inherit from parent)
+ NULL, // current directory (inherit from parent)
+ &startup_info,
+ &process_info)) {
child_handle_ = process_info.hProcess;
CloseHandleOrDie(process_info.hThread);
child_stdin_ = stdin_pipe_write;
@@ -292,8 +295,8 @@ void Subprocess::Start(const string& program, SearchMode search_mode) {
int stdin_pipe[2];
int stdout_pipe[2];
- pipe(stdin_pipe);
- pipe(stdout_pipe);
+ GOOGLE_CHECK(pipe(stdin_pipe) != -1);
+ GOOGLE_CHECK(pipe(stdout_pipe) != -1);
char* argv[2] = { strdup(program.c_str()), NULL };
@@ -321,9 +324,11 @@ void Subprocess::Start(const string& program, SearchMode search_mode) {
// Write directly to STDERR_FILENO to avoid stdio code paths that may do
// stuff that is unsafe here.
- write(STDERR_FILENO, argv[0], strlen(argv[0]));
+ int ignored;
+ ignored = write(STDERR_FILENO, argv[0], strlen(argv[0]));
const char* message = ": program not found or is not executable\n";
- write(STDERR_FILENO, message, strlen(message));
+ ignored = write(STDERR_FILENO, message, strlen(message));
+ (void) ignored;
// Must use _exit() rather than exit() to avoid flushing output buffers
// that will also be flushed by the parent.
@@ -444,7 +449,7 @@ bool Subprocess::Communicate(const Message& input, Message* output,
}
if (!output->ParseFromString(output_data)) {
- *error = "Plugin output is unparseable.";
+ *error = "Plugin output is unparseable: " + CEscape(output_data);
return false;
}
diff --git a/src/google/protobuf/compiler/subprocess.h b/src/google/protobuf/compiler/subprocess.h
index f9e8ae8..2513863 100644
--- a/src/google/protobuf/compiler/subprocess.h
+++ b/src/google/protobuf/compiler/subprocess.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -40,9 +40,10 @@
#include <sys/types.h>
#include <unistd.h>
#endif // !_WIN32
+#include <google/protobuf/stubs/common.h>
#include <string>
-#include <google/protobuf/stubs/common.h>
+
namespace google {
namespace protobuf {
@@ -52,7 +53,7 @@ class Message;
namespace compiler {
// Utility class for launching sub-processes.
-class Subprocess {
+class LIBPROTOC_EXPORT Subprocess {
public:
Subprocess();
~Subprocess();
@@ -76,7 +77,7 @@ class Subprocess {
#ifdef _WIN32
// Given an error code, returns a human-readable error message. This is
// defined here so that CommandLineInterface can share it.
- static string Subprocess::Win32ErrorMessage(DWORD error_code);
+ static string Win32ErrorMessage(DWORD error_code);
#endif
private:
diff --git a/src/google/protobuf/compiler/test_plugin.cc b/src/google/protobuf/compiler/test_plugin.cc
index 5cbbf3d..4830fd7 100644
--- a/src/google/protobuf/compiler/test_plugin.cc
+++ b/src/google/protobuf/compiler/test_plugin.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
diff --git a/src/google/protobuf/compiler/zip_output_unittest.sh b/src/google/protobuf/compiler/zip_output_unittest.sh
index 259d5d2..6fc7136 100755
--- a/src/google/protobuf/compiler/zip_output_unittest.sh
+++ b/src/google/protobuf/compiler/zip_output_unittest.sh
@@ -2,7 +2,7 @@
#
# Protocol Buffers - Google's data interchange format
# Copyright 2009 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -39,45 +39,51 @@ fail() {
exit 1
}
+TEST_TMPDIR=.
+PROTOC=./protoc
+
echo '
+ syntax = "proto2";
option java_multiple_files = true;
option java_package = "test.jar";
option java_outer_classname = "Outer";
message Foo {}
message Bar {}
-' > testzip.proto
+' > $TEST_TMPDIR/testzip.proto
-./protoc --cpp_out=testzip.zip --python_out=testzip.zip --java_out=testzip.jar \
- testzip.proto || fail 'protoc failed.'
+$PROTOC \
+ --cpp_out=$TEST_TMPDIR/testzip.zip --python_out=$TEST_TMPDIR/testzip.zip \
+ --java_out=$TEST_TMPDIR/testzip.jar -I$TEST_TMPDIR testzip.proto \
+ || fail 'protoc failed.'
echo "Testing output to zip..."
if unzip -h > /dev/null; then
- unzip -t testzip.zip > testzip.list || fail 'unzip failed.'
+ unzip -t $TEST_TMPDIR/testzip.zip > $TEST_TMPDIR/testzip.list || fail 'unzip failed.'
- grep 'testing: testzip\.pb\.cc *OK$' testzip.list > /dev/null \
+ grep 'testing: testzip\.pb\.cc *OK$' $TEST_TMPDIR/testzip.list > /dev/null \
|| fail 'testzip.pb.cc not found in output zip.'
- grep 'testing: testzip\.pb\.h *OK$' testzip.list > /dev/null \
+ grep 'testing: testzip\.pb\.h *OK$' $TEST_TMPDIR/testzip.list > /dev/null \
|| fail 'testzip.pb.h not found in output zip.'
- grep 'testing: testzip_pb2\.py *OK$' testzip.list > /dev/null \
+ grep 'testing: testzip_pb2\.py *OK$' $TEST_TMPDIR/testzip.list > /dev/null \
|| fail 'testzip_pb2.py not found in output zip.'
- grep -i 'manifest' testzip.list > /dev/null \
+ grep -i 'manifest' $TEST_TMPDIR/testzip.list > /dev/null \
&& fail 'Zip file contained manifest.'
else
echo "Warning: 'unzip' command not available. Skipping test."
fi
echo "Testing output to jar..."
-if jar c testzip.proto > /dev/null; then
- jar tf testzip.jar > testzip.list || fail 'jar failed.'
+if jar c $TEST_TMPDIR/testzip.proto > /dev/null; then
+ jar tf $TEST_TMPDIR/testzip.jar > $TEST_TMPDIR/testzip.list || fail 'jar failed.'
- grep '^test/jar/Foo\.java$' testzip.list > /dev/null \
+ grep '^test/jar/Foo\.java$' $TEST_TMPDIR/testzip.list > /dev/null \
|| fail 'Foo.java not found in output jar.'
- grep '^test/jar/Bar\.java$' testzip.list > /dev/null \
+ grep '^test/jar/Bar\.java$' $TEST_TMPDIR/testzip.list > /dev/null \
|| fail 'Bar.java not found in output jar.'
- grep '^test/jar/Outer\.java$' testzip.list > /dev/null \
+ grep '^test/jar/Outer\.java$' $TEST_TMPDIR/testzip.list > /dev/null \
|| fail 'Outer.java not found in output jar.'
- grep '^META-INF/MANIFEST\.MF$' testzip.list > /dev/null \
- || fail 'Manifest not ofund in output jar.'
+ grep '^META-INF/MANIFEST\.MF$' $TEST_TMPDIR/testzip.list > /dev/null \
+ || fail 'Manifest not found in output jar.'
else
echo "Warning: 'jar' command not available. Skipping test."
fi
diff --git a/src/google/protobuf/compiler/zip_writer.cc b/src/google/protobuf/compiler/zip_writer.cc
index 53c1877..458cced 100644
--- a/src/google/protobuf/compiler/zip_writer.cc
+++ b/src/google/protobuf/compiler/zip_writer.cc
@@ -1,6 +1,36 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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.
+
+// 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
@@ -183,6 +213,6 @@ bool ZipWriter::WriteDirectory() {
return output.HadError();
}
-} // namespace google
-} // namespace protobuf
} // namespace compiler
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/compiler/zip_writer.h b/src/google/protobuf/compiler/zip_writer.h
index 4289553..602e508 100644
--- a/src/google/protobuf/compiler/zip_writer.h
+++ b/src/google/protobuf/compiler/zip_writer.h
@@ -1,6 +1,36 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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.
+
+// 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
@@ -58,6 +88,6 @@ class ZipWriter {
vector<FileInfo> files_;
};
-} // namespace google
-} // namespace protobuf
} // namespace compiler
+} // namespace protobuf
+} // namespace google