Branch data Line data Source code
1 : : /* C++ modules. Experimental! -*- c++ -*-
2 : : Copyright (C) 2017-2024 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 : :
66 : 3872 : module_resolver::module_resolver (bool map, bool xlate)
67 : 3872 : : default_map (map), default_translate (xlate)
68 : : {
69 : 3872 : }
70 : :
71 : 7460 : module_resolver::~module_resolver ()
72 : : {
73 : 3730 : if (fd_repo >= 0)
74 : 3624 : close (fd_repo);
75 : 7460 : }
76 : :
77 : : bool
78 : 3869 : module_resolver::set_repo (std::string &&r, bool force)
79 : : {
80 : 3869 : if (force || repo.empty ())
81 : : {
82 : 3869 : repo = std::move (r);
83 : 3869 : force = true;
84 : : }
85 : 3869 : return force;
86 : : }
87 : :
88 : : bool
89 : 27 : module_resolver::add_mapping (std::string &&module, std::string &&file,
90 : : bool force)
91 : : {
92 : 27 : auto res = map.emplace (std::move (module), std::move (file));
93 : 27 : if (res.second)
94 : : force = true;
95 : 0 : else if (force)
96 : 0 : res.first->second = std::move (file);
97 : :
98 : 27 : return force;
99 : : }
100 : :
101 : : int
102 : 24 : module_resolver::read_tuple_file (int fd, char const *prefix, bool force)
103 : : {
104 : 24 : struct stat stat;
105 : 24 : if (fstat (fd, &stat) < 0)
106 : 0 : return -errno;
107 : :
108 : 24 : if (!stat.st_size)
109 : : return 0;
110 : :
111 : 24 : 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 : 24 : buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
116 : 24 : if (buffer == MAP_FAILED)
117 : 0 : return -errno;
118 : 24 : struct Deleter {
119 : 24 : void operator()(void* p) const { munmap(p, size); }
120 : : size_t size;
121 : : };
122 : 24 : 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 : 24 : size_t prefix_len = prefix ? strlen (prefix) : 0;
134 : 24 : unsigned lineno = 0;
135 : :
136 : 75 : for (char const *begin = reinterpret_cast <char const *> (buffer),
137 : 24 : *end = begin + stat.st_size, *eol;
138 : 75 : begin != end; begin = eol + 1)
139 : : {
140 : 54 : lineno++;
141 : 54 : eol = std::find (begin, end, '\n');
142 : 54 : if (eol == end)
143 : : // last line has no \n, ignore the line, you lose
144 : : break;
145 : :
146 : 54 : auto *pos = begin;
147 : 54 : bool pfx_search = prefix_len != 0;
148 : :
149 : : pfx_search:
150 : 87 : while (*pos == ' ' || *pos == '\t')
151 : 15 : pos++;
152 : :
153 : : auto *space = pos;
154 : 411 : while (*space != '\n' && *space != ' ' && *space != '\t')
155 : 339 : space++;
156 : :
157 : 72 : if (pos == space)
158 : : // at end of line, nothing here
159 : 3 : continue;
160 : :
161 : 69 : if (pfx_search)
162 : : {
163 : 18 : if (size_t (space - pos) == prefix_len
164 : 18 : && std::equal (pos, space, prefix))
165 : : pfx_search = false;
166 : 18 : pos = space;
167 : 18 : goto pfx_search;
168 : : }
169 : :
170 : 99 : std::string module (pos, space);
171 : 177 : while (*space == ' ' || *space == '\t')
172 : 75 : space++;
173 : 99 : std::string file (space, eol);
174 : :
175 : 51 : if (module[0] == '$')
176 : : {
177 : 24 : if (module == "$root")
178 : 21 : set_repo (std::move (file));
179 : : else
180 : 3 : return lineno;
181 : : }
182 : : else
183 : : {
184 : 27 : if (file.empty ())
185 : 0 : file = GetCMIName (module);
186 : 27 : add_mapping (std::move (module), std::move (file), force);
187 : : }
188 : : }
189 : :
190 : : return 0;
191 : 24 : }
192 : :
193 : : char const *
194 : 35601 : module_resolver::GetCMISuffix ()
195 : : {
196 : 35601 : return "gcm";
197 : : }
198 : :
199 : : module_resolver *
200 : 3872 : module_resolver::ConnectRequest (Cody::Server *s, unsigned version,
201 : : std::string &a, std::string &i)
202 : : {
203 : 3872 : if (!version || version > Cody::Version)
204 : 0 : s->ErrorResponse ("version mismatch");
205 : 3872 : else if (a != "GCC")
206 : : // Refuse anything but GCC
207 : 0 : ErrorResponse (s, std::string ("only GCC supported"));
208 : 3872 : else if (!ident.empty () && ident != i)
209 : : // Failed ident check
210 : 0 : ErrorResponse (s, std::string ("bad ident"));
211 : : else
212 : : // Success!
213 : 3872 : s->ConnectResponse ("gcc");
214 : :
215 : 3872 : return this;
216 : : }
217 : :
218 : : int
219 : 3872 : module_resolver::ModuleRepoRequest (Cody::Server *s)
220 : : {
221 : 3872 : s->PathnameResponse (repo);
222 : 3872 : return 0;
223 : : }
224 : :
225 : : int
226 : 4525 : module_resolver::cmi_response (Cody::Server *s, std::string &module)
227 : : {
228 : 4525 : auto iter = map.find (module);
229 : 4525 : if (iter == map.end ())
230 : : {
231 : 4501 : std::string file = default_map ? GetCMIName (module) : std::string ();
232 : 4501 : auto res = map.emplace (module, file);
233 : 4501 : iter = res.first;
234 : 4501 : }
235 : :
236 : 4525 : if (iter->second.empty ())
237 : 3 : s->ErrorResponse ("no such module");
238 : : else
239 : 4522 : s->PathnameResponse (iter->second);
240 : :
241 : 4525 : return 0;
242 : : }
243 : :
244 : : int
245 : 2324 : module_resolver::ModuleExportRequest (Cody::Server *s, Cody::Flags,
246 : : std::string &module)
247 : : {
248 : 2324 : return cmi_response (s, module);
249 : : }
250 : :
251 : : int
252 : 2201 : module_resolver::ModuleImportRequest (Cody::Server *s, Cody::Flags,
253 : : std::string &module)
254 : : {
255 : 2201 : return cmi_response (s, module);
256 : : }
257 : :
258 : : int
259 : 31124 : module_resolver::IncludeTranslateRequest (Cody::Server *s, Cody::Flags,
260 : : std::string &include)
261 : : {
262 : 31124 : auto iter = map.find (include);
263 : 31124 : if (iter == map.end () && default_translate)
264 : : {
265 : : // Not found, look for it
266 : 31103 : auto file = GetCMIName (include);
267 : 31103 : struct stat statbuf;
268 : 31103 : bool ok = true;
269 : :
270 : : #if HAVE_FSTATAT
271 : 31103 : int fd_dir = AT_FDCWD;
272 : 31103 : if (!repo.empty ())
273 : : {
274 : 31100 : if (fd_repo == -1)
275 : : {
276 : 3833 : fd_repo = open (repo.c_str (),
277 : : O_RDONLY | O_CLOEXEC | O_DIRECTORY);
278 : 3833 : if (fd_repo < 0)
279 : 142 : fd_repo = -2;
280 : : }
281 : 31100 : fd_dir = fd_repo;
282 : : }
283 : :
284 : 31103 : if (!repo.empty () && fd_repo < 0)
285 : : ok = false;
286 : 30344 : else if (fstatat (fd_dir, file.c_str (), &statbuf, 0) < 0
287 : 30344 : || !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 : 31065 : file.clear ();
300 : 31103 : auto res = map.emplace (include, file);
301 : 31103 : iter = res.first;
302 : 31103 : }
303 : :
304 : 31124 : if (iter == map.end () || iter->second.empty ())
305 : 31086 : s->BoolResponse (false);
306 : : else
307 : 38 : s->PathnameResponse (iter->second);
308 : :
309 : 31124 : 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
317 : 2143 : module_resolver::ModuleCompiledRequest (Cody::Server *s, Cody::Flags,
318 : : std::string &)
319 : : {
320 : 2143 : s->OKResponse();
321 : 2143 : return 0;
322 : : }
|