Index: third_party/protobuf/src/google/protobuf/descriptor.cc |
diff --git a/third_party/protobuf/src/google/protobuf/descriptor.cc b/third_party/protobuf/src/google/protobuf/descriptor.cc |
index fb72e419fbbc4b3c4b332020791bd225c876fd72..85991dccdd35dd13b21d5a326fa5f8ea50f45b3a 100644 |
--- a/third_party/protobuf/src/google/protobuf/descriptor.cc |
+++ b/third_party/protobuf/src/google/protobuf/descriptor.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,7 +34,12 @@ |
#include <google/protobuf/stubs/hash.h> |
#include <map> |
+#include <memory> |
+#ifndef _SHARED_PTR_H |
+#include <google/protobuf/stubs/shared_ptr.h> |
+#endif |
#include <set> |
+#include <string> |
#include <vector> |
#include <algorithm> |
#include <limits> |
@@ -43,17 +48,22 @@ |
#include <google/protobuf/descriptor_database.h> |
#include <google/protobuf/descriptor.pb.h> |
#include <google/protobuf/dynamic_message.h> |
+#include <google/protobuf/generated_message_util.h> |
#include <google/protobuf/text_format.h> |
#include <google/protobuf/unknown_field_set.h> |
#include <google/protobuf/wire_format.h> |
+#include <google/protobuf/io/strtod.h> |
#include <google/protobuf/io/coded_stream.h> |
#include <google/protobuf/io/tokenizer.h> |
#include <google/protobuf/io/zero_copy_stream_impl.h> |
#include <google/protobuf/stubs/common.h> |
+#include <google/protobuf/stubs/logging.h> |
+#include <google/protobuf/stubs/mutex.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> |
+#include <google/protobuf/stubs/map_util.h> |
#include <google/protobuf/stubs/stl_util.h> |
#undef PACKAGE // autoheader #defines this. :( |
@@ -131,7 +141,21 @@ const char * const FieldDescriptor::kLabelToName[MAX_LABEL + 1] = { |
"repeated", // LABEL_REPEATED |
}; |
-// MSVC prior to VC 2015 doesn't need these and won't even accept them. |
+const char* FileDescriptor::SyntaxName(FileDescriptor::Syntax syntax) { |
+ switch (syntax) { |
+ case SYNTAX_PROTO2: |
+ return "proto2"; |
+ case SYNTAX_PROTO3: |
+ return "proto3"; |
+ case SYNTAX_UNKNOWN: |
+ return "unknown"; |
+ } |
+ GOOGLE_LOG(FATAL) << "can't reach here."; |
+ return NULL; |
+} |
+ |
+static const char * const kNonLinkedWeakMessageReplacementName = "google.protobuf.Empty"; |
+ |
#if !defined(_MSC_VER) || _MSC_VER >= 1900 |
const int FieldDescriptor::kMaxNumber; |
const int FieldDescriptor::kFirstReservedNumber; |
@@ -140,8 +164,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()); |
@@ -162,8 +186,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; |
@@ -200,9 +224,11 @@ struct PointerIntegerPairHash { |
return reinterpret_cast<intptr_t>(p.first) * ((1 << 16) - 1) + p.second; |
} |
+#ifdef _MSC_VER |
// Used only by MSVC and platforms where hash_map is not available. |
static const size_t bucket_size = 4; |
static const size_t min_buckets = 8; |
+#endif |
inline bool operator()(const PairType& a, const PairType& b) const { |
return a.first < b.first || |
(a.first == b.first && a.second < b.second); |
@@ -221,9 +247,11 @@ struct PointerStringPairHash { |
cstring_hash(p.second); |
} |
+#ifdef _MSC_VER |
// Used only by MSVC and platforms where hash_map is not available. |
static const size_t bucket_size = 4; |
static const size_t min_buckets = 8; |
+#endif |
inline bool operator()(const PointerStringPair& a, |
const PointerStringPair& b) const { |
if (a.first < b.first) return true; |
@@ -235,13 +263,14 @@ struct PointerStringPairHash { |
struct Symbol { |
enum Type { |
- NULL_SYMBOL, MESSAGE, FIELD, ENUM, ENUM_VALUE, SERVICE, METHOD, |
+ NULL_SYMBOL, MESSAGE, FIELD, ONEOF, ENUM, ENUM_VALUE, SERVICE, METHOD, |
PACKAGE |
}; |
Type type; |
union { |
const Descriptor* descriptor; |
const FieldDescriptor* field_descriptor; |
+ const OneofDescriptor* oneof_descriptor; |
const EnumDescriptor* enum_descriptor; |
const EnumValueDescriptor* enum_value_descriptor; |
const ServiceDescriptor* service_descriptor; |
@@ -267,6 +296,7 @@ struct Symbol { |
CONSTRUCTOR(Descriptor , MESSAGE , descriptor ) |
CONSTRUCTOR(FieldDescriptor , FIELD , field_descriptor ) |
+ CONSTRUCTOR(OneofDescriptor , ONEOF , oneof_descriptor ) |
CONSTRUCTOR(EnumDescriptor , ENUM , enum_descriptor ) |
CONSTRUCTOR(EnumValueDescriptor, ENUM_VALUE, enum_value_descriptor ) |
CONSTRUCTOR(ServiceDescriptor , SERVICE , service_descriptor ) |
@@ -279,6 +309,7 @@ struct Symbol { |
case NULL_SYMBOL: return NULL; |
case MESSAGE : return descriptor ->file(); |
case FIELD : return field_descriptor ->file(); |
+ case ONEOF : return oneof_descriptor ->containing_type()->file(); |
case ENUM : return enum_descriptor ->file(); |
case ENUM_VALUE : return enum_value_descriptor->type()->file(); |
case SERVICE : return service_descriptor ->file(); |
@@ -315,6 +346,36 @@ typedef hash_map<EnumIntPair, const EnumValueDescriptor*, |
// for that. |
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 DeleteAllowedProto3Extendee() { |
+ delete allowed_proto3_extendees_; |
+} |
+ |
+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"); |
+ google::protobuf::internal::OnShutdown(&DeleteAllowedProto3Extendee); |
+} |
+ |
+// 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 |
@@ -369,10 +430,18 @@ class DescriptorPool::Tables { |
vector<string> pending_files_; |
// A set of files which we have tried to load from the fallback database |
- // and encountered errors. We will not attempt to load them again. |
+ // and encountered errors. We will not attempt to load them again during |
+ // execution of the current public API call, but for compatibility with |
+ // legacy clients, this is cleared at the beginning of each public API call. |
// Not used when fallback_database_ == NULL. |
hash_set<string> known_bad_files_; |
+ // A set of symbols which we have tried to load from the fallback database |
+ // and encountered errors. We will not attempt to load them again during |
+ // execution of the current public API call, but for compatibility with |
+ // legacy clients, this is cleared at the beginning of each public API call. |
+ hash_set<string> known_bad_symbols_; |
+ |
// The set of descriptors for which we've already loaded the full |
// set of extensions numbers from fallback_database_. |
hash_set<const Descriptor*> extensions_loaded_from_db_; |
@@ -389,7 +458,7 @@ class DescriptorPool::Tables { |
// declaring Symbol in descriptor.h, which would drag all kinds of other |
// stuff into the header. Yay C++. |
Symbol FindByNameHelper( |
- const DescriptorPool* pool, const string& name) const; |
+ const DescriptorPool* pool, const string& name); |
// These return NULL if not found. |
inline const FileDescriptor* FindFile(const string& key) const; |
@@ -491,7 +560,7 @@ class FileDescriptorTables { |
~FileDescriptorTables(); |
// Empty table, used with placeholder files. |
- static const FileDescriptorTables kEmpty; |
+ inline static const FileDescriptorTables& GetEmptyInstance(); |
// ----------------------------------------------------------------- |
// Finding items. |
@@ -513,6 +582,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. |
@@ -530,17 +602,40 @@ class FileDescriptorTables { |
// fails because we allow duplicates; the first field by the name wins. |
void AddFieldByStylizedNames(const FieldDescriptor* field); |
+ // Populates p->first->locations_by_path_ from p->second. |
+ // Unusual signature dictated by GoogleOnceDynamic. |
+ static void BuildLocationsByPath( |
+ pair<const FileDescriptorTables*, const SourceCodeInfo*>* p); |
+ |
+ // Returns the location denoted by the specified path through info, |
+ // or NULL if not found. |
+ // The value of info must be that of the corresponding FileDescriptor. |
+ // (Conceptually a pure function, but stateful as an optimisation.) |
+ const SourceCodeInfo_Location* GetSourceLocation( |
+ const vector<int>& path, const SourceCodeInfo* info) const; |
+ |
private: |
SymbolsByParentMap symbols_by_parent_; |
FieldsByNameMap fields_by_lowercase_name_; |
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() |
// Start some hash_map and hash_set objects with a small # of buckets |
: known_bad_files_(3), |
+ known_bad_symbols_(3), |
extensions_loaded_from_db_(3), |
symbols_by_name_(3), |
files_by_name_(3) {} |
@@ -564,12 +659,38 @@ 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() {} |
-const FileDescriptorTables FileDescriptorTables::kEmpty; |
+namespace { |
+ |
+FileDescriptorTables* file_descriptor_tables_ = NULL; |
+GOOGLE_PROTOBUF_DECLARE_ONCE(file_descriptor_tables_once_init_); |
+ |
+void DeleteFileDescriptorTables() { |
+ delete file_descriptor_tables_; |
+ file_descriptor_tables_ = NULL; |
+} |
+ |
+void InitFileDescriptorTables() { |
+ file_descriptor_tables_ = new FileDescriptorTables(); |
+ internal::OnShutdown(&DeleteFileDescriptorTables); |
+} |
+ |
+inline void InitFileDescriptorTablesOnce() { |
+ ::google::protobuf::GoogleOnceInit( |
+ &file_descriptor_tables_once_init_, &InitFileDescriptorTables); |
+} |
+ |
+} // anonymous namespace |
+ |
+inline const FileDescriptorTables& FileDescriptorTables::GetEmptyInstance() { |
+ InitFileDescriptorTablesOnce(); |
+ return *file_descriptor_tables_; |
+} |
void DescriptorPool::Tables::AddCheckpoint() { |
checkpoints_.push_back(CheckPoint(this)); |
@@ -664,8 +785,10 @@ inline Symbol FileDescriptorTables::FindNestedSymbolOfType( |
} |
Symbol DescriptorPool::Tables::FindByNameHelper( |
- const DescriptorPool* pool, const string& name) const { |
+ const DescriptorPool* pool, const string& name) { |
MutexLockMaybe lock(pool->mutex_); |
+ known_bad_symbols_.clear(); |
+ known_bad_files_.clear(); |
Symbol result = FindSymbol(name); |
if (result.IsNull() && pool->underlay_ != NULL) { |
@@ -691,7 +814,7 @@ inline const FileDescriptor* DescriptorPool::Tables::FindFile( |
inline const FieldDescriptor* FileDescriptorTables::FindFieldByNumber( |
const Descriptor* parent, int number) const { |
- return FindPtrOrNull(fields_by_number_, make_pair(parent, number)); |
+ return FindPtrOrNull(fields_by_number_, std::make_pair(parent, number)); |
} |
inline const FieldDescriptor* FileDescriptorTables::FindFieldByLowercaseName( |
@@ -708,18 +831,71 @@ inline const FieldDescriptor* FileDescriptorTables::FindFieldByCamelcaseName( |
inline const EnumValueDescriptor* FileDescriptorTables::FindEnumValueByNumber( |
const EnumDescriptor* parent, int number) const { |
- return FindPtrOrNull(enum_values_by_number_, make_pair(parent, number)); |
+ return FindPtrOrNull(enum_values_by_number_, std::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_, std::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_, std::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_, std::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_, |
+ std::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)); |
+ return FindPtrOrNull(extensions_, std::make_pair(extendee, number)); |
} |
inline void DescriptorPool::Tables::FindAllExtensions( |
const Descriptor* extendee, vector<const FieldDescriptor*>* out) const { |
ExtensionsGroupedByDescriptorMap::const_iterator it = |
- extensions_.lower_bound(make_pair(extendee, 0)); |
+ extensions_.lower_bound(std::make_pair(extendee, 0)); |
for (; it != extensions_.end() && it->first.first == extendee; ++it) { |
out->push_back(it->second); |
} |
@@ -812,7 +988,7 @@ string* DescriptorPool::Tables::AllocateString(const string& value) { |
} |
template<typename Type> |
-Type* DescriptorPool::Tables::AllocateMessage(Type* dummy) { |
+Type* DescriptorPool::Tables::AllocateMessage(Type* /* dummy */) { |
Type* result = new Type; |
messages_.push_back(result); |
return result; |
@@ -836,6 +1012,22 @@ void* DescriptorPool::Tables::AllocateBytes(int size) { |
return result; |
} |
+void FileDescriptorTables::BuildLocationsByPath( |
+ pair<const FileDescriptorTables*, const SourceCodeInfo*>* p) { |
+ for (int i = 0, len = p->second->location_size(); i < len; ++i) { |
+ const SourceCodeInfo_Location* loc = &p->second->location().Get(i); |
+ p->first->locations_by_path_[Join(loc->path(), ",")] = loc; |
+ } |
+} |
+ |
+const SourceCodeInfo_Location* FileDescriptorTables::GetSourceLocation( |
+ const vector<int>& path, const SourceCodeInfo* info) const { |
+ pair<const FileDescriptorTables*, const SourceCodeInfo*> p( |
+ std::make_pair(this, info)); |
+ locations_by_path_once_.Init(&FileDescriptorTables::BuildLocationsByPath, &p); |
+ return FindPtrOrNull(locations_by_path_, Join(path, ",")); |
+} |
+ |
// =================================================================== |
// DescriptorPool |
@@ -848,7 +1040,8 @@ DescriptorPool::DescriptorPool() |
underlay_(NULL), |
tables_(new Tables), |
enforce_dependencies_(true), |
- allow_unknown_(false) {} |
+ allow_unknown_(false), |
+ enforce_weak_(false) {} |
DescriptorPool::DescriptorPool(DescriptorDatabase* fallback_database, |
ErrorCollector* error_collector) |
@@ -858,7 +1051,8 @@ DescriptorPool::DescriptorPool(DescriptorDatabase* fallback_database, |
underlay_(NULL), |
tables_(new Tables), |
enforce_dependencies_(true), |
- allow_unknown_(false) { |
+ allow_unknown_(false), |
+ enforce_weak_(false) { |
} |
DescriptorPool::DescriptorPool(const DescriptorPool* underlay) |
@@ -868,7 +1062,8 @@ DescriptorPool::DescriptorPool(const DescriptorPool* underlay) |
underlay_(underlay), |
tables_(new Tables), |
enforce_dependencies_(true), |
- allow_unknown_(false) {} |
+ allow_unknown_(false), |
+ enforce_weak_(false) {} |
DescriptorPool::~DescriptorPool() { |
if (mutex_ != NULL) delete mutex_; |
@@ -881,6 +1076,14 @@ void DescriptorPool::InternalDontEnforceDependencies() { |
enforce_dependencies_ = false; |
} |
+void DescriptorPool::AddUnusedImportTrackFile(const string& file_name) { |
+ unused_import_track_files_.insert(file_name); |
+} |
+ |
+void DescriptorPool::ClearUnusedImportTrackFiles() { |
+ unused_import_track_files_.clear(); |
+} |
+ |
bool DescriptorPool::InternalIsFileLoaded(const string& filename) const { |
MutexLockMaybe lock(mutex_); |
return tables_->FindFile(filename) != NULL; |
@@ -920,6 +1123,7 @@ const DescriptorPool* DescriptorPool::generated_pool() { |
return generated_pool_; |
} |
+ |
DescriptorPool* DescriptorPool::internal_generated_pool() { |
InitGeneratedPoolOnce(); |
return generated_pool_; |
@@ -962,6 +1166,8 @@ void DescriptorPool::InternalAddGeneratedFile( |
const FileDescriptor* DescriptorPool::FindFileByName(const string& name) const { |
MutexLockMaybe lock(mutex_); |
+ tables_->known_bad_symbols_.clear(); |
+ tables_->known_bad_files_.clear(); |
const FileDescriptor* result = tables_->FindFile(name); |
if (result != NULL) return result; |
if (underlay_ != NULL) { |
@@ -978,6 +1184,8 @@ const FileDescriptor* DescriptorPool::FindFileByName(const string& name) const { |
const FileDescriptor* DescriptorPool::FindFileContainingSymbol( |
const string& symbol_name) const { |
MutexLockMaybe lock(mutex_); |
+ tables_->known_bad_symbols_.clear(); |
+ tables_->known_bad_files_.clear(); |
Symbol result = tables_->FindSymbol(symbol_name); |
if (!result.IsNull()) return result.GetFile(); |
if (underlay_ != NULL) { |
@@ -1020,6 +1228,12 @@ const FieldDescriptor* DescriptorPool::FindExtensionByName( |
} |
} |
+const OneofDescriptor* DescriptorPool::FindOneofByName( |
+ const string& name) const { |
+ Symbol result = tables_->FindByNameHelper(this, name); |
+ return (result.type == Symbol::ONEOF) ? result.oneof_descriptor : NULL; |
+} |
+ |
const EnumDescriptor* DescriptorPool::FindEnumTypeByName( |
const string& name) const { |
Symbol result = tables_->FindByNameHelper(this, name); |
@@ -1048,6 +1262,8 @@ const MethodDescriptor* DescriptorPool::FindMethodByName( |
const FieldDescriptor* DescriptorPool::FindExtensionByNumber( |
const Descriptor* extendee, int number) const { |
MutexLockMaybe lock(mutex_); |
+ tables_->known_bad_symbols_.clear(); |
+ tables_->known_bad_files_.clear(); |
const FieldDescriptor* result = tables_->FindExtension(extendee, number); |
if (result != NULL) { |
return result; |
@@ -1068,6 +1284,8 @@ const FieldDescriptor* DescriptorPool::FindExtensionByNumber( |
void DescriptorPool::FindAllExtensions( |
const Descriptor* extendee, vector<const FieldDescriptor*>* out) const { |
MutexLockMaybe lock(mutex_); |
+ tables_->known_bad_symbols_.clear(); |
+ tables_->known_bad_files_.clear(); |
// Initialize tables_->extensions_ from the fallback database first |
// (but do this only once per descriptor). |
@@ -1092,6 +1310,7 @@ void DescriptorPool::FindAllExtensions( |
} |
} |
+ |
// ------------------------------------------------------------------- |
const FieldDescriptor* |
@@ -1138,6 +1357,17 @@ Descriptor::FindFieldByName(const string& key) const { |
} |
} |
+const OneofDescriptor* |
+Descriptor::FindOneofByName(const string& key) const { |
+ Symbol result = |
+ file()->tables_->FindNestedSymbolOfType(this, key, Symbol::ONEOF); |
+ if (!result.IsNull()) { |
+ return result.oneof_descriptor; |
+ } else { |
+ return NULL; |
+ } |
+} |
+ |
const FieldDescriptor* |
Descriptor::FindExtensionByName(const string& key) const { |
Symbol result = |
@@ -1220,6 +1450,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 = |
@@ -1302,16 +1537,29 @@ FileDescriptor::FindExtensionByCamelcaseName(const string& key) const { |
} |
} |
-bool Descriptor::IsExtensionNumber(int number) const { |
+const Descriptor::ExtensionRange* |
+Descriptor::FindExtensionRangeContainingNumber(int number) const { |
// Linear search should be fine because we don't expect a message to have |
// more than a couple extension ranges. |
for (int i = 0; i < extension_range_count(); i++) { |
if (number >= extension_range(i)->start && |
number < extension_range(i)->end) { |
- return true; |
+ return extension_range(i); |
} |
} |
- return false; |
+ return NULL; |
+} |
+ |
+const Descriptor::ReservedRange* |
+Descriptor::FindReservedRangeContainingNumber(int number) const { |
+ // TODO(chrisn): Consider a non-linear search. |
+ for (int i = 0; i < reserved_range_count(); i++) { |
+ if (number >= reserved_range(i)->start && |
+ number < reserved_range(i)->end) { |
+ return reserved_range(i); |
+ } |
+ } |
+ return NULL; |
} |
// ------------------------------------------------------------------- |
@@ -1327,7 +1575,6 @@ bool DescriptorPool::TryFindFileInFallbackDatabase(const string& name) const { |
tables_->known_bad_files_.insert(name); |
return false; |
} |
- |
return true; |
} |
@@ -1356,36 +1603,38 @@ bool DescriptorPool::IsSubSymbolOfBuiltType(const string& name) const { |
bool DescriptorPool::TryFindSymbolInFallbackDatabase(const string& name) const { |
if (fallback_database_ == NULL) return false; |
- // We skip looking in the fallback database if the name is a sub-symbol of |
- // any descriptor that already exists in the descriptor pool (except for |
- // package descriptors). This is valid because all symbols except for |
- // packages are defined in a single file, so if the symbol exists then we |
- // should already have its definition. |
- // |
- // The other reason to do this is to support "overriding" type definitions |
- // by merging two databases that define the same type. (Yes, people do |
- // this.) The main difficulty with making this work is that |
- // FindFileContainingSymbol() is allowed to return both false positives |
- // (e.g., SimpleDescriptorDatabase, UpgradedDescriptorDatabase) and false |
- // negatives (e.g. ProtoFileParser, SourceTreeDescriptorDatabase). When two |
- // such databases are merged, looking up a non-existent sub-symbol of a type |
- // that already exists in the descriptor pool can result in an attempt to |
- // load multiple definitions of the same type. The check below avoids this. |
- if (IsSubSymbolOfBuiltType(name)) return false; |
+ if (tables_->known_bad_symbols_.count(name) > 0) return false; |
FileDescriptorProto file_proto; |
- if (!fallback_database_->FindFileContainingSymbol(name, &file_proto)) { |
- return false; |
- } |
- |
- if (tables_->FindFile(file_proto.name()) != NULL) { |
- // We've already loaded this file, and it apparently doesn't contain the |
- // symbol we're looking for. Some DescriptorDatabases return false |
- // positives. |
- return false; |
- } |
- |
- if (BuildFileFromDatabase(file_proto) == NULL) { |
+ if (// We skip looking in the fallback database if the name is a sub-symbol |
+ // of any descriptor that already exists in the descriptor pool (except |
+ // for package descriptors). This is valid because all symbols except |
+ // for packages are defined in a single file, so if the symbol exists |
+ // then we should already have its definition. |
+ // |
+ // The other reason to do this is to support "overriding" type |
+ // definitions by merging two databases that define the same type. (Yes, |
+ // people do this.) The main difficulty with making this work is that |
+ // FindFileContainingSymbol() is allowed to return both false positives |
+ // (e.g., SimpleDescriptorDatabase, UpgradedDescriptorDatabase) and false |
+ // negatives (e.g. ProtoFileParser, SourceTreeDescriptorDatabase). |
+ // When two such databases are merged, looking up a non-existent |
+ // sub-symbol of a type that already exists in the descriptor pool can |
+ // result in an attempt to load multiple definitions of the same type. |
+ // The check below avoids this. |
+ IsSubSymbolOfBuiltType(name) |
+ |
+ // Look up file containing this symbol in fallback database. |
+ || !fallback_database_->FindFileContainingSymbol(name, &file_proto) |
+ |
+ // Check if we've already built this file. If so, it apparently doesn't |
+ // contain the symbol we're looking for. Some DescriptorDatabases |
+ // return false positives. |
+ || tables_->FindFile(file_proto.name()) != NULL |
+ |
+ // Build the file. |
+ || BuildFileFromDatabase(file_proto) == NULL) { |
+ tables_->known_bad_symbols_.insert(name); |
return false; |
} |
@@ -1418,6 +1667,10 @@ bool DescriptorPool::TryFindExtensionInFallbackDatabase( |
// =================================================================== |
+bool FieldDescriptor::is_map() const { |
+ return type() == TYPE_MESSAGE && message_type()->options().map_entry(); |
+} |
+ |
string FieldDescriptor::DefaultValueAsString(bool quote_string_type) const { |
GOOGLE_CHECK(has_default_value()) << "No default value"; |
switch (cpp_type()) { |
@@ -1469,6 +1722,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()); |
@@ -1500,8 +1755,23 @@ void FileDescriptor::CopyTo(FileDescriptorProto* proto) const { |
} |
} |
+void FileDescriptor::CopyJsonNameTo(FileDescriptorProto* proto) const { |
+ if (message_type_count() != proto->message_type_size() || |
+ extension_count() != proto->extension_size()) { |
+ GOOGLE_LOG(ERROR) << "Cannot copy json_name to a proto of a different size."; |
+ return; |
+ } |
+ for (int i = 0; i < message_type_count(); i++) { |
+ message_type(i)->CopyJsonNameTo(proto->mutable_message_type(i)); |
+ } |
+ for (int i = 0; i < extension_count(); i++) { |
+ extension(i)->CopyJsonNameTo(proto->mutable_extension(i)); |
+ } |
+} |
+ |
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_); |
} |
} |
@@ -1512,6 +1782,9 @@ void Descriptor::CopyTo(DescriptorProto* proto) const { |
for (int i = 0; i < field_count(); i++) { |
field(i)->CopyTo(proto->add_field()); |
} |
+ for (int i = 0; i < oneof_decl_count(); i++) { |
+ oneof_decl(i)->CopyTo(proto->add_oneof_decl()); |
+ } |
for (int i = 0; i < nested_type_count(); i++) { |
nested_type(i)->CopyTo(proto->add_nested_type()); |
} |
@@ -1526,15 +1799,44 @@ void Descriptor::CopyTo(DescriptorProto* proto) const { |
for (int i = 0; i < extension_count(); i++) { |
extension(i)->CopyTo(proto->add_extension()); |
} |
+ for (int i = 0; i < reserved_range_count(); i++) { |
+ DescriptorProto::ReservedRange* range = proto->add_reserved_range(); |
+ range->set_start(reserved_range(i)->start); |
+ range->set_end(reserved_range(i)->end); |
+ } |
+ for (int i = 0; i < reserved_name_count(); i++) { |
+ proto->add_reserved_name(reserved_name(i)); |
+ } |
if (&options() != &MessageOptions::default_instance()) { |
proto->mutable_options()->CopyFrom(options()); |
} |
} |
+void Descriptor::CopyJsonNameTo(DescriptorProto* proto) const { |
+ if (field_count() != proto->field_size() || |
+ nested_type_count() != proto->nested_type_size() || |
+ extension_count() != proto->extension_size()) { |
+ GOOGLE_LOG(ERROR) << "Cannot copy json_name to a proto of a different size."; |
+ return; |
+ } |
+ for (int i = 0; i < field_count(); i++) { |
+ field(i)->CopyJsonNameTo(proto->mutable_field(i)); |
+ } |
+ for (int i = 0; i < nested_type_count(); i++) { |
+ nested_type(i)->CopyJsonNameTo(proto->mutable_nested_type(i)); |
+ } |
+ for (int i = 0; i < extension_count(); i++) { |
+ extension(i)->CopyJsonNameTo(proto->mutable_extension(i)); |
+ } |
+} |
+ |
void FieldDescriptor::CopyTo(FieldDescriptorProto* proto) const { |
proto->set_name(name()); |
proto->set_number(number()); |
+ if (has_json_name_) { |
+ proto->set_json_name(json_name()); |
+ } |
// Some compilers do not allow static_cast directly between two enum types, |
// so we must cast to int first. |
@@ -1572,11 +1874,23 @@ void FieldDescriptor::CopyTo(FieldDescriptorProto* proto) const { |
proto->set_default_value(DefaultValueAsString(false)); |
} |
+ if (containing_oneof() != NULL && !is_extension()) { |
+ proto->set_oneof_index(containing_oneof()->index()); |
+ } |
+ |
if (&options() != &FieldOptions::default_instance()) { |
proto->mutable_options()->CopyFrom(options()); |
} |
} |
+void FieldDescriptor::CopyJsonNameTo(FieldDescriptorProto* proto) const { |
+ proto->set_json_name(json_name()); |
+} |
+ |
+void OneofDescriptor::CopyTo(OneofDescriptorProto* proto) const { |
+ proto->set_name(name()); |
+} |
+ |
void EnumDescriptor::CopyTo(EnumDescriptorProto* proto) const { |
proto->set_name(name()); |
@@ -1626,6 +1940,13 @@ void MethodDescriptor::CopyTo(MethodDescriptorProto* proto) const { |
if (&options() != &MethodOptions::default_instance()) { |
proto->mutable_options()->CopyFrom(options()); |
} |
+ |
+ if (client_streaming_) { |
+ proto->set_client_streaming(true); |
+ } |
+ if (server_streaming_) { |
+ proto->set_server_streaming(true); |
+ } |
} |
// DebugString methods =============================================== |
@@ -1680,7 +2001,7 @@ bool RetrieveOptions(int depth, |
bool FormatBracketedOptions(int depth, const Message &options, string *output) { |
vector<string> all_options; |
if (RetrieveOptions(depth, options, &all_options)) { |
- output->append(JoinStrings(all_options, ", ")); |
+ output->append(Join(all_options, ", ")); |
} |
return !all_options.empty(); |
} |
@@ -1698,10 +2019,93 @@ bool FormatLineOptions(int depth, const Message &options, string *output) { |
return !all_options.empty(); |
} |
+class SourceLocationCommentPrinter { |
+ public: |
+ template<typename DescType> |
+ 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_); |
+ } |
+ SourceLocationCommentPrinter(const FileDescriptor* file, |
+ const vector<int>& path, |
+ 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 && |
+ file->GetSourceLocation(path, &source_loc_); |
+ } |
+ void AddPreComment(string* output) { |
+ if (have_source_loc_) { |
+ // Detached leading comments. |
+ for (int i = 0 ; i < source_loc_.leading_detached_comments.size(); ++i) { |
+ *output += FormatComment(source_loc_.leading_detached_comments[i]); |
+ *output += "\n"; |
+ } |
+ // Attached leading comments. |
+ if (!source_loc_.leading_comments.empty()) { |
+ *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: |
+ |
+ 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; |
+ { |
+ vector<int> path; |
+ path.push_back(FileDescriptorProto::kSyntaxFieldNumber); |
+ SourceLocationCommentPrinter syntax_comment( |
+ this, path, "", debug_string_options); |
+ syntax_comment.AddPreComment(&contents); |
+ strings::SubstituteAndAppend(&contents, "syntax = \"$0\";\n\n", |
+ SyntaxName(syntax())); |
+ syntax_comment.AddPostComment(&contents); |
+ } |
+ |
+ SourceLocationCommentPrinter |
+ comment_printer(this, "", debug_string_options); |
+ comment_printer.AddPreComment(&contents); |
set<int> public_dependencies; |
set<int> weak_dependencies; |
@@ -1724,7 +2128,13 @@ string FileDescriptor::DebugString() const { |
} |
if (!package().empty()) { |
+ vector<int> path; |
+ path.push_back(FileDescriptorProto::kPackageFieldNumber); |
+ SourceLocationCommentPrinter package_comment( |
+ this, path, "", debug_string_options); |
+ package_comment.AddPreComment(&contents); |
strings::SubstituteAndAppend(&contents, "package $0;\n\n", package()); |
+ package_comment.AddPostComment(&contents); |
} |
if (FormatLineOptions(0, options(), &contents)) { |
@@ -1732,7 +2142,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"); |
} |
@@ -1747,15 +2157,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"); |
} |
@@ -1767,23 +2176,46 @@ string FileDescriptor::DebugString() const { |
strings::SubstituteAndAppend(&contents, "extend .$0 {\n", |
containing_type->full_name()); |
} |
- extension(i)->DebugString(1, &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 |
+ 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); |
@@ -1805,16 +2237,22 @@ 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++) { |
- field(i)->DebugString(depth, contents); |
+ if (field(i)->containing_oneof() == NULL) { |
+ 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, |
+ debug_string_options); |
+ } |
} |
for (int i = 0; i < extension_range_count(); i++) { |
@@ -1833,15 +2271,47 @@ void Descriptor::DebugString(int depth, string *contents) const { |
strings::SubstituteAndAppend(contents, "$0 extend .$1 {\n", |
prefix, containing_type->full_name()); |
} |
- extension(i)->DebugString(depth + 1, contents); |
+ extension(i)->DebugString( |
+ depth + 1, FieldDescriptor::PRINT_LABEL, contents, |
+ debug_string_options); |
} |
if (extension_count() > 0) |
strings::SubstituteAndAppend(contents, "$0 }\n", prefix); |
+ if (reserved_range_count() > 0) { |
+ strings::SubstituteAndAppend(contents, "$0 reserved ", prefix); |
+ for (int i = 0; i < reserved_range_count(); i++) { |
+ const Descriptor::ReservedRange* range = reserved_range(i); |
+ if (range->end == range->start + 1) { |
+ strings::SubstituteAndAppend(contents, "$0, ", range->start); |
+ } else { |
+ strings::SubstituteAndAppend(contents, "$0 to $1, ", |
+ range->start, range->end - 1); |
+ } |
+ } |
+ contents->replace(contents->size() - 2, 2, ";\n"); |
+ } |
+ |
+ if (reserved_name_count() > 0) { |
+ strings::SubstituteAndAppend(contents, "$0 reserved ", prefix); |
+ for (int i = 0; i < reserved_name_count(); i++) { |
+ strings::SubstituteAndAppend(contents, "\"$0\", ", |
+ CEscape(reserved_name(i))); |
+ } |
+ contents->replace(contents->size() - 2, 2, ";\n"); |
+ } |
+ |
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()) { |
@@ -1849,30 +2319,56 @@ string FieldDescriptor::DebugString() const { |
containing_type()->full_name()); |
depth = 1; |
} |
- DebugString(depth, &contents); |
+ DebugString(depth, PRINT_LABEL, &contents, debug_string_options); |
if (is_extension()) { |
- contents.append("}\n"); |
+ contents.append("}\n"); |
} |
return contents; |
} |
-void FieldDescriptor::DebugString(int depth, string *contents) const { |
- string prefix(depth * 2, ' '); |
- string field_type; |
- switch (type()) { |
+// The field type string used in FieldDescriptor::DebugString() |
+string FieldDescriptor::FieldTypeNameDebugString() const { |
+ switch(type()) { |
case TYPE_MESSAGE: |
- field_type = "." + message_type()->full_name(); |
- break; |
+ return "." + message_type()->full_name(); |
case TYPE_ENUM: |
- field_type = "." + enum_type()->full_name(); |
- break; |
+ return "." + enum_type()->full_name(); |
default: |
- field_type = kTypeToName[type()]; |
+ return kTypeToName[type()]; |
+ } |
+} |
+ |
+void FieldDescriptor::DebugString(int depth, |
+ PrintLabelFlag print_label_flag, |
+ string *contents, |
+ const DebugStringOptions& |
+ debug_string_options) const { |
+ string prefix(depth * 2, ' '); |
+ string field_type; |
+ |
+ // Special case map fields. |
+ if (is_map()) { |
+ 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 && !is_map()) { |
+ label = kLabelToName[this->label()]; |
+ label.push_back(' '); |
} |
- strings::SubstituteAndAppend(contents, "$0$1 $2 $3 = $4", |
+ SourceLocationCommentPrinter |
+ comment_printer(this, prefix, debug_string_options); |
+ comment_printer.AddPreComment(contents); |
+ |
+ strings::SubstituteAndAppend(contents, "$0$1$2 $3 = $4", |
prefix, |
- kLabelToName[label()], |
+ label, |
field_type, |
type() == TYPE_GROUP ? message_type()->name() : |
name(), |
@@ -1897,40 +2393,109 @@ void FieldDescriptor::DebugString(int depth, string *contents) const { |
} |
if (type() == TYPE_GROUP) { |
- message_type()->DebugString(depth, contents); |
+ if (debug_string_options.elide_group_body) { |
+ contents->append(" { ... };\n"); |
+ } else { |
+ 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, options); |
+ return contents; |
+} |
+ |
+void OneofDescriptor::DebugString(int depth, string* contents, |
+ const DebugStringOptions& |
+ debug_string_options) const { |
+ string prefix(depth * 2, ' '); |
+ ++depth; |
+ SourceLocationCommentPrinter |
+ comment_printer(this, prefix, debug_string_options); |
+ comment_printer.AddPreComment(contents); |
+ strings::SubstituteAndAppend( |
+ contents, "$0 oneof $1 {", prefix, name()); |
+ if (debug_string_options.elide_oneof_body) { |
+ contents->append(" ... }\n"); |
+ } else { |
+ for (int i = 0; i < field_count(); i++) { |
+ 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 |
+ 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 |
+ comment_printer(this, prefix, debug_string_options); |
+ comment_printer.AddPreComment(contents); |
+ |
strings::SubstituteAndAppend(contents, "$0$1 = $2", |
prefix, name(), number()); |
@@ -1939,39 +2504,70 @@ 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 |
+ 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; |
- strings::SubstituteAndAppend(contents, "$0rpc $1(.$2) returns (.$3)", |
+ |
+ SourceLocationCommentPrinter |
+ comment_printer(this, prefix, debug_string_options); |
+ comment_printer.AddPreComment(contents); |
+ |
+ strings::SubstituteAndAppend(contents, "$0rpc $1($4.$2) returns ($5.$3)", |
prefix, name(), |
input_type()->full_name(), |
- output_type()->full_name()); |
+ output_type()->full_name(), |
+ client_streaming() ? "stream " : "", |
+ server_streaming() ? "stream " : ""); |
string formatted_options; |
if (FormatLineOptions(depth, options(), &formatted_options)) { |
@@ -1980,34 +2576,31 @@ void MethodDescriptor::DebugString(int depth, string *contents) const { |
} else { |
contents->append(";\n"); |
} |
+ |
+ comment_printer.AddPostComment(contents); |
} |
// Location methods =============================================== |
-static bool PathsEqual(const vector<int>& x, const RepeatedField<int32>& y) { |
- if (x.size() != y.size()) return false; |
- for (int i = 0; i < x.size(); ++i) { |
- if (x[i] != y.Get(i)) return false; |
- } |
- return true; |
-} |
- |
bool FileDescriptor::GetSourceLocation(const vector<int>& path, |
SourceLocation* out_location) const { |
GOOGLE_CHECK_NOTNULL(out_location); |
- const SourceCodeInfo* info = source_code_info_; |
- for (int i = 0; info && i < info->location_size(); ++i) { |
- if (PathsEqual(path, info->location(i).path())) { |
- const RepeatedField<int32>& span = info->location(i).span(); |
+ if (source_code_info_) { |
+ if (const SourceCodeInfo_Location* loc = |
+ tables_->GetSourceLocation(path, source_code_info_)) { |
+ const RepeatedField<int32>& span = loc->span(); |
if (span.size() == 3 || span.size() == 4) { |
out_location->start_line = span.Get(0); |
out_location->start_column = span.Get(1); |
out_location->end_line = span.Get(span.size() == 3 ? 0 : 2); |
out_location->end_column = span.Get(span.size() - 1); |
- out_location->leading_comments = info->location(i).leading_comments(); |
- out_location->trailing_comments = info->location(i).trailing_comments(); |
+ out_location->leading_comments = loc->leading_comments(); |
+ out_location->trailing_comments = loc->trailing_comments(); |
+ out_location->leading_detached_comments.assign( |
+ loc->leading_detached_comments().begin(), |
+ loc->leading_detached_comments().end()); |
return true; |
} |
} |
@@ -2015,8 +2608,18 @@ 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(); |
+ if (!is_packable()) return false; |
+ if (file_->syntax() == FileDescriptor::SYNTAX_PROTO2) { |
+ return (options_ != NULL) && options_->packed(); |
+ } else { |
+ return options_ == NULL || !options_->has_packed() || options_->packed(); |
+ } |
} |
bool Descriptor::GetSourceLocation(SourceLocation* out_location) const { |
@@ -2031,6 +2634,12 @@ bool FieldDescriptor::GetSourceLocation(SourceLocation* out_location) const { |
return file()->GetSourceLocation(path, out_location); |
} |
+bool OneofDescriptor::GetSourceLocation(SourceLocation* out_location) const { |
+ vector<int> path; |
+ GetLocationPath(&path); |
+ return containing_type()->file()->GetSourceLocation(path, out_location); |
+} |
+ |
bool EnumDescriptor::GetSourceLocation(SourceLocation* out_location) const { |
vector<int> path; |
GetLocationPath(&path); |
@@ -2068,8 +2677,25 @@ void Descriptor::GetLocationPath(vector<int>* output) const { |
} |
void FieldDescriptor::GetLocationPath(vector<int>* output) const { |
+ if (is_extension()) { |
+ if (extension_scope() == NULL) { |
+ output->push_back(FileDescriptorProto::kExtensionFieldNumber); |
+ output->push_back(index()); |
+ } else { |
+ extension_scope()->GetLocationPath(output); |
+ output->push_back(DescriptorProto::kExtensionFieldNumber); |
+ output->push_back(index()); |
+ } |
+ } else { |
+ containing_type()->GetLocationPath(output); |
+ output->push_back(DescriptorProto::kFieldFieldNumber); |
+ output->push_back(index()); |
+ } |
+} |
+ |
+void OneofDescriptor::GetLocationPath(vector<int>* output) const { |
containing_type()->GetLocationPath(output); |
- output->push_back(DescriptorProto::kFieldFieldNumber); |
+ output->push_back(DescriptorProto::kOneofDeclFieldNumber); |
output->push_back(index()); |
} |
@@ -2106,7 +2732,7 @@ void MethodDescriptor::GetLocationPath(vector<int>* output) const { |
namespace { |
// Represents an options message to interpret. Extension names in the option |
-// name are respolved relative to name_scope. element_name and orig_opt are |
+// name are resolved relative to name_scope. element_name and orig_opt are |
// used only for error reporting (since the parser records locations against |
// pointers in the original options, not the mutable copy). The Message must be |
// one of the Options messages in descriptor.proto. |
@@ -2155,6 +2781,10 @@ class DescriptorBuilder { |
FileDescriptorTables* file_tables_; |
set<const FileDescriptor*> dependencies_; |
+ // unused_dependency_ is used to record the unused imported files. |
+ // Note: public import is not considered. |
+ set<const FileDescriptor*> unused_dependency_; |
+ |
// If LookupSymbol() finds a symbol that is in a file which is not a declared |
// dependency of this file, it will fail, but will set |
// possible_undeclared_dependency_ to point at that file. This is only used |
@@ -2165,10 +2795,22 @@ class DescriptorBuilder { |
const FileDescriptor* possible_undeclared_dependency_; |
string possible_undeclared_dependency_name_; |
+ // If LookupSymbol() could resolve a symbol which is not defined, |
+ // record the resolved name. This is only used by AddNotDefinedError() |
+ // to report a more useful error message. |
+ string undefine_resolved_name_; |
+ |
void AddError(const string& element_name, |
const Message& descriptor, |
DescriptorPool::ErrorCollector::ErrorLocation location, |
const string& error); |
+ void AddError(const string& element_name, |
+ const Message& descriptor, |
+ DescriptorPool::ErrorCollector::ErrorLocation location, |
+ const char* error); |
+ void AddRecursiveImportError(const FileDescriptorProto& proto, int from_here); |
+ void AddTwiceListedError(const FileDescriptorProto& proto, int index); |
+ void AddImportError(const FileDescriptorProto& proto, int index); |
// Adds an error indicating that undefined_symbol was not defined. Must |
// only be called after LookupSymbol() fails. |
@@ -2178,6 +2820,10 @@ class DescriptorBuilder { |
DescriptorPool::ErrorCollector::ErrorLocation location, |
const string& undefined_symbol); |
+ void AddWarning(const string& element_name, const Message& descriptor, |
+ DescriptorPool::ErrorCollector::ErrorLocation location, |
+ const string& error); |
+ |
// Silly helper which determines if the given file is in the given package. |
// I.e., either file->package() == package_name or file->package() is a |
// nested package within package_name. |
@@ -2239,7 +2885,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 |
@@ -2312,6 +2958,12 @@ class DescriptorBuilder { |
void BuildExtensionRange(const DescriptorProto::ExtensionRange& proto, |
const Descriptor* parent, |
Descriptor::ExtensionRange* result); |
+ void BuildReservedRange(const DescriptorProto::ReservedRange& proto, |
+ const Descriptor* parent, |
+ Descriptor::ReservedRange* result); |
+ void BuildOneof(const OneofDescriptorProto& proto, |
+ Descriptor* parent, |
+ OneofDescriptor* result); |
void BuildEnum(const EnumDescriptorProto& proto, |
const Descriptor* parent, |
EnumDescriptor* result); |
@@ -2325,6 +2977,9 @@ class DescriptorBuilder { |
const ServiceDescriptor* parent, |
MethodDescriptor* result); |
+ void LogUnusedDependency(const FileDescriptorProto& proto, |
+ const FileDescriptor* result); |
+ |
// Must be run only after building. |
// |
// NOTE: Options will not be available during cross-linking, as they |
@@ -2460,6 +3115,9 @@ class DescriptorBuilder { |
static inline bool get_allow_unknown(const DescriptorPool* pool) { |
return pool->allow_unknown_; |
} |
+ static inline bool get_enforce_weak(const DescriptorPool* pool) { |
+ return pool->enforce_weak_; |
+ } |
static inline bool get_is_placeholder(const Descriptor* descriptor) { |
return descriptor->is_placeholder_; |
} |
@@ -2489,9 +3147,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); |
}; |
@@ -2502,6 +3175,8 @@ const FileDescriptor* DescriptorPool::BuildFile( |
"DescriptorDatabase. You must instead find a way to get your file " |
"into the underlying database."; |
GOOGLE_CHECK(mutex_ == NULL); // Implied by the above GOOGLE_CHECK. |
+ tables_->known_bad_symbols_.clear(); |
+ tables_->known_bad_files_.clear(); |
return DescriptorBuilder(this, tables_.get(), NULL).BuildFile(proto); |
} |
@@ -2513,6 +3188,8 @@ const FileDescriptor* DescriptorPool::BuildFileCollectingErrors( |
"DescriptorDatabase. You must instead find a way to get your file " |
"into the underlying database."; |
GOOGLE_CHECK(mutex_ == NULL); // Implied by the above GOOGLE_CHECK. |
+ tables_->known_bad_symbols_.clear(); |
+ tables_->known_bad_files_.clear(); |
return DescriptorBuilder(this, tables_.get(), |
error_collector).BuildFile(proto); |
} |
@@ -2520,8 +3197,16 @@ const FileDescriptor* DescriptorPool::BuildFileCollectingErrors( |
const FileDescriptor* DescriptorPool::BuildFileFromDatabase( |
const FileDescriptorProto& proto) const { |
mutex_->AssertHeld(); |
- return DescriptorBuilder(this, tables_.get(), |
- default_error_collector_).BuildFile(proto); |
+ if (tables_->known_bad_files_.count(proto.name()) > 0) { |
+ return NULL; |
+ } |
+ const FileDescriptor* result = |
+ DescriptorBuilder(this, tables_.get(), |
+ default_error_collector_).BuildFile(proto); |
+ if (result == NULL) { |
+ tables_->known_bad_files_.insert(proto.name()); |
+ } |
+ return result; |
} |
DescriptorBuilder::DescriptorBuilder( |
@@ -2532,7 +3217,8 @@ DescriptorBuilder::DescriptorBuilder( |
tables_(tables), |
error_collector_(error_collector), |
had_errors_(false), |
- possible_undeclared_dependency_(NULL) {} |
+ possible_undeclared_dependency_(NULL), |
+ undefine_resolved_name_("") {} |
DescriptorBuilder::~DescriptorBuilder() {} |
@@ -2554,21 +3240,53 @@ void DescriptorBuilder::AddError( |
had_errors_ = true; |
} |
+void DescriptorBuilder::AddError( |
+ const string& element_name, |
+ const Message& descriptor, |
+ DescriptorPool::ErrorCollector::ErrorLocation location, |
+ const char* error) { |
+ AddError(element_name, descriptor, location, string(error)); |
+} |
+ |
void DescriptorBuilder::AddNotDefinedError( |
const string& element_name, |
const Message& descriptor, |
DescriptorPool::ErrorCollector::ErrorLocation location, |
const string& undefined_symbol) { |
- if (possible_undeclared_dependency_ == NULL) { |
+ if (possible_undeclared_dependency_ == NULL && |
+ undefine_resolved_name_.empty()) { |
AddError(element_name, descriptor, location, |
"\"" + undefined_symbol + "\" is not defined."); |
} else { |
- AddError(element_name, descriptor, location, |
- "\"" + possible_undeclared_dependency_name_ + |
- "\" seems to be defined in \"" + |
- possible_undeclared_dependency_->name() + "\", which is not " |
- "imported by \"" + filename_ + "\". To use it here, please " |
- "add the necessary import."); |
+ if (possible_undeclared_dependency_ != NULL) { |
+ AddError(element_name, descriptor, location, |
+ "\"" + possible_undeclared_dependency_name_ + |
+ "\" seems to be defined in \"" + |
+ possible_undeclared_dependency_->name() + "\", which is not " |
+ "imported by \"" + filename_ + "\". To use it here, please " |
+ "add the necessary import."); |
+ } |
+ if (!undefine_resolved_name_.empty()) { |
+ AddError(element_name, descriptor, location, |
+ "\"" + undefined_symbol + "\" is resolved to \"" + |
+ undefine_resolved_name_ + "\", which is not defined. " |
+ "The innermost scope is searched first in name resolution. " |
+ "Consider using a leading '.'(i.e., \"." |
+ + undefined_symbol + |
+ "\") to start from the outermost scope."); |
+ } |
+ } |
+} |
+ |
+void DescriptorBuilder::AddWarning( |
+ const string& element_name, const Message& descriptor, |
+ DescriptorPool::ErrorCollector::ErrorLocation location, |
+ const string& error) { |
+ if (error_collector_ == NULL) { |
+ GOOGLE_LOG(WARNING) << filename_ << " " << element_name << ": " << error; |
+ } else { |
+ error_collector_->AddWarning(filename_, element_name, &descriptor, location, |
+ error); |
} |
} |
@@ -2629,7 +3347,10 @@ Symbol DescriptorBuilder::FindSymbol(const string& name) { |
// Only find symbols which were defined in this file or one of its |
// dependencies. |
const FileDescriptor* file = result.GetFile(); |
- if (file == file_ || dependencies_.count(file) > 0) return result; |
+ if (file == file_ || dependencies_.count(file) > 0) { |
+ unused_dependency_.erase(file); |
+ return result; |
+ } |
if (result.type == Symbol::PACKAGE) { |
// Arg, this is overcomplicated. The symbol is a package name. It could |
@@ -2655,6 +3376,7 @@ Symbol DescriptorBuilder::FindSymbol(const string& name) { |
Symbol DescriptorBuilder::LookupSymbolNoPlaceholder( |
const string& name, const string& relative_to, ResolveMode resolve_mode) { |
possible_undeclared_dependency_ = NULL; |
+ undefine_resolved_name_.clear(); |
if (name.size() > 0 && name[0] == '.') { |
// Fully-qualified name. |
@@ -2703,7 +3425,11 @@ Symbol DescriptorBuilder::LookupSymbolNoPlaceholder( |
if (result.IsAggregate()) { |
scope_to_try.append(name, first_part_of_name.size(), |
name.size() - first_part_of_name.size()); |
- return FindSymbol(scope_to_try); |
+ result = FindSymbol(scope_to_try); |
+ if (result.IsNull()) { |
+ undefine_resolved_name_ = scope_to_try; |
+ } |
+ return result; |
} else { |
// We found a symbol but it's not an aggregate. Continue the loop. |
} |
@@ -2756,23 +3482,14 @@ Symbol DescriptorBuilder::NewPlaceholder(const string& name, |
placeholder_name = tables_->AllocateString( |
placeholder_full_name->substr(dotpos + 1)); |
} else { |
- placeholder_package = &::google::protobuf::internal::GetEmptyString(); |
+ placeholder_package = &internal::GetEmptyString(); |
placeholder_name = placeholder_full_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; |
- // All other fields are zero or NULL. |
if (placeholder_type == PLACEHOLDER_ENUM) { |
placeholder_file->enum_type_count_ = 1; |
@@ -2836,16 +3553,19 @@ 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)); |
placeholder->name_ = tables_->AllocateString(name); |
- placeholder->package_ = &::google::protobuf::internal::GetEmptyString(); |
+ placeholder->package_ = &internal::GetEmptyString(); |
placeholder->pool_ = pool_; |
placeholder->options_ = &FileOptions::default_instance(); |
- placeholder->tables_ = &FileDescriptorTables::kEmpty; |
+ placeholder->tables_ = &FileDescriptorTables::GetEmptyInstance(); |
+ 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; |
@@ -3016,6 +3736,53 @@ template<class DescriptorT> void DescriptorBuilder::AllocateOptionsImpl( |
METHOD(INPUT.NAME(i), PARENT, OUTPUT->NAME##s_ + i); \ |
} |
+void DescriptorBuilder::AddRecursiveImportError( |
+ const FileDescriptorProto& proto, int from_here) { |
+ string error_message("File recursively imports itself: "); |
+ for (int i = from_here; i < tables_->pending_files_.size(); i++) { |
+ error_message.append(tables_->pending_files_[i]); |
+ error_message.append(" -> "); |
+ } |
+ error_message.append(proto.name()); |
+ |
+ AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, |
+ error_message); |
+} |
+ |
+void DescriptorBuilder::AddTwiceListedError(const FileDescriptorProto& proto, |
+ int index) { |
+ AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, |
+ "Import \"" + proto.dependency(index) + "\" was listed twice."); |
+} |
+ |
+void DescriptorBuilder::AddImportError(const FileDescriptorProto& proto, |
+ int index) { |
+ string message; |
+ if (pool_->fallback_database_ == NULL) { |
+ message = "Import \"" + proto.dependency(index) + |
+ "\" has not been loaded."; |
+ } else { |
+ message = "Import \"" + proto.dependency(index) + |
+ "\" was not found or had errors."; |
+ } |
+ AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, message); |
+} |
+ |
+static bool ExistingFileMatchesProto(const FileDescriptor* existing_file, |
+ const FileDescriptorProto& proto) { |
+ FileDescriptorProto existing_proto; |
+ existing_file->CopyTo(&existing_proto); |
+ // TODO(liujisi): Remove it when CopyTo supports copying syntax params when |
+ // syntax="proto2". |
+ if (existing_file->syntax() == FileDescriptor::SYNTAX_PROTO2 && |
+ proto.has_syntax()) { |
+ existing_proto.set_syntax( |
+ existing_file->SyntaxName(existing_file->syntax())); |
+ } |
+ |
+ return existing_proto.SerializeAsString() == proto.SerializeAsString(); |
+} |
+ |
const FileDescriptor* DescriptorBuilder::BuildFile( |
const FileDescriptorProto& proto) { |
filename_ = proto.name(); |
@@ -3024,13 +3791,11 @@ const FileDescriptor* DescriptorBuilder::BuildFile( |
// Note: This only works if the input is canonical -- that is, it |
// fully-qualifies all type names, has no UninterpretedOptions, etc. |
// This is fine, because this idempotency "feature" really only exists to |
- // accomodate one hack in the proto1->proto2 migration layer. |
+ // accommodate one hack in the proto1->proto2 migration layer. |
const FileDescriptor* existing_file = tables_->FindFile(filename_); |
if (existing_file != NULL) { |
// File already in pool. Compare the existing one to the input. |
- FileDescriptorProto existing_proto; |
- existing_file->CopyTo(&existing_proto); |
- if (existing_proto.SerializeAsString() == proto.SerializeAsString()) { |
+ if (ExistingFileMatchesProto(existing_file, proto)) { |
// They're identical. Return the existing descriptor. |
return existing_file; |
} |
@@ -3049,15 +3814,7 @@ const FileDescriptor* DescriptorBuilder::BuildFile( |
// at all. |
for (int i = 0; i < tables_->pending_files_.size(); i++) { |
if (tables_->pending_files_[i] == proto.name()) { |
- string error_message("File recursively imports itself: "); |
- for (; i < tables_->pending_files_.size(); i++) { |
- error_message.append(tables_->pending_files_[i]); |
- error_message.append(" -> "); |
- } |
- error_message.append(proto.name()); |
- |
- AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, |
- error_message); |
+ AddRecursiveImportError(proto, i); |
return NULL; |
} |
} |
@@ -3084,6 +3841,7 @@ const FileDescriptor* DescriptorBuilder::BuildFile( |
FileDescriptor* result = tables_->Allocate<FileDescriptor>(); |
file_ = result; |
+ result->is_placeholder_ = false; |
if (proto.has_source_code_info()) { |
SourceCodeInfo *info = tables_->AllocateMessage<SourceCodeInfo>(); |
info->CopyFrom(proto.source_code_info()); |
@@ -3100,6 +3858,18 @@ 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()); |
@@ -3130,11 +3900,14 @@ const FileDescriptor* DescriptorBuilder::BuildFile( |
result->dependency_count_ = proto.dependency_size(); |
result->dependencies_ = |
tables_->AllocateArray<const FileDescriptor*>(proto.dependency_size()); |
+ unused_dependency_.clear(); |
+ set<int> weak_deps; |
+ for (int i = 0; i < proto.weak_dependency_size(); ++i) { |
+ weak_deps.insert(proto.weak_dependency(i)); |
+ } |
for (int i = 0; i < proto.dependency_size(); i++) { |
if (!seen_dependencies.insert(proto.dependency(i)).second) { |
- AddError(proto.name(), proto, |
- DescriptorPool::ErrorCollector::OTHER, |
- "Import \"" + proto.dependency(i) + "\" was listed twice."); |
+ AddTwiceListedError(proto, i); |
} |
const FileDescriptor* dependency = tables_->FindFile(proto.dependency(i)); |
@@ -3143,20 +3916,20 @@ const FileDescriptor* DescriptorBuilder::BuildFile( |
} |
if (dependency == NULL) { |
- if (pool_->allow_unknown_) { |
+ if (pool_->allow_unknown_ || |
+ (!pool_->enforce_weak_ && weak_deps.find(i) != weak_deps.end())) { |
dependency = NewPlaceholderFile(proto.dependency(i)); |
} else { |
- string message; |
- if (pool_->fallback_database_ == NULL) { |
- message = "Import \"" + proto.dependency(i) + |
- "\" has not been loaded."; |
- } else { |
- message = "Import \"" + proto.dependency(i) + |
- "\" was not found or had errors."; |
- } |
- AddError(proto.name(), proto, |
- DescriptorPool::ErrorCollector::OTHER, |
- message); |
+ AddImportError(proto, i); |
+ } |
+ } else { |
+ // Add to unused_dependency_ to track unused imported files. |
+ // Note: do not track unused imported files for public import. |
+ if (pool_->enforce_dependencies_ && |
+ (pool_->unused_import_track_files_.find(proto.name()) != |
+ pool_->unused_import_track_files_.end()) && |
+ (dependency->public_dependency_count() == 0)) { |
+ unused_dependency_.insert(dependency); |
} |
} |
@@ -3172,6 +3945,8 @@ const FileDescriptor* DescriptorBuilder::BuildFile( |
int index = proto.public_dependency(i); |
if (index >= 0 && index < proto.dependency_size()) { |
result->public_dependencies_[public_dependency_count++] = index; |
+ // Do not track unused imported files for public import. |
+ unused_dependency_.erase(result->dependency(index)); |
} else { |
AddError(proto.name(), proto, |
DescriptorPool::ErrorCollector::OTHER, |
@@ -3238,6 +4013,19 @@ 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(proto, result); |
+ } |
+ |
if (had_errors_) { |
tables_->RollbackToLastCheckpoint(); |
return NULL; |
@@ -3265,11 +4053,24 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, |
result->is_placeholder_ = false; |
result->is_unqualified_placeholder_ = false; |
+ // Build oneofs first so that fields and extension ranges can refer to them. |
+ BUILD_ARRAY(proto, result, oneof_decl , BuildOneof , result); |
BUILD_ARRAY(proto, result, field , BuildField , result); |
BUILD_ARRAY(proto, result, nested_type , BuildMessage , result); |
BUILD_ARRAY(proto, result, enum_type , BuildEnum , result); |
BUILD_ARRAY(proto, result, extension_range, BuildExtensionRange, result); |
BUILD_ARRAY(proto, result, extension , BuildExtension , result); |
+ BUILD_ARRAY(proto, result, reserved_range , BuildReservedRange , result); |
+ |
+ // Copy reserved names. |
+ int reserved_name_count = proto.reserved_name_size(); |
+ result->reserved_name_count_ = reserved_name_count; |
+ result->reserved_names_ = |
+ tables_->AllocateArray<const string*>(reserved_name_count); |
+ for (int i = 0; i < reserved_name_count; ++i) { |
+ result->reserved_names_[i] = |
+ tables_->AllocateString(proto.reserved_name(i)); |
+ } |
// Copy options. |
if (!proto.has_options()) { |
@@ -3281,7 +4082,34 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, |
AddSymbol(result->full_name(), parent, result->name(), |
proto, Symbol(result)); |
- // Check that no fields have numbers in extension ranges. |
+ for (int i = 0; i < proto.reserved_range_size(); i++) { |
+ const DescriptorProto_ReservedRange& range1 = proto.reserved_range(i); |
+ for (int j = i + 1; j < proto.reserved_range_size(); j++) { |
+ const DescriptorProto_ReservedRange& range2 = proto.reserved_range(j); |
+ if (range1.end() > range2.start() && range2.end() > range1.start()) { |
+ AddError(result->full_name(), proto.reserved_range(i), |
+ DescriptorPool::ErrorCollector::NUMBER, |
+ strings::Substitute("Reserved range $0 to $1 overlaps with " |
+ "already-defined range $2 to $3.", |
+ range2.start(), range2.end() - 1, |
+ range1.start(), range1.end() - 1)); |
+ } |
+ } |
+ } |
+ |
+ hash_set<string> reserved_name_set; |
+ for (int i = 0; i < proto.reserved_name_size(); i++) { |
+ const string& name = proto.reserved_name(i); |
+ if (reserved_name_set.find(name) == reserved_name_set.end()) { |
+ reserved_name_set.insert(name); |
+ } else { |
+ AddError(name, proto, DescriptorPool::ErrorCollector::NAME, |
+ strings::Substitute( |
+ "Field name \"$0\" is reserved multiple times.", |
+ name)); |
+ } |
+ } |
+ |
for (int i = 0; i < result->field_count(); i++) { |
const FieldDescriptor* field = result->field(i); |
for (int j = 0; j < result->extension_range_count(); j++) { |
@@ -3295,11 +4123,39 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, |
field->name(), field->number())); |
} |
} |
+ for (int j = 0; j < result->reserved_range_count(); j++) { |
+ const Descriptor::ReservedRange* range = result->reserved_range(j); |
+ if (range->start <= field->number() && field->number() < range->end) { |
+ AddError(field->full_name(), proto.reserved_range(j), |
+ DescriptorPool::ErrorCollector::NUMBER, |
+ strings::Substitute( |
+ "Field \"$0\" uses reserved number $1.", |
+ field->name(), field->number())); |
+ } |
+ } |
+ if (reserved_name_set.find(field->name()) != reserved_name_set.end()) { |
+ AddError(field->full_name(), proto.field(i), |
+ DescriptorPool::ErrorCollector::NAME, |
+ strings::Substitute( |
+ "Field name \"$0\" is reserved.", field->name())); |
+ } |
} |
- // Check that extension ranges don't overlap. |
+ // Check that extension ranges don't overlap and don't include |
+ // reserved field numbers. |
for (int i = 0; i < result->extension_range_count(); i++) { |
const Descriptor::ExtensionRange* range1 = result->extension_range(i); |
+ for (int j = 0; j < result->reserved_range_count(); j++) { |
+ const Descriptor::ReservedRange* range2 = result->reserved_range(j); |
+ if (range1->end > range2->start && range2->end > range1->start) { |
+ AddError(result->full_name(), proto.extension_range(j), |
+ DescriptorPool::ErrorCollector::NUMBER, |
+ strings::Substitute("Extension range $0 to $1 overlaps with " |
+ "reserved range $2 to $3.", |
+ range1->start, range1->end - 1, |
+ range2->start, range2->end - 1)); |
+ } |
+ } |
for (int j = i + 1; j < result->extension_range_count(); j++) { |
const Descriptor::ExtensionRange* range2 = result->extension_range(j); |
if (range1->end > range2->start && range2->end > range1->start) { |
@@ -3314,6 +4170,7 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, |
} |
} |
+ |
void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, |
const Descriptor* parent, |
FieldDescriptor* result, |
@@ -3346,7 +4203,17 @@ 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)); |
+ |
+ if (proto.has_json_name()) { |
+ result->has_json_name_ = true; |
+ result->json_name_ = tables_->AllocateString(proto.json_name()); |
+ } else { |
+ result->has_json_name_ = false; |
+ result->json_name_ = result->camelcase_name_; |
+ } |
// Some compilers do not allow static_cast directly between two enum types, |
// so we must cast to int first. |
@@ -3355,10 +4222,22 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, |
result->label_ = static_cast<FieldDescriptor::Label>( |
implicit_cast<int>(proto.label())); |
+ // An extension cannot have a required field (b/13365836). |
+ if (result->is_extension_ && |
+ result->label_ == FieldDescriptor::LABEL_REQUIRED) { |
+ AddError(result->full_name(), proto, |
+ // Error location `TYPE`: we would really like to indicate |
+ // `LABEL`, but the `ErrorLocation` enum has no entry for this, and |
+ // we don't necessarily know about all implementations of the |
+ // `ErrorCollector` interface to extend them to handle the new |
+ // error location type properly. |
+ DescriptorPool::ErrorCollector::TYPE, |
+ "Message extensions cannot have required fields."); |
+ } |
+ |
// 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; |
@@ -3397,8 +4276,8 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, |
} else if (proto.default_value() == "nan") { |
result->default_value_float_ = numeric_limits<float>::quiet_NaN(); |
} else { |
- result->default_value_float_ = |
- NoLocaleStrtod(proto.default_value().c_str(), &end_pos); |
+ result->default_value_float_ = io::SafeDoubleToFloat( |
+ io::NoLocaleStrtod(proto.default_value().c_str(), &end_pos)); |
} |
break; |
case FieldDescriptor::CPPTYPE_DOUBLE: |
@@ -3410,7 +4289,7 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, |
result->default_value_double_ = numeric_limits<double>::quiet_NaN(); |
} else { |
result->default_value_double_ = |
- NoLocaleStrtod(proto.default_value().c_str(), &end_pos); |
+ io::NoLocaleStrtod(proto.default_value().c_str(), &end_pos); |
} |
break; |
case FieldDescriptor::CPPTYPE_BOOL: |
@@ -3452,7 +4331,8 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, |
if (proto.default_value().empty() || *end_pos != '\0') { |
AddError(result->full_name(), proto, |
DescriptorPool::ErrorCollector::DEFAULT_VALUE, |
- "Couldn't parse default value."); |
+ "Couldn't parse default value \"" + proto.default_value() + |
+ "\"."); |
} |
} |
} else { |
@@ -3484,7 +4364,7 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, |
result->default_value_enum_ = NULL; |
break; |
case FieldDescriptor::CPPTYPE_STRING: |
- result->default_value_string_ = &::google::protobuf::internal::GetEmptyString(); |
+ result->default_value_string_ = &internal::GetEmptyString(); |
break; |
case FieldDescriptor::CPPTYPE_MESSAGE: |
break; |
@@ -3525,6 +4405,16 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, |
} |
result->extension_scope_ = parent; |
+ |
+ if (proto.has_oneof_index()) { |
+ AddError(result->full_name(), proto, |
+ DescriptorPool::ErrorCollector::OTHER, |
+ "FieldDescriptorProto.oneof_index should not be set for " |
+ "extensions."); |
+ } |
+ |
+ // Fill in later (maybe). |
+ result->containing_oneof_ = NULL; |
} else { |
if (proto.has_extendee()) { |
AddError(result->full_name(), proto, |
@@ -3533,6 +4423,23 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, |
} |
result->containing_type_ = parent; |
+ |
+ if (proto.has_oneof_index()) { |
+ if (proto.oneof_index() < 0 || |
+ proto.oneof_index() >= parent->oneof_decl_count()) { |
+ AddError(result->full_name(), proto, |
+ DescriptorPool::ErrorCollector::OTHER, |
+ strings::Substitute("FieldDescriptorProto.oneof_index $0 is " |
+ "out of range for type \"$1\".", |
+ proto.oneof_index(), |
+ parent->name())); |
+ result->containing_oneof_ = NULL; |
+ } else { |
+ result->containing_oneof_ = parent->oneof_decl(proto.oneof_index()); |
+ } |
+ } else { |
+ result->containing_oneof_ = NULL; |
+ } |
} |
// Copy options. |
@@ -3542,6 +4449,7 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, |
AllocateOptions(proto.options(), result); |
} |
+ |
AddSymbol(result->full_name(), parent, result->name(), |
proto, Symbol(result)); |
} |
@@ -3570,6 +4478,41 @@ void DescriptorBuilder::BuildExtensionRange( |
} |
} |
+void DescriptorBuilder::BuildReservedRange( |
+ const DescriptorProto::ReservedRange& proto, |
+ const Descriptor* parent, |
+ Descriptor::ReservedRange* result) { |
+ result->start = proto.start(); |
+ result->end = proto.end(); |
+ if (result->start <= 0) { |
+ AddError(parent->full_name(), proto, |
+ DescriptorPool::ErrorCollector::NUMBER, |
+ "Reserved numbers must be positive integers."); |
+ } |
+} |
+ |
+void DescriptorBuilder::BuildOneof(const OneofDescriptorProto& proto, |
+ Descriptor* parent, |
+ OneofDescriptor* result) { |
+ string* full_name = tables_->AllocateString(parent->full_name()); |
+ full_name->append(1, '.'); |
+ full_name->append(proto.name()); |
+ |
+ ValidateSymbolName(proto.name(), *full_name, proto); |
+ |
+ result->name_ = tables_->AllocateString(proto.name()); |
+ result->full_name_ = full_name; |
+ |
+ result->containing_type_ = parent; |
+ |
+ // We need to fill these in later. |
+ result->field_count_ = 0; |
+ result->fields_ = NULL; |
+ |
+ AddSymbol(result->full_name(), parent, result->name(), |
+ proto, Symbol(result)); |
+} |
+ |
void DescriptorBuilder::BuildEnum(const EnumDescriptorProto& proto, |
const Descriptor* parent, |
EnumDescriptor* result) { |
@@ -3678,7 +4621,7 @@ void DescriptorBuilder::BuildEnumValue(const EnumValueDescriptorProto& proto, |
} |
void DescriptorBuilder::BuildService(const ServiceDescriptorProto& proto, |
- const void* dummy, |
+ const void* /* dummy */, |
ServiceDescriptor* result) { |
string* full_name = tables_->AllocateString(file_->package()); |
if (!full_name->empty()) full_name->append(1, '.'); |
@@ -3727,6 +4670,9 @@ void DescriptorBuilder::BuildMethod(const MethodDescriptorProto& proto, |
AllocateOptions(proto.options(), result); |
} |
+ result->client_streaming_ = proto.client_streaming(); |
+ result->server_streaming_ = proto.server_streaming(); |
+ |
AddSymbol(result->full_name(), parent, result->name(), |
proto, Symbol(result)); |
} |
@@ -3779,6 +4725,63 @@ void DescriptorBuilder::CrossLinkMessage( |
for (int i = 0; i < message->extension_count(); i++) { |
CrossLinkField(&message->extensions_[i], proto.extension(i)); |
} |
+ |
+ // Set up field array for each oneof. |
+ |
+ // First count the number of fields per oneof. |
+ for (int i = 0; i < message->field_count(); i++) { |
+ const OneofDescriptor* oneof_decl = message->field(i)->containing_oneof(); |
+ if (oneof_decl != NULL) { |
+ // Make sure fields belonging to the same oneof are defined consecutively. |
+ // This enables optimizations in codegens and reflection libraries to |
+ // skip fields in the oneof group, as only one of the field can be set. |
+ // Note that field_count() returns how many fields in this oneof we have |
+ // seen so far. field_count() > 0 guarantees that i > 0, so field(i-1) is |
+ // safe. |
+ if (oneof_decl->field_count() > 0 && |
+ message->field(i - 1)->containing_oneof() != oneof_decl) { |
+ AddError( |
+ message->full_name() + "." + message->field(i - 1)->name(), |
+ proto.field(i - 1), DescriptorPool::ErrorCollector::OTHER, |
+ strings::Substitute( |
+ "Fields in the same oneof must be defined consecutively. " |
+ "\"$0\" cannot be defined before the completion of the " |
+ "\"$1\" oneof definition.", |
+ message->field(i - 1)->name(), oneof_decl->name())); |
+ } |
+ // Must go through oneof_decls_ array to get a non-const version of the |
+ // OneofDescriptor. |
+ ++message->oneof_decls_[oneof_decl->index()].field_count_; |
+ } |
+ } |
+ |
+ // Then allocate the arrays. |
+ for (int i = 0; i < message->oneof_decl_count(); i++) { |
+ OneofDescriptor* oneof_decl = &message->oneof_decls_[i]; |
+ |
+ if (oneof_decl->field_count() == 0) { |
+ AddError(message->full_name() + "." + oneof_decl->name(), |
+ proto.oneof_decl(i), |
+ DescriptorPool::ErrorCollector::NAME, |
+ "Oneof must have at least one field."); |
+ } |
+ |
+ oneof_decl->fields_ = |
+ tables_->AllocateArray<const FieldDescriptor*>(oneof_decl->field_count_); |
+ oneof_decl->field_count_ = 0; |
+ } |
+ |
+ // Then fill them in. |
+ for (int i = 0; i < message->field_count(); i++) { |
+ const OneofDescriptor* oneof_decl = message->field(i)->containing_oneof(); |
+ if (oneof_decl != NULL) { |
+ OneofDescriptor* mutable_oneof_decl = |
+ &message->oneof_decls_[oneof_decl->index()]; |
+ message->fields_[i].index_in_oneof_ = mutable_oneof_decl->field_count_; |
+ mutable_oneof_decl->fields_[mutable_oneof_decl->field_count_++] = |
+ message->field(i); |
+ } |
+ } |
} |
void DescriptorBuilder::CrossLinkField( |
@@ -3803,7 +4806,10 @@ void DescriptorBuilder::CrossLinkField( |
} |
field->containing_type_ = extendee.descriptor; |
- if (!field->containing_type()->IsExtensionNumber(field->number())) { |
+ const Descriptor::ExtensionRange* extension_range = field->containing_type() |
+ ->FindExtensionRangeContainingNumber(field->number()); |
+ |
+ if (extension_range == NULL) { |
AddError(field->full_name(), proto, |
DescriptorPool::ErrorCollector::NUMBER, |
strings::Substitute("\"$0\" does not declare $1 as an " |
@@ -3813,6 +4819,17 @@ void DescriptorBuilder::CrossLinkField( |
} |
} |
+ if (field->containing_oneof() != NULL) { |
+ if (field->label() != FieldDescriptor::LABEL_OPTIONAL) { |
+ // Note that this error will never happen when parsing .proto files. |
+ // It can only happen if you manually construct a FileDescriptorProto |
+ // that is incorrect. |
+ AddError(field->full_name(), proto, |
+ DescriptorPool::ErrorCollector::NAME, |
+ "Fields of oneofs must themselves have label LABEL_OPTIONAL."); |
+ } |
+ } |
+ |
if (proto.has_type_name()) { |
// Assume we are expecting a message type unless the proto contains some |
// evidence that it expects an enum type. This only makes a difference if |
@@ -3825,6 +4842,11 @@ void DescriptorBuilder::CrossLinkField( |
expecting_enum ? PLACEHOLDER_ENUM : PLACEHOLDER_MESSAGE, |
LOOKUP_TYPES); |
+ // If the type is a weak type, we change the type to a google.protobuf.Empty field. |
+ if (type.IsNull() && !pool_->enforce_weak_ && proto.options().weak()) { |
+ type = FindSymbol(kNonLinkedWeakMessageReplacementName); |
+ } |
+ |
if (type.IsNull()) { |
AddNotDefinedError(field->full_name(), proto, |
DescriptorPool::ErrorCollector::TYPE, |
@@ -3876,21 +4898,34 @@ void DescriptorBuilder::CrossLinkField( |
} |
if (field->has_default_value()) { |
- // We can't just use field->enum_type()->FindValueByName() here |
- // because that locks the pool's mutex, which we have already locked |
- // at this point. |
- Symbol default_value = |
- LookupSymbolNoPlaceholder(proto.default_value(), |
- field->enum_type()->full_name()); |
- |
- if (default_value.type == Symbol::ENUM_VALUE && |
- default_value.enum_value_descriptor->type() == field->enum_type()) { |
- field->default_value_enum_ = default_value.enum_value_descriptor; |
- } else { |
+ // Ensure that the default value is an identifier. Parser cannot always |
+ // verify this because it does not have complete type information. |
+ // N.B. that this check yields better error messages but is not |
+ // necessary for correctness (an enum symbol must be a valid identifier |
+ // anyway), only for better errors. |
+ if (!io::Tokenizer::IsIdentifier(proto.default_value())) { |
AddError(field->full_name(), proto, |
DescriptorPool::ErrorCollector::DEFAULT_VALUE, |
- "Enum type \"" + field->enum_type()->full_name() + |
- "\" has no value named \"" + proto.default_value() + "\"."); |
+ "Default value for an enum field must be an identifier."); |
+ } else { |
+ // We can't just use field->enum_type()->FindValueByName() here |
+ // because that locks the pool's mutex, which we have already locked |
+ // at this point. |
+ Symbol default_value = |
+ LookupSymbolNoPlaceholder(proto.default_value(), |
+ field->enum_type()->full_name()); |
+ |
+ if (default_value.type == Symbol::ENUM_VALUE && |
+ default_value.enum_value_descriptor->type() == |
+ field->enum_type()) { |
+ field->default_value_enum_ = default_value.enum_value_descriptor; |
+ } else { |
+ AddError(field->full_name(), proto, |
+ DescriptorPool::ErrorCollector::DEFAULT_VALUE, |
+ "Enum type \"" + field->enum_type()->full_name() + |
+ "\" has no value named \"" + proto.default_value() + |
+ "\"."); |
+ } |
} |
} else if (field->enum_type()->value_count() > 0) { |
// All enums must have at least one value, or we would have reported |
@@ -3934,12 +4969,26 @@ void DescriptorBuilder::CrossLinkField( |
field->containing_type()->full_name(), |
conflicting_field->name())); |
} |
- } |
- |
- if (field->is_extension()) { |
- // No need for error checking: if the extension number collided, |
- // we've already been informed of it by the if() above. |
- tables_->AddExtension(field); |
+ } else { |
+ if (field->is_extension()) { |
+ if (!tables_->AddExtension(field)) { |
+ const FieldDescriptor* conflicting_field = |
+ tables_->FindExtension(field->containing_type(), field->number()); |
+ string error_msg = strings::Substitute( |
+ "Extension number $0 has already been used in \"$1\" by extension " |
+ "\"$2\" defined in $3.", |
+ field->number(), |
+ field->containing_type()->full_name(), |
+ conflicting_field->full_name(), |
+ conflicting_field->file()->name()); |
+ // Conflicting extension numbers should be an error. However, before |
+ // turning this into an error we need to fix all existing broken |
+ // protos first. |
+ // TODO(xiaofeng): Change this to an error. |
+ AddWarning(field->full_name(), proto, |
+ DescriptorPool::ErrorCollector::NUMBER, error_msg); |
+ } |
+ } |
} |
// Add the field to the lowercase-name and camelcase-name tables. |
@@ -3958,7 +5007,8 @@ void DescriptorBuilder::CrossLinkEnum( |
} |
void DescriptorBuilder::CrossLinkEnumValue( |
- EnumValueDescriptor* enum_value, const EnumValueDescriptorProto& proto) { |
+ EnumValueDescriptor* enum_value, |
+ const EnumValueDescriptorProto& /* proto */) { |
if (enum_value->options_ == NULL) { |
enum_value->options_ = &EnumValueOptions::default_instance(); |
} |
@@ -4047,6 +5097,135 @@ 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."); |
+ } |
+} |
+ |
+static string ToLowercaseWithoutUnderscores(const string& name) { |
+ string result; |
+ for (int i = 0; i < name.size(); ++i) { |
+ if (name[i] != '_') { |
+ if (name[i] >= 'A' && name[i] <= 'Z') { |
+ result.push_back(name[i] - 'A' + 'a'); |
+ } else { |
+ result.push_back(name[i]); |
+ } |
+ } |
+ } |
+ return result; |
+} |
+ |
+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."); |
+ } |
+ |
+ // In proto3, we reject field names if they conflict in camelCase. |
+ // Note that we currently enforce a stricter rule: Field names must be |
+ // unique after being converted to lowercase with underscores removed. |
+ map<string, const FieldDescriptor*> name_to_field; |
+ for (int i = 0; i < message->field_count(); ++i) { |
+ string lowercase_name = ToLowercaseWithoutUnderscores( |
+ message->field(i)->name()); |
+ if (name_to_field.find(lowercase_name) != name_to_field.end()) { |
+ AddError(message->full_name(), proto, |
+ DescriptorPool::ErrorCollector::OTHER, |
+ "The JSON camcel-case name of field \"" + |
+ message->field(i)->name() + "\" conflicts with field \"" + |
+ name_to_field[lowercase_name]->name() + "\". This is not " + |
+ "allowed in proto3."); |
+ } else { |
+ name_to_field[lowercase_name] = message->field(i); |
+ } |
+ } |
+} |
+ |
+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."); |
+ } |
+ if (field->type() == FieldDescriptor::TYPE_GROUP) { |
+ AddError(field->full_name(), proto, |
+ DescriptorPool::ErrorCollector::TYPE, |
+ "Groups are not supported in proto3 syntax."); |
+ } |
+} |
+ |
+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, |
@@ -4073,10 +5252,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) { |
@@ -4125,6 +5300,16 @@ void DescriptorBuilder::ValidateFieldOptions(FieldDescriptor* field, |
"a lite type, but the reverse is allowed."); |
} |
+ // Validate map types. |
+ if (field->is_map()) { |
+ 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, |
@@ -4158,7 +5343,8 @@ void DescriptorBuilder::ValidateEnumOptions(EnumDescriptor* enm, |
} |
void DescriptorBuilder::ValidateEnumValueOptions( |
- EnumValueDescriptor* enum_value, const EnumValueDescriptorProto& proto) { |
+ EnumValueDescriptor* /* enum_value */, |
+ const EnumValueDescriptorProto& /* proto */) { |
// Nothing to do so far. |
} |
void DescriptorBuilder::ValidateServiceOptions(ServiceDescriptor* service, |
@@ -4176,62 +5362,140 @@ void DescriptorBuilder::ValidateServiceOptions(ServiceDescriptor* service, |
VALIDATE_OPTIONS_FROM_ARRAY(service, method, Method); |
} |
-void DescriptorBuilder::ValidateMethodOptions(MethodDescriptor* method, |
- const MethodDescriptorProto& proto) { |
+void DescriptorBuilder::ValidateMethodOptions(MethodDescriptor* /* method */, |
+ const MethodDescriptorProto& /* proto */) { |
// 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; |
} |
- |
- 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; |
+ if (value->label() != FieldDescriptor::LABEL_OPTIONAL || |
+ value->number() != 2 || value->name() != "value") { |
+ return false; |
} |
- // 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); |
- |
- 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; |
+ // 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 complain when new types |
+ // are added. |
} |
- 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; |
+ if (value->type() == FieldDescriptor::TYPE_ENUM) { |
+ if (value->enum_type()->value(0)->number() != 0) { |
+ AddError( |
+ field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, |
+ "Enum value in map must define 0 as the first value."); |
+ } |
} |
- 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; |
- } |
+ return true; |
+} |
- field->experimental_map_key_ = key_field; |
+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(std::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."); |
+ } |
+ } |
+ // 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."); |
+ } |
+ } |
+ // 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."); |
+ } |
+ } |
} |
@@ -4298,11 +5562,19 @@ bool DescriptorBuilder::OptionInterpreter::InterpretOptions( |
// UnknownFieldSet and wait there until the message is parsed by something |
// that does know about the options. |
string buf; |
- options->AppendToString(&buf); |
- GOOGLE_CHECK(options->ParseFromString(buf)) |
+ GOOGLE_CHECK(options->AppendPartialToString(&buf)) |
+ << "Protocol message could not be serialized."; |
+ GOOGLE_CHECK(options->ParsePartialFromString(buf)) |
<< "Protocol message serialized itself in invalid fashion."; |
+ if (!options->IsInitialized()) { |
+ builder_->AddWarning( |
+ options_to_interpret->element_name, *original_options, |
+ DescriptorPool::ErrorCollector::OTHER, |
+ "Options could not be fully parsed using the proto descriptors " |
+ "compiled into this binary. Missing required fields: " + |
+ options->InitializationErrorString()); |
+ } |
} |
- |
return !failed; |
} |
@@ -4319,6 +5591,18 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption( |
"\"uninterpreted_option\"."); |
} |
+ // TODO(xyzzyz): remove when all uses in Chromium are removed |
+ if (uninterpreted_option_->name(0).name_part() == "retain_unknown_fields") { |
+ // Chromium patch to protobuf used to introduce a retain_unknown_fields |
+ // option that would make the protobuf_lite runtime retain unknown fields |
+ // just like the protobuf_full would. A newer upstream version of protobuf |
+ // retains these unknown fields even in lite runtime, so the option is no |
+ // longer necessary. Therefore, we ignore this option, and when we remove |
+ // all the occurrences of the option from Chromium, we can remove this |
+ // ignore. |
+ return true; |
+ } |
+ |
const Descriptor* options_descriptor = NULL; |
// Get the options message's descriptor from the builder's pool, so that we |
// get the version that knows about any extension options declared in the |
@@ -4386,6 +5670,15 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption( |
// so we will just leave it as uninterpreted. |
AddWithoutInterpreting(*uninterpreted_option_, options); |
return true; |
+ } else if (!(builder_->undefine_resolved_name_).empty()) { |
+ // Option is resolved to a name which is not defined. |
+ return AddNameError( |
+ "Option \"" + debug_msg_name + "\" is resolved to \"(" + |
+ builder_->undefine_resolved_name_ + |
+ ")\", which is not defined. The innermost scope is searched first " |
+ "in name resolution. Consider using a leading '.'(i.e., \"(." + |
+ debug_msg_name.substr(1) + |
+ "\") to start from the outermost scope."); |
} else { |
return AddNameError("Option \"" + debug_msg_name + "\" unknown."); |
} |
@@ -4406,14 +5699,15 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption( |
"\" is not a field or extension of message \"" + |
descriptor->name() + "\"."); |
} |
- } else if (field->is_repeated()) { |
- return AddNameError("Option field \"" + debug_msg_name + |
- "\" is repeated. Repeated options are not " |
- "supported."); |
} else if (i < uninterpreted_option_->name_size() - 1) { |
if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { |
return AddNameError("Option \"" + debug_msg_name + |
"\" is an atomic type, not a message."); |
+ } else if (field->is_repeated()) { |
+ return AddNameError("Option field \"" + debug_msg_name + |
+ "\" is a repeated message. Repeated message " |
+ "options must be initialized using an " |
+ "aggregate value."); |
} else { |
// Drill down into the submessage. |
intermediate_fields.push_back(field); |
@@ -4430,7 +5724,7 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption( |
// known will populate them correctly. |
// First see if the option is already set. |
- if (!ExamineIfOptionIsSet( |
+ if (!field->is_repeated() && !ExamineIfOptionIsSet( |
intermediate_fields.begin(), |
intermediate_fields.end(), |
field, debug_msg_name, |
@@ -4441,7 +5735,7 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption( |
// First set the value on the UnknownFieldSet corresponding to the |
// innermost message. |
- scoped_ptr<UnknownFieldSet> unknown_fields(new UnknownFieldSet()); |
+ google::protobuf::scoped_ptr<UnknownFieldSet> unknown_fields(new UnknownFieldSet()); |
if (!SetOptionValue(field, unknown_fields.get())) { |
return false; // SetOptionValue() already added the error. |
} |
@@ -4451,13 +5745,14 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption( |
for (vector<const FieldDescriptor*>::reverse_iterator iter = |
intermediate_fields.rbegin(); |
iter != intermediate_fields.rend(); ++iter) { |
- scoped_ptr<UnknownFieldSet> parent_unknown_fields(new UnknownFieldSet()); |
+ google::protobuf::scoped_ptr<UnknownFieldSet> parent_unknown_fields( |
+ new UnknownFieldSet()); |
switch ((*iter)->type()) { |
case FieldDescriptor::TYPE_MESSAGE: { |
io::StringOutputStream outstr( |
parent_unknown_fields->AddLengthDelimited((*iter)->number())); |
io::CodedOutputStream out(&outstr); |
- internal::WireFormatLite::SerializeUnknownFields(*unknown_fields, &out); |
+ internal::WireFormat::SerializeUnknownFields(*unknown_fields, &out); |
GOOGLE_CHECK(!out.HadError()) |
<< "Unexpected failure while serializing option submessage " |
<< debug_msg_name << "\"."; |
@@ -4805,14 +6100,16 @@ class AggregateErrorCollector : public io::ErrorCollector { |
public: |
string error_; |
- virtual void AddError(int line, int column, const string& message) { |
+ virtual void AddError(int /* line */, int /* column */, |
+ const string& message) { |
if (!error_.empty()) { |
error_ += "; "; |
} |
error_ += message; |
} |
- virtual void AddWarning(int line, int column, const string& message) { |
+ virtual void AddWarning(int /* line */, int /* column */, |
+ const string& /* message */) { |
// Ignore warnings |
} |
}; |
@@ -4835,7 +6132,7 @@ bool DescriptorBuilder::OptionInterpreter::SetAggregateOption( |
} |
const Descriptor* type = option_field->message_type(); |
- scoped_ptr<Message> dynamic(dynamic_factory_.GetPrototype(type)->New()); |
+ google::protobuf::scoped_ptr<Message> dynamic(dynamic_factory_.GetPrototype(type)->New()); |
GOOGLE_CHECK(dynamic.get() != NULL) |
<< "Could not create an instance of " << option_field->DebugString(); |
@@ -4943,5 +6240,40 @@ void DescriptorBuilder::OptionInterpreter::SetUInt64(int number, uint64 value, |
} |
} |
+void DescriptorBuilder::LogUnusedDependency(const FileDescriptorProto& proto, |
+ const FileDescriptor* result) { |
+ |
+ if (!unused_dependency_.empty()) { |
+ std::set<string> annotation_extensions; |
+ annotation_extensions.insert("google.protobuf.MessageOptions"); |
+ annotation_extensions.insert("google.protobuf.FileOptions"); |
+ annotation_extensions.insert("google.protobuf.FieldOptions"); |
+ annotation_extensions.insert("google.protobuf.EnumOptions"); |
+ annotation_extensions.insert("google.protobuf.EnumValueOptions"); |
+ annotation_extensions.insert("google.protobuf.ServiceOptions"); |
+ annotation_extensions.insert("google.protobuf.MethodOptions"); |
+ annotation_extensions.insert("google.protobuf.StreamOptions"); |
+ for (set<const FileDescriptor*>::const_iterator |
+ it = unused_dependency_.begin(); |
+ it != unused_dependency_.end(); ++it) { |
+ // Do not log warnings for proto files which extend annotations. |
+ int i; |
+ for (i = 0 ; i < (*it)->extension_count(); ++i) { |
+ if (annotation_extensions.find( |
+ (*it)->extension(i)->containing_type()->full_name()) |
+ != annotation_extensions.end()) { |
+ break; |
+ } |
+ } |
+ // Log warnings for unused imported files. |
+ if (i == (*it)->extension_count()) { |
+ string error_message = "Import " + (*it)->name() + " but not used."; |
+ AddWarning((*it)->name(), proto, DescriptorPool::ErrorCollector::OTHER, |
+ error_message); |
+ } |
+ } |
+ } |
+} |
+ |
} // namespace protobuf |
} // namespace google |