19c6930d0c3e5d9e35a939fbd2bb6ca109c2a095
[gcc.git] / gcc / go / gofrontend / embed.cc
1 // embed.cc -- Go frontend go:embed handling.
2
3 // Copyright 2021 The Go Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file.
6
7 #include "go-system.h"
8
9 #include "operator.h"
10 #include "go-diagnostics.h"
11 #include "lex.h"
12 #include "gogo.h"
13
14 #ifndef O_BINARY
15 #define O_BINARY 0
16 #endif
17
18 // Read a file into *DATA. Returns false on error.
19
20 static bool
21 read_file(const char* filename, Location loc, std::string* data)
22 {
23 int fd = open(filename, O_RDONLY | O_BINARY);
24 if (fd < 0)
25 {
26 go_error_at(loc, "%s: %m", filename);
27 return false;
28 }
29
30 struct stat st;
31 if (fstat(fd, &st) < 0)
32 {
33 go_error_at(loc, "%s: %m", filename);
34 return false;
35 }
36 off_t want = st.st_size;
37
38 // Most files read here are going to be incorporated into the object file
39 // and then the executable. Set a limit on the size we will accept.
40 if (want > 2000000000)
41 {
42 go_error_at(loc, "%s: file too large", filename);
43 return false;
44 }
45
46 data->resize(want);
47 off_t got = 0;
48 while (want > 0)
49 {
50 // C++11 requires that std::string use contiguous bytes, so this
51 // is safe.
52 ssize_t n = read(fd, &(*data)[got], want);
53 if (n < 0)
54 {
55 close(fd);
56 go_error_at(loc, "%s: %m", filename);
57 return false;
58 }
59 if (n == 0)
60 {
61 data->resize(got);
62 break;
63 }
64 got += n;
65 want -= n;
66 }
67
68 close(fd);
69 return true;
70 }
71
72 // A JSON value as read from an embedcfg file. For our purposes a
73 // JSON value is a string, or a list of strings, or a mapping from
74 // strings to values. We don't expect any numbers. We also don't
75 // expect an array of anything other than strings; that is, we don't
76 // accept an array of general JSON values.
77
78 class Json_value
79 {
80 public:
81 // The types of values.
82 enum Json_value_classification
83 {
84 JSON_VALUE_UNKNOWN,
85 JSON_VALUE_STRING,
86 JSON_VALUE_ARRAY,
87 JSON_VALUE_MAP
88 };
89
90 Json_value()
91 : classification_(JSON_VALUE_UNKNOWN), string_(), array_(), map_()
92 { }
93
94 ~Json_value();
95
96 Json_value_classification
97 classification() const
98 { return this->classification_; }
99
100 // Set to a string value.
101 void
102 set_string(const std::string& str)
103 {
104 go_assert(this->classification_ == JSON_VALUE_UNKNOWN);
105 this->classification_ = JSON_VALUE_STRING;
106 this->string_ = str;
107 }
108
109 // Start an array value.
110 void
111 start_array()
112 {
113 go_assert(this->classification_ == JSON_VALUE_UNKNOWN);
114 this->classification_ = JSON_VALUE_ARRAY;
115 }
116
117 // Add an array entry.
118 void
119 add_array_entry(const std::string& s)
120 {
121 go_assert(this->classification_ == JSON_VALUE_ARRAY);
122 this->array_.push_back(s);
123 }
124
125 // Start a map value.
126 void
127 start_map()
128 {
129 go_assert(this->classification_ == JSON_VALUE_UNKNOWN);
130 this->classification_ = JSON_VALUE_MAP;
131 }
132
133 // Add a map entry.
134 void
135 add_map_entry(const std::string& key, Json_value* val)
136 {
137 go_assert(this->classification_ == JSON_VALUE_MAP);
138 this->map_[key] = val;
139 }
140
141 // Return the strings from a string value.
142 const std::string&
143 to_string() const
144 {
145 go_assert(this->classification_ == JSON_VALUE_STRING);
146 return this->string_;
147 }
148
149 // Fetch a vector of strings, and drop them from the JSON value.
150 void
151 get_and_clear_array(std::vector<std::string>* v)
152 {
153 go_assert(this->classification_ == JSON_VALUE_ARRAY);
154 std::swap(*v, this->array_);
155 }
156
157 // Look up a map entry. Returns NULL if not found.
158 Json_value*
159 lookup_map_entry(const std::string& key);
160
161 // Iterate over a map.
162 typedef Unordered_map(std::string, Json_value*)::iterator map_iterator;
163
164 map_iterator
165 map_begin()
166 {
167 go_assert(this->classification_ == JSON_VALUE_MAP);
168 return this->map_.begin();
169 }
170
171 map_iterator
172 map_end()
173 { return this->map_.end(); }
174
175 private:
176 // Classification.
177 Json_value_classification classification_;
178 // A string, for JSON_VALUE_STRING.
179 std::string string_;
180 // Array, for JSON_VALUE_ARRAY.
181 std::vector<std::string> array_;
182 // Mapping, for JSON_VALUE_MAP.
183 Unordered_map(std::string, Json_value*) map_;
184 };
185
186 // Delete a JSON value.
187
188 Json_value::~Json_value()
189 {
190 if (this->classification_ == JSON_VALUE_MAP)
191 {
192 for (map_iterator p = this->map_begin();
193 p != this->map_end();
194 ++p)
195 delete p->second;
196 }
197 }
198
199 // Look up a map entry in a JSON value.
200
201 Json_value*
202 Json_value::lookup_map_entry(const std::string& key)
203 {
204 go_assert(this->classification_ == JSON_VALUE_MAP);
205 Unordered_map(std::string, Json_value*)::iterator p = this->map_.find(key);
206 if (p == this->map_.end())
207 return NULL;
208 return p->second;
209 }
210
211 // Manage reading the embedcfg file.
212
213 class Embedcfg_reader
214 {
215 public:
216 Embedcfg_reader(const char* filename)
217 : filename_(filename), data_(), p_(NULL), pend_(NULL)
218 {}
219
220 // Read the contents of FILENAME. Return whether it succeeded.
221 bool
222 initialize_from_file();
223
224 // Read a JSON object.
225 bool
226 read_object(Json_value*);
227
228 // Report an error if not at EOF.
229 void
230 check_eof();
231
232 // Report an error for the embedcfg file.
233 void
234 error(const char* msg);
235
236 private:
237 bool
238 read_value(Json_value*);
239
240 bool
241 read_array(Json_value*);
242
243 bool
244 read_string(std::string*);
245
246 bool
247 skip_whitespace(bool eof_ok);
248
249 // File name.
250 const char* filename_;
251 // File contents.
252 std::string data_;
253 // Next character to process.
254 const char *p_;
255 // End of data.
256 const char *pend_;
257 };
258
259 // Read the embedcfg file.
260
261 void
262 Gogo::read_embedcfg(const char *filename)
263 {
264 class Embedcfg_reader r(filename);
265 if (!r.initialize_from_file())
266 return;
267
268 Json_value val;
269 if (!r.read_object(&val))
270 return;
271
272 r.check_eof();
273
274 if (val.classification() != Json_value::JSON_VALUE_MAP)
275 {
276 r.error("invalid embedcfg: not a JSON object");
277 return;
278 }
279
280 Json_value* patterns = val.lookup_map_entry("Patterns");
281 if (patterns == NULL)
282 {
283 r.error("invalid embedcfg: missing Patterns");
284 return;
285 }
286 if (patterns->classification() != Json_value::JSON_VALUE_MAP)
287 {
288 r.error("invalid embedcfg: Patterns is not a JSON object");
289 return;
290 }
291
292 Json_value* files = val.lookup_map_entry("Files");
293 if (files == NULL)
294 {
295 r.error("invalid embedcfg: missing Files");
296 return;
297 }
298 if (files->classification() != Json_value::JSON_VALUE_MAP)
299 {
300 r.error("invalid embedcfg: Files is not a JSON object");
301 return;
302 }
303
304 // TODO: Actually do something with patterns and files.
305 }
306
307 // Read the contents of FILENAME into this->data_. Returns whether it
308 // succeeded.
309
310 bool
311 Embedcfg_reader::initialize_from_file()
312 {
313 if (!read_file(this->filename_, Linemap::unknown_location(), &this->data_))
314 return false;
315 if (this->data_.empty())
316 {
317 this->error("empty file");
318 return false;
319 }
320 this->p_ = this->data_.data();
321 this->pend_ = this->p_ + this->data_.size();
322 return true;
323 }
324
325 // Read a JSON object into VAL. Return whether it succeeded.
326
327 bool
328 Embedcfg_reader::read_object(Json_value* val)
329 {
330 if (!this->skip_whitespace(false))
331 return false;
332 if (*this->p_ != '{')
333 {
334 this->error("expected %<{%>");
335 return false;
336 }
337 ++this->p_;
338
339 val->start_map();
340
341 if (!this->skip_whitespace(false))
342 return false;
343 if (*this->p_ == '}')
344 {
345 ++this->p_;
346 return true;
347 }
348
349 while (true)
350 {
351 if (!this->skip_whitespace(false))
352 return false;
353 if (*this->p_ != '"')
354 {
355 this->error("expected %<\"%>");
356 return false;
357 }
358
359 std::string key;
360 if (!this->read_string(&key))
361 return false;
362
363 if (!this->skip_whitespace(false))
364 return false;
365 if (*this->p_ != ':')
366 {
367 this->error("expected %<:%>");
368 return false;
369 }
370 ++this->p_;
371
372 Json_value* subval = new Json_value();
373 if (!this->read_value(subval))
374 return false;
375
376 val->add_map_entry(key, subval);
377
378 if (!this->skip_whitespace(false))
379 return false;
380 if (*this->p_ == '}')
381 {
382 ++this->p_;
383 return true;
384 }
385 if (*this->p_ != ',')
386 {
387 this->error("expected %<,%> or %<}%>");
388 return false;
389 }
390 ++this->p_;
391 }
392 }
393
394 // Read a JSON array into VAL. Return whether it succeeded.
395
396 bool
397 Embedcfg_reader::read_array(Json_value* val)
398 {
399 if (!this->skip_whitespace(false))
400 return false;
401 if (*this->p_ != '[')
402 {
403 this->error("expected %<[%>");
404 return false;
405 }
406 ++this->p_;
407
408 val->start_array();
409
410 if (!this->skip_whitespace(false))
411 return false;
412 if (*this->p_ == ']')
413 {
414 ++this->p_;
415 return true;
416 }
417
418 while (true)
419 {
420 // If we were parsing full JSON we would call read_value here,
421 // not read_string.
422
423 std::string s;
424 if (!this->read_string(&s))
425 return false;
426
427 val->add_array_entry(s);
428
429 if (!this->skip_whitespace(false))
430 return false;
431 if (*this->p_ == ']')
432 {
433 ++this->p_;
434 return true;
435 }
436 if (*this->p_ != ',')
437 {
438 this->error("expected %<,%> or %<]%>");
439 return false;
440 }
441 ++this->p_;
442 }
443 }
444
445 // Read a JSON value into VAL. Return whether it succeeded.
446
447 bool
448 Embedcfg_reader::read_value(Json_value* val)
449 {
450 if (!this->skip_whitespace(false))
451 return false;
452 switch (*this->p_)
453 {
454 case '"':
455 {
456 std::string s;
457 if (!this->read_string(&s))
458 return false;
459 val->set_string(s);
460 return true;
461 }
462
463 case '{':
464 return this->read_object(val);
465
466 case '[':
467 return this->read_array(val);
468
469 default:
470 this->error("invalid JSON syntax");
471 return false;
472 }
473 }
474
475 // Read a JSON string. Return whether it succeeded.
476
477 bool
478 Embedcfg_reader::read_string(std::string* str)
479 {
480 if (!this->skip_whitespace(false))
481 return false;
482 if (*this->p_ != '"')
483 {
484 this->error("expected %<\"%>");
485 return false;
486 }
487 ++this->p_;
488
489 str->clear();
490 while (this->p_ < this->pend_ && *this->p_ != '"')
491 {
492 if (*this->p_ != '\\')
493 {
494 str->push_back(*this->p_);
495 ++this->p_;
496 continue;
497 }
498
499 ++this->p_;
500 if (this->p_ >= this->pend_)
501 {
502 this->error("unterminated string");
503 return false;
504 }
505 switch (*this->p_)
506 {
507 case '"': case '\\': case '/':
508 str->push_back(*this->p_);
509 ++this->p_;
510 break;
511
512 case 'b':
513 str->push_back('\b');
514 ++this->p_;
515 break;
516
517 case 'f':
518 str->push_back('\f');
519 ++this->p_;
520 break;
521
522 case 'n':
523 str->push_back('\n');
524 ++this->p_;
525 break;
526
527 case 'r':
528 str->push_back('\r');
529 ++this->p_;
530 break;
531
532 case 't':
533 str->push_back('\t');
534 ++this->p_;
535 break;
536
537 case 'u':
538 {
539 ++this->p_;
540 unsigned int rune = 0;
541 for (int i = 0; i < 4; i++)
542 {
543 if (this->p_ >= this->pend_)
544 {
545 this->error("unterminated string");
546 return false;
547 }
548 unsigned char c = *this->p_;
549 ++this->p_;
550 rune <<= 4;
551 if (c >= '0' && c <= '9')
552 rune += c - '0';
553 else if (c >= 'A' && c <= 'F')
554 rune += c - 'A' + 10;
555 else if (c >= 'a' && c <= 'f')
556 rune += c - 'a' + 10;
557 else
558 {
559 this->error("invalid hex digit");
560 return false;
561 }
562 }
563 Lex::append_char(rune, false, str, Linemap::unknown_location());
564 }
565 break;
566
567 default:
568 this->error("unrecognized string escape");
569 return false;
570 }
571 }
572
573 if (*this->p_ == '"')
574 {
575 ++this->p_;
576 return true;
577 }
578
579 this->error("unterminated string");
580 return false;
581 }
582
583 // Report an error if not at EOF.
584
585 void
586 Embedcfg_reader::check_eof()
587 {
588 if (this->skip_whitespace(true))
589 this->error("extraneous data at end of file");
590 }
591
592 // Skip whitespace. Return whether there is more to read.
593
594 bool
595 Embedcfg_reader::skip_whitespace(bool eof_ok)
596 {
597 while (this->p_ < this->pend_)
598 {
599 switch (*this->p_)
600 {
601 case ' ': case '\t': case '\n': case '\r':
602 ++this->p_;
603 break;
604 default:
605 return true;
606 }
607 }
608 if (!eof_ok)
609 this->error("unexpected EOF");
610 return false;
611 }
612
613 // Report an error.
614
615 void
616 Embedcfg_reader::error(const char* msg)
617 {
618 if (!this->data_.empty() && this->p_ != NULL)
619 go_error_at(Linemap::unknown_location(),
620 "%<-fgo-embedcfg%>: %s: %lu: %s",
621 this->filename_,
622 static_cast<unsigned long>(this->p_ - this->data_.data()),
623 msg);
624 else
625 go_error_at(Linemap::unknown_location(),
626 "%<-fgo-embedcfg%>: %s: %s",
627 this->filename_, msg);
628 }