| 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
|
|
|