LCOV - code coverage report
Current view: top level - gcc/go/gofrontend - embed.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 69.5 % 442 307
Test Date: 2026-02-28 14:20:25 Functions: 95.8 % 24 23
Legend: Lines:     hit not hit

            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           68 : }
     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         3972 :   while (this->p_ < this->pend_ && *this->p_ != '"')
     528              :     {
     529         3845 :       if (*this->p_ != '\\')
     530              :         {
     531         3845 :           str->push_back(*this->p_);
     532         3845 :           ++this->p_;
     533         3845 :           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           22 : }
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.