aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/google/protobuf/descriptor.cc
diff options
context:
space:
mode:
authorGravatar Feng Xiao <xfxyjwf@gmail.com>2014-11-10 17:34:54 -0800
committerGravatar Feng Xiao <xfxyjwf@gmail.com>2014-11-10 17:34:54 -0800
commit6ef984af4b0c63c1c33127a12dcfc8e6359f0c9e (patch)
treed17c61ff9f3ae28224fbddac6d26bfc59e2cf755 /src/google/protobuf/descriptor.cc
parentbaca1a8a1aa180c42de6278d3b8286c4496c6a10 (diff)
Down-integrate from internal code base.
Diffstat (limited to 'src/google/protobuf/descriptor.cc')
-rw-r--r--src/google/protobuf/descriptor.cc719
1 files changed, 606 insertions, 113 deletions
diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc
index 21dda598..ab8691fd 100644
--- a/src/google/protobuf/descriptor.cc
+++ b/src/google/protobuf/descriptor.cc
@@ -54,6 +54,7 @@
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/stubs/stringprintf.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
#include <google/protobuf/stubs/map_util.h>
@@ -134,6 +135,17 @@ const char * const FieldDescriptor::kLabelToName[MAX_LABEL + 1] = {
"repeated", // LABEL_REPEATED
};
+const char* FileDescriptor::SyntaxName(FileDescriptor::Syntax syntax) {
+ switch (syntax) {
+ case SYNTAX_PROTO2:
+ return "proto2";
+ case SYNTAX_PROTO3:
+ return "proto3";
+ case SYNTAX_UNKNOWN:
+ return "unknown";
+ }
+}
+
static const char * const kNonLinkedWeakMessageReplacementName = "google.protobuf.Empty";
#ifndef _MSC_VER // MSVC doesn't need these and won't even accept them.
@@ -144,8 +156,8 @@ const int FieldDescriptor::kLastReservedNumber;
namespace {
-string ToCamelCase(const string& input) {
- bool capitalize_next = false;
+string ToCamelCase(const string& input, bool lower_first) {
+ bool capitalize_next = !lower_first;
string result;
result.reserve(input.size());
@@ -166,8 +178,8 @@ string ToCamelCase(const string& input) {
}
// Lower-case the first letter.
- if (!result.empty() && 'A' <= result[0] && result[0] <= 'Z') {
- result[0] = result[0] - 'A' + 'a';
+ if (lower_first && !result.empty() && 'A' <= result[0] && result[0] <= 'Z') {
+ result[0] = result[0] - 'A' + 'a';
}
return result;
@@ -327,6 +339,31 @@ typedef hash_map<EnumIntPair, const EnumValueDescriptor*,
typedef map<DescriptorIntPair, const FieldDescriptor*>
ExtensionsGroupedByDescriptorMap;
typedef hash_map<string, const SourceCodeInfo_Location*> LocationsByPathMap;
+
+set<string>* allowed_proto3_extendees_ = NULL;
+GOOGLE_PROTOBUF_DECLARE_ONCE(allowed_proto3_extendees_init_);
+
+void InitAllowedProto3Extendee() {
+ allowed_proto3_extendees_ = new set<string>;
+ allowed_proto3_extendees_->insert("google.protobuf.FileOptions");
+ allowed_proto3_extendees_->insert("google.protobuf.MessageOptions");
+ allowed_proto3_extendees_->insert("google.protobuf.FieldOptions");
+ allowed_proto3_extendees_->insert("google.protobuf.EnumOptions");
+ allowed_proto3_extendees_->insert("google.protobuf.EnumValueOptions");
+ allowed_proto3_extendees_->insert("google.protobuf.ServiceOptions");
+ allowed_proto3_extendees_->insert("google.protobuf.MethodOptions");
+}
+
+// Checks whether the extendee type is allowed in proto3.
+// Only extensions to descriptor options are allowed. We use name comparison
+// instead of comparing the descriptor directly because the extensions may be
+// defined in a different pool.
+bool AllowedExtendeeInProto3(const string& name) {
+ ::google::protobuf::GoogleOnceInit(&allowed_proto3_extendees_init_, &InitAllowedProto3Extendee);
+ return allowed_proto3_extendees_->find(name) !=
+ allowed_proto3_extendees_->end();
+}
+
} // anonymous namespace
// ===================================================================
@@ -532,6 +569,9 @@ class FileDescriptorTables {
const void* parent, const string& camelcase_name) const;
inline const EnumValueDescriptor* FindEnumValueByNumber(
const EnumDescriptor* parent, int number) const;
+ // This creates a new EnumValueDescriptor if not found, in a thread-safe way.
+ inline const EnumValueDescriptor* FindEnumValueByNumberCreatingIfUnknown(
+ const EnumDescriptor* parent, int number) const;
// -----------------------------------------------------------------
// Adding items.
@@ -567,10 +607,16 @@ class FileDescriptorTables {
FieldsByNameMap fields_by_camelcase_name_;
FieldsByNumberMap fields_by_number_; // Not including extensions.
EnumValuesByNumberMap enum_values_by_number_;
+ mutable EnumValuesByNumberMap unknown_enum_values_by_number_
+ GOOGLE_GUARDED_BY(unknown_enum_values_mu_);
// Populated on first request to save space, hence constness games.
mutable GoogleOnceDynamic locations_by_path_once_;
mutable LocationsByPathMap locations_by_path_;
+
+ // Mutex to protect the unknown-enum-value map due to dynamic
+ // EnumValueDescriptor creation on unknown values.
+ mutable Mutex unknown_enum_values_mu_;
};
DescriptorPool::Tables::Tables()
@@ -600,7 +646,8 @@ FileDescriptorTables::FileDescriptorTables()
fields_by_lowercase_name_(3),
fields_by_camelcase_name_(3),
fields_by_number_(3),
- enum_values_by_number_(3) {
+ enum_values_by_number_(3),
+ unknown_enum_values_by_number_(3) {
}
FileDescriptorTables::~FileDescriptorTables() {}
@@ -749,6 +796,60 @@ inline const EnumValueDescriptor* FileDescriptorTables::FindEnumValueByNumber(
return FindPtrOrNull(enum_values_by_number_, make_pair(parent, number));
}
+inline const EnumValueDescriptor*
+FileDescriptorTables::FindEnumValueByNumberCreatingIfUnknown(
+ const EnumDescriptor* parent, int number) const {
+ // First try, with map of compiled-in values.
+ {
+ const EnumValueDescriptor* desc = FindPtrOrNull(
+ enum_values_by_number_, make_pair(parent, number));
+ if (desc != NULL) {
+ return desc;
+ }
+ }
+ // Second try, with reader lock held on unknown enum values: common case.
+ {
+ ReaderMutexLock l(&unknown_enum_values_mu_);
+ const EnumValueDescriptor* desc = FindPtrOrNull(
+ unknown_enum_values_by_number_, make_pair(parent, number));
+ if (desc != NULL) {
+ return desc;
+ }
+ }
+ // If not found, try again with writer lock held, and create new descriptor if
+ // necessary.
+ {
+ WriterMutexLock l(&unknown_enum_values_mu_);
+ const EnumValueDescriptor* desc = FindPtrOrNull(
+ unknown_enum_values_by_number_, make_pair(parent, number));
+ if (desc != NULL) {
+ return desc;
+ }
+
+ // Create an EnumValueDescriptor dynamically. We don't insert it into the
+ // EnumDescriptor (it's not a part of the enum as originally defined), but
+ // we do insert it into the table so that we can return the same pointer
+ // later.
+ string enum_value_name = StringPrintf(
+ "UNKNOWN_ENUM_VALUE_%s_%d", parent->name().c_str(), number);
+ DescriptorPool::Tables* tables =
+ const_cast<DescriptorPool::Tables*>(DescriptorPool::generated_pool()->
+ tables_.get());
+ EnumValueDescriptor* result = tables->Allocate<EnumValueDescriptor>();
+ result->name_ = tables->AllocateString(enum_value_name);
+ result->full_name_ = tables->AllocateString(parent->full_name() +
+ "." + enum_value_name);
+ result->number_ = number;
+ result->type_ = parent;
+ result->options_ = &EnumValueOptions::default_instance();
+ InsertIfNotPresent(&unknown_enum_values_by_number_,
+ make_pair(parent, number),
+ result);
+ return result;
+ }
+}
+
+
inline const FieldDescriptor* DescriptorPool::Tables::FindExtension(
const Descriptor* extendee, int number) {
return FindPtrOrNull(extensions_, make_pair(extendee, number));
@@ -1311,6 +1412,11 @@ EnumDescriptor::FindValueByNumber(int key) const {
return file()->tables_->FindEnumValueByNumber(this, key);
}
+const EnumValueDescriptor*
+EnumDescriptor::FindValueByNumberCreatingIfUnknown(int key) const {
+ return file()->tables_->FindEnumValueByNumberCreatingIfUnknown(this, key);
+}
+
const MethodDescriptor*
ServiceDescriptor::FindMethodByName(const string& key) const {
Symbol result =
@@ -1562,6 +1668,8 @@ string FieldDescriptor::DefaultValueAsString(bool quote_string_type) const {
void FileDescriptor::CopyTo(FileDescriptorProto* proto) const {
proto->set_name(name());
if (!package().empty()) proto->set_package(package());
+ // TODO(liujisi): Also populate when syntax="proto2".
+ if (syntax() == SYNTAX_PROTO3) proto->set_syntax(SyntaxName(syntax()));
for (int i = 0; i < dependency_count(); i++) {
proto->add_dependency(dependency(i)->name());
@@ -1594,7 +1702,8 @@ void FileDescriptor::CopyTo(FileDescriptorProto* proto) const {
}
void FileDescriptor::CopySourceCodeInfoTo(FileDescriptorProto* proto) const {
- if (source_code_info_ != &SourceCodeInfo::default_instance()) {
+ if (source_code_info_ &&
+ source_code_info_ != &SourceCodeInfo::default_instance()) {
proto->mutable_source_code_info()->CopyFrom(*source_code_info_);
}
}
@@ -1802,10 +1911,67 @@ bool FormatLineOptions(int depth, const Message &options, string *output) {
return !all_options.empty();
}
+template<typename DescType>
+class SourceLocationCommentPrinter {
+ public:
+ SourceLocationCommentPrinter(const DescType* desc,
+ const string& prefix,
+ const DebugStringOptions& options)
+ : options_(options), prefix_(prefix) {
+ // Perform the SourceLocation lookup only if we're including user comments,
+ // because the lookup is fairly expensive.
+ have_source_loc_ = options.include_comments &&
+ desc->GetSourceLocation(&source_loc_);
+ }
+ void AddPreComment(string* output) {
+ if (have_source_loc_ && source_loc_.leading_comments.size() > 0) {
+ *output += FormatComment(source_loc_.leading_comments);
+ }
+ }
+ void AddPostComment(string* output) {
+ if (have_source_loc_ && source_loc_.trailing_comments.size() > 0) {
+ *output += FormatComment(source_loc_.trailing_comments);
+ }
+ }
+
+ // Format comment such that each line becomes a full-line C++-style comment in
+ // the DebugString() output.
+ string FormatComment(const string& comment_text) {
+ string stripped_comment = comment_text;
+ StripWhitespace(&stripped_comment);
+ vector<string> lines = Split(stripped_comment, "\n");
+ string output;
+ for (int i = 0; i < lines.size(); ++i) {
+ const string& line = lines[i];
+ strings::SubstituteAndAppend(&output, "$0// $1\n", prefix_, line);
+ }
+ return output;
+ }
+
+ private:
+ const DescType* desc_;
+ bool have_source_loc_;
+ SourceLocation source_loc_;
+ DebugStringOptions options_;
+ string prefix_;
+};
+
} // anonymous namespace
string FileDescriptor::DebugString() const {
- string contents = "syntax = \"proto2\";\n\n";
+ DebugStringOptions options; // default options
+ return DebugStringWithOptions(options);
+}
+
+string FileDescriptor::DebugStringWithOptions(
+ const DebugStringOptions& debug_string_options) const {
+ string contents;
+ strings::SubstituteAndAppend(&contents, "syntax = \"$0\";\n\n",
+ SyntaxName(syntax()));
+
+ SourceLocationCommentPrinter<FileDescriptor>
+ comment_printer(this, "", debug_string_options);
+ comment_printer.AddPreComment(&contents);
set<int> public_dependencies;
set<int> weak_dependencies;
@@ -1836,7 +2002,7 @@ string FileDescriptor::DebugString() const {
}
for (int i = 0; i < enum_type_count(); i++) {
- enum_type(i)->DebugString(0, &contents);
+ enum_type(i)->DebugString(0, &contents, debug_string_options);
contents.append("\n");
}
@@ -1851,15 +2017,14 @@ string FileDescriptor::DebugString() const {
for (int i = 0; i < message_type_count(); i++) {
if (groups.count(message_type(i)) == 0) {
- strings::SubstituteAndAppend(&contents, "message $0",
- message_type(i)->name());
- message_type(i)->DebugString(0, &contents);
+ message_type(i)->DebugString(0, &contents, debug_string_options,
+ /* include_opening_clause */ true);
contents.append("\n");
}
}
for (int i = 0; i < service_count(); i++) {
- service(i)->DebugString(&contents);
+ service(i)->DebugString(&contents, debug_string_options);
contents.append("\n");
}
@@ -1871,23 +2036,46 @@ string FileDescriptor::DebugString() const {
strings::SubstituteAndAppend(&contents, "extend .$0 {\n",
containing_type->full_name());
}
- extension(i)->DebugString(1, FieldDescriptor::PRINT_LABEL, &contents);
+ extension(i)->DebugString(1, FieldDescriptor::PRINT_LABEL, &contents,
+ debug_string_options);
}
if (extension_count() > 0) contents.append("}\n\n");
+ comment_printer.AddPostComment(&contents);
+
return contents;
}
string Descriptor::DebugString() const {
+ DebugStringOptions options; // default options
+ return DebugStringWithOptions(options);
+}
+
+string Descriptor::DebugStringWithOptions(
+ const DebugStringOptions& options) const {
string contents;
- strings::SubstituteAndAppend(&contents, "message $0", name());
- DebugString(0, &contents);
+ DebugString(0, &contents, options, /* include_opening_clause */ true);
return contents;
}
-void Descriptor::DebugString(int depth, string *contents) const {
+void Descriptor::DebugString(int depth, string *contents,
+ const DebugStringOptions&
+ debug_string_options,
+ bool include_opening_clause) const {
+ if (options().map_entry()) {
+ // Do not generate debug string for auto-generated map-entry type.
+ return;
+ }
string prefix(depth * 2, ' ');
++depth;
+
+ SourceLocationCommentPrinter<Descriptor>
+ comment_printer(this, prefix, debug_string_options);
+ comment_printer.AddPreComment(contents);
+
+ if (include_opening_clause) {
+ strings::SubstituteAndAppend(contents, "$0message $1", prefix, name());
+ }
contents->append(" {\n");
FormatLineOptions(depth, options(), contents);
@@ -1909,20 +2097,21 @@ void Descriptor::DebugString(int depth, string *contents) const {
for (int i = 0; i < nested_type_count(); i++) {
if (groups.count(nested_type(i)) == 0) {
- strings::SubstituteAndAppend(contents, "$0 message $1",
- prefix, nested_type(i)->name());
- nested_type(i)->DebugString(depth, contents);
+ nested_type(i)->DebugString(depth, contents, debug_string_options,
+ /* include_opening_clause */ true);
}
}
for (int i = 0; i < enum_type_count(); i++) {
- enum_type(i)->DebugString(depth, contents);
+ enum_type(i)->DebugString(depth, contents, debug_string_options);
}
for (int i = 0; i < field_count(); i++) {
if (field(i)->containing_oneof() == NULL) {
- field(i)->DebugString(depth, FieldDescriptor::PRINT_LABEL, contents);
+ field(i)->DebugString(depth, FieldDescriptor::PRINT_LABEL, contents,
+ debug_string_options);
} else if (field(i)->containing_oneof()->field(0) == field(i)) {
// This is the first field in this oneof, so print the whole oneof.
- field(i)->containing_oneof()->DebugString(depth, contents);
+ field(i)->containing_oneof()->DebugString(depth, contents,
+ debug_string_options);
}
}
@@ -1943,15 +2132,23 @@ void Descriptor::DebugString(int depth, string *contents) const {
prefix, containing_type->full_name());
}
extension(i)->DebugString(
- depth + 1, FieldDescriptor::PRINT_LABEL, contents);
+ depth + 1, FieldDescriptor::PRINT_LABEL, contents,
+ debug_string_options);
}
if (extension_count() > 0)
strings::SubstituteAndAppend(contents, "$0 }\n", prefix);
strings::SubstituteAndAppend(contents, "$0}\n", prefix);
+ comment_printer.AddPostComment(contents);
}
string FieldDescriptor::DebugString() const {
+ DebugStringOptions options; // default options
+ return DebugStringWithOptions(options);
+}
+
+string FieldDescriptor::DebugStringWithOptions(
+ const DebugStringOptions& debug_string_options) const {
string contents;
int depth = 0;
if (is_extension()) {
@@ -1959,35 +2156,55 @@ string FieldDescriptor::DebugString() const {
containing_type()->full_name());
depth = 1;
}
- DebugString(depth, PRINT_LABEL, &contents);
+ DebugString(depth, PRINT_LABEL, &contents, debug_string_options);
if (is_extension()) {
contents.append("}\n");
}
return contents;
}
+// The field type string used in FieldDescriptor::DebugString()
+string FieldDescriptor::FieldTypeNameDebugString() const {
+ switch(type()) {
+ case TYPE_MESSAGE:
+ return "." + message_type()->full_name();
+ case TYPE_ENUM:
+ return "." + enum_type()->full_name();
+ default:
+ return kTypeToName[type()];
+ }
+}
+
void FieldDescriptor::DebugString(int depth,
PrintLabelFlag print_label_flag,
- string *contents) const {
+ string *contents,
+ const DebugStringOptions&
+ debug_string_options) const {
string prefix(depth * 2, ' ');
string field_type;
- switch (type()) {
- case TYPE_MESSAGE:
- field_type = "." + message_type()->full_name();
- break;
- case TYPE_ENUM:
- field_type = "." + enum_type()->full_name();
- break;
- default:
- field_type = kTypeToName[type()];
+
+ // Special case map fields.
+ bool is_map = false;
+ if (type() == TYPE_MESSAGE && message_type()->options().map_entry()) {
+ is_map = true;
+ strings::SubstituteAndAppend(
+ &field_type, "map<$0, $1>",
+ message_type()->field(0)->FieldTypeNameDebugString(),
+ message_type()->field(1)->FieldTypeNameDebugString());
+ } else {
+ field_type = FieldTypeNameDebugString();
}
string label;
- if (print_label_flag == PRINT_LABEL) {
+ if (print_label_flag == PRINT_LABEL && !is_map) {
label = kLabelToName[this->label()];
label.push_back(' ');
}
+ SourceLocationCommentPrinter<FieldDescriptor>
+ comment_printer(this, prefix, debug_string_options);
+ comment_printer.AddPreComment(contents);
+
strings::SubstituteAndAppend(contents, "$0$1$2 $3 = $4",
prefix,
label,
@@ -2015,57 +2232,101 @@ void FieldDescriptor::DebugString(int depth,
}
if (type() == TYPE_GROUP) {
- message_type()->DebugString(depth, contents);
+ message_type()->DebugString(depth, contents, debug_string_options,
+ /* include_opening_clause */ false);
} else {
contents->append(";\n");
}
+
+ comment_printer.AddPostComment(contents);
}
string OneofDescriptor::DebugString() const {
+ DebugStringOptions options; // default values
+ return DebugStringWithOptions(options);
+}
+
+string OneofDescriptor::DebugStringWithOptions(
+ const DebugStringOptions& options) const {
string contents;
- DebugString(0, &contents);
+ DebugString(0, &contents, options);
return contents;
}
-void OneofDescriptor::DebugString(int depth, string* contents) const {
+void OneofDescriptor::DebugString(int depth, string* contents,
+ const DebugStringOptions&
+ debug_string_options) const {
string prefix(depth * 2, ' ');
++depth;
+ SourceLocationCommentPrinter<OneofDescriptor>
+ comment_printer(this, prefix, debug_string_options);
+ comment_printer.AddPreComment(contents);
strings::SubstituteAndAppend(
contents, "$0 oneof $1 {\n", prefix, name());
for (int i = 0; i < field_count(); i++) {
- field(i)->DebugString(depth, FieldDescriptor::OMIT_LABEL, contents);
+ field(i)->DebugString(depth, FieldDescriptor::OMIT_LABEL, contents,
+ debug_string_options);
}
strings::SubstituteAndAppend(contents, "$0}\n", prefix);
+ comment_printer.AddPostComment(contents);
}
string EnumDescriptor::DebugString() const {
+ DebugStringOptions options; // default values
+ return DebugStringWithOptions(options);
+}
+
+string EnumDescriptor::DebugStringWithOptions(
+ const DebugStringOptions& options) const {
string contents;
- DebugString(0, &contents);
+ DebugString(0, &contents, options);
return contents;
}
-void EnumDescriptor::DebugString(int depth, string *contents) const {
+void EnumDescriptor::DebugString(int depth, string *contents,
+ const DebugStringOptions&
+ debug_string_options) const {
string prefix(depth * 2, ' ');
++depth;
+
+ SourceLocationCommentPrinter<EnumDescriptor>
+ comment_printer(this, prefix, debug_string_options);
+ comment_printer.AddPreComment(contents);
+
strings::SubstituteAndAppend(contents, "$0enum $1 {\n",
prefix, name());
FormatLineOptions(depth, options(), contents);
for (int i = 0; i < value_count(); i++) {
- value(i)->DebugString(depth, contents);
+ value(i)->DebugString(depth, contents, debug_string_options);
}
strings::SubstituteAndAppend(contents, "$0}\n", prefix);
+
+ comment_printer.AddPostComment(contents);
}
string EnumValueDescriptor::DebugString() const {
+ DebugStringOptions options; // default values
+ return DebugStringWithOptions(options);
+}
+
+string EnumValueDescriptor::DebugStringWithOptions(
+ const DebugStringOptions& options) const {
string contents;
- DebugString(0, &contents);
+ DebugString(0, &contents, options);
return contents;
}
-void EnumValueDescriptor::DebugString(int depth, string *contents) const {
+void EnumValueDescriptor::DebugString(int depth, string *contents,
+ const DebugStringOptions&
+ debug_string_options) const {
string prefix(depth * 2, ' ');
+
+ SourceLocationCommentPrinter<EnumValueDescriptor>
+ comment_printer(this, prefix, debug_string_options);
+ comment_printer.AddPreComment(contents);
+
strings::SubstituteAndAppend(contents, "$0$1 = $2",
prefix, name(), number());
@@ -2074,35 +2335,64 @@ void EnumValueDescriptor::DebugString(int depth, string *contents) const {
strings::SubstituteAndAppend(contents, " [$0]", formatted_options);
}
contents->append(";\n");
+
+ comment_printer.AddPostComment(contents);
}
string ServiceDescriptor::DebugString() const {
+ DebugStringOptions options; // default values
+ return DebugStringWithOptions(options);
+}
+
+string ServiceDescriptor::DebugStringWithOptions(
+ const DebugStringOptions& options) const {
string contents;
- DebugString(&contents);
+ DebugString(&contents, options);
return contents;
}
-void ServiceDescriptor::DebugString(string *contents) const {
+void ServiceDescriptor::DebugString(string *contents,
+ const DebugStringOptions&
+ debug_string_options) const {
+ SourceLocationCommentPrinter<ServiceDescriptor>
+ comment_printer(this, /* prefix */ "", debug_string_options);
+ comment_printer.AddPreComment(contents);
+
strings::SubstituteAndAppend(contents, "service $0 {\n", name());
FormatLineOptions(1, options(), contents);
for (int i = 0; i < method_count(); i++) {
- method(i)->DebugString(1, contents);
+ method(i)->DebugString(1, contents, debug_string_options);
}
contents->append("}\n");
+
+ comment_printer.AddPostComment(contents);
}
string MethodDescriptor::DebugString() const {
+ DebugStringOptions options; // default values
+ return DebugStringWithOptions(options);
+}
+
+string MethodDescriptor::DebugStringWithOptions(
+ const DebugStringOptions& options) const {
string contents;
- DebugString(0, &contents);
+ DebugString(0, &contents, options);
return contents;
}
-void MethodDescriptor::DebugString(int depth, string *contents) const {
+void MethodDescriptor::DebugString(int depth, string *contents,
+ const DebugStringOptions&
+ debug_string_options) const {
string prefix(depth * 2, ' ');
++depth;
+
+ SourceLocationCommentPrinter<MethodDescriptor>
+ comment_printer(this, prefix, debug_string_options);
+ comment_printer.AddPreComment(contents);
+
strings::SubstituteAndAppend(contents, "$0rpc $1(.$2) returns (.$3)",
prefix, name(),
input_type()->full_name(),
@@ -2115,6 +2405,8 @@ void MethodDescriptor::DebugString(int depth, string *contents) const {
} else {
contents->append(";\n");
}
+
+ comment_printer.AddPostComment(contents);
}
@@ -2142,6 +2434,11 @@ bool FileDescriptor::GetSourceLocation(const vector<int>& path,
return false;
}
+bool FileDescriptor::GetSourceLocation(SourceLocation* out_location) const {
+ vector<int> path; // empty path for root FileDescriptor
+ return GetSourceLocation(path, out_location);
+}
+
bool FieldDescriptor::is_packed() const {
return is_packable() && (options_ != NULL) && options_->packed();
}
@@ -2409,7 +2706,7 @@ class DescriptorBuilder {
// Creates a placeholder file. Never returns NULL. This is used when an
// import is not found and AllowUnknownDependencies() is enabled.
- const FileDescriptor* NewPlaceholderFile(const string& name);
+ FileDescriptor* NewPlaceholderFile(const string& name);
// Calls tables_->AddSymbol() and records an error if it fails. Returns
// true if successful or false if failed, though most callers can ignore
@@ -2667,9 +2964,24 @@ class DescriptorBuilder {
const ServiceDescriptorProto& proto);
void ValidateMethodOptions(MethodDescriptor* method,
const MethodDescriptorProto& proto);
-
- void ValidateMapKey(FieldDescriptor* field,
- const FieldDescriptorProto& proto);
+ void ValidateProto3(FileDescriptor* file,
+ const FileDescriptorProto& proto);
+ void ValidateProto3Message(Descriptor* message,
+ const DescriptorProto& proto);
+ void ValidateProto3Field(FieldDescriptor* field,
+ const FieldDescriptorProto& proto);
+ void ValidateProto3Enum(EnumDescriptor* enm,
+ const EnumDescriptorProto& proto);
+
+ // Returns true if the map entry message is compatible with the
+ // auto-generated entry message from map fields syntax.
+ bool ValidateMapEntry(FieldDescriptor* field,
+ const FieldDescriptorProto& proto);
+
+ // Recursively detects naming conflicts with map entry types for a
+ // better error message.
+ void DetectMapConflicts(const Descriptor* message,
+ const DescriptorProto& proto);
};
@@ -2992,19 +3304,9 @@ Symbol DescriptorBuilder::NewPlaceholder(const string& name,
}
// Create the placeholders.
- FileDescriptor* placeholder_file = tables_->Allocate<FileDescriptor>();
- memset(placeholder_file, 0, sizeof(*placeholder_file));
-
- placeholder_file->source_code_info_ = &SourceCodeInfo::default_instance();
-
- placeholder_file->name_ =
- tables_->AllocateString(*placeholder_full_name + ".placeholder.proto");
+ FileDescriptor* placeholder_file = NewPlaceholderFile(
+ *placeholder_full_name + ".placeholder.proto");
placeholder_file->package_ = placeholder_package;
- placeholder_file->pool_ = pool_;
- placeholder_file->options_ = &FileOptions::default_instance();
- placeholder_file->tables_ = &FileDescriptorTables::kEmpty;
- placeholder_file->is_placeholder_ = true;
- // All other fields are zero or NULL.
if (placeholder_type == PLACEHOLDER_ENUM) {
placeholder_file->enum_type_count_ = 1;
@@ -3068,7 +3370,7 @@ Symbol DescriptorBuilder::NewPlaceholder(const string& name,
}
}
-const FileDescriptor* DescriptorBuilder::NewPlaceholderFile(
+FileDescriptor* DescriptorBuilder::NewPlaceholderFile(
const string& name) {
FileDescriptor* placeholder = tables_->Allocate<FileDescriptor>();
memset(placeholder, 0, sizeof(*placeholder));
@@ -3078,7 +3380,9 @@ const FileDescriptor* DescriptorBuilder::NewPlaceholderFile(
placeholder->pool_ = pool_;
placeholder->options_ = &FileOptions::default_instance();
placeholder->tables_ = &FileDescriptorTables::kEmpty;
+ placeholder->source_code_info_ = &SourceCodeInfo::default_instance();
placeholder->is_placeholder_ = true;
+ placeholder->syntax_ = FileDescriptor::SYNTAX_PROTO2;
// All other fields are zero or NULL.
return placeholder;
@@ -3363,6 +3667,19 @@ const FileDescriptor* DescriptorBuilder::BuildFile(
"Missing field: FileDescriptorProto.name.");
}
+ // TODO(liujisi): Report error when the syntax is empty after all the protos
+ // have added the syntax statement.
+ if (proto.syntax().empty() || proto.syntax() == "proto2") {
+ file_->syntax_ = FileDescriptor::SYNTAX_PROTO2;
+ } else if (proto.syntax() == "proto3") {
+ file_->syntax_ = FileDescriptor::SYNTAX_PROTO3;
+ } else {
+ file_->syntax_ = FileDescriptor::SYNTAX_UNKNOWN;
+ AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER,
+ "Unrecognized syntax: " + proto.syntax());
+ }
+
+
result->name_ = tables_->AllocateString(proto.name());
if (proto.has_package()) {
result->package_ = tables_->AllocateString(proto.package());
@@ -3506,6 +3823,14 @@ const FileDescriptor* DescriptorBuilder::BuildFile(
ValidateFileOptions(result, proto);
}
+ // Additional naming conflict check for map entry types. Only need to check
+ // this if there are already errors.
+ if (had_errors_) {
+ for (int i = 0; i < proto.message_type_size(); ++i) {
+ DetectMapConflicts(result->message_type(i), proto.message_type(i));
+ }
+ }
+
if (!unused_dependency_.empty()) {
LogUnusedDependency(result);
@@ -3621,7 +3946,9 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto,
// Don't bother with the above optimization for camel-case names since
// .proto files that follow the guide shouldn't be using names in this
// format, so the optimization wouldn't help much.
- result->camelcase_name_ = tables_->AllocateString(ToCamelCase(proto.name()));
+ result->camelcase_name_ =
+ tables_->AllocateString(ToCamelCase(proto.name(),
+ /* lower_first = */ true));
// Some compilers do not allow static_cast directly between two enum types,
// so we must cast to int first.
@@ -3646,7 +3973,6 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto,
// Some of these may be filled in when cross-linking.
result->containing_type_ = NULL;
result->extension_scope_ = NULL;
- result->experimental_map_key_ = NULL;
result->message_type_ = NULL;
result->enum_type_ = NULL;
@@ -4472,8 +4798,98 @@ void DescriptorBuilder::ValidateFileOptions(FileDescriptor* file,
}
}
}
+ if (file->syntax() == FileDescriptor::SYNTAX_PROTO3) {
+ ValidateProto3(file, proto);
+ }
}
+void DescriptorBuilder::ValidateProto3(
+ FileDescriptor* file, const FileDescriptorProto& proto) {
+ for (int i = 0; i < file->extension_count(); ++i) {
+ ValidateProto3Field(file->extensions_ + i, proto.extension(i));
+ }
+ for (int i = 0; i < file->message_type_count(); ++i) {
+ ValidateProto3Message(file->message_types_ + i, proto.message_type(i));
+ }
+ for (int i = 0; i < file->enum_type_count(); ++i) {
+ ValidateProto3Enum(file->enum_types_ + i, proto.enum_type(i));
+ }
+ if (IsLite(file)) {
+ AddError(file->name(), proto,
+ DescriptorPool::ErrorCollector::OTHER,
+ "Lite runtime is not supported in proto3.");
+ }
+}
+
+void DescriptorBuilder::ValidateProto3Message(
+ Descriptor* message, const DescriptorProto& proto) {
+ for (int i = 0; i < message->nested_type_count(); ++i) {
+ ValidateProto3Message(message->nested_types_ + i,
+ proto.nested_type(i));
+ }
+ for (int i = 0; i < message->enum_type_count(); ++i) {
+ ValidateProto3Enum(message->enum_types_ + i,
+ proto.enum_type(i));
+ }
+ for (int i = 0; i < message->field_count(); ++i) {
+ ValidateProto3Field(message->fields_ + i, proto.field(i));
+ }
+ for (int i = 0; i < message->extension_count(); ++i) {
+ ValidateProto3Field(message->extensions_ +i, proto.extension(i));
+ }
+ if (message->extension_range_count() > 0) {
+ AddError(message->full_name(), proto,
+ DescriptorPool::ErrorCollector::OTHER,
+ "Extension ranges are not allowed in proto3.");
+ }
+ if (message->options().message_set_wire_format()) {
+ // Using MessageSet doesn't make sense since we disallow extensions.
+ AddError(message->full_name(), proto,
+ DescriptorPool::ErrorCollector::OTHER,
+ "MessageSet is not supported in proto3.");
+ }
+}
+
+void DescriptorBuilder::ValidateProto3Field(
+ FieldDescriptor* field, const FieldDescriptorProto& proto) {
+ if (field->is_extension() &&
+ !AllowedExtendeeInProto3(field->containing_type()->full_name())) {
+ AddError(field->full_name(), proto,
+ DescriptorPool::ErrorCollector::OTHER,
+ "Extensions in proto3 are only allowed for defining options.");
+ }
+ if (field->is_required()) {
+ AddError(field->full_name(), proto,
+ DescriptorPool::ErrorCollector::OTHER,
+ "Required fields are not allowed in proto3.");
+ }
+ if (field->has_default_value()) {
+ AddError(
+ field->full_name(), proto, DescriptorPool::ErrorCollector::OTHER,
+ "Explicit default values are not allowed in proto3.");
+ }
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
+ field->enum_type() &&
+ field->enum_type()->file()->syntax() != FileDescriptor::SYNTAX_PROTO3) {
+ // Proto3 messages can only use Proto3 enum types; otherwise we can't
+ // guarantee that the default value is zero.
+ AddError(field->full_name(), proto,
+ DescriptorPool::ErrorCollector::TYPE,
+ "Enum type \"" + field->enum_type()->full_name() +
+ "\" is not a proto3 enum, but is used in \"" +
+ field->containing_type()->full_name() +
+ "\" which is a proto3 message type.");
+ }
+}
+
+void DescriptorBuilder::ValidateProto3Enum(
+ EnumDescriptor* enm, const EnumDescriptorProto& proto) {
+ if (enm->value_count() > 0 && enm->value(0)->number() != 0) {
+ AddError(
+ enm->full_name(), proto, DescriptorPool::ErrorCollector::OTHER,
+ "The first enum value must be zero in proto3.");
+ }
+}
void DescriptorBuilder::ValidateMessageOptions(Descriptor* message,
const DescriptorProto& proto) {
@@ -4499,10 +4915,6 @@ void DescriptorBuilder::ValidateMessageOptions(Descriptor* message,
void DescriptorBuilder::ValidateFieldOptions(FieldDescriptor* field,
const FieldDescriptorProto& proto) {
- if (field->options().has_experimental_map_key()) {
- ValidateMapKey(field, proto);
- }
-
// Only message type fields may be lazy.
if (field->options().lazy()) {
if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
@@ -4551,6 +4963,17 @@ void DescriptorBuilder::ValidateFieldOptions(FieldDescriptor* field,
"a lite type, but the reverse is allowed.");
}
+ // Validate map types.
+ if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
+ field->message_type()->options().map_entry()) {
+ if (!ValidateMapEntry(field, proto)) {
+ AddError(field->full_name(), proto,
+ DescriptorPool::ErrorCollector::OTHER,
+ "map_entry should not be set explicitly. Use map<KeyType, "
+ "ValueType> instead.");
+ }
+ }
+
}
void DescriptorBuilder::ValidateEnumOptions(EnumDescriptor* enm,
@@ -4608,57 +5031,127 @@ void DescriptorBuilder::ValidateMethodOptions(MethodDescriptor* /* method */,
// Nothing to do so far.
}
-void DescriptorBuilder::ValidateMapKey(FieldDescriptor* field,
- const FieldDescriptorProto& proto) {
- if (!field->is_repeated()) {
- AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
- "map type is only allowed for repeated fields.");
- return;
+bool DescriptorBuilder::ValidateMapEntry(FieldDescriptor* field,
+ const FieldDescriptorProto& proto) {
+ const Descriptor* message = field->message_type();
+ if (// Must not contain extensions, extension range or nested message or
+ // enums
+ message->extension_count() != 0 ||
+ field->label() != FieldDescriptor::LABEL_REPEATED ||
+ message->extension_range_count() != 0 ||
+ message->nested_type_count() != 0 || message->enum_type_count() != 0 ||
+ // Must contain exactly two fields
+ message->field_count() != 2 ||
+ // Field name and message name must match
+ message->name() != ToCamelCase(field->name(), false) + "Entry" ||
+ // Entry message must be in the same containing type of the field.
+ field->containing_type() != message->containing_type()) {
+ return false;
}
- if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
- AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
- "map type is only allowed for fields with a message type.");
- return;
+ const FieldDescriptor* key = message->field(0);
+ const FieldDescriptor* value = message->field(1);
+ if (key->label() != FieldDescriptor::LABEL_OPTIONAL || key->number() != 1 ||
+ key->name() != "key") {
+ return false;
+ }
+ if (value->label() != FieldDescriptor::LABEL_OPTIONAL ||
+ value->number() != 2 || value->name() != "value") {
+ return false;
}
- const Descriptor* item_type = field->message_type();
- if (item_type == NULL) {
- AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
- "Could not find field type.");
- return;
+ // Check key types are legal.
+ switch (key->type()) {
+ case FieldDescriptor::TYPE_ENUM:
+ AddError(
+ field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+ "Key in map fields cannot be enum types.");
+ break;
+ case FieldDescriptor::TYPE_FLOAT:
+ case FieldDescriptor::TYPE_DOUBLE:
+ case FieldDescriptor::TYPE_MESSAGE:
+ case FieldDescriptor::TYPE_GROUP:
+ case FieldDescriptor::TYPE_BYTES:
+ AddError(
+ field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+ "Key in map fields cannot be float/double, bytes or message types.");
+ break;
+ case FieldDescriptor::TYPE_BOOL:
+ case FieldDescriptor::TYPE_INT32:
+ case FieldDescriptor::TYPE_INT64:
+ case FieldDescriptor::TYPE_SINT32:
+ case FieldDescriptor::TYPE_SINT64:
+ case FieldDescriptor::TYPE_STRING:
+ case FieldDescriptor::TYPE_UINT32:
+ case FieldDescriptor::TYPE_UINT64:
+ case FieldDescriptor::TYPE_FIXED32:
+ case FieldDescriptor::TYPE_FIXED64:
+ case FieldDescriptor::TYPE_SFIXED32:
+ case FieldDescriptor::TYPE_SFIXED64:
+ // Legal cases
+ break;
+ // Do not add a default, so that the compiler will complian when new types
+ // are added.
}
- // Find the field in item_type named by "experimental_map_key"
- const string& key_name = field->options().experimental_map_key();
- const Symbol key_symbol = LookupSymbol(
- key_name,
- // We append ".key_name" to the containing type's name since
- // LookupSymbol() searches for peers of the supplied name, not
- // children of the supplied name.
- item_type->full_name() + "." + key_name);
+ return true;
+}
- if (key_symbol.IsNull() || key_symbol.field_descriptor->is_extension()) {
- AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
- "Could not find field named \"" + key_name + "\" in type \"" +
- item_type->full_name() + "\".");
- return;
+void DescriptorBuilder::DetectMapConflicts(const Descriptor* message,
+ const DescriptorProto& proto) {
+ map<string, const Descriptor*> seen_types;
+ for (int i = 0; i < message->nested_type_count(); ++i) {
+ const Descriptor* nested = message->nested_type(i);
+ pair<map<string, const Descriptor*>::iterator, bool> result =
+ seen_types.insert(make_pair(nested->name(), nested));
+ if (!result.second) {
+ if (result.first->second->options().map_entry() ||
+ nested->options().map_entry()) {
+ AddError(message->full_name(), proto,
+ DescriptorPool::ErrorCollector::NAME,
+ "Expanded map entry type " + nested->name() +
+ " conflicts with an existing nested message type.");
+ }
+ }
+ // Recursively test on the nested types.
+ DetectMapConflicts(message->nested_type(i), proto.nested_type(i));
+ }
+ // Check for conflicted field names.
+ for (int i = 0; i < message->field_count(); ++i) {
+ const FieldDescriptor* field = message->field(i);
+ map<string, const Descriptor*>::iterator iter =
+ seen_types.find(field->name());
+ if (iter != seen_types.end() && iter->second->options().map_entry()) {
+ AddError(message->full_name(), proto,
+ DescriptorPool::ErrorCollector::NAME,
+ "Expanded map entry type " + iter->second->name() +
+ " conflicts with an existing field.");
+ }
}
- const FieldDescriptor* key_field = key_symbol.field_descriptor;
-
- if (key_field->is_repeated()) {
- AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
- "map_key must not name a repeated field.");
- return;
+ // Check for conflicted enum names.
+ for (int i = 0; i < message->enum_type_count(); ++i) {
+ const EnumDescriptor* enum_desc = message->enum_type(i);
+ map<string, const Descriptor*>::iterator iter =
+ seen_types.find(enum_desc->name());
+ if (iter != seen_types.end() && iter->second->options().map_entry()) {
+ AddError(message->full_name(), proto,
+ DescriptorPool::ErrorCollector::NAME,
+ "Expanded map entry type " + iter->second->name() +
+ " conflicts with an existing enum type.");
+ }
}
-
- if (key_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
- "map key must name a scalar or string field.");
- return;
+ // Check for conflicted oneof names.
+ for (int i = 0; i < message->oneof_decl_count(); ++i) {
+ const OneofDescriptor* oneof_desc = message->oneof_decl(i);
+ map<string, const Descriptor*>::iterator iter =
+ seen_types.find(oneof_desc->name());
+ if (iter != seen_types.end() && iter->second->options().map_entry()) {
+ AddError(message->full_name(), proto,
+ DescriptorPool::ErrorCollector::NAME,
+ "Expanded map entry type " + iter->second->name() +
+ " conflicts with an existing oneof type.");
+ }
}
-
- field->experimental_map_key_ = key_field;
}