Branch data Line data Source code
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 "types.h"
13 : : #include "expressions.h"
14 : : #include "gogo.h"
15 : :
16 : : #ifndef O_BINARY
17 : : #define O_BINARY 0
18 : : #endif
19 : :
20 : : // Read a file into *DATA. Returns false on error.
21 : :
22 : : bool
23 : 441 : Gogo::read_file(const char* filename, Location loc, std::string* data)
24 : : {
25 : 441 : int fd = open(filename, O_RDONLY | O_BINARY);
26 : 441 : if (fd < 0)
27 : : {
28 : 0 : go_error_at(loc, "%s: %m", filename);
29 : 0 : return false;
30 : : }
31 : :
32 : 441 : struct stat st;
33 : 441 : if (fstat(fd, &st) < 0)
34 : : {
35 : 0 : go_error_at(loc, "%s: %m", filename);
36 : 0 : return false;
37 : : }
38 : 441 : off_t want = st.st_size;
39 : :
40 : : // Most files read here are going to be incorporated into the object file
41 : : // and then the executable. Set a limit on the size we will accept.
42 : 441 : if (want > 2000000000)
43 : : {
44 : 0 : go_error_at(loc, "%s: file too large", filename);
45 : 0 : return false;
46 : : }
47 : :
48 : 441 : data->resize(want);
49 : 441 : off_t got = 0;
50 : 1319 : while (want > 0)
51 : : {
52 : : // C++11 requires that std::string use contiguous bytes, so this
53 : : // is safe.
54 : 437 : ssize_t n = read(fd, &(*data)[got], want);
55 : 437 : if (n < 0)
56 : : {
57 : 0 : close(fd);
58 : 0 : go_error_at(loc, "%s: %m", filename);
59 : 0 : return false;
60 : : }
61 : 437 : if (n == 0)
62 : : {
63 : 0 : data->resize(got);
64 : 0 : break;
65 : : }
66 : 437 : got += n;
67 : 437 : want -= n;
68 : : }
69 : :
70 : 441 : close(fd);
71 : 441 : return true;
72 : : }
73 : :
74 : : // A JSON value as read from an embedcfg file. For our purposes a
75 : : // JSON value is a string, or a list of strings, or a mapping from
76 : : // strings to values. We don't expect any numbers. We also don't
77 : : // expect an array of anything other than strings; that is, we don't
78 : : // accept an array of general JSON values.
79 : :
80 : : class Json_value
81 : : {
82 : : public:
83 : : // The types of values.
84 : : enum Json_value_classification
85 : : {
86 : : JSON_VALUE_UNKNOWN,
87 : : JSON_VALUE_STRING,
88 : : JSON_VALUE_ARRAY,
89 : : JSON_VALUE_MAP
90 : : };
91 : :
92 : 68 : Json_value()
93 : 68 : : classification_(JSON_VALUE_UNKNOWN), string_(), array_(), map_()
94 : 68 : { }
95 : :
96 : : ~Json_value();
97 : :
98 : : Json_value_classification
99 : 68 : classification() const
100 : 44 : { return this->classification_; }
101 : :
102 : : // Set to a string value.
103 : : void
104 : 27 : set_string(const std::string& str)
105 : : {
106 : 27 : go_assert(this->classification_ == JSON_VALUE_UNKNOWN);
107 : 27 : this->classification_ = JSON_VALUE_STRING;
108 : 27 : this->string_ = str;
109 : 27 : }
110 : :
111 : : // Start an array value.
112 : : void
113 : 17 : start_array()
114 : : {
115 : 17 : go_assert(this->classification_ == JSON_VALUE_UNKNOWN);
116 : 17 : this->classification_ = JSON_VALUE_ARRAY;
117 : 17 : }
118 : :
119 : : // Add an array entry.
120 : : void
121 : 40 : add_array_entry(const std::string& s)
122 : : {
123 : 40 : go_assert(this->classification_ == JSON_VALUE_ARRAY);
124 : 40 : this->array_.push_back(s);
125 : 40 : }
126 : :
127 : : // Start a map value.
128 : : void
129 : 24 : start_map()
130 : : {
131 : 24 : go_assert(this->classification_ == JSON_VALUE_UNKNOWN);
132 : 24 : this->classification_ = JSON_VALUE_MAP;
133 : 24 : }
134 : :
135 : : // Add a map entry.
136 : : void
137 : 60 : add_map_entry(const std::string& key, Json_value* val)
138 : : {
139 : 60 : go_assert(this->classification_ == JSON_VALUE_MAP);
140 : 60 : this->map_[key] = val;
141 : 60 : }
142 : :
143 : : // Return the strings from a string value.
144 : : const std::string&
145 : 27 : to_string() const
146 : : {
147 : 27 : go_assert(this->classification_ == JSON_VALUE_STRING);
148 : 27 : return this->string_;
149 : : }
150 : :
151 : : // Fetch a vector of strings, and drop them from the JSON value.
152 : : void
153 : 17 : get_and_clear_array(std::vector<std::string>* v)
154 : : {
155 : 17 : go_assert(this->classification_ == JSON_VALUE_ARRAY);
156 : 17 : std::swap(*v, this->array_);
157 : 17 : }
158 : :
159 : : // Look up a map entry. Returns NULL if not found.
160 : : Json_value*
161 : : lookup_map_entry(const std::string& key);
162 : :
163 : : // Iterate over a map.
164 : : typedef Unordered_map(std::string, Json_value*)::iterator map_iterator;
165 : :
166 : : map_iterator
167 : 40 : map_begin()
168 : : {
169 : 40 : go_assert(this->classification_ == JSON_VALUE_MAP);
170 : 40 : return this->map_.begin();
171 : : }
172 : :
173 : : map_iterator
174 : 144 : map_end()
175 : 144 : { return this->map_.end(); }
176 : :
177 : : private:
178 : : // Classification.
179 : : Json_value_classification classification_;
180 : : // A string, for JSON_VALUE_STRING.
181 : : std::string string_;
182 : : // Array, for JSON_VALUE_ARRAY.
183 : : std::vector<std::string> array_;
184 : : // Mapping, for JSON_VALUE_MAP.
185 : : Unordered_map(std::string, Json_value*) map_;
186 : : };
187 : :
188 : : // Delete a JSON value.
189 : :
190 : 68 : Json_value::~Json_value()
191 : : {
192 : 68 : if (this->classification_ == JSON_VALUE_MAP)
193 : : {
194 : 84 : for (map_iterator p = this->map_begin();
195 : 84 : p != this->map_end();
196 : 60 : ++p)
197 : 60 : delete p->second;
198 : : }
199 : 136 : }
200 : :
201 : : // Look up a map entry in a JSON value.
202 : :
203 : : Json_value*
204 : 16 : Json_value::lookup_map_entry(const std::string& key)
205 : : {
206 : 16 : go_assert(this->classification_ == JSON_VALUE_MAP);
207 : 16 : Unordered_map(std::string, Json_value*)::iterator p = this->map_.find(key);
208 : 16 : if (p == this->map_.end())
209 : : return NULL;
210 : 16 : return p->second;
211 : : }
212 : :
213 : : // Manage reading the embedcfg file.
214 : :
215 : 0 : class Embedcfg_reader
216 : : {
217 : : public:
218 : 8 : Embedcfg_reader(const char* filename)
219 : 8 : : filename_(filename), data_(), p_(NULL), pend_(NULL)
220 : : {}
221 : :
222 : : // Read the contents of FILENAME. Return whether it succeeded.
223 : : bool
224 : : initialize_from_file();
225 : :
226 : : // Read a JSON object.
227 : : bool
228 : : read_object(Json_value*);
229 : :
230 : : // Report an error if not at EOF.
231 : : void
232 : : check_eof();
233 : :
234 : : // Report an error for the embedcfg file.
235 : : void
236 : : error(const char* msg);
237 : :
238 : : private:
239 : : bool
240 : : read_value(Json_value*);
241 : :
242 : : bool
243 : : read_array(Json_value*);
244 : :
245 : : bool
246 : : read_string(std::string*);
247 : :
248 : : bool
249 : : skip_whitespace(bool eof_ok);
250 : :
251 : : // File name.
252 : : const char* filename_;
253 : : // File contents.
254 : : std::string data_;
255 : : // Next character to process.
256 : : const char *p_;
257 : : // End of data.
258 : : const char *pend_;
259 : : };
260 : :
261 : : // Read the embedcfg file.
262 : :
263 : : void
264 : 8 : Gogo::read_embedcfg(const char *filename)
265 : : {
266 : 8 : class Embedcfg_reader r(filename);
267 : 8 : if (!r.initialize_from_file())
268 : : return;
269 : :
270 : 8 : Json_value val;
271 : 8 : if (!r.read_object(&val))
272 : : return;
273 : :
274 : 8 : r.check_eof();
275 : :
276 : 8 : if (val.classification() != Json_value::JSON_VALUE_MAP)
277 : : {
278 : 0 : r.error("invalid embedcfg: not a JSON object");
279 : 0 : return;
280 : : }
281 : :
282 : 8 : Json_value* patterns = val.lookup_map_entry("Patterns");
283 : 8 : if (patterns == NULL)
284 : : {
285 : 0 : r.error("invalid embedcfg: missing Patterns");
286 : 0 : return;
287 : : }
288 : 8 : if (patterns->classification() != Json_value::JSON_VALUE_MAP)
289 : : {
290 : 0 : r.error("invalid embedcfg: Patterns is not a JSON object");
291 : 0 : return;
292 : : }
293 : :
294 : 8 : Json_value* files = val.lookup_map_entry("Files");
295 : 8 : if (files == NULL)
296 : : {
297 : 0 : r.error("invalid embedcfg: missing Files");
298 : 0 : return;
299 : : }
300 : 8 : if (files->classification() != Json_value::JSON_VALUE_MAP)
301 : : {
302 : 0 : r.error("invalid embedcfg: Files is not a JSON object");
303 : 0 : return;
304 : : }
305 : :
306 : 25 : for (Json_value::map_iterator p = patterns->map_begin();
307 : 25 : p != patterns->map_end();
308 : 17 : ++p)
309 : : {
310 : 17 : if (p->second->classification() != Json_value::JSON_VALUE_ARRAY)
311 : : {
312 : 0 : r.error("invalid embedcfg: Patterns entry is not an array");
313 : 0 : return;
314 : : }
315 : 17 : std::vector<std::string> files;
316 : 17 : p->second->get_and_clear_array(&files);
317 : :
318 : 17 : std::pair<std::string, std::vector<std::string> > val;
319 : 17 : val.first = p->first;
320 : 17 : std::pair<Embed_patterns::iterator, bool> ins =
321 : 17 : this->embed_patterns_.insert(val);
322 : 17 : if (!ins.second)
323 : : {
324 : 0 : r.error("invalid embedcfg: duplicate Patterns entry");
325 : 0 : return;
326 : : }
327 : 17 : std::swap(ins.first->second, files);
328 : 17 : }
329 : :
330 : 8 : for (Json_value::map_iterator p = files->map_begin();
331 : 35 : p != files->map_end();
332 : 27 : ++p)
333 : : {
334 : 27 : if (p->second->classification() != Json_value::JSON_VALUE_STRING)
335 : : {
336 : 0 : r.error("invalid embedcfg: Files entry is not a string");
337 : 0 : return;
338 : : }
339 : 27 : this->embed_files_[p->first] = p->second->to_string();
340 : : }
341 : 8 : }
342 : :
343 : : // Read the contents of FILENAME into this->data_. Returns whether it
344 : : // succeeded.
345 : :
346 : : bool
347 : 8 : Embedcfg_reader::initialize_from_file()
348 : : {
349 : 8 : if (!Gogo::read_file(this->filename_, Linemap::unknown_location(),
350 : : &this->data_))
351 : : return false;
352 : 8 : if (this->data_.empty())
353 : : {
354 : 0 : this->error("empty file");
355 : 0 : return false;
356 : : }
357 : 8 : this->p_ = this->data_.data();
358 : 8 : this->pend_ = this->p_ + this->data_.size();
359 : 8 : return true;
360 : : }
361 : :
362 : : // Read a JSON object into VAL. Return whether it succeeded.
363 : :
364 : : bool
365 : 24 : Embedcfg_reader::read_object(Json_value* val)
366 : : {
367 : 24 : if (!this->skip_whitespace(false))
368 : : return false;
369 : 24 : if (*this->p_ != '{')
370 : : {
371 : 0 : this->error("expected %<{%>");
372 : 0 : return false;
373 : : }
374 : 24 : ++this->p_;
375 : :
376 : 24 : val->start_map();
377 : :
378 : 24 : if (!this->skip_whitespace(false))
379 : : return false;
380 : 24 : if (*this->p_ == '}')
381 : : {
382 : 0 : ++this->p_;
383 : 0 : return true;
384 : : }
385 : :
386 : 96 : while (true)
387 : : {
388 : 60 : if (!this->skip_whitespace(false))
389 : 24 : return false;
390 : 60 : if (*this->p_ != '"')
391 : : {
392 : 0 : this->error("expected %<\"%>");
393 : 0 : return false;
394 : : }
395 : :
396 : 60 : std::string key;
397 : 60 : if (!this->read_string(&key))
398 : : return false;
399 : :
400 : 60 : if (!this->skip_whitespace(false))
401 : : return false;
402 : 60 : if (*this->p_ != ':')
403 : : {
404 : 0 : this->error("expected %<:%>");
405 : 0 : return false;
406 : : }
407 : 60 : ++this->p_;
408 : :
409 : 60 : Json_value* subval = new Json_value();
410 : 60 : if (!this->read_value(subval))
411 : : return false;
412 : :
413 : 60 : val->add_map_entry(key, subval);
414 : :
415 : 60 : if (!this->skip_whitespace(false))
416 : : return false;
417 : 60 : if (*this->p_ == '}')
418 : : {
419 : 24 : ++this->p_;
420 : 24 : return true;
421 : : }
422 : 36 : if (*this->p_ != ',')
423 : : {
424 : 0 : this->error("expected %<,%> or %<}%>");
425 : 0 : return false;
426 : : }
427 : 36 : ++this->p_;
428 : 60 : }
429 : : }
430 : :
431 : : // Read a JSON array into VAL. Return whether it succeeded.
432 : :
433 : : bool
434 : 17 : Embedcfg_reader::read_array(Json_value* val)
435 : : {
436 : 17 : if (!this->skip_whitespace(false))
437 : : return false;
438 : 17 : if (*this->p_ != '[')
439 : : {
440 : 0 : this->error("expected %<[%>");
441 : 0 : return false;
442 : : }
443 : 17 : ++this->p_;
444 : :
445 : 17 : val->start_array();
446 : :
447 : 17 : if (!this->skip_whitespace(false))
448 : : return false;
449 : 17 : if (*this->p_ == ']')
450 : : {
451 : 0 : ++this->p_;
452 : 0 : return true;
453 : : }
454 : :
455 : 63 : while (true)
456 : : {
457 : : // If we were parsing full JSON we would call read_value here,
458 : : // not read_string.
459 : :
460 : 40 : std::string s;
461 : 40 : if (!this->read_string(&s))
462 : : return false;
463 : :
464 : 40 : val->add_array_entry(s);
465 : :
466 : 40 : if (!this->skip_whitespace(false))
467 : : return false;
468 : 40 : if (*this->p_ == ']')
469 : : {
470 : 17 : ++this->p_;
471 : 17 : return true;
472 : : }
473 : 23 : if (*this->p_ != ',')
474 : : {
475 : 0 : this->error("expected %<,%> or %<]%>");
476 : 0 : return false;
477 : : }
478 : 23 : ++this->p_;
479 : 40 : }
480 : : }
481 : :
482 : : // Read a JSON value into VAL. Return whether it succeeded.
483 : :
484 : : bool
485 : 60 : Embedcfg_reader::read_value(Json_value* val)
486 : : {
487 : 60 : if (!this->skip_whitespace(false))
488 : : return false;
489 : 60 : switch (*this->p_)
490 : : {
491 : 27 : case '"':
492 : 27 : {
493 : 27 : std::string s;
494 : 27 : if (!this->read_string(&s))
495 : : return false;
496 : 27 : val->set_string(s);
497 : 27 : return true;
498 : 27 : }
499 : :
500 : 16 : case '{':
501 : 16 : return this->read_object(val);
502 : :
503 : 17 : case '[':
504 : 17 : return this->read_array(val);
505 : :
506 : 0 : default:
507 : 0 : this->error("invalid JSON syntax");
508 : 0 : return false;
509 : : }
510 : : }
511 : :
512 : : // Read a JSON string. Return whether it succeeded.
513 : :
514 : : bool
515 : 127 : Embedcfg_reader::read_string(std::string* str)
516 : : {
517 : 127 : if (!this->skip_whitespace(false))
518 : : return false;
519 : 127 : if (*this->p_ != '"')
520 : : {
521 : 0 : this->error("expected %<\"%>");
522 : 0 : return false;
523 : : }
524 : 127 : ++this->p_;
525 : :
526 : 127 : str->clear();
527 : 3950 : while (this->p_ < this->pend_ && *this->p_ != '"')
528 : : {
529 : 3823 : if (*this->p_ != '\\')
530 : : {
531 : 3823 : str->push_back(*this->p_);
532 : 3823 : ++this->p_;
533 : 3823 : continue;
534 : : }
535 : :
536 : 0 : ++this->p_;
537 : 0 : if (this->p_ >= this->pend_)
538 : : {
539 : 0 : this->error("unterminated string");
540 : 0 : return false;
541 : : }
542 : 0 : switch (*this->p_)
543 : : {
544 : 0 : case '"': case '\\': case '/':
545 : 0 : str->push_back(*this->p_);
546 : 0 : ++this->p_;
547 : 0 : break;
548 : :
549 : 0 : case 'b':
550 : 0 : str->push_back('\b');
551 : 0 : ++this->p_;
552 : 0 : break;
553 : :
554 : 0 : case 'f':
555 : 0 : str->push_back('\f');
556 : 0 : ++this->p_;
557 : 0 : break;
558 : :
559 : 0 : case 'n':
560 : 0 : str->push_back('\n');
561 : 0 : ++this->p_;
562 : 0 : break;
563 : :
564 : 0 : case 'r':
565 : 0 : str->push_back('\r');
566 : 0 : ++this->p_;
567 : 0 : break;
568 : :
569 : 0 : case 't':
570 : 0 : str->push_back('\t');
571 : 0 : ++this->p_;
572 : 0 : break;
573 : :
574 : 0 : case 'u':
575 : 0 : {
576 : 0 : ++this->p_;
577 : 0 : unsigned int rune = 0;
578 : 0 : for (int i = 0; i < 4; i++)
579 : : {
580 : 0 : if (this->p_ >= this->pend_)
581 : : {
582 : 0 : this->error("unterminated string");
583 : 0 : return false;
584 : : }
585 : 0 : unsigned char c = *this->p_;
586 : 0 : ++this->p_;
587 : 0 : rune <<= 4;
588 : 0 : if (c >= '0' && c <= '9')
589 : 0 : rune += c - '0';
590 : 0 : else if (c >= 'A' && c <= 'F')
591 : 0 : rune += c - 'A' + 10;
592 : 0 : else if (c >= 'a' && c <= 'f')
593 : 0 : rune += c - 'a' + 10;
594 : : else
595 : : {
596 : 0 : this->error("invalid hex digit");
597 : 0 : return false;
598 : : }
599 : : }
600 : 0 : Lex::append_char(rune, false, str, Linemap::unknown_location());
601 : : }
602 : 0 : break;
603 : :
604 : 0 : default:
605 : 0 : this->error("unrecognized string escape");
606 : 0 : return false;
607 : : }
608 : : }
609 : :
610 : 127 : if (*this->p_ == '"')
611 : : {
612 : 127 : ++this->p_;
613 : 127 : return true;
614 : : }
615 : :
616 : 0 : this->error("unterminated string");
617 : 0 : return false;
618 : : }
619 : :
620 : : // Report an error if not at EOF.
621 : :
622 : : void
623 : 8 : Embedcfg_reader::check_eof()
624 : : {
625 : 8 : if (this->skip_whitespace(true))
626 : 0 : this->error("extraneous data at end of file");
627 : 8 : }
628 : :
629 : : // Skip whitespace. Return whether there is more to read.
630 : :
631 : : bool
632 : 497 : Embedcfg_reader::skip_whitespace(bool eof_ok)
633 : : {
634 : 972 : while (this->p_ < this->pend_)
635 : : {
636 : 964 : switch (*this->p_)
637 : : {
638 : 475 : case ' ': case '\t': case '\n': case '\r':
639 : 475 : ++this->p_;
640 : 475 : break;
641 : : default:
642 : : return true;
643 : : }
644 : : }
645 : 8 : if (!eof_ok)
646 : 0 : this->error("unexpected EOF");
647 : : return false;
648 : : }
649 : :
650 : : // Report an error.
651 : :
652 : : void
653 : 0 : Embedcfg_reader::error(const char* msg)
654 : : {
655 : 0 : if (!this->data_.empty() && this->p_ != NULL)
656 : 0 : go_error_at(Linemap::unknown_location(),
657 : : "%<-fgo-embedcfg%>: %s: %lu: %s",
658 : : this->filename_,
659 : 0 : static_cast<unsigned long>(this->p_ - this->data_.data()),
660 : : msg);
661 : : else
662 : 0 : go_error_at(Linemap::unknown_location(),
663 : : "%<-fgo-embedcfg%>: %s: %s",
664 : : this->filename_, msg);
665 : 0 : }
666 : :
667 : : // Implement the sort order for a list of embedded files, as discussed
668 : : // at the docs for embed.FS.
669 : :
670 : : class Embedfs_sort
671 : : {
672 : : public:
673 : : bool
674 : : operator()(const std::string& p1, const std::string& p2) const;
675 : :
676 : : private:
677 : : void
678 : : split(const std::string&, size_t*, size_t*, size_t*) const;
679 : : };
680 : :
681 : : bool
682 : 184 : Embedfs_sort::operator()(const std::string& p1, const std::string& p2) const
683 : : {
684 : 184 : size_t dirlen1, elem1, elemlen1;
685 : 184 : this->split(p1, &dirlen1, &elem1, &elemlen1);
686 : 184 : size_t dirlen2, elem2, elemlen2;
687 : 184 : this->split(p2, &dirlen2, &elem2, &elemlen2);
688 : :
689 : 184 : if (dirlen1 == 0)
690 : : {
691 : 23 : if (dirlen2 > 0)
692 : : {
693 : 12 : int i = p2.compare(0, dirlen2, ".");
694 : 12 : if (i != 0)
695 : 12 : return i > 0;
696 : : }
697 : : }
698 : 161 : else if (dirlen2 == 0)
699 : : {
700 : 36 : int i = p1.compare(0, dirlen1, ".");
701 : 36 : if (i != 0)
702 : 36 : return i < 0;
703 : : }
704 : : else
705 : : {
706 : 125 : int i = p1.compare(0, dirlen1, p2, 0, dirlen2);
707 : 125 : if (i != 0)
708 : 87 : return i < 0;
709 : : }
710 : :
711 : 49 : int i = p1.compare(elem1, elemlen1, p2, elem2, elemlen2);
712 : 49 : return i < 0;
713 : : }
714 : :
715 : : // Pick out the directory and file name components for comparison.
716 : :
717 : : void
718 : 368 : Embedfs_sort::split(const std::string& s, size_t* dirlen, size_t* elem,
719 : : size_t* elemlen) const
720 : : {
721 : 368 : size_t len = s.size();
722 : 368 : if (len > 0 && s[len - 1] == '/')
723 : : --len;
724 : 368 : size_t slash = s.rfind('/', len - 1);
725 : 368 : if (slash == std::string::npos)
726 : : {
727 : 70 : *dirlen = 0;
728 : 70 : *elem = 0;
729 : 70 : *elemlen = len;
730 : : }
731 : : else
732 : : {
733 : 298 : *dirlen = slash;
734 : 298 : *elem = slash + 1;
735 : 298 : *elemlen = len - (slash + 1);
736 : : }
737 : 368 : }
738 : :
739 : : // Convert the go:embed directives for a variable into an initializer
740 : : // for that variable.
741 : :
742 : : Expression*
743 : 22 : Gogo::initializer_for_embeds(Type* type,
744 : : const std::vector<std::string>* embeds,
745 : : Location loc)
746 : : {
747 : 22 : if (this->embed_patterns_.empty())
748 : : {
749 : 0 : go_error_at(loc,
750 : : ("invalid go:embed: build system did not "
751 : : "supply embed configuration"));
752 : 0 : return Expression::make_error(loc);
753 : : }
754 : :
755 : 22 : type = type->unalias();
756 : :
757 : 22 : enum {
758 : : EMBED_STRING = 0,
759 : : EMBED_BYTES = 1,
760 : : EMBED_FS = 2
761 : : } embed_kind;
762 : :
763 : 22 : const Named_type* nt = type->named_type();
764 : 22 : if (nt != NULL
765 : 16 : && nt->named_object()->package() != NULL
766 : 11 : && nt->named_object()->package()->pkgpath() == "embed"
767 : 33 : && nt->name() == "FS")
768 : : embed_kind = EMBED_FS;
769 : 11 : else if (type->is_string_type())
770 : : embed_kind = EMBED_STRING;
771 : 7 : else if (type->is_slice_type()
772 : 14 : && type->array_type()->element_type()->integer_type() != NULL
773 : 21 : && type->array_type()->element_type()->integer_type()->is_byte())
774 : : embed_kind = EMBED_BYTES;
775 : : else
776 : : {
777 : 0 : go_error_at(loc, "invalid type for go:embed");
778 : 0 : return Expression::make_error(loc);
779 : : }
780 : :
781 : : // The patterns in the go:embed directive(s) are in EMBEDS. Find
782 : : // them in the patterns in the embedcfg file.
783 : :
784 : 22 : Unordered_set(std::string) have;
785 : 22 : std::vector<std::string> paths;
786 : 46 : for (std::vector<std::string>::const_iterator pe = embeds->begin();
787 : 46 : pe != embeds->end();
788 : 24 : pe++)
789 : : {
790 : 24 : Embed_patterns::const_iterator pp = this->embed_patterns_.find(*pe);
791 : 24 : if (pp == this->embed_patterns_.end())
792 : : {
793 : 0 : go_error_at(loc,
794 : : ("invalid go:embed: build system did not "
795 : : "map pattern %qs"),
796 : : pe->c_str());
797 : 0 : continue;
798 : : }
799 : :
800 : : // Each pattern in the embedcfg file maps to a list of file
801 : : // names. Add those file names to PATHS.
802 : 77 : for (std::vector<std::string>::const_iterator pf = pp->second.begin();
803 : 77 : pf != pp->second.end();
804 : 53 : pf++)
805 : : {
806 : 53 : if (this->embed_files_.find(*pf) == this->embed_files_.end())
807 : : {
808 : 0 : go_error_at(loc,
809 : : ("invalid go:embed: build system did not "
810 : : "map file %qs"),
811 : : pf->c_str());
812 : 0 : continue;
813 : : }
814 : :
815 : 53 : std::pair<Unordered_set(std::string)::iterator, bool> ins
816 : 53 : = have.insert(*pf);
817 : 53 : if (ins.second)
818 : : {
819 : 53 : const std::string& path(*pf);
820 : 53 : paths.push_back(path);
821 : :
822 : 53 : if (embed_kind == EMBED_FS)
823 : : {
824 : : // Add each required directory, with a trailing slash.
825 : : size_t i = std::string::npos;
826 : 94 : while (i > 0)
827 : : {
828 : 91 : i = path.rfind('/', i);
829 : 91 : if (i == std::string::npos)
830 : : break;
831 : 52 : std::string dir = path.substr(0, i + 1);
832 : 52 : ins = have.insert(dir);
833 : 52 : if (ins.second)
834 : 23 : paths.push_back(dir);
835 : 52 : --i;
836 : 52 : }
837 : : }
838 : : }
839 : : }
840 : : }
841 : :
842 : 22 : if (embed_kind == EMBED_STRING || embed_kind == EMBED_BYTES)
843 : : {
844 : 11 : if (paths.size() > 1)
845 : : {
846 : 0 : go_error_at(loc,
847 : : ("invalid go:embed: multiple files for "
848 : 0 : "string or byte slice"));;
849 : 0 : return Expression::make_error(loc);
850 : : }
851 : :
852 : 11 : std::string data;
853 : 11 : if (!Gogo::read_file(this->embed_files_[paths[0]].c_str(), loc, &data))
854 : 0 : return Expression::make_error(loc);
855 : :
856 : 11 : Expression* e = Expression::make_string(data, loc);
857 : 11 : if (embed_kind == EMBED_BYTES)
858 : 7 : e = Expression::make_cast(type, e, loc);
859 : 11 : return e;
860 : 11 : }
861 : :
862 : 11 : std::sort(paths.begin(), paths.end(), Embedfs_sort());
863 : :
864 : 22 : if (type->struct_type() == NULL
865 : 11 : || type->struct_type()->field_count() != 1)
866 : : {
867 : 0 : go_error_at(loc,
868 : : ("internal error: embed.FS should be struct type "
869 : : "with one field"));
870 : 0 : return Expression::make_error(loc);
871 : : }
872 : :
873 : 22 : Type* ptr_type = type->struct_type()->field(0)->type();
874 : 11 : if (ptr_type->points_to() == NULL)
875 : : {
876 : 0 : go_error_at(loc,
877 : : "internal error: embed.FS struct field should be pointer");
878 : 0 : return Expression::make_error(loc);
879 : : }
880 : :
881 : 11 : Type* slice_type = ptr_type->points_to();
882 : 11 : if (!slice_type->is_slice_type())
883 : : {
884 : 0 : go_error_at(loc,
885 : : ("internal error: embed.FS struct field should be "
886 : : "pointer to slice"));
887 : 0 : return Expression::make_error(loc);
888 : : }
889 : :
890 : 22 : Type* file_type = slice_type->array_type()->element_type();
891 : 22 : if (file_type->struct_type() == NULL
892 : 33 : || (file_type->struct_type()->find_local_field(".embed.name", NULL)
893 : : == NULL)
894 : 33 : || (file_type->struct_type()->find_local_field(".embed.data", NULL)
895 : : == NULL))
896 : : {
897 : 0 : go_error_at(loc,
898 : : ("internal error: embed.FS slice element should be struct "
899 : : "with name and data fields"));
900 : 0 : return Expression::make_error(loc);
901 : : }
902 : :
903 : 22 : const Struct_field_list* file_fields = file_type->struct_type()->fields();
904 : 11 : Expression_list* file_vals = new(Expression_list);
905 : 11 : file_vals->reserve(paths.size());
906 : 11 : for (std::vector<std::string>::const_iterator pp = paths.begin();
907 : 76 : pp != paths.end();
908 : 65 : ++pp)
909 : : {
910 : 65 : std::string data;
911 : 65 : if ((*pp)[pp->size() - 1] != '/')
912 : : {
913 : 42 : if (!Gogo::read_file(this->embed_files_[*pp].c_str(), loc, &data))
914 : 0 : return Expression::make_error(loc);
915 : : }
916 : :
917 : 65 : Expression_list* field_vals = new(Expression_list);
918 : 260 : for (Struct_field_list::const_iterator pf = file_fields->begin();
919 : 260 : pf != file_fields->end();
920 : 195 : ++pf)
921 : : {
922 : 195 : if (pf->is_field_name(".embed.name"))
923 : 65 : field_vals->push_back(Expression::make_string(*pp, loc));
924 : 130 : else if (pf->is_field_name(".embed.data"))
925 : 65 : field_vals->push_back(Expression::make_string(data, loc));
926 : : else
927 : : {
928 : : // FIXME: The embed.file type has a hash field, which is
929 : : // currently unused. We should fill it in, but don't.
930 : : // The hash is a SHA256, and we don't have convenient
931 : : // SHA256 code. Do this later when the field is
932 : : // actually used.
933 : 65 : field_vals->push_back(NULL);
934 : : }
935 : : }
936 : :
937 : 65 : Expression* file_val =
938 : 65 : Expression::make_struct_composite_literal(file_type, field_vals, loc);
939 : 65 : file_vals->push_back(file_val);
940 : 65 : }
941 : :
942 : 11 : Expression* slice_init =
943 : 11 : Expression::make_slice_composite_literal(slice_type, file_vals, loc);
944 : 11 : Expression* fs_init = Expression::make_heap_expression(slice_init, loc);
945 : 11 : Expression_list* fs_vals = new Expression_list();
946 : 11 : fs_vals->push_back(fs_init);
947 : 11 : return Expression::make_struct_composite_literal(type, fs_vals, loc);
948 : 44 : }
|