#include "operator.h"
#include "go-diagnostics.h"
#include "lex.h"
+#include "types.h"
+#include "expressions.h"
#include "gogo.h"
#ifndef O_BINARY
return;
}
- // TODO: Actually do something with patterns and files.
+ for (Json_value::map_iterator p = patterns->map_begin();
+ p != patterns->map_end();
+ ++p)
+ {
+ if (p->second->classification() != Json_value::JSON_VALUE_ARRAY)
+ {
+ r.error("invalid embedcfg: Patterns entry is not an array");
+ return;
+ }
+ std::vector<std::string> files;
+ p->second->get_and_clear_array(&files);
+
+ std::pair<std::string, std::vector<std::string> > val;
+ val.first = p->first;
+ std::pair<Embed_patterns::iterator, bool> ins =
+ this->embed_patterns_.insert(val);
+ if (!ins.second)
+ {
+ r.error("invalid embedcfg: duplicate Patterns entry");
+ return;
+ }
+ std::swap(ins.first->second, files);
+ }
+
+ for (Json_value::map_iterator p = files->map_begin();
+ p != files->map_end();
+ ++p)
+ {
+ if (p->second->classification() != Json_value::JSON_VALUE_STRING)
+ {
+ r.error("invalid embedcfg: Files entry is not a string");
+ return;
+ }
+ this->embed_files_[p->first] = p->second->to_string();
+ }
}
// Read the contents of FILENAME into this->data_. Returns whether it
// the package has been imported if there is at least one alias.
return !p->second->aliases().empty();
}
+
+// Implement the sort order for a list of embedded files, as discussed
+// at the docs for embed.FS.
+
+class Embedfs_sort
+{
+ public:
+ bool
+ operator()(const std::string& p1, const std::string& p2) const;
+
+ private:
+ void
+ split(const std::string&, size_t*, size_t*, size_t*) const;
+};
+
+bool
+Embedfs_sort::operator()(const std::string& p1, const std::string& p2) const
+{
+ size_t dirlen1, elem1, elemlen1;
+ this->split(p1, &dirlen1, &elem1, &elemlen1);
+ size_t dirlen2, elem2, elemlen2;
+ this->split(p2, &dirlen2, &elem2, &elemlen2);
+
+ if (dirlen1 == 0)
+ {
+ if (dirlen2 > 0)
+ {
+ int i = p2.compare(0, dirlen2, ".");
+ if (i != 0)
+ return i > 0;
+ }
+ }
+ else if (dirlen2 == 0)
+ {
+ int i = p1.compare(0, dirlen1, ".");
+ if (i != 0)
+ return i < 0;
+ }
+ else
+ {
+ int i = p1.compare(0, dirlen1, p2, 0, dirlen2);
+ if (i != 0)
+ return i < 0;
+ }
+
+ int i = p1.compare(elem1, elemlen1, p2, elem2, elemlen2);
+ return i < 0;
+}
+
+// Pick out the directory and file name components for comparison.
+
+void
+Embedfs_sort::split(const std::string& s, size_t* dirlen, size_t* elem,
+ size_t* elemlen) const
+{
+ size_t len = s.size();
+ if (len > 0 && s[len - 1] == '/')
+ --len;
+ size_t slash = s.rfind('/', len - 1);
+ if (slash == std::string::npos)
+ {
+ *dirlen = 0;
+ *elem = 0;
+ *elemlen = len;
+ }
+ else
+ {
+ *dirlen = slash;
+ *elem = slash + 1;
+ *elemlen = len - (slash + 1);
+ }
+}
+
+// Convert the go:embed directives for a variable into an initializer
+// for that variable.
+
+Expression*
+Gogo::initializer_for_embeds(Type* type,
+ const std::vector<std::string>* embeds,
+ Location loc)
+{
+ if (this->embed_patterns_.empty())
+ {
+ go_error_at(loc,
+ ("invalid go:embed: build system did not "
+ "supply embed configuration"));
+ return Expression::make_error(loc);
+ }
+
+ type = type->unalias();
+
+ enum {
+ EMBED_STRING = 0,
+ EMBED_BYTES = 1,
+ EMBED_FS = 2
+ } embed_kind;
+
+ const Named_type* nt = type->named_type();
+ if (nt != NULL
+ && nt->named_object()->package() != NULL
+ && nt->named_object()->package()->pkgpath() == "embed"
+ && nt->name() == "FS")
+ embed_kind = EMBED_FS;
+ else if (type->is_string_type())
+ embed_kind = EMBED_STRING;
+ else if (type->is_slice_type()
+ && type->array_type()->element_type()->integer_type() != NULL
+ && type->array_type()->element_type()->integer_type()->is_byte())
+ embed_kind = EMBED_BYTES;
+ else
+ {
+ go_error_at(loc, "invalid type for go:embed");
+ return Expression::make_error(loc);
+ }
+
+ // The patterns in the go:embed directive(s) are in EMBEDS. Find
+ // them in the patterns in the embedcfg file.
+
+ Unordered_set(std::string) have;
+ std::vector<std::string> paths;
+ for (std::vector<std::string>::const_iterator pe = embeds->begin();
+ pe != embeds->end();
+ pe++)
+ {
+ Embed_patterns::const_iterator pp = this->embed_patterns_.find(*pe);
+ if (pp == this->embed_patterns_.end())
+ {
+ go_error_at(loc,
+ ("invalid go:embed: build system did not "
+ "map pattern %<%s%>"),
+ pe->c_str());
+ continue;
+ }
+
+ // Each pattern in the embedcfg file maps to a list of file
+ // names. For each file name, the embedcfg file records an
+ // absolute path. Add those absolute paths to PATHS.
+ for (std::vector<std::string>::const_iterator pf = pp->second.begin();
+ pf != pp->second.end();
+ pf++)
+ {
+ if (this->embed_files_.find(*pf) == this->embed_files_.end())
+ {
+ go_error_at(loc,
+ ("invalid go:embed: build system did not "
+ "map file %<%s%>"),
+ pf->c_str());
+ continue;
+ }
+
+ std::pair<Unordered_set(std::string)::iterator, bool> ins
+ = have.insert(*pf);
+ if (ins.second)
+ {
+ const std::string& path(*pf);
+ paths.push_back(path);
+
+ if (embed_kind == EMBED_FS)
+ {
+ // Add each required directory, with a trailing slash.
+ size_t i = std::string::npos;
+ while (i > 0)
+ {
+ i = path.rfind('/', i);
+ if (i == std::string::npos)
+ break;
+ std::string dir = path.substr(0, i + 1);
+ ins = have.insert(dir);
+ if (ins.second)
+ paths.push_back(dir);
+ --i;
+ }
+ }
+ }
+ }
+ }
+
+ if (embed_kind == EMBED_STRING || embed_kind == EMBED_BYTES)
+ {
+ if (paths.size() > 1)
+ {
+ go_error_at(loc,
+ ("invalid go:embed: multiple files for "
+ "string or byte slice"));;
+ return Expression::make_error(loc);
+ }
+
+ std::string data;
+ if (!read_file(paths[0].c_str(), loc, &data))
+ return Expression::make_error(loc);
+
+ Expression* e = Expression::make_string(data, loc);
+ if (embed_kind == EMBED_BYTES)
+ e = Expression::make_cast(type, e, loc);
+ return e;
+ }
+
+ std::sort(paths.begin(), paths.end(), Embedfs_sort());
+
+ if (type->struct_type() == NULL
+ || type->struct_type()->field_count() != 1)
+ {
+ go_error_at(loc,
+ ("internal error: embed.FS should be struct type "
+ "with one field"));
+ return Expression::make_error(loc);
+ }
+
+ Type* ptr_type = type->struct_type()->field(0)->type();
+ if (ptr_type->points_to() == NULL)
+ {
+ go_error_at(loc,
+ "internal error: embed.FS struct field should be pointer");
+ return Expression::make_error(loc);
+ }
+
+ Type* slice_type = ptr_type->points_to();
+ if (!slice_type->is_slice_type())
+ {
+ go_error_at(loc,
+ ("internal error: embed.FS struct field should be "
+ "pointer to slice"));
+ return Expression::make_error(loc);
+ }
+
+ Type* file_type = slice_type->array_type()->element_type();
+ if (file_type->struct_type() == NULL
+ || (file_type->struct_type()->find_local_field(".embed.name", NULL)
+ == NULL)
+ || (file_type->struct_type()->find_local_field(".embed.data", NULL)
+ == NULL))
+ {
+ go_error_at(loc,
+ ("internal error: embed.FS slice element should be struct "
+ "with name and data fields"));
+ return Expression::make_error(loc);
+ }
+
+ const Struct_field_list* file_fields = file_type->struct_type()->fields();
+ Expression_list* file_vals = new(Expression_list);
+ file_vals->reserve(paths.size());
+ for (std::vector<std::string>::const_iterator pp = paths.begin();
+ pp != paths.end();
+ ++pp)
+ {
+ std::string data;
+ if ((*pp)[pp->size() - 1] != '/')
+ {
+ if (!read_file(this->embed_files_[*pp].c_str(), loc, &data))
+ return Expression::make_error(loc);
+ }
+
+ Expression_list* field_vals = new(Expression_list);
+ for (Struct_field_list::const_iterator pf = file_fields->begin();
+ pf != file_fields->end();
+ ++pf)
+ {
+ if (pf->is_field_name(".embed.name"))
+ field_vals->push_back(Expression::make_string(*pp, loc));
+ else if (pf->is_field_name(".embed.data"))
+ field_vals->push_back(Expression::make_string(data, loc));
+ else
+ {
+ // FIXME: The embed.file type has a hash field, which is
+ // currently unused. We should fill it in, but don't.
+ // The hash is a SHA256, and we don't have convenient
+ // SHA256 code. Do this later when the field is
+ // actually used.
+ field_vals->push_back(NULL);
+ }
+ }
+
+ Expression* file_val =
+ Expression::make_struct_composite_literal(file_type, field_vals, loc);
+ file_vals->push_back(file_val);
+ }
+
+ Expression* slice_init =
+ Expression::make_slice_composite_literal(slice_type, file_vals, loc);
+ Expression* fs_init = Expression::make_heap_expression(slice_init, loc);
+ Expression_list* fs_vals = new Expression_list();
+ fs_vals->push_back(fs_init);
+ return Expression::make_struct_composite_literal(type, fs_vals, loc);
+}