| Index: third_party/protobuf/src/google/protobuf/descriptor.cc
|
| ===================================================================
|
| --- third_party/protobuf/src/google/protobuf/descriptor.cc (revision 216642)
|
| +++ third_party/protobuf/src/google/protobuf/descriptor.cc (working copy)
|
| @@ -54,7 +54,7 @@
|
| #include <google/protobuf/stubs/strutil.h>
|
| #include <google/protobuf/stubs/substitute.h>
|
| #include <google/protobuf/stubs/map-util.h>
|
| -#include <google/protobuf/stubs/stl_util-inl.h>
|
| +#include <google/protobuf/stubs/stl_util.h>
|
|
|
| #undef PACKAGE // autoheader #defines this. :(
|
|
|
| @@ -108,6 +108,21 @@
|
| "sint64", // TYPE_SINT64
|
| };
|
|
|
| +const char * const FieldDescriptor::kCppTypeToName[MAX_CPPTYPE + 1] = {
|
| + "ERROR", // 0 is reserved for errors
|
| +
|
| + "int32", // CPPTYPE_INT32
|
| + "int64", // CPPTYPE_INT64
|
| + "uint32", // CPPTYPE_UINT32
|
| + "uint64", // CPPTYPE_UINT64
|
| + "double", // CPPTYPE_DOUBLE
|
| + "float", // CPPTYPE_FLOAT
|
| + "bool", // CPPTYPE_BOOL
|
| + "enum", // CPPTYPE_ENUM
|
| + "string", // CPPTYPE_STRING
|
| + "message", // CPPTYPE_MESSAGE
|
| +};
|
| +
|
| const char * const FieldDescriptor::kLabelToName[MAX_LABEL + 1] = {
|
| "ERROR", // 0 is reserved for errors
|
|
|
| @@ -221,7 +236,8 @@
|
|
|
| struct Symbol {
|
| enum Type {
|
| - NULL_SYMBOL, MESSAGE, FIELD, ENUM, ENUM_VALUE, SERVICE, METHOD, PACKAGE
|
| + NULL_SYMBOL, MESSAGE, FIELD, ENUM, ENUM_VALUE, SERVICE, METHOD,
|
| + PACKAGE
|
| };
|
| Type type;
|
| union {
|
| @@ -311,18 +327,43 @@
|
| Tables();
|
| ~Tables();
|
|
|
| - // Checkpoint the state of the tables. Future calls to Rollback() will
|
| - // return the Tables to this state. This is used when building files, since
|
| - // some kinds of validation errors cannot be detected until the file's
|
| - // descriptors have already been added to the tables. BuildFile() calls
|
| - // Checkpoint() before it starts building and Rollback() if it encounters
|
| - // an error.
|
| - void Checkpoint();
|
| + // Record the current state of the tables to the stack of checkpoints.
|
| + // Each call to AddCheckpoint() must be paired with exactly one call to either
|
| + // ClearLastCheckpoint() or RollbackToLastCheckpoint().
|
| + //
|
| + // This is used when building files, since some kinds of validation errors
|
| + // cannot be detected until the file's descriptors have already been added to
|
| + // the tables.
|
| + //
|
| + // This supports recursive checkpoints, since building a file may trigger
|
| + // recursive building of other files. Note that recursive checkpoints are not
|
| + // normally necessary; explicit dependencies are built prior to checkpointing.
|
| + // So although we recursively build transitive imports, there is at most one
|
| + // checkpoint in the stack during dependency building.
|
| + //
|
| + // Recursive checkpoints only arise during cross-linking of the descriptors.
|
| + // Symbol references must be resolved, via DescriptorBuilder::FindSymbol and
|
| + // friends. If the pending file references an unknown symbol
|
| + // (e.g., it is not defined in the pending file's explicit dependencies), and
|
| + // the pool is using a fallback database, and that database contains a file
|
| + // defining that symbol, and that file has not yet been built by the pool,
|
| + // the pool builds the file during cross-linking, leading to another
|
| + // checkpoint.
|
| + void AddCheckpoint();
|
|
|
| - // Roll back the Tables to the state of the last Checkpoint(), removing
|
| - // everything that was added after that point.
|
| - void Rollback();
|
| + // Mark the last checkpoint as having cleared successfully, removing it from
|
| + // the stack. If the stack is empty, all pending symbols will be committed.
|
| + //
|
| + // Note that this does not guarantee that the symbols added since the last
|
| + // checkpoint won't be rolled back: if a checkpoint gets rolled back,
|
| + // everything past that point gets rolled back, including symbols added after
|
| + // checkpoints that were pushed onto the stack after it and marked as cleared.
|
| + void ClearLastCheckpoint();
|
|
|
| + // Roll back the Tables to the state of the checkpoint at the top of the
|
| + // stack, removing everything that was added after that point.
|
| + void RollbackToLastCheckpoint();
|
| +
|
| // The stack of files which are currently being built. Used to detect
|
| // cyclic dependencies when loading files from a DescriptorDatabase. Not
|
| // used when fallback_database_ == NULL.
|
| @@ -405,10 +446,28 @@
|
| FilesByNameMap files_by_name_;
|
| ExtensionsGroupedByDescriptorMap extensions_;
|
|
|
| - int strings_before_checkpoint_;
|
| - int messages_before_checkpoint_;
|
| - int file_tables_before_checkpoint_;
|
| - int allocations_before_checkpoint_;
|
| + struct CheckPoint {
|
| + explicit CheckPoint(const Tables* tables)
|
| + : strings_before_checkpoint(tables->strings_.size()),
|
| + messages_before_checkpoint(tables->messages_.size()),
|
| + file_tables_before_checkpoint(tables->file_tables_.size()),
|
| + allocations_before_checkpoint(tables->allocations_.size()),
|
| + pending_symbols_before_checkpoint(
|
| + tables->symbols_after_checkpoint_.size()),
|
| + pending_files_before_checkpoint(
|
| + tables->files_after_checkpoint_.size()),
|
| + pending_extensions_before_checkpoint(
|
| + tables->extensions_after_checkpoint_.size()) {
|
| + }
|
| + int strings_before_checkpoint;
|
| + int messages_before_checkpoint;
|
| + int file_tables_before_checkpoint;
|
| + int allocations_before_checkpoint;
|
| + int pending_symbols_before_checkpoint;
|
| + int pending_files_before_checkpoint;
|
| + int pending_extensions_before_checkpoint;
|
| + };
|
| + vector<CheckPoint> checkpoints_;
|
| vector<const char* > symbols_after_checkpoint_;
|
| vector<const char* > files_after_checkpoint_;
|
| vector<DescriptorIntPair> extensions_after_checkpoint_;
|
| @@ -481,11 +540,15 @@
|
| };
|
|
|
| DescriptorPool::Tables::Tables()
|
| - : strings_before_checkpoint_(0),
|
| - messages_before_checkpoint_(0),
|
| - allocations_before_checkpoint_(0) {}
|
| + // Start some hash_map and hash_set objects with a small # of buckets
|
| + : known_bad_files_(3),
|
| + extensions_loaded_from_db_(3),
|
| + symbols_by_name_(3),
|
| + files_by_name_(3) {}
|
|
|
| +
|
| DescriptorPool::Tables::~Tables() {
|
| + GOOGLE_DCHECK(checkpoints_.empty());
|
| // Note that the deletion order is important, since the destructors of some
|
| // messages may refer to objects in allocations_.
|
| STLDeleteElements(&messages_);
|
| @@ -496,51 +559,80 @@
|
| STLDeleteElements(&file_tables_);
|
| }
|
|
|
| -FileDescriptorTables::FileDescriptorTables() {}
|
| +FileDescriptorTables::FileDescriptorTables()
|
| + // Initialize all the hash tables to start out with a small # of buckets
|
| + : symbols_by_parent_(3),
|
| + fields_by_lowercase_name_(3),
|
| + fields_by_camelcase_name_(3),
|
| + fields_by_number_(3),
|
| + enum_values_by_number_(3) {
|
| +}
|
| +
|
| FileDescriptorTables::~FileDescriptorTables() {}
|
|
|
| const FileDescriptorTables FileDescriptorTables::kEmpty;
|
|
|
| -void DescriptorPool::Tables::Checkpoint() {
|
| - strings_before_checkpoint_ = strings_.size();
|
| - messages_before_checkpoint_ = messages_.size();
|
| - file_tables_before_checkpoint_ = file_tables_.size();
|
| - allocations_before_checkpoint_ = allocations_.size();
|
| +void DescriptorPool::Tables::AddCheckpoint() {
|
| + checkpoints_.push_back(CheckPoint(this));
|
| +}
|
|
|
| - symbols_after_checkpoint_.clear();
|
| - files_after_checkpoint_.clear();
|
| - extensions_after_checkpoint_.clear();
|
| +void DescriptorPool::Tables::ClearLastCheckpoint() {
|
| + GOOGLE_DCHECK(!checkpoints_.empty());
|
| + checkpoints_.pop_back();
|
| + if (checkpoints_.empty()) {
|
| + // All checkpoints have been cleared: we can now commit all of the pending
|
| + // data.
|
| + symbols_after_checkpoint_.clear();
|
| + files_after_checkpoint_.clear();
|
| + extensions_after_checkpoint_.clear();
|
| + }
|
| }
|
|
|
| -void DescriptorPool::Tables::Rollback() {
|
| - for (int i = 0; i < symbols_after_checkpoint_.size(); i++) {
|
| +void DescriptorPool::Tables::RollbackToLastCheckpoint() {
|
| + GOOGLE_DCHECK(!checkpoints_.empty());
|
| + const CheckPoint& checkpoint = checkpoints_.back();
|
| +
|
| + for (int i = checkpoint.pending_symbols_before_checkpoint;
|
| + i < symbols_after_checkpoint_.size();
|
| + i++) {
|
| symbols_by_name_.erase(symbols_after_checkpoint_[i]);
|
| }
|
| - for (int i = 0; i < files_after_checkpoint_.size(); i++) {
|
| + for (int i = checkpoint.pending_files_before_checkpoint;
|
| + i < files_after_checkpoint_.size();
|
| + i++) {
|
| files_by_name_.erase(files_after_checkpoint_[i]);
|
| }
|
| - for (int i = 0; i < extensions_after_checkpoint_.size(); i++) {
|
| + for (int i = checkpoint.pending_extensions_before_checkpoint;
|
| + i < extensions_after_checkpoint_.size();
|
| + i++) {
|
| extensions_.erase(extensions_after_checkpoint_[i]);
|
| }
|
|
|
| - symbols_after_checkpoint_.clear();
|
| - files_after_checkpoint_.clear();
|
| - extensions_after_checkpoint_.clear();
|
| + symbols_after_checkpoint_.resize(
|
| + checkpoint.pending_symbols_before_checkpoint);
|
| + files_after_checkpoint_.resize(checkpoint.pending_files_before_checkpoint);
|
| + extensions_after_checkpoint_.resize(
|
| + checkpoint.pending_extensions_before_checkpoint);
|
|
|
| STLDeleteContainerPointers(
|
| - strings_.begin() + strings_before_checkpoint_, strings_.end());
|
| + strings_.begin() + checkpoint.strings_before_checkpoint, strings_.end());
|
| STLDeleteContainerPointers(
|
| - messages_.begin() + messages_before_checkpoint_, messages_.end());
|
| + messages_.begin() + checkpoint.messages_before_checkpoint,
|
| + messages_.end());
|
| STLDeleteContainerPointers(
|
| - file_tables_.begin() + file_tables_before_checkpoint_, file_tables_.end());
|
| - for (int i = allocations_before_checkpoint_; i < allocations_.size(); i++) {
|
| + file_tables_.begin() + checkpoint.file_tables_before_checkpoint,
|
| + file_tables_.end());
|
| + for (int i = checkpoint.allocations_before_checkpoint;
|
| + i < allocations_.size();
|
| + i++) {
|
| operator delete(allocations_[i]);
|
| }
|
|
|
| - strings_.resize(strings_before_checkpoint_);
|
| - messages_.resize(messages_before_checkpoint_);
|
| - file_tables_.resize(file_tables_before_checkpoint_);
|
| - allocations_.resize(allocations_before_checkpoint_);
|
| + strings_.resize(checkpoint.strings_before_checkpoint);
|
| + messages_.resize(checkpoint.messages_before_checkpoint);
|
| + file_tables_.resize(checkpoint.file_tables_before_checkpoint);
|
| + allocations_.resize(checkpoint.allocations_before_checkpoint);
|
| + checkpoints_.pop_back();
|
| }
|
|
|
| // -------------------------------------------------------------------
|
| @@ -811,7 +903,7 @@
|
| generated_pool_ = NULL;
|
| }
|
|
|
| -void InitGeneratedPool() {
|
| +static void InitGeneratedPool() {
|
| generated_database_ = new EncodedDescriptorDatabase;
|
| generated_pool_ = new DescriptorPool(generated_database_);
|
|
|
| @@ -874,11 +966,11 @@
|
| const FileDescriptor* result = tables_->FindFile(name);
|
| if (result != NULL) return result;
|
| if (underlay_ != NULL) {
|
| - const FileDescriptor* result = underlay_->FindFileByName(name);
|
| + result = underlay_->FindFileByName(name);
|
| if (result != NULL) return result;
|
| }
|
| if (TryFindFileInFallbackDatabase(name)) {
|
| - const FileDescriptor* result = tables_->FindFile(name);
|
| + result = tables_->FindFile(name);
|
| if (result != NULL) return result;
|
| }
|
| return NULL;
|
| @@ -890,12 +982,12 @@
|
| Symbol result = tables_->FindSymbol(symbol_name);
|
| if (!result.IsNull()) return result.GetFile();
|
| if (underlay_ != NULL) {
|
| - const FileDescriptor* result =
|
| + const FileDescriptor* file_result =
|
| underlay_->FindFileContainingSymbol(symbol_name);
|
| - if (result != NULL) return result;
|
| + if (file_result != NULL) return file_result;
|
| }
|
| if (TryFindSymbolInFallbackDatabase(symbol_name)) {
|
| - Symbol result = tables_->FindSymbol(symbol_name);
|
| + result = tables_->FindSymbol(symbol_name);
|
| if (!result.IsNull()) return result.GetFile();
|
| }
|
| return NULL;
|
| @@ -962,12 +1054,11 @@
|
| return result;
|
| }
|
| if (underlay_ != NULL) {
|
| - const FieldDescriptor* result =
|
| - underlay_->FindExtensionByNumber(extendee, number);
|
| + result = underlay_->FindExtensionByNumber(extendee, number);
|
| if (result != NULL) return result;
|
| }
|
| if (TryFindExtensionInFallbackDatabase(extendee, number)) {
|
| - const FieldDescriptor* result = tables_->FindExtension(extendee, number);
|
| + result = tables_->FindExtension(extendee, number);
|
| if (result != NULL) {
|
| return result;
|
| }
|
| @@ -1241,9 +1332,48 @@
|
| return true;
|
| }
|
|
|
| +bool DescriptorPool::IsSubSymbolOfBuiltType(const string& name) const {
|
| + string prefix = name;
|
| + for (;;) {
|
| + string::size_type dot_pos = prefix.find_last_of('.');
|
| + if (dot_pos == string::npos) {
|
| + break;
|
| + }
|
| + prefix = prefix.substr(0, dot_pos);
|
| + Symbol symbol = tables_->FindSymbol(prefix);
|
| + // If the symbol type is anything other than PACKAGE, then its complete
|
| + // definition is already known.
|
| + if (!symbol.IsNull() && symbol.type != Symbol::PACKAGE) {
|
| + return true;
|
| + }
|
| + }
|
| + if (underlay_ != NULL) {
|
| + // Check to see if any prefix of this symbol exists in the underlay.
|
| + return underlay_->IsSubSymbolOfBuiltType(name);
|
| + }
|
| + return false;
|
| +}
|
| +
|
| 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;
|
| +
|
| FileDescriptorProto file_proto;
|
| if (!fallback_database_->FindFileContainingSymbol(name, &file_proto)) {
|
| return false;
|
| @@ -1345,6 +1475,14 @@
|
| proto->add_dependency(dependency(i)->name());
|
| }
|
|
|
| + for (int i = 0; i < public_dependency_count(); i++) {
|
| + proto->add_public_dependency(public_dependencies_[i]);
|
| + }
|
| +
|
| + for (int i = 0; i < weak_dependency_count(); i++) {
|
| + proto->add_weak_dependency(weak_dependencies_[i]);
|
| + }
|
| +
|
| for (int i = 0; i < message_type_count(); i++) {
|
| message_type(i)->CopyTo(proto->add_message_type());
|
| }
|
| @@ -1363,6 +1501,12 @@
|
| }
|
| }
|
|
|
| +void FileDescriptor::CopySourceCodeInfoTo(FileDescriptorProto* proto) const {
|
| + if (source_code_info_ != &SourceCodeInfo::default_instance()) {
|
| + proto->mutable_source_code_info()->CopyFrom(*source_code_info_);
|
| + }
|
| +}
|
| +
|
| void Descriptor::CopyTo(DescriptorProto* proto) const {
|
| proto->set_name(name());
|
|
|
| @@ -1490,16 +1634,14 @@
|
| namespace {
|
|
|
| // Used by each of the option formatters.
|
| -bool RetrieveOptions(const Message &options, vector<string> *option_entries) {
|
| +bool RetrieveOptions(int depth,
|
| + const Message &options,
|
| + vector<string> *option_entries) {
|
| option_entries->clear();
|
| const Reflection* reflection = options.GetReflection();
|
| vector<const FieldDescriptor*> fields;
|
| reflection->ListFields(options, &fields);
|
| for (int i = 0; i < fields.size(); i++) {
|
| - // Doesn't make sense to have message type fields here
|
| - if (fields[i]->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
|
| - continue;
|
| - }
|
| int count = 1;
|
| bool repeated = false;
|
| if (fields[i]->is_repeated()) {
|
| @@ -1508,9 +1650,27 @@
|
| }
|
| for (int j = 0; j < count; j++) {
|
| string fieldval;
|
| - TextFormat::PrintFieldValueToString(options, fields[i],
|
| - repeated ? count : -1, &fieldval);
|
| - option_entries->push_back(fields[i]->name() + " = " + fieldval);
|
| + if (fields[i]->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
|
| + string tmp;
|
| + TextFormat::Printer printer;
|
| + printer.SetInitialIndentLevel(depth + 1);
|
| + printer.PrintFieldValueToString(options, fields[i],
|
| + repeated ? j : -1, &tmp);
|
| + fieldval.append("{\n");
|
| + fieldval.append(tmp);
|
| + fieldval.append(depth * 2, ' ');
|
| + fieldval.append("}");
|
| + } else {
|
| + TextFormat::PrintFieldValueToString(options, fields[i],
|
| + repeated ? j : -1, &fieldval);
|
| + }
|
| + string name;
|
| + if (fields[i]->is_extension()) {
|
| + name = "(." + fields[i]->full_name() + ")";
|
| + } else {
|
| + name = fields[i]->name();
|
| + }
|
| + option_entries->push_back(name + " = " + fieldval);
|
| }
|
| }
|
| return !option_entries->empty();
|
| @@ -1518,9 +1678,9 @@
|
|
|
| // Formats options that all appear together in brackets. Does not include
|
| // brackets.
|
| -bool FormatBracketedOptions(const Message &options, string *output) {
|
| +bool FormatBracketedOptions(int depth, const Message &options, string *output) {
|
| vector<string> all_options;
|
| - if (RetrieveOptions(options, &all_options)) {
|
| + if (RetrieveOptions(depth, options, &all_options)) {
|
| output->append(JoinStrings(all_options, ", "));
|
| }
|
| return !all_options.empty();
|
| @@ -1530,7 +1690,7 @@
|
| bool FormatLineOptions(int depth, const Message &options, string *output) {
|
| string prefix(depth * 2, ' ');
|
| vector<string> all_options;
|
| - if (RetrieveOptions(options, &all_options)) {
|
| + if (RetrieveOptions(depth, options, &all_options)) {
|
| for (int i = 0; i < all_options.size(); i++) {
|
| strings::SubstituteAndAppend(output, "$0option $1;\n",
|
| prefix, all_options[i]);
|
| @@ -1544,9 +1704,24 @@
|
| string FileDescriptor::DebugString() const {
|
| string contents = "syntax = \"proto2\";\n\n";
|
|
|
| + set<int> public_dependencies;
|
| + set<int> weak_dependencies;
|
| + public_dependencies.insert(public_dependencies_,
|
| + public_dependencies_ + public_dependency_count_);
|
| + weak_dependencies.insert(weak_dependencies_,
|
| + weak_dependencies_ + weak_dependency_count_);
|
| +
|
| for (int i = 0; i < dependency_count(); i++) {
|
| - strings::SubstituteAndAppend(&contents, "import \"$0\";\n",
|
| - dependency(i)->name());
|
| + if (public_dependencies.count(i) > 0) {
|
| + strings::SubstituteAndAppend(&contents, "import public \"$0\";\n",
|
| + dependency(i)->name());
|
| + } else if (weak_dependencies.count(i) > 0) {
|
| + strings::SubstituteAndAppend(&contents, "import weak \"$0\";\n",
|
| + dependency(i)->name());
|
| + } else {
|
| + strings::SubstituteAndAppend(&contents, "import \"$0\";\n",
|
| + dependency(i)->name());
|
| + }
|
| }
|
|
|
| if (!package().empty()) {
|
| @@ -1712,7 +1887,7 @@
|
| }
|
|
|
| string formatted_options;
|
| - if (FormatBracketedOptions(options(), &formatted_options)) {
|
| + if (FormatBracketedOptions(depth, options(), &formatted_options)) {
|
| contents->append(bracketed ? ", " : " [");
|
| bracketed = true;
|
| contents->append(formatted_options);
|
| @@ -1761,7 +1936,7 @@
|
| prefix, name(), number());
|
|
|
| string formatted_options;
|
| - if (FormatBracketedOptions(options(), &formatted_options)) {
|
| + if (FormatBracketedOptions(depth, options(), &formatted_options)) {
|
| strings::SubstituteAndAppend(contents, " [$0]", formatted_options);
|
| }
|
| contents->append(";\n");
|
| @@ -1807,6 +1982,126 @@
|
| contents->append(";\n");
|
| }
|
| }
|
| +
|
| +
|
| +// 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 (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();
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +bool FieldDescriptor::is_packed() const {
|
| + return is_packable() && (options_ != NULL) && options_->packed();
|
| +}
|
| +
|
| +bool Descriptor::GetSourceLocation(SourceLocation* out_location) const {
|
| + vector<int> path;
|
| + GetLocationPath(&path);
|
| + return file()->GetSourceLocation(path, out_location);
|
| +}
|
| +
|
| +bool FieldDescriptor::GetSourceLocation(SourceLocation* out_location) const {
|
| + vector<int> path;
|
| + GetLocationPath(&path);
|
| + return file()->GetSourceLocation(path, out_location);
|
| +}
|
| +
|
| +bool EnumDescriptor::GetSourceLocation(SourceLocation* out_location) const {
|
| + vector<int> path;
|
| + GetLocationPath(&path);
|
| + return file()->GetSourceLocation(path, out_location);
|
| +}
|
| +
|
| +bool MethodDescriptor::GetSourceLocation(SourceLocation* out_location) const {
|
| + vector<int> path;
|
| + GetLocationPath(&path);
|
| + return service()->file()->GetSourceLocation(path, out_location);
|
| +}
|
| +
|
| +bool ServiceDescriptor::GetSourceLocation(SourceLocation* out_location) const {
|
| + vector<int> path;
|
| + GetLocationPath(&path);
|
| + return file()->GetSourceLocation(path, out_location);
|
| +}
|
| +
|
| +bool EnumValueDescriptor::GetSourceLocation(
|
| + SourceLocation* out_location) const {
|
| + vector<int> path;
|
| + GetLocationPath(&path);
|
| + return type()->file()->GetSourceLocation(path, out_location);
|
| +}
|
| +
|
| +void Descriptor::GetLocationPath(vector<int>* output) const {
|
| + if (containing_type()) {
|
| + containing_type()->GetLocationPath(output);
|
| + output->push_back(DescriptorProto::kNestedTypeFieldNumber);
|
| + output->push_back(index());
|
| + } else {
|
| + output->push_back(FileDescriptorProto::kMessageTypeFieldNumber);
|
| + output->push_back(index());
|
| + }
|
| +}
|
| +
|
| +void FieldDescriptor::GetLocationPath(vector<int>* output) const {
|
| + containing_type()->GetLocationPath(output);
|
| + output->push_back(DescriptorProto::kFieldFieldNumber);
|
| + output->push_back(index());
|
| +}
|
| +
|
| +void EnumDescriptor::GetLocationPath(vector<int>* output) const {
|
| + if (containing_type()) {
|
| + containing_type()->GetLocationPath(output);
|
| + output->push_back(DescriptorProto::kEnumTypeFieldNumber);
|
| + output->push_back(index());
|
| + } else {
|
| + output->push_back(FileDescriptorProto::kEnumTypeFieldNumber);
|
| + output->push_back(index());
|
| + }
|
| +}
|
| +
|
| +void EnumValueDescriptor::GetLocationPath(vector<int>* output) const {
|
| + type()->GetLocationPath(output);
|
| + output->push_back(EnumDescriptorProto::kValueFieldNumber);
|
| + output->push_back(index());
|
| +}
|
| +
|
| +void ServiceDescriptor::GetLocationPath(vector<int>* output) const {
|
| + output->push_back(FileDescriptorProto::kServiceFieldNumber);
|
| + output->push_back(index());
|
| +}
|
| +
|
| +void MethodDescriptor::GetLocationPath(vector<int>* output) const {
|
| + service()->GetLocationPath(output);
|
| + output->push_back(ServiceDescriptorProto::kMethodFieldNumber);
|
| + output->push_back(index());
|
| +}
|
| +
|
| // ===================================================================
|
|
|
| namespace {
|
| @@ -1859,6 +2154,7 @@
|
| string filename_;
|
| FileDescriptor* file_;
|
| FileDescriptorTables* file_tables_;
|
| + set<const FileDescriptor*> dependencies_;
|
|
|
| // 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
|
| @@ -1888,6 +2184,10 @@
|
| // nested package within package_name.
|
| bool IsInPackage(const FileDescriptor* file, const string& package_name);
|
|
|
| + // Helper function which finds all public dependencies of the given file, and
|
| + // stores the them in the dependencies_ set in the builder.
|
| + void RecordPublicDependencies(const FileDescriptor* file);
|
| +
|
| // Like tables_->FindSymbol(), but additionally:
|
| // - Search the pool's underlay if not found in tables_.
|
| // - Insure that the resulting Symbol is from one of the file's declared
|
| @@ -1898,6 +2198,10 @@
|
| // file's declared dependencies.
|
| Symbol FindSymbolNotEnforcingDeps(const string& name);
|
|
|
| + // This implements the body of FindSymbolNotEnforcingDeps().
|
| + Symbol FindSymbolNotEnforcingDepsHelper(const DescriptorPool* pool,
|
| + const string& name);
|
| +
|
| // Like FindSymbol(), but looks up the name relative to some other symbol
|
| // name. This first searches siblings of relative_to, then siblings of its
|
| // parents, etc. For example, LookupSymbol("foo.bar", "baz.qux.corge") makes
|
| @@ -2189,6 +2493,7 @@
|
|
|
| void ValidateMapKey(FieldDescriptor* field,
|
| const FieldDescriptorProto& proto);
|
| +
|
| };
|
|
|
| const FileDescriptor* DescriptorPool::BuildFile(
|
| @@ -2275,31 +2580,48 @@
|
| file->package()[package_name.size()] == '.');
|
| }
|
|
|
| -Symbol DescriptorBuilder::FindSymbolNotEnforcingDeps(const string& name) {
|
| - Symbol result;
|
| +void DescriptorBuilder::RecordPublicDependencies(const FileDescriptor* file) {
|
| + if (file == NULL || !dependencies_.insert(file).second) return;
|
| + for (int i = 0; file != NULL && i < file->public_dependency_count(); i++) {
|
| + RecordPublicDependencies(file->public_dependency(i));
|
| + }
|
| +}
|
|
|
| - // We need to search our pool and all its underlays.
|
| - const DescriptorPool* pool = pool_;
|
| - while (true) {
|
| - // If we are looking at an underlay, we must lock its mutex_, since we are
|
| - // accessing the underlay's tables_ dircetly.
|
| - MutexLockMaybe lock((pool == pool_) ? NULL : pool->mutex_);
|
| +Symbol DescriptorBuilder::FindSymbolNotEnforcingDepsHelper(
|
| + const DescriptorPool* pool, const string& name) {
|
| + // If we are looking at an underlay, we must lock its mutex_, since we are
|
| + // accessing the underlay's tables_ directly.
|
| + MutexLockMaybe lock((pool == pool_) ? NULL : pool->mutex_);
|
|
|
| - // Note that we don't have to check fallback_database_ here because the
|
| - // symbol has to be in one of its file's direct dependencies, and we have
|
| - // already loaded those by the time we get here.
|
| - result = pool->tables_->FindSymbol(name);
|
| - if (!result.IsNull()) break;
|
| - if (pool->underlay_ == NULL) return kNullSymbol;
|
| - pool = pool->underlay_;
|
| + Symbol result = pool->tables_->FindSymbol(name);
|
| + if (result.IsNull() && pool->underlay_ != NULL) {
|
| + // Symbol not found; check the underlay.
|
| + result = FindSymbolNotEnforcingDepsHelper(pool->underlay_, name);
|
| }
|
|
|
| + if (result.IsNull()) {
|
| + // In theory, we shouldn't need to check fallback_database_ because the
|
| + // symbol should be in one of its file's direct dependencies, and we have
|
| + // already loaded those by the time we get here. But we check anyway so
|
| + // that we can generate better error message when dependencies are missing
|
| + // (i.e., "missing dependency" rather than "type is not defined").
|
| + if (pool->TryFindSymbolInFallbackDatabase(name)) {
|
| + result = pool->tables_->FindSymbol(name);
|
| + }
|
| + }
|
| +
|
| return result;
|
| }
|
|
|
| +Symbol DescriptorBuilder::FindSymbolNotEnforcingDeps(const string& name) {
|
| + return FindSymbolNotEnforcingDepsHelper(pool_, name);
|
| +}
|
| +
|
| Symbol DescriptorBuilder::FindSymbol(const string& name) {
|
| Symbol result = FindSymbolNotEnforcingDeps(name);
|
|
|
| + if (result.IsNull()) return result;
|
| +
|
| if (!pool_->enforce_dependencies_) {
|
| // Hack for CompilerUpgrader.
|
| return result;
|
| @@ -2308,10 +2630,7 @@
|
| // Only find symbols which were defined in this file or one of its
|
| // dependencies.
|
| const FileDescriptor* file = result.GetFile();
|
| - if (file == file_) return result;
|
| - for (int i = 0; i < file_->dependency_count(); i++) {
|
| - if (file == file_->dependency(i)) return result;
|
| - }
|
| + if (file == file_ || dependencies_.count(file) > 0) return result;
|
|
|
| if (result.type == Symbol::PACKAGE) {
|
| // Arg, this is overcomplicated. The symbol is a package name. It could
|
| @@ -2322,12 +2641,10 @@
|
| // dependency also defines the same package. We can't really rule out this
|
| // symbol unless none of the dependencies define it.
|
| if (IsInPackage(file_, name)) return result;
|
| - for (int i = 0; i < file_->dependency_count(); i++) {
|
| + for (set<const FileDescriptor*>::const_iterator it = dependencies_.begin();
|
| + it != dependencies_.end(); ++it) {
|
| // Note: A dependency may be NULL if it was not found or had errors.
|
| - if (file_->dependency(i) != NULL &&
|
| - IsInPackage(file_->dependency(i), name)) {
|
| - return result;
|
| - }
|
| + if (*it != NULL && IsInPackage(*it, name)) return result;
|
| }
|
| }
|
|
|
| @@ -2448,6 +2765,8 @@
|
| 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");
|
| placeholder_file->package_ = placeholder_package;
|
| @@ -2757,11 +3076,19 @@
|
| }
|
|
|
| // Checkpoint the tables so that we can roll back if something goes wrong.
|
| - tables_->Checkpoint();
|
| + tables_->AddCheckpoint();
|
|
|
| FileDescriptor* result = tables_->Allocate<FileDescriptor>();
|
| file_ = result;
|
|
|
| + if (proto.has_source_code_info()) {
|
| + SourceCodeInfo *info = tables_->AllocateMessage<SourceCodeInfo>();
|
| + info->CopyFrom(proto.source_code_info());
|
| + result->source_code_info_ = info;
|
| + } else {
|
| + result->source_code_info_ = &SourceCodeInfo::default_instance();
|
| + }
|
| +
|
| file_tables_ = tables_->AllocateFileTables();
|
| file_->tables_ = file_tables_;
|
|
|
| @@ -2788,7 +3115,7 @@
|
| "A file with this name is already in the pool.");
|
| // Bail out early so that if this is actually the exact same file, we
|
| // don't end up reporting that every single symbol is already defined.
|
| - tables_->Rollback();
|
| + tables_->RollbackToLastCheckpoint();
|
| return NULL;
|
| }
|
| if (!result->package().empty()) {
|
| @@ -2833,6 +3160,45 @@
|
| result->dependencies_[i] = dependency;
|
| }
|
|
|
| + // Check public dependencies.
|
| + int public_dependency_count = 0;
|
| + result->public_dependencies_ = tables_->AllocateArray<int>(
|
| + proto.public_dependency_size());
|
| + for (int i = 0; i < proto.public_dependency_size(); i++) {
|
| + // Only put valid public dependency indexes.
|
| + int index = proto.public_dependency(i);
|
| + if (index >= 0 && index < proto.dependency_size()) {
|
| + result->public_dependencies_[public_dependency_count++] = index;
|
| + } else {
|
| + AddError(proto.name(), proto,
|
| + DescriptorPool::ErrorCollector::OTHER,
|
| + "Invalid public dependency index.");
|
| + }
|
| + }
|
| + result->public_dependency_count_ = public_dependency_count;
|
| +
|
| + // Build dependency set
|
| + dependencies_.clear();
|
| + for (int i = 0; i < result->dependency_count(); i++) {
|
| + RecordPublicDependencies(result->dependency(i));
|
| + }
|
| +
|
| + // Check weak dependencies.
|
| + int weak_dependency_count = 0;
|
| + result->weak_dependencies_ = tables_->AllocateArray<int>(
|
| + proto.weak_dependency_size());
|
| + for (int i = 0; i < proto.weak_dependency_size(); i++) {
|
| + int index = proto.weak_dependency(i);
|
| + if (index >= 0 && index < proto.dependency_size()) {
|
| + result->weak_dependencies_[weak_dependency_count++] = index;
|
| + } else {
|
| + AddError(proto.name(), proto,
|
| + DescriptorPool::ErrorCollector::OTHER,
|
| + "Invalid weak dependency index.");
|
| + }
|
| + }
|
| + result->weak_dependency_count_ = weak_dependency_count;
|
| +
|
| // Convert children.
|
| BUILD_ARRAY(proto, result, message_type, BuildMessage , NULL);
|
| BUILD_ARRAY(proto, result, enum_type , BuildEnum , NULL);
|
| @@ -2870,10 +3236,10 @@
|
| }
|
|
|
| if (had_errors_) {
|
| - tables_->Rollback();
|
| + tables_->RollbackToLastCheckpoint();
|
| return NULL;
|
| } else {
|
| - tables_->Checkpoint();
|
| + tables_->ClearLastCheckpoint();
|
| return result;
|
| }
|
| }
|
| @@ -3065,7 +3431,7 @@
|
| UnescapeCEscapeString(proto.default_value()));
|
| } else {
|
| result->default_value_string_ =
|
| - tables_->AllocateString(proto.default_value());
|
| + tables_->AllocateString(proto.default_value());
|
| }
|
| break;
|
| case FieldDescriptor::CPPTYPE_MESSAGE:
|
| @@ -3126,7 +3492,15 @@
|
| if (result->number() <= 0) {
|
| AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER,
|
| "Field numbers must be positive integers.");
|
| - } else if (result->number() > FieldDescriptor::kMaxNumber) {
|
| + } else if (!is_extension && result->number() > FieldDescriptor::kMaxNumber) {
|
| + // Only validate that the number is within the valid field range if it is
|
| + // not an extension. Since extension numbers are validated with the
|
| + // extendee's valid set of extension numbers, and those are in turn
|
| + // validated against the max allowed number, the check is unnecessary for
|
| + // extension fields.
|
| + // This avoids cross-linking issues that arise when attempting to check if
|
| + // the extendee is a message_set_wire_format message, which has a higher max
|
| + // on extension numbers.
|
| AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER,
|
| strings::Substitute("Field numbers cannot be greater than $0.",
|
| FieldDescriptor::kMaxNumber));
|
| @@ -3181,12 +3555,10 @@
|
| "Extension numbers must be positive integers.");
|
| }
|
|
|
| - if (result->end > FieldDescriptor::kMaxNumber + 1) {
|
| - AddError(parent->full_name(), proto,
|
| - DescriptorPool::ErrorCollector::NUMBER,
|
| - strings::Substitute("Extension numbers cannot be greater than $0.",
|
| - FieldDescriptor::kMaxNumber));
|
| - }
|
| + // Checking of the upper bound of the extension range is deferred until after
|
| + // options interpreting. This allows messages with message_set_wire_format to
|
| + // have extensions beyond FieldDescriptor::kMaxNumber, since the extension
|
| + // numbers are actually used as int32s in the message_set_wire_format.
|
|
|
| if (result->start >= result->end) {
|
| AddError(parent->full_name(), proto,
|
| @@ -3681,6 +4053,20 @@
|
| VALIDATE_OPTIONS_FROM_ARRAY(message, nested_type, Message);
|
| VALIDATE_OPTIONS_FROM_ARRAY(message, enum_type, Enum);
|
| VALIDATE_OPTIONS_FROM_ARRAY(message, extension, Field);
|
| +
|
| + const int64 max_extension_range =
|
| + static_cast<int64>(message->options().message_set_wire_format() ?
|
| + kint32max :
|
| + FieldDescriptor::kMaxNumber);
|
| + for (int i = 0; i < message->extension_range_count(); ++i) {
|
| + if (message->extension_range(i)->end > max_extension_range + 1) {
|
| + AddError(
|
| + message->full_name(), proto.extension_range(i),
|
| + DescriptorPool::ErrorCollector::NUMBER,
|
| + strings::Substitute("Extension numbers cannot be greater than $0.",
|
| + max_extension_range));
|
| + }
|
| + }
|
| }
|
|
|
| void DescriptorBuilder::ValidateFieldOptions(FieldDescriptor* field,
|
| @@ -3689,6 +4075,15 @@
|
| ValidateMapKey(field, proto);
|
| }
|
|
|
| + // Only message type fields may be lazy.
|
| + if (field->options().lazy()) {
|
| + if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
|
| + AddError(field->full_name(), proto,
|
| + DescriptorPool::ErrorCollector::TYPE,
|
| + "[lazy = true] can only be specified for submessage fields.");
|
| + }
|
| + }
|
| +
|
| // Only repeated primitive fields may be packed.
|
| if (field->options().packed() && !field->is_packable()) {
|
| AddError(
|
| @@ -3727,11 +4122,27 @@
|
| "files. Note that you cannot extend a non-lite type to contain "
|
| "a lite type, but the reverse is allowed.");
|
| }
|
| +
|
| }
|
|
|
| void DescriptorBuilder::ValidateEnumOptions(EnumDescriptor* enm,
|
| const EnumDescriptorProto& proto) {
|
| VALIDATE_OPTIONS_FROM_ARRAY(enm, value, EnumValue);
|
| + if (!enm->options().allow_alias()) {
|
| + map<int, string> used_values;
|
| + for (int i = 0; i < enm->value_count(); ++i) {
|
| + const EnumValueDescriptor* enum_value = enm->value(i);
|
| + if (used_values.find(enum_value->number()) != used_values.end()) {
|
| + AddError(enm->full_name(), proto,
|
| + DescriptorPool::ErrorCollector::NUMBER,
|
| + "\"" + enum_value->full_name() +
|
| + "\" uses the same enum value as \"" +
|
| + used_values[enum_value->number()] + "\"");
|
| + } else {
|
| + used_values[enum_value->number()] = enum_value->full_name();
|
| + }
|
| + }
|
| + }
|
| }
|
|
|
| void DescriptorBuilder::ValidateEnumValueOptions(
|
| @@ -3811,6 +4222,7 @@
|
| field->experimental_map_key_ = key_field;
|
| }
|
|
|
| +
|
| #undef VALIDATE_OPTIONS_FROM_ARRAY
|
|
|
| // -------------------------------------------------------------------
|
| @@ -3901,9 +4313,11 @@
|
| // file we're currently building. The descriptor should be there as long as
|
| // the file we're building imported "google/protobuf/descriptors.proto".
|
|
|
| - // Note that we use DescriptorBuilder::FindSymbol(), not
|
| + // Note that we use DescriptorBuilder::FindSymbolNotEnforcingDeps(), not
|
| // DescriptorPool::FindMessageTypeByName() because we're already holding the
|
| - // pool's mutex, and the latter method locks it again.
|
| + // pool's mutex, and the latter method locks it again. We don't use
|
| + // FindSymbol() because files that use custom options only need to depend on
|
| + // the file that defines the option, not descriptor.proto itself.
|
| Symbol symbol = builder_->FindSymbolNotEnforcingDeps(
|
| options->GetDescriptor()->full_name());
|
| if (!symbol.IsNull() && symbol.type == Symbol::MESSAGE) {
|
| @@ -3939,8 +4353,8 @@
|
| // DescriptorPool::FindExtensionByName(), for two reasons: 1) It allows
|
| // relative lookups, and 2) because we're already holding the pool's
|
| // mutex, and the latter method locks it again.
|
| - Symbol symbol = builder_->LookupSymbol(name_part,
|
| - options_to_interpret_->name_scope);
|
| + symbol = builder_->LookupSymbol(name_part,
|
| + options_to_interpret_->name_scope);
|
| if (!symbol.IsNull() && symbol.type == Symbol::FIELD) {
|
| field = symbol.field_descriptor;
|
| }
|
| @@ -4344,14 +4758,32 @@
|
| virtual const FieldDescriptor* FindExtension(
|
| Message* message, const string& name) const {
|
| assert_mutex_held(builder_->pool_);
|
| + const Descriptor* descriptor = message->GetDescriptor();
|
| Symbol result = builder_->LookupSymbolNoPlaceholder(
|
| - name, message->GetDescriptor()->full_name());
|
| + name, descriptor->full_name());
|
| if (result.type == Symbol::FIELD &&
|
| result.field_descriptor->is_extension()) {
|
| return result.field_descriptor;
|
| - } else {
|
| - return NULL;
|
| + } else if (result.type == Symbol::MESSAGE &&
|
| + descriptor->options().message_set_wire_format()) {
|
| + const Descriptor* foreign_type = result.descriptor;
|
| + // The text format allows MessageSet items to be specified using
|
| + // the type name, rather than the extension identifier. If the symbol
|
| + // lookup returned a Message, and the enclosing Message has
|
| + // message_set_wire_format = true, then return the message set
|
| + // extension, if one exists.
|
| + for (int i = 0; i < foreign_type->extension_count(); i++) {
|
| + const FieldDescriptor* extension = foreign_type->extension(i);
|
| + if (extension->containing_type() == descriptor &&
|
| + extension->type() == FieldDescriptor::TYPE_MESSAGE &&
|
| + extension->is_optional() &&
|
| + extension->message_type() == foreign_type) {
|
| + // Found it.
|
| + return extension;
|
| + }
|
| + }
|
| }
|
| + return NULL;
|
| }
|
| };
|
|
|
| @@ -4409,7 +4841,13 @@
|
| } else {
|
| string serial;
|
| dynamic->SerializeToString(&serial); // Never fails
|
| - unknown_fields->AddLengthDelimited(option_field->number(), serial);
|
| + if (option_field->type() == FieldDescriptor::TYPE_MESSAGE) {
|
| + unknown_fields->AddLengthDelimited(option_field->number(), serial);
|
| + } else {
|
| + GOOGLE_CHECK_EQ(option_field->type(), FieldDescriptor::TYPE_GROUP);
|
| + UnknownFieldSet* group = unknown_fields->AddGroup(option_field->number());
|
| + group->ParseFromString(serial);
|
| + }
|
| return true;
|
| }
|
| }
|
|
|