xref: /netbsd-src/external/gpl3/gcc/dist/c++tools/resolver.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /* C++ modules.  Experimental!	-*- c++ -*-
2    Copyright (C) 2017-2022 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 <cstring>
29 // OS
30 #include <fcntl.h>
31 #include <unistd.h>
32 #if 0 // 1 for testing no mmap
33 #define MAPPED_READING 0
34 #else
35 #ifdef IN_GCC
36 #if HAVE_MMAP_FILE && _POSIX_MAPPED_FILES > 0
37 #define MAPPED_READING 1
38 #else
39 #define MAPPED_READING 0
40 #endif
41 #else
42 #ifdef HAVE_SYS_MMAN_H
43 #include <sys/mman.h>
44 #define MAPPED_READING 1
45 #else
46 #define MAPPED_READING 0
47 #endif
48 #endif
49 #endif
50 
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 
54 #if !defined (IN_GCC) && !MAPPED_READING
55 #define xmalloc(X) malloc(X)
56 #endif
57 
58 #if !HOST_HAS_O_CLOEXEC
59 #define O_CLOEXEC 0
60 #endif
61 
62 #ifndef DIR_SEPARATOR
63 #define DIR_SEPARATOR '/'
64 #endif
65 
module_resolver(bool map,bool xlate)66 module_resolver::module_resolver (bool map, bool xlate)
67   : default_map (map), default_translate (xlate)
68 {
69 }
70 
~module_resolver()71 module_resolver::~module_resolver ()
72 {
73   if (fd_repo >= 0)
74     close (fd_repo);
75 }
76 
77 bool
set_repo(std::string && r,bool force)78 module_resolver::set_repo (std::string &&r, bool force)
79 {
80   if (force || repo.empty ())
81     {
82       repo = std::move (r);
83       force = true;
84     }
85   return force;
86 }
87 
88 bool
add_mapping(std::string && module,std::string && file,bool force)89 module_resolver::add_mapping (std::string &&module, std::string &&file,
90 			      bool force)
91 {
92   auto res = map.emplace (std::move (module), std::move (file));
93   if (res.second)
94     force = true;
95   else if (force)
96     res.first->second = std::move (file);
97 
98   return force;
99 }
100 
101 int
read_tuple_file(int fd,char const * prefix,bool force)102 module_resolver::read_tuple_file (int fd, char const *prefix, bool force)
103 {
104   struct stat stat;
105   if (fstat (fd, &stat) < 0)
106     return -errno;
107 
108   if (!stat.st_size)
109     return 0;
110 
111   void *buffer = nullptr;
112 #if MAPPED_READING
113   // Just map the file, we're gonna read all of it, so no need for
114   // line buffering
115   buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
116   if (buffer == MAP_FAILED)
117     return -errno;
118   struct Deleter {
119     void operator()(void* p) const { munmap(p, size); }
120     size_t size;
121   };
122   std::unique_ptr<void, Deleter> guard(buffer, Deleter{(size_t)stat.st_size});
123 #else
124   buffer = xmalloc (stat.st_size);
125   if (!buffer)
126     return -errno;
127   struct Deleter { void operator()(void* p) const { free(p); } };
128   std::unique_ptr<void, Deleter> guard(buffer);
129   if (read (fd, buffer, stat.st_size) != stat.st_size)
130     return -errno;
131 #endif
132 
133   size_t prefix_len = prefix ? strlen (prefix) : 0;
134   unsigned lineno = 0;
135 
136   for (char const *begin = reinterpret_cast <char const *> (buffer),
137 	 *end = begin + stat.st_size, *eol;
138        begin != end; begin = eol + 1)
139     {
140       lineno++;
141       eol = std::find (begin, end, '\n');
142       if (eol == end)
143 	// last line has no \n, ignore the line, you lose
144 	break;
145 
146       auto *pos = begin;
147       bool pfx_search = prefix_len != 0;
148 
149     pfx_search:
150       while (*pos == ' ' || *pos == '\t')
151 	pos++;
152 
153       auto *space = pos;
154       while (*space != '\n' && *space != ' ' && *space != '\t')
155 	space++;
156 
157       if (pos == space)
158 	// at end of line, nothing here
159 	continue;
160 
161       if (pfx_search)
162 	{
163 	  if (size_t (space - pos) == prefix_len
164 	      && std::equal (pos, space, prefix))
165 	    pfx_search = false;
166 	  pos = space;
167 	  goto pfx_search;
168 	}
169 
170       std::string module (pos, space);
171       while (*space == ' ' || *space == '\t')
172 	space++;
173       std::string file (space, eol);
174 
175       if (module[0] == '$')
176 	{
177 	  if (module == "$root")
178 	    set_repo (std::move (file));
179 	  else
180 	    return lineno;
181 	}
182       else
183 	{
184 	  if (file.empty ())
185 	    file = GetCMIName (module);
186 	  add_mapping (std::move (module), std::move (file), force);
187 	}
188     }
189 
190   return 0;
191 }
192 
193 char const *
GetCMISuffix()194 module_resolver::GetCMISuffix ()
195 {
196   return "gcm";
197 }
198 
199 module_resolver *
ConnectRequest(Cody::Server * s,unsigned version,std::string & a,std::string & i)200 module_resolver::ConnectRequest (Cody::Server *s, unsigned version,
201 				 std::string &a, std::string &i)
202 {
203   if (!version || version > Cody::Version)
204     s->ErrorResponse ("version mismatch");
205   else if (a != "GCC")
206     // Refuse anything but GCC
207     ErrorResponse (s, std::string ("only GCC supported"));
208   else if (!ident.empty () && ident != i)
209     // Failed ident check
210     ErrorResponse (s, std::string ("bad ident"));
211   else
212     // Success!
213     s->ConnectResponse ("gcc");
214 
215   return this;
216 }
217 
218 int
ModuleRepoRequest(Cody::Server * s)219 module_resolver::ModuleRepoRequest (Cody::Server *s)
220 {
221   s->PathnameResponse (repo);
222   return 0;
223 }
224 
225 int
cmi_response(Cody::Server * s,std::string & module)226 module_resolver::cmi_response (Cody::Server *s, std::string &module)
227 {
228   auto iter = map.find (module);
229   if (iter == map.end ())
230     {
231       std::string file = default_map ? GetCMIName (module) : std::string ();
232       auto res = map.emplace (module, file);
233       iter = res.first;
234     }
235 
236   if (iter->second.empty ())
237     s->ErrorResponse ("no such module");
238   else
239     s->PathnameResponse (iter->second);
240 
241   return 0;
242 }
243 
244 int
ModuleExportRequest(Cody::Server * s,Cody::Flags,std::string & module)245 module_resolver::ModuleExportRequest (Cody::Server *s, Cody::Flags,
246 				      std::string &module)
247 {
248   return cmi_response (s, module);
249 }
250 
251 int
ModuleImportRequest(Cody::Server * s,Cody::Flags,std::string & module)252 module_resolver::ModuleImportRequest (Cody::Server *s, Cody::Flags,
253 				      std::string &module)
254 {
255   return cmi_response (s, module);
256 }
257 
258 int
IncludeTranslateRequest(Cody::Server * s,Cody::Flags,std::string & include)259 module_resolver::IncludeTranslateRequest (Cody::Server *s, Cody::Flags,
260 					  std::string &include)
261 {
262   auto iter = map.find (include);
263   if (iter == map.end () && default_translate)
264     {
265       // Not found, look for it
266       auto file = GetCMIName (include);
267       struct stat statbuf;
268       bool ok = true;
269 
270 #if HAVE_FSTATAT
271       int fd_dir = AT_FDCWD;
272       if (!repo.empty ())
273 	{
274 	  if (fd_repo == -1)
275 	    {
276 	      fd_repo = open (repo.c_str (),
277 			      O_RDONLY | O_CLOEXEC | O_DIRECTORY);
278 	      if (fd_repo < 0)
279 		fd_repo = -2;
280 	    }
281 	  fd_dir = fd_repo;
282 	}
283 
284       if (!repo.empty () && fd_repo < 0)
285 	ok = false;
286       else if (fstatat (fd_dir, file.c_str (), &statbuf, 0) < 0
287 	       || !S_ISREG (statbuf.st_mode))
288 	ok = false;
289 #else
290       auto append = repo;
291       append.push_back (DIR_SEPARATOR);
292       append.append (file);
293       if (stat (append.c_str (), &statbuf) < 0
294 	  || !S_ISREG (statbuf.st_mode))
295 	ok = false;
296 #endif
297       if (!ok)
298 	// Mark as not present
299 	file.clear ();
300       auto res = map.emplace (include, file);
301       iter = res.first;
302     }
303 
304   if (iter == map.end () || iter->second.empty ())
305     s->BoolResponse (false);
306   else
307     s->PathnameResponse (iter->second);
308 
309   return 0;
310 }
311 
312 /* This handles a client notification to the server that a CMI has been
313    produced for a module.  For this simplified server, we just accept
314    the transaction and respond with "OK".  */
315 
316 int
ModuleCompiledRequest(Cody::Server * s,Cody::Flags,std::string &)317 module_resolver::ModuleCompiledRequest (Cody::Server *s, Cody::Flags,
318 				      std::string &)
319 {
320   s->OKResponse();
321   return 0;
322 }
323