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