LCOV - code coverage report
Current view: top level - third_party/protobuf/src/google/protobuf/compiler - importer.cc (source / functions) Hit Total Coverage
Test: tmp.zDYK9MVh93 Lines: 99 145 68.3 %
Date: 2015-10-10 Functions: 25 37 67.6 %

          Line data    Source code
       1             : // Protocol Buffers - Google's data interchange format
       2             : // Copyright 2008 Google Inc.  All rights reserved.
       3             : // https://developers.google.com/protocol-buffers/
       4             : //
       5             : // Redistribution and use in source and binary forms, with or without
       6             : // modification, are permitted provided that the following conditions are
       7             : // met:
       8             : //
       9             : //     * Redistributions of source code must retain the above copyright
      10             : // notice, this list of conditions and the following disclaimer.
      11             : //     * Redistributions in binary form must reproduce the above
      12             : // copyright notice, this list of conditions and the following disclaimer
      13             : // in the documentation and/or other materials provided with the
      14             : // distribution.
      15             : //     * Neither the name of Google Inc. nor the names of its
      16             : // contributors may be used to endorse or promote products derived from
      17             : // this software without specific prior written permission.
      18             : //
      19             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      20             : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      21             : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      22             : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      23             : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      24             : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      25             : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      26             : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      27             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      28             : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      29             : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      30             : 
      31             : // Author: kenton@google.com (Kenton Varda)
      32             : //  Based on original Protocol Buffers design by
      33             : //  Sanjay Ghemawat, Jeff Dean, and others.
      34             : 
      35             : #ifdef _MSC_VER
      36             : #include <io.h>
      37             : #else
      38             : #include <unistd.h>
      39             : #endif
      40             : #include <sys/types.h>
      41             : #include <sys/stat.h>
      42             : #include <fcntl.h>
      43             : #include <errno.h>
      44             : 
      45             : #include <algorithm>
      46             : #include <memory>
      47             : #ifndef _SHARED_PTR_H
      48             : #include <google/protobuf/stubs/shared_ptr.h>
      49             : #endif
      50             : 
      51             : #include <google/protobuf/compiler/importer.h>
      52             : 
      53             : #include <google/protobuf/compiler/parser.h>
      54             : #include <google/protobuf/io/tokenizer.h>
      55             : #include <google/protobuf/io/zero_copy_stream_impl.h>
      56             : #include <google/protobuf/stubs/strutil.h>
      57             : 
      58             : namespace google {
      59             : namespace protobuf {
      60             : namespace compiler {
      61             : 
      62             : #ifdef _WIN32
      63             : #ifndef F_OK
      64             : #define F_OK 00  // not defined by MSVC for whatever reason
      65             : #endif
      66             : #include <ctype.h>
      67             : #endif
      68             : 
      69             : // Returns true if the text looks like a Windows-style absolute path, starting
      70             : // with a drive letter.  Example:  "C:\foo".  TODO(kenton):  Share this with
      71             : // copy in command_line_interface.cc?
      72             : static bool IsWindowsAbsolutePath(const string& text) {
      73             : #if defined(_WIN32) || defined(__CYGWIN__)
      74             :   return text.size() >= 3 && text[1] == ':' &&
      75             :          isalpha(text[0]) &&
      76             :          (text[2] == '/' || text[2] == '\\') &&
      77             :          text.find_last_of(':') == 1;
      78             : #else
      79             :   return false;
      80             : #endif
      81             : }
      82             : 
      83          17 : MultiFileErrorCollector::~MultiFileErrorCollector() {}
      84             : 
      85             : // This class serves two purposes:
      86             : // - It implements the ErrorCollector interface (used by Tokenizer and Parser)
      87             : //   in terms of MultiFileErrorCollector, using a particular filename.
      88             : // - It lets us check if any errors have occurred.
      89             : class SourceTreeDescriptorDatabase::SingleFileErrorCollector
      90             :     : public io::ErrorCollector {
      91             :  public:
      92          78 :   SingleFileErrorCollector(const string& filename,
      93             :                            MultiFileErrorCollector* multi_file_error_collector)
      94             :     : filename_(filename),
      95             :       multi_file_error_collector_(multi_file_error_collector),
      96         156 :       had_errors_(false) {}
      97         156 :   ~SingleFileErrorCollector() {}
      98             : 
      99             :   bool had_errors() { return had_errors_; }
     100             : 
     101             :   // implements ErrorCollector ---------------------------------------
     102           0 :   void AddError(int line, int column, const string& message) {
     103           0 :     if (multi_file_error_collector_ != NULL) {
     104           0 :       multi_file_error_collector_->AddError(filename_, line, column, message);
     105             :     }
     106           0 :     had_errors_ = true;
     107           0 :   }
     108             : 
     109             :  private:
     110             :   string filename_;
     111             :   MultiFileErrorCollector* multi_file_error_collector_;
     112             :   bool had_errors_;
     113             : };
     114             : 
     115             : // ===================================================================
     116             : 
     117          17 : SourceTreeDescriptorDatabase::SourceTreeDescriptorDatabase(
     118             :     SourceTree* source_tree)
     119             :   : source_tree_(source_tree),
     120             :     error_collector_(NULL),
     121             :     using_validation_error_collector_(false),
     122          34 :     validation_error_collector_(this) {}
     123             : 
     124          17 : SourceTreeDescriptorDatabase::~SourceTreeDescriptorDatabase() {}
     125             : 
     126          78 : bool SourceTreeDescriptorDatabase::FindFileByName(
     127             :     const string& filename, FileDescriptorProto* output) {
     128          78 :   google::protobuf::scoped_ptr<io::ZeroCopyInputStream> input(source_tree_->Open(filename));
     129          78 :   if (input == NULL) {
     130           0 :     if (error_collector_ != NULL) {
     131             :       error_collector_->AddError(filename, -1, 0,
     132           0 :                                  source_tree_->GetLastErrorMessage());
     133             :     }
     134             :     return false;
     135             :   }
     136             : 
     137             :   // Set up the tokenizer and parser.
     138         156 :   SingleFileErrorCollector file_error_collector(filename, error_collector_);
     139         156 :   io::Tokenizer tokenizer(input.get(), &file_error_collector);
     140             : 
     141         156 :   Parser parser;
     142          78 :   if (error_collector_ != NULL) {
     143          78 :     parser.RecordErrorsTo(&file_error_collector);
     144             :   }
     145          78 :   if (using_validation_error_collector_) {
     146          78 :     parser.RecordSourceLocationsTo(&source_locations_);
     147             :   }
     148             : 
     149             :   // Parse it.
     150             :   output->set_name(filename);
     151         156 :   return parser.Parse(&tokenizer, output) &&
     152          78 :          !file_error_collector.had_errors();
     153             : }
     154             : 
     155          58 : bool SourceTreeDescriptorDatabase::FindFileContainingSymbol(
     156             :     const string& symbol_name, FileDescriptorProto* output) {
     157          58 :   return false;
     158             : }
     159             : 
     160           0 : bool SourceTreeDescriptorDatabase::FindFileContainingExtension(
     161             :     const string& containing_type, int field_number,
     162             :     FileDescriptorProto* output) {
     163           0 :   return false;
     164             : }
     165             : 
     166             : // -------------------------------------------------------------------
     167             : 
     168          17 : SourceTreeDescriptorDatabase::ValidationErrorCollector::
     169             : ValidationErrorCollector(SourceTreeDescriptorDatabase* owner)
     170          34 :   : owner_(owner) {}
     171             : 
     172          17 : SourceTreeDescriptorDatabase::ValidationErrorCollector::
     173          17 : ~ValidationErrorCollector() {}
     174             : 
     175           0 : void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddError(
     176             :     const string& filename,
     177             :     const string& element_name,
     178             :     const Message* descriptor,
     179             :     ErrorLocation location,
     180             :     const string& message) {
     181           0 :   if (owner_->error_collector_ == NULL) return;
     182             : 
     183             :   int line, column;
     184           0 :   owner_->source_locations_.Find(descriptor, location, &line, &column);
     185           0 :   owner_->error_collector_->AddError(filename, line, column, message);
     186             : }
     187             : 
     188             : // ===================================================================
     189             : 
     190          17 : Importer::Importer(SourceTree* source_tree,
     191             :                    MultiFileErrorCollector* error_collector)
     192             :   : database_(source_tree),
     193          34 :     pool_(&database_, database_.GetValidationErrorCollector()) {
     194          17 :   pool_.EnforceWeakDependencies(true);
     195          17 :   database_.RecordErrorsTo(error_collector);
     196          17 : }
     197             : 
     198          17 : Importer::~Importer() {}
     199             : 
     200          57 : const FileDescriptor* Importer::Import(const string& filename) {
     201          57 :   return pool_.FindFileByName(filename);
     202             : }
     203             : 
     204          57 : void Importer::AddUnusedImportTrackFile(const string& file_name) {
     205          57 :   pool_.AddUnusedImportTrackFile(file_name);
     206          57 : }
     207             : 
     208          57 : void Importer::ClearUnusedImportTrackFiles() {
     209          57 :   pool_.ClearUnusedImportTrackFiles();
     210          57 : }
     211             : 
     212             : // ===================================================================
     213             : 
     214          17 : SourceTree::~SourceTree() {}
     215             : 
     216           0 : string SourceTree::GetLastErrorMessage() {
     217           0 :   return "File not found.";
     218             : }
     219             : 
     220          68 : DiskSourceTree::DiskSourceTree() {}
     221             : 
     222          34 : DiskSourceTree::~DiskSourceTree() {}
     223             : 
     224             : static inline char LastChar(const string& str) {
     225         306 :   return str[str.size() - 1];
     226             : }
     227             : 
     228             : // Given a path, returns an equivalent path with these changes:
     229             : // - On Windows, any backslashes are replaced with forward slashes.
     230             : // - Any instances of the directory "." are removed.
     231             : // - Any consecutive '/'s are collapsed into a single slash.
     232             : // Note that the resulting string may be empty.
     233             : //
     234             : // TODO(kenton):  It would be nice to handle "..", e.g. so that we can figure
     235             : //   out that "foo/bar.proto" is inside "baz/../foo".  However, if baz is a
     236             : //   symlink or doesn't exist, then things get complicated, and we can't
     237             : //   actually determine this without investigating the filesystem, probably
     238             : //   in non-portable ways.  So, we punt.
     239             : //
     240             : // TODO(kenton):  It would be nice to use realpath() here except that it
     241             : //   resolves symbolic links.  This could cause problems if people place
     242             : //   symbolic links in their source tree.  For example, if you executed:
     243             : //     protoc --proto_path=foo foo/bar/baz.proto
     244             : //   then if foo/bar is a symbolic link, foo/bar/baz.proto will canonicalize
     245             : //   to a path which does not appear to be under foo, and thus the compiler
     246             : //   will complain that baz.proto is not inside the --proto_path.
     247         153 : static string CanonicalizePath(string path) {
     248             : #ifdef _WIN32
     249             :   // The Win32 API accepts forward slashes as a path delimiter even though
     250             :   // backslashes are standard.  Let's avoid confusion and use only forward
     251             :   // slashes.
     252             :   if (HasPrefixString(path, "\\\\")) {
     253             :     // Avoid converting two leading backslashes.
     254             :     path = "\\\\" + StringReplace(path.substr(2), "\\", "/", true);
     255             :   } else {
     256             :     path = StringReplace(path, "\\", "/", true);
     257             :   }
     258             : #endif
     259             : 
     260             :   vector<string> canonical_parts;
     261             :   vector<string> parts = Split(
     262         306 :       path, "/", true);  // Note:  Removes empty parts.
     263        1191 :   for (int i = 0; i < parts.size(); i++) {
     264        1557 :     if (parts[i] == ".") {
     265             :       // Ignore.
     266             :     } else {
     267        1004 :       canonical_parts.push_back(parts[i]);
     268             :     }
     269             :   }
     270         153 :   string result = Join(canonical_parts, "/");
     271         306 :   if (!path.empty() && path[0] == '/') {
     272             :     // Restore leading slash.
     273           2 :     result = '/' + result;
     274             :   }
     275         306 :   if (!path.empty() && LastChar(path) == '/' &&
     276         153 :       !result.empty() && LastChar(result) != '/') {
     277             :     // Restore trailing slash.
     278             :     result += '/';
     279             :   }
     280         153 :   return result;
     281             : }
     282             : 
     283         213 : static inline bool ContainsParentReference(const string& path) {
     284         213 :   return path == ".." ||
     285        1065 :          HasPrefixString(path, "../") ||
     286        1065 :          HasSuffixString(path, "/..") ||
     287         426 :          path.find("/../") != string::npos;
     288             : }
     289             : 
     290             : // Maps a file from an old location to a new one.  Typically, old_prefix is
     291             : // a virtual path and new_prefix is its corresponding disk path.  Returns
     292             : // false if the filename did not start with old_prefix, otherwise replaces
     293             : // old_prefix with new_prefix and stores the result in *result.  Examples:
     294             : //   string result;
     295             : //   assert(ApplyMapping("foo/bar", "", "baz", &result));
     296             : //   assert(result == "baz/foo/bar");
     297             : //
     298             : //   assert(ApplyMapping("foo/bar", "foo", "baz", &result));
     299             : //   assert(result == "baz/bar");
     300             : //
     301             : //   assert(ApplyMapping("foo", "foo", "bar", &result));
     302             : //   assert(result == "bar");
     303             : //
     304             : //   assert(!ApplyMapping("foo/bar", "baz", "qux", &result));
     305             : //   assert(!ApplyMapping("foo/bar", "baz", "qux", &result));
     306             : //   assert(!ApplyMapping("foobar", "foo", "baz", &result));
     307         135 : static bool ApplyMapping(const string& filename,
     308             :                          const string& old_prefix,
     309             :                          const string& new_prefix,
     310             :                          string* result) {
     311         135 :   if (old_prefix.empty()) {
     312             :     // old_prefix matches any relative path.
     313         135 :     if (ContainsParentReference(filename)) {
     314             :       // We do not allow the file name to use "..".
     315             :       return false;
     316             :     }
     317         270 :     if (HasPrefixString(filename, "/") ||
     318         135 :         IsWindowsAbsolutePath(filename)) {
     319             :       // This is an absolute path, so it isn't matched by the empty string.
     320             :       return false;
     321             :     }
     322         135 :     result->assign(new_prefix);
     323         135 :     if (!result->empty()) result->push_back('/');
     324         135 :     result->append(filename);
     325         135 :     return true;
     326           0 :   } else if (HasPrefixString(filename, old_prefix)) {
     327             :     // old_prefix is a prefix of the filename.  Is it the whole filename?
     328           0 :     if (filename.size() == old_prefix.size()) {
     329             :       // Yep, it's an exact match.
     330             :       *result = new_prefix;
     331           0 :       return true;
     332             :     } else {
     333             :       // Not an exact match.  Is the next character a '/'?  Otherwise,
     334             :       // this isn't actually a match at all.  E.g. the prefix "foo/bar"
     335             :       // does not match the filename "foo/barbaz".
     336           0 :       int after_prefix_start = -1;
     337           0 :       if (filename[old_prefix.size()] == '/') {
     338           0 :         after_prefix_start = old_prefix.size() + 1;
     339           0 :       } else if (filename[old_prefix.size() - 1] == '/') {
     340             :         // old_prefix is never empty, and canonicalized paths never have
     341             :         // consecutive '/' characters.
     342           0 :         after_prefix_start = old_prefix.size();
     343             :       }
     344           0 :       if (after_prefix_start != -1) {
     345             :         // Yep.  So the prefixes are directories and the filename is a file
     346             :         // inside them.
     347           0 :         string after_prefix = filename.substr(after_prefix_start);
     348           0 :         if (ContainsParentReference(after_prefix)) {
     349             :           // We do not allow the file name to use "..".
     350             :           return false;
     351             :         }
     352           0 :         result->assign(new_prefix);
     353           0 :         if (!result->empty()) result->push_back('/');
     354           0 :         result->append(after_prefix);
     355             :         return true;
     356             :       }
     357             :     }
     358             :   }
     359             : 
     360             :   return false;
     361             : }
     362             : 
     363          18 : void DiskSourceTree::MapPath(const string& virtual_path,
     364             :                              const string& disk_path) {
     365          54 :   mappings_.push_back(Mapping(virtual_path, CanonicalizePath(disk_path)));
     366          18 : }
     367             : 
     368             : DiskSourceTree::DiskFileToVirtualFileResult
     369          57 : DiskSourceTree::DiskFileToVirtualFile(
     370             :     const string& disk_file,
     371             :     string* virtual_file,
     372             :     string* shadowing_disk_file) {
     373          57 :   int mapping_index = -1;
     374         114 :   string canonical_disk_file = CanonicalizePath(disk_file);
     375             : 
     376         228 :   for (int i = 0; i < mappings_.size(); i++) {
     377             :     // Apply the mapping in reverse.
     378         114 :     if (ApplyMapping(canonical_disk_file, mappings_[i].disk_path,
     379         285 :                      mappings_[i].virtual_path, virtual_file)) {
     380             :       // Success.
     381             :       mapping_index = i;
     382             :       break;
     383             :     }
     384             :   }
     385             : 
     386          57 :   if (mapping_index == -1) {
     387             :     return NO_MAPPING;
     388             :   }
     389             : 
     390             :   // Iterate through all mappings with higher precedence and verify that none
     391             :   // of them map this file to some other existing file.
     392           0 :   for (int i = 0; i < mapping_index; i++) {
     393           0 :     if (ApplyMapping(*virtual_file, mappings_[i].virtual_path,
     394           0 :                      mappings_[i].disk_path, shadowing_disk_file)) {
     395           0 :       if (access(shadowing_disk_file->c_str(), F_OK) >= 0) {
     396             :         // File exists.
     397             :         return SHADOWED;
     398             :       }
     399             :     }
     400             :   }
     401             :   shadowing_disk_file->clear();
     402             : 
     403             :   // Verify that we can open the file.  Note that this also has the side-effect
     404             :   // of verifying that we are not canonicalizing away any non-existent
     405             :   // directories.
     406          57 :   google::protobuf::scoped_ptr<io::ZeroCopyInputStream> stream(OpenDiskFile(disk_file));
     407          57 :   if (stream == NULL) {
     408             :     return CANNOT_OPEN;
     409             :   }
     410             : 
     411          57 :   return SUCCESS;
     412             : }
     413             : 
     414           0 : bool DiskSourceTree::VirtualFileToDiskFile(const string& virtual_file,
     415             :                                            string* disk_file) {
     416             :   google::protobuf::scoped_ptr<io::ZeroCopyInputStream> stream(
     417           0 :       OpenVirtualFile(virtual_file, disk_file));
     418           0 :   return stream != NULL;
     419             : }
     420             : 
     421          78 : io::ZeroCopyInputStream* DiskSourceTree::Open(const string& filename) {
     422          78 :   return OpenVirtualFile(filename, NULL);
     423             : }
     424             : 
     425           0 : string DiskSourceTree::GetLastErrorMessage() {
     426           0 :   return last_error_message_;
     427             : }
     428             : 
     429          78 : io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile(
     430             :     const string& virtual_file,
     431             :     string* disk_file) {
     432         390 :   if (virtual_file != CanonicalizePath(virtual_file) ||
     433          78 :       ContainsParentReference(virtual_file)) {
     434             :     // We do not allow importing of paths containing things like ".." or
     435             :     // consecutive slashes since the compiler expects files to be uniquely
     436             :     // identified by file name.
     437           0 :     last_error_message_ = "Backslashes, consecutive slashes, \".\", or \"..\" "
     438             :                           "are not allowed in the virtual path";
     439           0 :     return NULL;
     440             :   }
     441             : 
     442         312 :   for (int i = 0; i < mappings_.size(); i++) {
     443             :     string temp_disk_file;
     444         156 :     if (ApplyMapping(virtual_file, mappings_[i].virtual_path,
     445         390 :                      mappings_[i].disk_path, &temp_disk_file)) {
     446          78 :       io::ZeroCopyInputStream* stream = OpenDiskFile(temp_disk_file);
     447          78 :       if (stream != NULL) {
     448          78 :         if (disk_file != NULL) {
     449             :           *disk_file = temp_disk_file;
     450             :         }
     451          78 :         return stream;
     452             :       }
     453             : 
     454           0 :       if (errno == EACCES) {
     455             :         // The file exists but is not readable.
     456           0 :         last_error_message_ = "Read access is denied for file: " +
     457             :                               temp_disk_file;
     458           0 :         return NULL;
     459             :       }
     460             :     }
     461             :   }
     462           0 :   last_error_message_ = "File not found.";
     463           0 :   return NULL;
     464             : }
     465             : 
     466         135 : io::ZeroCopyInputStream* DiskSourceTree::OpenDiskFile(
     467             :     const string& filename) {
     468             :   int file_descriptor;
     469         135 :   do {
     470         270 :     file_descriptor = open(filename.c_str(), O_RDONLY);
     471           0 :   } while (file_descriptor < 0 && errno == EINTR);
     472         135 :   if (file_descriptor >= 0) {
     473         135 :     io::FileInputStream* result = new io::FileInputStream(file_descriptor);
     474             :     result->SetCloseOnDelete(true);
     475         135 :     return result;
     476             :   } else {
     477             :     return NULL;
     478             :   }
     479             : }
     480             : 
     481             : }  // namespace compiler
     482             : }  // namespace protobuf
     483             : }  // namespace google

Generated by: LCOV version 1.10