From d36c0c538a545fac5d9db6ba65c525246d4efa95 Mon Sep 17 00:00:00 2001 From: Feng Xiao Date: Wed, 29 Mar 2017 14:32:48 -0700 Subject: Down-integrate from google3. --- src/google/protobuf/descriptor.cc | 679 +++++++++++++++++++++++++++----------- 1 file changed, 487 insertions(+), 192 deletions(-) (limited to 'src/google/protobuf/descriptor.cc') diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index c87327ce..6a807926 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -72,6 +72,79 @@ namespace google { namespace protobuf { +struct Symbol { + enum Type { + 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; + const MethodDescriptor* method_descriptor; + const FileDescriptor* package_file_descriptor; + }; + + inline Symbol() : type(NULL_SYMBOL) { descriptor = NULL; } + inline bool IsNull() const { return type == NULL_SYMBOL; } + inline bool IsType() const { return type == MESSAGE || type == ENUM; } + inline bool IsAggregate() const { + return type == MESSAGE || type == PACKAGE || type == ENUM || + type == SERVICE; + } + +#define CONSTRUCTOR(TYPE, TYPE_CONSTANT, FIELD) \ + inline explicit Symbol(const TYPE* value) { \ + type = TYPE_CONSTANT; \ + this->FIELD = value; \ + } + + 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) + CONSTRUCTOR(MethodDescriptor, METHOD, method_descriptor) + CONSTRUCTOR(FileDescriptor, PACKAGE, package_file_descriptor) +#undef CONSTRUCTOR + + const FileDescriptor* GetFile() const { + switch (type) { + 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(); + case METHOD: + return method_descriptor->service()->file(); + case PACKAGE: + return package_file_descriptor; + } + return NULL; + } +}; + const FieldDescriptor::CppType FieldDescriptor::kTypeToCppTypeMap[MAX_TYPE + 1] = { static_cast(0), // 0 is reserved for errors @@ -369,65 +442,6 @@ struct PointerStringPairHash { }; -struct Symbol { - enum Type { - 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; - const MethodDescriptor* method_descriptor; - const FileDescriptor* package_file_descriptor; - }; - - inline Symbol() : type(NULL_SYMBOL) { descriptor = NULL; } - inline bool IsNull() const { return type == NULL_SYMBOL; } - inline bool IsType() const { - return type == MESSAGE || type == ENUM; - } - inline bool IsAggregate() const { - return type == MESSAGE || type == PACKAGE - || type == ENUM || type == SERVICE; - } - -#define CONSTRUCTOR(TYPE, TYPE_CONSTANT, FIELD) \ - inline explicit Symbol(const TYPE* value) { \ - type = TYPE_CONSTANT; \ - this->FIELD = value; \ - } - - 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 ) - CONSTRUCTOR(MethodDescriptor , METHOD , method_descriptor ) - CONSTRUCTOR(FileDescriptor , PACKAGE , package_file_descriptor) -#undef CONSTRUCTOR - - const FileDescriptor* GetFile() const { - switch (type) { - 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(); - case METHOD : return method_descriptor ->service()->file(); - case PACKAGE : return package_file_descriptor; - } - return NULL; - } -}; - const Symbol kNullSymbol; typedef hash_map strings_; // All strings in the pool. std::vector messages_; // All messages in the pool. + std::vector + once_dynamics_; // All GoogleOnceDynamics in the pool. std::vector file_tables_; // All file tables in the pool. std::vector allocations_; // All other memory allocated in the pool. @@ -632,19 +652,20 @@ class DescriptorPool::Tables { 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()) { - } + : strings_before_checkpoint(tables->strings_.size()), + messages_before_checkpoint(tables->messages_.size()), + once_dynamics_before_checkpoint(tables->once_dynamics_.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 once_dynamics_before_checkpoint; int file_tables_before_checkpoint; int allocations_before_checkpoint; int pending_symbols_before_checkpoint; @@ -767,6 +788,7 @@ DescriptorPool::Tables::~Tables() { } STLDeleteElements(&strings_); STLDeleteElements(&file_tables_); + STLDeleteElements(&once_dynamics_); } FileDescriptorTables::FileDescriptorTables() @@ -855,6 +877,9 @@ void DescriptorPool::Tables::RollbackToLastCheckpoint() { STLDeleteContainerPointers( messages_.begin() + checkpoint.messages_before_checkpoint, messages_.end()); + STLDeleteContainerPointers( + once_dynamics_.begin() + checkpoint.once_dynamics_before_checkpoint, + once_dynamics_.end()); STLDeleteContainerPointers( file_tables_.begin() + checkpoint.file_tables_before_checkpoint, file_tables_.end()); @@ -866,6 +891,7 @@ void DescriptorPool::Tables::RollbackToLastCheckpoint() { strings_.resize(checkpoint.strings_before_checkpoint); messages_.resize(checkpoint.messages_before_checkpoint); + once_dynamics_.resize(checkpoint.once_dynamics_before_checkpoint); file_tables_.resize(checkpoint.file_tables_before_checkpoint); allocations_.resize(checkpoint.allocations_before_checkpoint); checkpoints_.pop_back(); @@ -1104,6 +1130,12 @@ string* DescriptorPool::Tables::AllocateString(const string& value) { return result; } +GoogleOnceDynamic* DescriptorPool::Tables::AllocateOnceDynamic() { + GoogleOnceDynamic* result = new GoogleOnceDynamic(); + once_dynamics_.push_back(result); + return result; +} + template Type* DescriptorPool::Tables::AllocateMessage(Type* /* dummy */) { Type* result = new Type; @@ -1157,8 +1189,10 @@ DescriptorPool::DescriptorPool() underlay_(NULL), tables_(new Tables), enforce_dependencies_(true), + lazily_build_dependencies_(false), allow_unknown_(false), - enforce_weak_(false) {} + enforce_weak_(false), + disallow_enforce_utf8_(false) {} DescriptorPool::DescriptorPool(DescriptorDatabase* fallback_database, ErrorCollector* error_collector) @@ -1168,8 +1202,10 @@ DescriptorPool::DescriptorPool(DescriptorDatabase* fallback_database, underlay_(NULL), tables_(new Tables), enforce_dependencies_(true), + lazily_build_dependencies_(false), allow_unknown_(false), - enforce_weak_(false) { + enforce_weak_(false), + disallow_enforce_utf8_(false) { } DescriptorPool::DescriptorPool(const DescriptorPool* underlay) @@ -1179,8 +1215,10 @@ DescriptorPool::DescriptorPool(const DescriptorPool* underlay) underlay_(underlay), tables_(new Tables), enforce_dependencies_(true), + lazily_build_dependencies_(false), allow_unknown_(false), - enforce_weak_(false) {} + enforce_weak_(false), + disallow_enforce_utf8_(false) {} DescriptorPool::~DescriptorPool() { if (mutex_ != NULL) delete mutex_; @@ -1225,6 +1263,7 @@ void DeleteGeneratedPool() { static void InitGeneratedPool() { generated_database_ = new EncodedDescriptorDatabase; generated_pool_ = new DescriptorPool(generated_database_); + generated_pool_->InternalSetLazilyBuildDependencies(); internal::OnShutdown(&DeleteGeneratedPool); } @@ -3023,15 +3062,16 @@ class DescriptorBuilder { // - Search the pool's underlay if not found in tables_. // - Insure that the resulting Symbol is from one of the file's declared // dependencies. - Symbol FindSymbol(const string& name); + Symbol FindSymbol(const string& name, bool build_it = true); // Like FindSymbol() but does not require that the symbol is in one of the // file's declared dependencies. - Symbol FindSymbolNotEnforcingDeps(const string& name); + Symbol FindSymbolNotEnforcingDeps(const string& name, bool build_it = true); // This implements the body of FindSymbolNotEnforcingDeps(). Symbol FindSymbolNotEnforcingDepsHelper(const DescriptorPool* pool, - const string& name); + const string& name, + bool build_it = true); // Like FindSymbol(), but looks up the name relative to some other symbol // name. This first searches siblings of relative_to, then siblings of its @@ -3047,31 +3087,21 @@ class DescriptorBuilder { // that LookupSymbol may still return a non-type symbol in LOOKUP_TYPES mode, // if it believes that's all it could refer to. The caller should always // check that it receives the type of symbol it was expecting. - enum PlaceholderType { - PLACEHOLDER_MESSAGE, - PLACEHOLDER_ENUM, - PLACEHOLDER_EXTENDABLE_MESSAGE - }; enum ResolveMode { LOOKUP_ALL, LOOKUP_TYPES }; Symbol LookupSymbol(const string& name, const string& relative_to, - PlaceholderType placeholder_type = PLACEHOLDER_MESSAGE, - ResolveMode resolve_mode = LOOKUP_ALL); + DescriptorPool::PlaceholderType placeholder_type = + DescriptorPool::PLACEHOLDER_MESSAGE, + ResolveMode resolve_mode = LOOKUP_ALL, + bool build_it = true); // Like LookupSymbol() but will not return a placeholder even if // AllowUnknownDependencies() has been used. Symbol LookupSymbolNoPlaceholder(const string& name, const string& relative_to, - ResolveMode resolve_mode = LOOKUP_ALL); - - // Creates a placeholder type suitable for return from LookupSymbol(). May - // return kNullSymbol if the name is not a valid type name. - Symbol NewPlaceholder(const string& name, PlaceholderType placeholder_type); - - // Creates a placeholder file. Never returns NULL. This is used when an - // import is not found and AllowUnknownDependencies() is enabled. - FileDescriptor* NewPlaceholderFile(const string& name); + ResolveMode resolve_mode = LOOKUP_ALL, + bool build_it = true); // Calls tables_->AddSymbol() and records an error if it fails. Returns // true if successful or false if failed, though most callers can ignore @@ -3093,10 +3123,6 @@ class DescriptorBuilder { void ValidateSymbolName(const string& name, const string& full_name, const Message& proto); - // Like ValidateSymbolName(), but the name is allowed to contain periods and - // an error is indicated by returning false (not recording the error). - bool ValidateQualifiedName(const string& name); - // Used by BUILD_ARRAY macro (below) to avoid having to have the type // specified as a macro parameter. template @@ -3495,7 +3521,7 @@ void DescriptorBuilder::RecordPublicDependencies(const FileDescriptor* file) { } Symbol DescriptorBuilder::FindSymbolNotEnforcingDepsHelper( - const DescriptorPool* pool, const string& name) { + const DescriptorPool* pool, const string& name, bool build_it) { // 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_); @@ -3507,12 +3533,14 @@ Symbol DescriptorBuilder::FindSymbolNotEnforcingDepsHelper( } 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)) { + // With lazily_build_dependencies_, a symbol lookup at cross link time is + // not guaranteed to be successful. In most cases, build_it will be false, + // which intentionally prevents us from building an import until it's + // actually needed. In some cases, like registering an extension, we want + // to build the file containing the symbol, and build_it will be set. + // Also, build_it will be true when !lazily_build_dependencies_, to provide + // better error reporting of missing dependencies. + if (build_it && pool->TryFindSymbolInFallbackDatabase(name)) { result = pool->tables_->FindSymbol(name); } } @@ -3520,17 +3548,18 @@ Symbol DescriptorBuilder::FindSymbolNotEnforcingDepsHelper( return result; } -Symbol DescriptorBuilder::FindSymbolNotEnforcingDeps(const string& name) { - return FindSymbolNotEnforcingDepsHelper(pool_, name); +Symbol DescriptorBuilder::FindSymbolNotEnforcingDeps(const string& name, + bool build_it) { + return FindSymbolNotEnforcingDepsHelper(pool_, name, build_it); } -Symbol DescriptorBuilder::FindSymbol(const string& name) { - Symbol result = FindSymbolNotEnforcingDeps(name); +Symbol DescriptorBuilder::FindSymbol(const string& name, bool build_it) { + Symbol result = FindSymbolNotEnforcingDeps(name, build_it); if (result.IsNull()) return result; if (!pool_->enforce_dependencies_) { - // Hack for CompilerUpgrader. + // Hack for CompilerUpgrader, and also used for lazily_build_dependencies_ return result; } @@ -3564,14 +3593,16 @@ Symbol DescriptorBuilder::FindSymbol(const string& name) { return kNullSymbol; } -Symbol DescriptorBuilder::LookupSymbolNoPlaceholder( - const string& name, const string& relative_to, ResolveMode resolve_mode) { +Symbol DescriptorBuilder::LookupSymbolNoPlaceholder(const string& name, + const string& relative_to, + ResolveMode resolve_mode, + bool build_it) { possible_undeclared_dependency_ = NULL; undefine_resolved_name_.clear(); - if (name.size() > 0 && name[0] == '.') { + if (!name.empty() && name[0] == '.') { // Fully-qualified name. - return FindSymbol(name.substr(1)); + return FindSymbol(name.substr(1), build_it); } // If name is something like "Foo.Bar.baz", and symbols named "Foo" are @@ -3599,7 +3630,7 @@ Symbol DescriptorBuilder::LookupSymbolNoPlaceholder( // Chop off the last component of the scope. string::size_type dot_pos = scope_to_try.find_last_of('.'); if (dot_pos == string::npos) { - return FindSymbol(name); + return FindSymbol(name, build_it); } else { scope_to_try.erase(dot_pos); } @@ -3608,7 +3639,7 @@ Symbol DescriptorBuilder::LookupSymbolNoPlaceholder( string::size_type old_size = scope_to_try.size(); scope_to_try.append(1, '.'); scope_to_try.append(first_part_of_name); - Symbol result = FindSymbol(scope_to_try); + Symbol result = FindSymbol(scope_to_try, build_it); if (!result.IsNull()) { if (first_part_of_name.size() < name.size()) { // name is a compound symbol, of which we only found the first part. @@ -3616,7 +3647,7 @@ Symbol DescriptorBuilder::LookupSymbolNoPlaceholder( if (result.IsAggregate()) { scope_to_try.append(name, first_part_of_name.size(), name.size() - first_part_of_name.size()); - result = FindSymbol(scope_to_try); + result = FindSymbol(scope_to_try, build_it); if (result.IsNull()) { undefine_resolved_name_ = scope_to_try; } @@ -3640,19 +3671,49 @@ Symbol DescriptorBuilder::LookupSymbolNoPlaceholder( Symbol DescriptorBuilder::LookupSymbol( const string& name, const string& relative_to, - PlaceholderType placeholder_type, ResolveMode resolve_mode) { - Symbol result = LookupSymbolNoPlaceholder( - name, relative_to, resolve_mode); + DescriptorPool::PlaceholderType placeholder_type, ResolveMode resolve_mode, + bool build_it) { + Symbol result = + LookupSymbolNoPlaceholder(name, relative_to, resolve_mode, build_it); if (result.IsNull() && pool_->allow_unknown_) { // Not found, but AllowUnknownDependencies() is enabled. Return a // placeholder instead. - result = NewPlaceholder(name, placeholder_type); + result = pool_->NewPlaceholderWithMutexHeld(name, placeholder_type); } return result; } -Symbol DescriptorBuilder::NewPlaceholder(const string& name, - PlaceholderType placeholder_type) { +static bool ValidateQualifiedName(const string& name) { + bool last_was_period = false; + + for (int i = 0; i < name.size(); i++) { + // I don't trust isalnum() due to locales. :( + if (('a' <= name[i] && name[i] <= 'z') || + ('A' <= name[i] && name[i] <= 'Z') || + ('0' <= name[i] && name[i] <= '9') || (name[i] == '_')) { + last_was_period = false; + } else if (name[i] == '.') { + if (last_was_period) return false; + last_was_period = true; + } else { + return false; + } + } + + return !name.empty() && !last_was_period; +} + +Symbol DescriptorPool::NewPlaceholder(const string& name, + PlaceholderType placeholder_type) const { + MutexLockMaybe lock(mutex_); + return NewPlaceholderWithMutexHeld(name, placeholder_type); +} + +Symbol DescriptorPool::NewPlaceholderWithMutexHeld( + const string& name, PlaceholderType placeholder_type) const { + if (mutex_) { + mutex_->AssertHeld(); + } // Compute names. const string* placeholder_full_name; const string* placeholder_name; @@ -3678,7 +3739,7 @@ Symbol DescriptorBuilder::NewPlaceholder(const string& name, } // Create the placeholders. - FileDescriptor* placeholder_file = NewPlaceholderFile( + FileDescriptor* placeholder_file = NewPlaceholderFileWithMutexHeld( *placeholder_full_name + ".placeholder.proto"); placeholder_file->package_ = placeholder_package; @@ -3744,19 +3805,28 @@ Symbol DescriptorBuilder::NewPlaceholder(const string& name, } } -FileDescriptor* DescriptorBuilder::NewPlaceholderFile( - const string& name) { +FileDescriptor* DescriptorPool::NewPlaceholderFile(const string& name) const { + MutexLockMaybe lock(mutex_); + return NewPlaceholderFileWithMutexHeld(name); +} + +FileDescriptor* DescriptorPool::NewPlaceholderFileWithMutexHeld( + const string& name) const { + if (mutex_) { + mutex_->AssertHeld(); + } FileDescriptor* placeholder = tables_->Allocate(); memset(placeholder, 0, sizeof(*placeholder)); placeholder->name_ = tables_->AllocateString(name); placeholder->package_ = &internal::GetEmptyString(); - placeholder->pool_ = pool_; + placeholder->pool_ = this; placeholder->options_ = &FileOptions::default_instance(); placeholder->tables_ = &FileDescriptorTables::GetEmptyInstance(); placeholder->source_code_info_ = &SourceCodeInfo::default_instance(); placeholder->is_placeholder_ = true; placeholder->syntax_ = FileDescriptor::SYNTAX_PROTO2; + placeholder->finished_building_ = true; // All other fields are zero or NULL. return placeholder; @@ -3850,27 +3920,6 @@ void DescriptorBuilder::ValidateSymbolName( } } -bool DescriptorBuilder::ValidateQualifiedName(const string& name) { - bool last_was_period = false; - - for (int i = 0; i < name.size(); i++) { - // I don't trust isalnum() due to locales. :( - if (('a' <= name[i] && name[i] <= 'z') || - ('A' <= name[i] && name[i] <= 'Z') || - ('0' <= name[i] && name[i] <= '9') || - (name[i] == '_')) { - last_was_period = false; - } else if (name[i] == '.') { - if (last_was_period) return false; - last_was_period = true; - } else { - return false; - } - } - - return !name.empty() && !last_was_period; -} - // ------------------------------------------------------------------- // This generic implementation is good for all descriptors except @@ -4014,20 +4063,22 @@ const FileDescriptor* DescriptorBuilder::BuildFile( } } - // If we have a fallback_database_, attempt to load all dependencies now, - // before checkpointing tables_. This avoids confusion with recursive - // checkpoints. - if (pool_->fallback_database_ != NULL) { - tables_->pending_files_.push_back(proto.name()); - for (int i = 0; i < proto.dependency_size(); i++) { - if (tables_->FindFile(proto.dependency(i)) == NULL && - (pool_->underlay_ == NULL || - pool_->underlay_->FindFileByName(proto.dependency(i)) == NULL)) { - // We don't care what this returns since we'll find out below anyway. - pool_->TryFindFileInFallbackDatabase(proto.dependency(i)); + // If we have a fallback_database_, and we aren't doing lazy import building, + // attempt to load all dependencies now, before checkpointing tables_. This + // avoids confusion with recursive checkpoints. + if (!pool_->lazily_build_dependencies_) { + if (pool_->fallback_database_ != NULL) { + tables_->pending_files_.push_back(proto.name()); + for (int i = 0; i < proto.dependency_size(); i++) { + if (tables_->FindFile(proto.dependency(i)) == NULL && + (pool_->underlay_ == NULL || + pool_->underlay_->FindFileByName(proto.dependency(i)) == NULL)) { + // We don't care what this returns since we'll find out below anyway. + pool_->TryFindFileInFallbackDatabase(proto.dependency(i)); + } } + tables_->pending_files_.pop_back(); } - tables_->pending_files_.pop_back(); } return BuildFileImpl(proto); } @@ -4041,6 +4092,7 @@ const FileDescriptor* DescriptorBuilder::BuildFileImpl( file_ = result; result->is_placeholder_ = false; + result->finished_building_ = false; if (proto.has_source_code_info()) { SourceCodeInfo *info = tables_->AllocateMessage(); info->CopyFrom(proto.source_code_info()); @@ -4098,7 +4150,17 @@ const FileDescriptor* DescriptorBuilder::BuildFileImpl( std::set seen_dependencies; result->dependency_count_ = proto.dependency_size(); result->dependencies_ = - tables_->AllocateArray(proto.dependency_size()); + tables_->AllocateArray(proto.dependency_size()); + if (pool_->lazily_build_dependencies_) { + result->dependencies_once_ = tables_->AllocateOnceDynamic(); + result->dependencies_names_ = + tables_->AllocateArray(proto.dependency_size()); + memset(result->dependencies_names_, 0, + sizeof(*result->dependencies_names_) * proto.dependency_size()); + } else { + result->dependencies_once_ = NULL; + result->dependencies_names_ = NULL; + } unused_dependency_.clear(); std::set weak_deps; for (int i = 0; i < proto.weak_dependency_size(); ++i) { @@ -4125,9 +4187,12 @@ const FileDescriptor* DescriptorBuilder::BuildFileImpl( if (dependency == NULL) { if (pool_->allow_unknown_ || (!pool_->enforce_weak_ && weak_deps.find(i) != weak_deps.end())) { - dependency = NewPlaceholderFile(proto.dependency(i)); + dependency = + pool_->NewPlaceholderFileWithMutexHeld(proto.dependency(i)); } else { - AddImportError(proto, i); + if (!pool_->lazily_build_dependencies_) { + AddImportError(proto, i); + } } } else { // Add to unused_dependency_ to track unused imported files. @@ -4141,6 +4206,10 @@ const FileDescriptor* DescriptorBuilder::BuildFileImpl( } result->dependencies_[i] = dependency; + if (pool_->lazily_build_dependencies_ && !dependency) { + result->dependencies_names_[i] = + tables_->AllocateString(proto.dependency(i)); + } } // Check public dependencies. @@ -4153,7 +4222,12 @@ const FileDescriptor* DescriptorBuilder::BuildFileImpl( 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)); + // Calling dependency(i) builds that file when doing lazy imports, + // need to avoid doing this. Unused dependency detection isn't done + // when building lazily, anyways. + if (!pool_->lazily_build_dependencies_) { + unused_dependency_.erase(result->dependency(index)); + } } else { AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, @@ -4164,8 +4238,13 @@ const FileDescriptor* DescriptorBuilder::BuildFileImpl( // Build dependency set dependencies_.clear(); - for (int i = 0; i < result->dependency_count(); i++) { - RecordPublicDependencies(result->dependency(i)); + // We don't/can't do proper dependency error checking when + // lazily_build_dependencies_, and calling dependency(i) will force + // a dependency to be built, which we don't want. + if (!pool_->lazily_build_dependencies_) { + for (int i = 0; i < result->dependency_count(); i++) { + RecordPublicDependencies(result->dependency(i)); + } } // Check weak dependencies. @@ -4215,8 +4294,9 @@ const FileDescriptor* DescriptorBuilder::BuildFileImpl( options_to_interpret_.clear(); } - // Validate options. - if (!had_errors_) { + // Validate options. See comments at InternalSetLazilyBuildDependencies about + // error checking and lazy import building. + if (!had_errors_ && !pool_->lazily_build_dependencies_) { ValidateFileOptions(result, proto); } @@ -4229,7 +4309,9 @@ const FileDescriptor* DescriptorBuilder::BuildFileImpl( } - if (!unused_dependency_.empty()) { + // Again, see comments at InternalSetLazilyBuildDependencies about error + // checking. + if (!unused_dependency_.empty() && !pool_->lazily_build_dependencies_) { LogUnusedDependency(proto, result); } @@ -4238,6 +4320,7 @@ const FileDescriptor* DescriptorBuilder::BuildFileImpl( return NULL; } else { tables_->ClearLastCheckpoint(); + result->finished_building_ = true; return result; } } @@ -4447,6 +4530,10 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, result->extension_scope_ = NULL; result->message_type_ = NULL; result->enum_type_ = NULL; + result->type_name_ = NULL; + result->type_once_ = NULL; + result->default_value_enum_ = NULL; + result->default_value_enum_name_ = NULL; result->has_default_value_ = proto.has_default_value(); if (proto.has_default_value() && result->is_repeated()) { @@ -4947,8 +5034,8 @@ void DescriptorBuilder::BuildMethod(const MethodDescriptorProto& proto, ValidateSymbolName(proto.name(), *full_name, proto); // These will be filled in when cross-linking. - result->input_type_ = NULL; - result->output_type_ = NULL; + result->input_type_.Init(); + result->output_type_.Init(); // Copy options. if (!proto.has_options()) { @@ -5081,9 +5168,13 @@ void DescriptorBuilder::CrossLinkField( field->options_ = &FieldOptions::default_instance(); } + // Add the field to the lowercase-name and camelcase-name tables. + file_tables_->AddFieldByStylizedNames(field); + if (proto.has_extendee()) { - Symbol extendee = LookupSymbol(proto.extendee(), field->full_name(), - PLACEHOLDER_EXTENDABLE_MESSAGE); + Symbol extendee = + LookupSymbol(proto.extendee(), field->full_name(), + DescriptorPool::PLACEHOLDER_EXTENDABLE_MESSAGE); if (extendee.IsNull()) { AddNotDefinedError(field->full_name(), proto, DescriptorPool::ErrorCollector::EXTENDEE, @@ -5129,9 +5220,10 @@ void DescriptorBuilder::CrossLinkField( proto.has_default_value(); Symbol type = - LookupSymbol(proto.type_name(), field->full_name(), - expecting_enum ? PLACEHOLDER_ENUM : PLACEHOLDER_MESSAGE, - LOOKUP_TYPES); + LookupSymbol(proto.type_name(), field->full_name(), + expecting_enum ? DescriptorPool::PLACEHOLDER_ENUM + : DescriptorPool::PLACEHOLDER_MESSAGE, + LOOKUP_TYPES, !pool_->lazily_build_dependencies_); // 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()) { @@ -5139,10 +5231,35 @@ void DescriptorBuilder::CrossLinkField( } if (type.IsNull()) { - AddNotDefinedError(field->full_name(), proto, - DescriptorPool::ErrorCollector::TYPE, - proto.type_name()); - return; + if (pool_->lazily_build_dependencies_) { + // Save the symbol names for later for lookup, and allocate the once + // object needed for the accessors. + string name = proto.type_name(); + if (!pool_->enforce_weak_ && proto.options().weak()) { + name = kNonLinkedWeakMessageReplacementName; + } + field->type_once_ = tables_->AllocateOnceDynamic(); + field->type_name_ = tables_->AllocateString(name); + if (proto.has_default_value()) { + field->default_value_enum_name_ = + tables_->AllocateString(proto.default_value()); + } + // AddFieldByNumber and AddExtension are done later in this function, + // and can/must be done if the field type was not found. The related + // error checking is not necessary when in lazily_build_dependencies_ + // mode, and can't be done without building the type's descriptor, + // which we don't want to do. + file_tables_->AddFieldByNumber(field); + if (field->is_extension()) { + tables_->AddExtension(field); + } + return; + } else { + AddNotDefinedError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + proto.type_name()); + return; + } } if (!proto.has_type()) { @@ -5238,7 +5355,10 @@ void DescriptorBuilder::CrossLinkField( // Add the field to the fields-by-number table. // Note: We have to do this *after* cross-linking because extensions do not - // know their containing type until now. + // know their containing type until now. If we're in + // lazily_build_dependencies_ mode, we're guaranteed there's no errors, so no + // risk to calling containing_type() or other accessors that will build + // dependencies. if (!file_tables_->AddFieldByNumber(field)) { const FieldDescriptor* conflicting_field = file_tables_->FindFieldByNumber(field->containing_type(), @@ -5284,9 +5404,6 @@ void DescriptorBuilder::CrossLinkField( } } } - - // Add the field to the lowercase-name and camelcase-name tables. - file_tables_->AddFieldByStylizedNames(field); } void DescriptorBuilder::CrossLinkEnum( @@ -5325,30 +5442,44 @@ void DescriptorBuilder::CrossLinkMethod( method->options_ = &MethodOptions::default_instance(); } - Symbol input_type = LookupSymbol(proto.input_type(), method->full_name()); + Symbol input_type = + LookupSymbol(proto.input_type(), method->full_name(), + DescriptorPool::PLACEHOLDER_MESSAGE, LOOKUP_ALL, + !pool_->lazily_build_dependencies_); if (input_type.IsNull()) { - AddNotDefinedError(method->full_name(), proto, - DescriptorPool::ErrorCollector::INPUT_TYPE, - proto.input_type()); + if (!pool_->lazily_build_dependencies_) { + AddNotDefinedError(method->full_name(), proto, + DescriptorPool::ErrorCollector::INPUT_TYPE, + proto.input_type()); + } else { + method->input_type_.SetLazy(proto.input_type(), file_); + } } else if (input_type.type != Symbol::MESSAGE) { AddError(method->full_name(), proto, DescriptorPool::ErrorCollector::INPUT_TYPE, "\"" + proto.input_type() + "\" is not a message type."); } else { - method->input_type_ = input_type.descriptor; + method->input_type_.Set(input_type.descriptor); } - Symbol output_type = LookupSymbol(proto.output_type(), method->full_name()); + Symbol output_type = + LookupSymbol(proto.output_type(), method->full_name(), + DescriptorPool::PLACEHOLDER_MESSAGE, LOOKUP_ALL, + !pool_->lazily_build_dependencies_); if (output_type.IsNull()) { - AddNotDefinedError(method->full_name(), proto, - DescriptorPool::ErrorCollector::OUTPUT_TYPE, - proto.output_type()); + if (!pool_->lazily_build_dependencies_) { + AddNotDefinedError(method->full_name(), proto, + DescriptorPool::ErrorCollector::OUTPUT_TYPE, + proto.output_type()); + } else { + method->output_type_.SetLazy(proto.output_type(), file_); + } } else if (output_type.type != Symbol::MESSAGE) { AddError(method->full_name(), proto, DescriptorPool::ErrorCollector::OUTPUT_TYPE, "\"" + proto.output_type() + "\" is not a message type."); } else { - method->output_type_ = output_type.descriptor; + method->output_type_.Set(output_type.descriptor); } } @@ -5539,8 +5670,12 @@ void DescriptorBuilder::ValidateMessageOptions(Descriptor* message, } } + void DescriptorBuilder::ValidateFieldOptions(FieldDescriptor* field, const FieldDescriptorProto& proto) { + if (pool_->lazily_build_dependencies_ && (!field || !field->message_type())) { + return; + } // Only message type fields may be lazy. if (field->options().lazy()) { if (field->type() != FieldDescriptor::TYPE_MESSAGE) { @@ -6553,5 +6688,165 @@ void DescriptorBuilder::LogUnusedDependency(const FileDescriptorProto& proto, } } +Symbol DescriptorPool::CrossLinkOnDemandHelper(const string& name, + bool expecting_enum) const { + string lookup_name = name; + if (!lookup_name.empty() && lookup_name[0] == '.') { + lookup_name = lookup_name.substr(1); + } + Symbol result = tables_->FindByNameHelper(this, lookup_name); + return result; +} + +// Handle the lazy import building for a message field whose type wasn't built +// at cross link time. If that was the case, we saved the name of the type to +// be looked up when the accessor for the type was called. Set type_, +// enum_type_, message_type_, and default_value_enum_ appropriately. +void FieldDescriptor::InternalTypeOnceInit() const { + GOOGLE_CHECK(file()->finished_building_ == true); + if (type_name_) { + Symbol result = file()->pool()->CrossLinkOnDemandHelper( + *type_name_, type_ == FieldDescriptor::TYPE_ENUM); + if (result.type == Symbol::MESSAGE) { + type_ = FieldDescriptor::TYPE_MESSAGE; + message_type_ = result.descriptor; + } else if (result.type == Symbol::ENUM) { + type_ = FieldDescriptor::TYPE_ENUM; + enum_type_ = result.enum_descriptor; + } + } + if (enum_type_ && !default_value_enum_) { + if (default_value_enum_name_) { + // Have to build the full name now instead of at CrossLink time, + // because enum_type_ may not be known at the time. + string name = enum_type_->full_name(); + // Enum values reside in the same scope as the enum type. + string::size_type last_dot = name.find_last_of('.'); + if (last_dot != string::npos) { + name = name.substr(0, last_dot) + "." + *default_value_enum_name_; + } else { + name = *default_value_enum_name_; + } + Symbol result = file()->pool()->CrossLinkOnDemandHelper(name, true); + if (result.type == Symbol::ENUM_VALUE) { + default_value_enum_ = result.enum_value_descriptor; + } + } + if (!default_value_enum_) { + // We use the first defined value as the default + // if a default is not explicitly defined. + GOOGLE_CHECK(enum_type_->value_count()); + default_value_enum_ = enum_type_->value(0); + } + } +} + +void FieldDescriptor::TypeOnceInit(const FieldDescriptor* to_init) { + to_init->InternalTypeOnceInit(); +} + +// message_type(), enum_type(), default_value_enum(), and type() +// all share the same GoogleOnceDynamic init path to do lazy +// import building and cross linking of a field of a message. +const Descriptor* FieldDescriptor::message_type() const { + if (type_once_) { + type_once_->Init(&FieldDescriptor::TypeOnceInit, this); + } + return message_type_; +} + +const EnumDescriptor* FieldDescriptor::enum_type() const { + if (type_once_) { + type_once_->Init(&FieldDescriptor::TypeOnceInit, this); + } + return enum_type_; +} + +const EnumValueDescriptor* FieldDescriptor::default_value_enum() const { + if (type_once_) { + type_once_->Init(&FieldDescriptor::TypeOnceInit, this); + } + return default_value_enum_; +} + +FieldDescriptor::Type FieldDescriptor::type() const { + if (type_once_) { + type_once_->Init(&FieldDescriptor::TypeOnceInit, this); + } + return type_; +} + +void FileDescriptor::InternalDependenciesOnceInit() const { + GOOGLE_CHECK(finished_building_ == true); + for (int i = 0; i < dependency_count(); i++) { + if (dependencies_names_[i]) { + dependencies_[i] = pool_->FindFileByName(*dependencies_names_[i]); + } + } +} + +void FileDescriptor::DependenciesOnceInit(const FileDescriptor* to_init) { + to_init->InternalDependenciesOnceInit(); +} + +const FileDescriptor* FileDescriptor::dependency(int index) const { + if (dependencies_once_) { + // Do once init for all indicies, as it's unlikely only a single index would + // be called, and saves on GoogleOnceDynamic allocations. + dependencies_once_->Init(&FileDescriptor::DependenciesOnceInit, this); + } + return dependencies_[index]; +} + +const Descriptor* MethodDescriptor::input_type() const { + return input_type_.Get(); +} + +const Descriptor* MethodDescriptor::output_type() const { + return output_type_.Get(); +} + + +namespace internal { +void LazyDescriptor::Set(const Descriptor* descriptor) { + GOOGLE_CHECK(!name_); + GOOGLE_CHECK(!once_); + GOOGLE_CHECK(!file_); + descriptor_ = descriptor; +} + +void LazyDescriptor::SetLazy(const string& name, const FileDescriptor* file) { + // verify Init() has been called and Set hasn't been called yet. + GOOGLE_CHECK(!descriptor_); + GOOGLE_CHECK(!file_); + GOOGLE_CHECK(!name_); + GOOGLE_CHECK(!once_); + GOOGLE_CHECK(file && file->pool_); + GOOGLE_CHECK(file->pool_->lazily_build_dependencies_); + GOOGLE_CHECK(!file->finished_building_); + file_ = file; + name_ = file->pool_->tables_->AllocateString(name); + once_ = file->pool_->tables_->AllocateOnceDynamic(); +} + +void LazyDescriptor::Once() { + if (once_) { + once_->Init(&LazyDescriptor::OnceStatic, this); + } +} + +void LazyDescriptor::OnceStatic(LazyDescriptor* lazy) { lazy->OnceInternal(); } + +void LazyDescriptor::OnceInternal() { + GOOGLE_CHECK(file_->finished_building_); + if (!descriptor_ && name_) { + Symbol result = file_->pool_->CrossLinkOnDemandHelper(*name_, false); + if (!result.IsNull() && result.type == Symbol::MESSAGE) { + descriptor_ = result.descriptor; + } + } +} +} // namespace internal + } // namespace protobuf } // namespace google -- cgit v1.2.3