LCOV - code coverage report
Current view: top level - c++tools - resolver.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 93.3 % 120 112
Test Date: 2026-10-24 16:24:59 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* C++ modules.  Experimental!  -*- c++ -*-
       2              :    Copyright (C) 2017-2026 Free Software Foundation, Inc.
       3              :    Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
       4              : 
       5              :    This file is part of GCC.
       6              : 
       7              :    GCC is free software; you can redistribute it and/or modify it
       8              :    under the terms of the GNU General Public License as published by
       9              :    the Free Software Foundation; either version 3, or (at your option)
      10              :    any later version.
      11              : 
      12              :    GCC is distributed in the hope that it will be useful, but
      13              :    WITHOUT ANY WARRANTY; without even the implied warranty of
      14              :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15              :    General Public License for more details.
      16              : 
      17              : You should have received a copy of the GNU General Public License
      18              : along with GCC; see the file COPYING3.  If not see
      19              : <http://www.gnu.org/licenses/>.  */
      20              : 
      21              : #include "config.h"
      22              : 
      23              : #include "resolver.h"
      24              : // C++
      25              : #include <algorithm>
      26              : #include <memory>
      27              : // C
      28              : #include <stdlib.h> // for malloc & free
      29              : #include <string.h> // for strlen
      30              : // OS
      31              : #include <fcntl.h>
      32              : #include <unistd.h>
      33              : #if 0 // 1 for testing no mmap
      34              : #define MAPPED_READING 0
      35              : #else
      36              : #ifdef IN_GCC
      37              : #if HAVE_MMAP_FILE && _POSIX_MAPPED_FILES > 0
      38              : #define MAPPED_READING 1
      39              : #else
      40              : #define MAPPED_READING 0
      41              : #endif
      42              : #else
      43              : #ifdef HAVE_SYS_MMAN_H
      44              : #include <sys/mman.h>
      45              : #define MAPPED_READING 1
      46              : #else
      47              : #define MAPPED_READING 0
      48              : #endif
      49              : #endif
      50              : #endif
      51              : 
      52              : #include <sys/types.h>
      53              : #include <sys/stat.h>
      54              : 
      55              : #if !defined (IN_GCC) && !MAPPED_READING
      56              : #define xmalloc(X) malloc(X)
      57              : #endif
      58              : 
      59              : #if !HOST_HAS_O_CLOEXEC
      60              : #define O_CLOEXEC 0
      61              : #endif
      62              : 
      63              : #ifndef DIR_SEPARATOR
      64              : #define DIR_SEPARATOR '/'
      65              : #endif
      66              : 
      67         5024 : module_resolver::module_resolver (bool map, bool xlate)
      68         5024 :   : default_map (map), default_translate (xlate)
      69              : {
      70         5024 : }
      71              : 
      72         9680 : module_resolver::~module_resolver ()
      73              : {
      74         4840 :   if (fd_repo >= 0)
      75          758 :     close (fd_repo);
      76         9680 : }
      77              : 
      78              : bool
      79         5021 : module_resolver::set_repo (std::string &&r, bool force)
      80              : {
      81         5021 :   if (force || repo.empty ())
      82              :     {
      83         5021 :       repo = std::move (r);
      84         5021 :       force = true;
      85              :     }
      86         5021 :   return force;
      87              : }
      88              : 
      89              : bool
      90           33 : module_resolver::add_mapping (std::string &&module, std::string &&file,
      91              :                               bool force)
      92              : {
      93           33 :   auto res = map.emplace (std::move (module), std::move (file));
      94           33 :   if (res.second)
      95              :     force = true;
      96            0 :   else if (force)
      97            0 :     res.first->second = std::move (file);
      98              : 
      99           33 :   return force;
     100              : }
     101              : 
     102              : int
     103           30 : module_resolver::read_tuple_file (int fd, char const *prefix, bool force)
     104              : {
     105           30 :   struct stat stat;
     106           30 :   if (fstat (fd, &stat) < 0)
     107            0 :     return -errno;
     108              : 
     109           30 :   if (!stat.st_size)
     110              :     return 0;
     111              : 
     112           30 :   void *buffer = nullptr;
     113              : #if MAPPED_READING
     114              :   // Just map the file, we're gonna read all of it, so no need for
     115              :   // line buffering
     116           30 :   buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
     117           30 :   if (buffer == MAP_FAILED)
     118            0 :     return -errno;
     119           30 :   struct Deleter {
     120           30 :     void operator()(void* p) const { munmap(p, size); }
     121              :     size_t size;
     122              :   };
     123           30 :   std::unique_ptr<void, Deleter> guard(buffer, Deleter{(size_t)stat.st_size});
     124              : #else
     125              :   buffer = xmalloc (stat.st_size);
     126              :   if (!buffer)
     127              :     return -errno;
     128              :   struct Deleter { void operator()(void* p) const { free(p); } };
     129              :   std::unique_ptr<void, Deleter> guard(buffer);
     130              :   if (read (fd, buffer, stat.st_size) != stat.st_size)
     131              :     return -errno;
     132              : #endif
     133              : 
     134           30 :   size_t prefix_len = prefix ? strlen (prefix) : 0;
     135           30 :   unsigned lineno = 0;
     136              : 
     137           93 :   for (char const *begin = reinterpret_cast <char const *> (buffer),
     138           30 :          *end = begin + stat.st_size, *eol;
     139           93 :        begin != end; begin = eol + 1)
     140              :     {
     141           66 :       lineno++;
     142           66 :       eol = std::find (begin, end, '\n');
     143           66 :       if (eol == end)
     144              :         // last line has no \n, ignore the line, you lose
     145              :         break;
     146              : 
     147           66 :       auto *pos = begin;
     148           66 :       bool pfx_search = prefix_len != 0;
     149              : 
     150           33 :     pfx_search:
     151           99 :       while (*pos == ' ' || *pos == '\t')
     152           15 :         pos++;
     153              : 
     154              :       auto *space = pos;
     155          489 :       while (*space != '\n' && *space != ' ' && *space != '\t')
     156          405 :         space++;
     157              : 
     158           84 :       if (pos == space)
     159              :         // at end of line, nothing here 
     160            3 :         continue;
     161              : 
     162           81 :       if (pfx_search)
     163              :         {
     164           18 :           if (size_t (space - pos) == prefix_len
     165           18 :               && std::equal (pos, space, prefix))
     166              :             pfx_search = false;
     167           18 :           pos = space;
     168           18 :           goto pfx_search;
     169              :         }
     170              : 
     171          123 :       std::string module (pos, space);
     172          213 :       while (*space == ' ' || *space == '\t')
     173           87 :         space++;
     174          123 :       std::string file (space, eol);
     175              : 
     176           63 :       if (module[0] == '$')
     177              :         {
     178           30 :           if (module == "$root")
     179           27 :             set_repo (std::move (file));
     180              :           else
     181            3 :             return lineno;
     182              :         }
     183              :       else
     184              :         {
     185           33 :           if (file.empty ())
     186            0 :             file = GetCMIName (module);
     187           33 :           add_mapping (std::move (module), std::move (file), force);
     188              :         }
     189              :     }
     190              : 
     191              :   return 0;
     192           30 : }
     193              : 
     194              : char const *
     195        30535 : module_resolver::GetCMISuffix ()
     196              : {
     197        30535 :   return "gcm";
     198              : }
     199              : 
     200              : module_resolver *
     201         5024 : module_resolver::ConnectRequest (Cody::Server *s, unsigned version,
     202              :                                  std::string &a, std::string &i)
     203              : {
     204         5024 :   if (!version || version > Cody::Version)
     205            0 :     s->ErrorResponse ("version mismatch");
     206         5024 :   else if (a != "GCC")
     207              :     // Refuse anything but GCC
     208            0 :     ErrorResponse (s, std::string ("only GCC supported"));
     209         5024 :   else if (!ident.empty () && ident != i)
     210              :     // Failed ident check
     211            0 :     ErrorResponse (s, std::string ("bad ident"));
     212              :   else
     213              :     // Success!
     214         5024 :     s->ConnectResponse ("gcc");
     215              : 
     216         5024 :   return this;
     217              : }
     218              : 
     219              : int
     220         5024 : module_resolver::ModuleRepoRequest (Cody::Server *s)
     221              : {
     222         5024 :   s->PathnameResponse (repo);
     223         5024 :   return 0;
     224              : }
     225              : 
     226              : int
     227         5852 : module_resolver::cmi_response (Cody::Server *s, std::string &module)
     228              : {
     229         5852 :   auto iter = map.find (module);
     230         5852 :   if (iter == map.end ())
     231              :     {
     232         5822 :       std::string file = default_map ? GetCMIName (module) : std::string ();
     233         5822 :       auto res = map.emplace (module, file);
     234         5822 :       iter = res.first;
     235         5822 :     }
     236              : 
     237         5852 :   if (iter->second.empty ())
     238            3 :     s->ErrorResponse ("no such module");
     239              :   else
     240         5849 :     s->PathnameResponse (iter->second);
     241              : 
     242         5852 :   return 0;
     243              : }
     244              : 
     245              : int
     246         3019 : module_resolver::ModuleExportRequest (Cody::Server *s, Cody::Flags,
     247              :                                       std::string &module)
     248              : {
     249         3019 :   return cmi_response (s, module);
     250              : }
     251              : 
     252              : int
     253         2833 : module_resolver::ModuleImportRequest (Cody::Server *s, Cody::Flags,
     254              :                                       std::string &module)
     255              : {
     256         2833 :   return cmi_response (s, module);
     257              : }
     258              : 
     259              : int
     260        25010 : module_resolver::IncludeTranslateRequest (Cody::Server *s, Cody::Flags,
     261              :                                           std::string &include)
     262              : {
     263        25010 :   auto iter = map.find (include);
     264        25010 :   if (iter == map.end () && default_translate)
     265              :     {
     266              :       // Not found, look for it
     267        24716 :       auto file = GetCMIName (include);
     268        24716 :       struct stat statbuf;
     269        24716 :       bool ok = true;
     270              : 
     271              : #if HAVE_FSTATAT
     272        24716 :       int fd_dir = AT_FDCWD;
     273        24716 :       if (!repo.empty ())
     274              :         {
     275        24716 :           if (fd_repo == -1)
     276              :             {
     277          788 :               fd_repo = open (repo.c_str (),
     278              :                               O_RDONLY | O_CLOEXEC | O_DIRECTORY);
     279          788 :               if (fd_repo < 0)
     280           15 :                 fd_repo = -2;
     281              :             }
     282        24716 :           fd_dir = fd_repo;
     283              :         }
     284              : 
     285        24716 :       if (!repo.empty () && fd_repo < 0)
     286              :         ok = false;
     287        22307 :       else if (fstatat (fd_dir, file.c_str (), &statbuf, 0) < 0
     288        22307 :                || !S_ISREG (statbuf.st_mode))
     289              :         ok = false;
     290              : #else
     291              :       auto append = repo;
     292              :       append.push_back (DIR_SEPARATOR);
     293              :       append.append (file);
     294              :       if (stat (append.c_str (), &statbuf) < 0
     295              :           || !S_ISREG (statbuf.st_mode))
     296              :         ok = false;
     297              : #endif
     298              :       if (!ok)
     299              :         // Mark as not present
     300        24675 :         file.clear ();
     301        24716 :       auto res = map.emplace (include, file);
     302        24716 :       iter = res.first;
     303        24716 :     }
     304              : 
     305        25010 :   if (iter == map.end () || iter->second.empty ())
     306        24969 :     s->BoolResponse (false);
     307              :   else
     308           41 :     s->PathnameResponse (iter->second);
     309              : 
     310        25010 :   return 0;
     311              : }
     312              : 
     313              : /* This handles a client notification to the server that a CMI has been
     314              :    produced for a module.  For this simplified server, we just accept
     315              :    the transaction and respond with "OK".  */
     316              : 
     317              : int
     318         2806 : module_resolver::ModuleCompiledRequest (Cody::Server *s, Cody::Flags,
     319              :                                       std::string &)
     320              : {
     321         2806 :   s->OKResponse();
     322         2806 :   return 0;
     323              : }
        

Generated by: LCOV version 2.5.0-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.