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 : }
|