diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 1f549f93554..fa86b494eb2 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -993,7 +993,7 @@ class Parser : public ParserState { std::string file_identifier_; std::string file_extension_; - std::map included_files_; + std::map included_files_; std::map> files_included_per_file_; std::vector native_included_files_; diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 7cdddaffd7e..36e00301e0b 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -3153,13 +3153,29 @@ CheckedError Parser::ParseRoot(const char *source, const char **include_paths, return NoError(); } +// Generate a unique hash for a file based on its name and contents (if any). +static uint64_t HashFile(const char *source_filename, const char *source) { + auto stripped_source_fillename = StripPath(source_filename); + auto hash = HashFnv1a(stripped_source_fillename.c_str()); + if (source && *source) hash ^= HashFnv1a(source); + + return hash; +} + CheckedError Parser::DoParse(const char *source, const char **include_paths, const char *source_filename, const char *include_filename) { + uint64_t source_hash = 0; if (source_filename) { - if (included_files_.find(source_filename) == included_files_.end()) { - included_files_[source_filename] = - include_filename ? include_filename : ""; + // If the file is in-memory, don't include its contents in the hash as we + // won't be able to load them later. + if (FileExists(source_filename)) + source_hash = HashFile(source_filename, source); + else + source_hash = HashFile(source_filename, nullptr); + + if (included_files_.find(source_hash) == included_files_.end()) { + included_files_[source_hash] = include_filename ? include_filename : ""; files_included_per_file_[source_filename] = std::set(); } else { return NoError(); @@ -3210,12 +3226,14 @@ CheckedError Parser::DoParse(const char *source, const char **include_paths, return Error("unable to locate include file: " + name); if (source_filename) files_included_per_file_[source_filename].insert(filepath); - if (included_files_.find(filepath) == included_files_.end()) { + + std::string contents; + bool file_loaded = LoadFile(filepath.c_str(), true, &contents); + if (included_files_.find(HashFile(filepath.c_str(), contents.c_str())) == + included_files_.end()) { // We found an include file that we have not parsed yet. - // Load it and parse it. - std::string contents; - if (!LoadFile(filepath.c_str(), true, &contents)) - return Error("unable to load include file: " + name); + // Parse it. + if (!file_loaded) return Error("unable to load include file: " + name); ECHECK(DoParse(contents.c_str(), include_paths, filepath.c_str(), name.c_str())); // We generally do not want to output code for any included files: @@ -3232,7 +3250,7 @@ CheckedError Parser::DoParse(const char *source, const char **include_paths, // entered into included_files_. // This is recursive, but only go as deep as the number of include // statements. - if (source_filename) { included_files_.erase(source_filename); } + included_files_.erase(source_hash); return DoParse(source, include_paths, source_filename, include_filename); }