github.com/kidsbmilk/gofronted_all@v0.0.0-20220701224323-6479d5976c5d/go/embed.cc (about)

     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
    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  
    92    Json_value()
    93      : classification_(JSON_VALUE_UNKNOWN), string_(), array_(), map_()
    94    { }
    95  
    96    ~Json_value();
    97  
    98    Json_value_classification
    99    classification() const
   100    { return this->classification_; }
   101  
   102    // Set to a string value.
   103    void
   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
   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
   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
   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
   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&
   145    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
   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
   167    map_begin()
   168    {
   169      go_assert(this->classification_ == JSON_VALUE_MAP);
   170      return this->map_.begin();
   171    }
   172  
   173    map_iterator
   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  
   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*
   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:
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   681  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
   717  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*
   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  }