]>
Commit | Line | Data |
---|---|---|
35fc243f | 1 | /* C++ modules. Experimental! -*- c++ -*- |
7adcbafe | 2 | Copyright (C) 2017-2022 Free Software Foundation, Inc. |
35fc243f NS |
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> | |
c9bf4d43 | 26 | #include <memory> |
35fc243f NS |
27 | // C |
28 | #include <cstring> | |
29 | // OS | |
30 | #include <fcntl.h> | |
31 | #include <unistd.h> | |
09616422 NS |
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 | |
35fc243f | 43 | #include <sys/mman.h> |
09616422 NS |
44 | #define MAPPED_READING 1 |
45 | #else | |
46 | #define MAPPED_READING 0 | |
47 | #endif | |
48 | #endif | |
49 | #endif | |
50 | ||
35fc243f NS |
51 | #include <sys/types.h> |
52 | #include <sys/stat.h> | |
53 | ||
09616422 NS |
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 | ||
35fc243f NS |
62 | #ifndef DIR_SEPARATOR |
63 | #define DIR_SEPARATOR '/' | |
64 | #endif | |
65 | ||
66 | module_resolver::module_resolver (bool map, bool xlate) | |
67 | : default_map (map), default_translate (xlate) | |
68 | { | |
69 | } | |
70 | ||
71 | module_resolver::~module_resolver () | |
72 | { | |
73 | if (fd_repo >= 0) | |
74 | close (fd_repo); | |
75 | } | |
76 | ||
77 | bool | |
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 | |
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 | |
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 | ||
09616422 NS |
111 | void *buffer = nullptr; |
112 | #if MAPPED_READING | |
35fc243f NS |
113 | // Just map the file, we're gonna read all of it, so no need for |
114 | // line buffering | |
09616422 | 115 | buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
35fc243f NS |
116 | if (buffer == MAP_FAILED) |
117 | return -errno; | |
c9bf4d43 JW |
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}); | |
09616422 NS |
123 | #else |
124 | buffer = xmalloc (stat.st_size); | |
125 | if (!buffer) | |
126 | return -errno; | |
c9bf4d43 JW |
127 | struct Deleter { void operator()(void* p) const { free(p); } }; |
128 | std::unique_ptr<void, Deleter> guard(buffer); | |
09616422 NS |
129 | if (read (fd, buffer, stat.st_size) != stat.st_size) |
130 | return -errno; | |
131 | #endif | |
35fc243f NS |
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 | ||
35fc243f NS |
190 | return 0; |
191 | } | |
192 | ||
193 | char const * | |
194 | module_resolver::GetCMISuffix () | |
195 | { | |
196 | return "gcm"; | |
197 | } | |
198 | ||
199 | module_resolver * | |
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 | |
219 | module_resolver::ModuleRepoRequest (Cody::Server *s) | |
220 | { | |
221 | s->PathnameResponse (repo); | |
222 | return 0; | |
223 | } | |
224 | ||
225 | int | |
226 | module_resolver::cmi_response (Cody::Server *s, std::string &module) | |
227 | { | |
228 | auto iter = map.find (module); | |
229 | if (iter == map.end ()) | |
230 | { | |
62881833 | 231 | std::string file = default_map ? GetCMIName (module) : std::string (); |
35fc243f NS |
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 | |
245 | module_resolver::ModuleExportRequest (Cody::Server *s, Cody::Flags, | |
246 | std::string &module) | |
247 | { | |
248 | return cmi_response (s, module); | |
249 | } | |
250 | ||
251 | int | |
252 | module_resolver::ModuleImportRequest (Cody::Server *s, Cody::Flags, | |
253 | std::string &module) | |
254 | { | |
255 | return cmi_response (s, module); | |
256 | } | |
257 | ||
258 | int | |
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 | ||
c89d8053 IS |
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 | module_resolver::ModuleCompiledRequest (Cody::Server *s, Cody::Flags, | |
318 | std::string &) | |
319 | { | |
320 | s->OKResponse(); | |
321 | return 0; | |
322 | } |