1 // embed.cc -- Go frontend go:embed handling.
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.
10 #include "go-diagnostics.h"
18 // Read a file into *DATA. Returns false on error.
21 read_file(const char* filename
, Location loc
, std::string
* data
)
23 int fd
= open(filename
, O_RDONLY
| O_BINARY
);
26 go_error_at(loc
, "%s: %m", filename
);
31 if (fstat(fd
, &st
) < 0)
33 go_error_at(loc
, "%s: %m", filename
);
36 off_t want
= st
.st_size
;
38 // Most files read here are going to be incorporated into the object file
39 // and then the executable. Set a limit on the size we will accept.
40 if (want
> 2000000000)
42 go_error_at(loc
, "%s: file too large", filename
);
50 // C++11 requires that std::string use contiguous bytes, so this
52 ssize_t n
= read(fd
, &(*data
)[got
], want
);
56 go_error_at(loc
, "%s: %m", filename
);
72 // A JSON value as read from an embedcfg file. For our purposes a
73 // JSON value is a string, or a list of strings, or a mapping from
74 // strings to values. We don't expect any numbers. We also don't
75 // expect an array of anything other than strings; that is, we don't
76 // accept an array of general JSON values.
81 // The types of values.
82 enum Json_value_classification
91 : classification_(JSON_VALUE_UNKNOWN
), string_(), array_(), map_()
96 Json_value_classification
97 classification() const
98 { return this->classification_
; }
100 // Set to a string value.
102 set_string(const std::string
& str
)
104 go_assert(this->classification_
== JSON_VALUE_UNKNOWN
);
105 this->classification_
= JSON_VALUE_STRING
;
109 // Start an array value.
113 go_assert(this->classification_
== JSON_VALUE_UNKNOWN
);
114 this->classification_
= JSON_VALUE_ARRAY
;
117 // Add an array entry.
119 add_array_entry(const std::string
& s
)
121 go_assert(this->classification_
== JSON_VALUE_ARRAY
);
122 this->array_
.push_back(s
);
125 // Start a map value.
129 go_assert(this->classification_
== JSON_VALUE_UNKNOWN
);
130 this->classification_
= JSON_VALUE_MAP
;
135 add_map_entry(const std::string
& key
, Json_value
* val
)
137 go_assert(this->classification_
== JSON_VALUE_MAP
);
138 this->map_
[key
] = val
;
141 // Return the strings from a string value.
145 go_assert(this->classification_
== JSON_VALUE_STRING
);
146 return this->string_
;
149 // Fetch a vector of strings, and drop them from the JSON value.
151 get_and_clear_array(std::vector
<std::string
>* v
)
153 go_assert(this->classification_
== JSON_VALUE_ARRAY
);
154 std::swap(*v
, this->array_
);
157 // Look up a map entry. Returns NULL if not found.
159 lookup_map_entry(const std::string
& key
);
161 // Iterate over a map.
162 typedef Unordered_map(std::string
, Json_value
*)::iterator map_iterator
;
167 go_assert(this->classification_
== JSON_VALUE_MAP
);
168 return this->map_
.begin();
173 { return this->map_
.end(); }
177 Json_value_classification classification_
;
178 // A string, for JSON_VALUE_STRING.
180 // Array, for JSON_VALUE_ARRAY.
181 std::vector
<std::string
> array_
;
182 // Mapping, for JSON_VALUE_MAP.
183 Unordered_map(std::string
, Json_value
*) map_
;
186 // Delete a JSON value.
188 Json_value::~Json_value()
190 if (this->classification_
== JSON_VALUE_MAP
)
192 for (map_iterator p
= this->map_begin();
193 p
!= this->map_end();
199 // Look up a map entry in a JSON value.
202 Json_value::lookup_map_entry(const std::string
& key
)
204 go_assert(this->classification_
== JSON_VALUE_MAP
);
205 Unordered_map(std::string
, Json_value
*)::iterator p
= this->map_
.find(key
);
206 if (p
== this->map_
.end())
211 // Manage reading the embedcfg file.
213 class Embedcfg_reader
216 Embedcfg_reader(const char* filename
)
217 : filename_(filename
), data_(), p_(NULL
), pend_(NULL
)
220 // Read the contents of FILENAME. Return whether it succeeded.
222 initialize_from_file();
224 // Read a JSON object.
226 read_object(Json_value
*);
228 // Report an error if not at EOF.
232 // Report an error for the embedcfg file.
234 error(const char* msg
);
238 read_value(Json_value
*);
241 read_array(Json_value
*);
244 read_string(std::string
*);
247 skip_whitespace(bool eof_ok
);
250 const char* filename_
;
253 // Next character to process.
259 // Read the embedcfg file.
262 Gogo::read_embedcfg(const char *filename
)
264 class Embedcfg_reader
r(filename
);
265 if (!r
.initialize_from_file())
269 if (!r
.read_object(&val
))
274 if (val
.classification() != Json_value::JSON_VALUE_MAP
)
276 r
.error("invalid embedcfg: not a JSON object");
280 Json_value
* patterns
= val
.lookup_map_entry("Patterns");
281 if (patterns
== NULL
)
283 r
.error("invalid embedcfg: missing Patterns");
286 if (patterns
->classification() != Json_value::JSON_VALUE_MAP
)
288 r
.error("invalid embedcfg: Patterns is not a JSON object");
292 Json_value
* files
= val
.lookup_map_entry("Files");
295 r
.error("invalid embedcfg: missing Files");
298 if (files
->classification() != Json_value::JSON_VALUE_MAP
)
300 r
.error("invalid embedcfg: Files is not a JSON object");
304 // TODO: Actually do something with patterns and files.
307 // Read the contents of FILENAME into this->data_. Returns whether it
311 Embedcfg_reader::initialize_from_file()
313 if (!read_file(this->filename_
, Linemap::unknown_location(), &this->data_
))
315 if (this->data_
.empty())
317 this->error("empty file");
320 this->p_
= this->data_
.data();
321 this->pend_
= this->p_
+ this->data_
.size();
325 // Read a JSON object into VAL. Return whether it succeeded.
328 Embedcfg_reader::read_object(Json_value
* val
)
330 if (!this->skip_whitespace(false))
332 if (*this->p_
!= '{')
334 this->error("expected %<{%>");
341 if (!this->skip_whitespace(false))
343 if (*this->p_
== '}')
351 if (!this->skip_whitespace(false))
353 if (*this->p_
!= '"')
355 this->error("expected %<\"%>");
360 if (!this->read_string(&key
))
363 if (!this->skip_whitespace(false))
365 if (*this->p_
!= ':')
367 this->error("expected %<:%>");
372 Json_value
* subval
= new Json_value();
373 if (!this->read_value(subval
))
376 val
->add_map_entry(key
, subval
);
378 if (!this->skip_whitespace(false))
380 if (*this->p_
== '}')
385 if (*this->p_
!= ',')
387 this->error("expected %<,%> or %<}%>");
394 // Read a JSON array into VAL. Return whether it succeeded.
397 Embedcfg_reader::read_array(Json_value
* val
)
399 if (!this->skip_whitespace(false))
401 if (*this->p_
!= '[')
403 this->error("expected %<[%>");
410 if (!this->skip_whitespace(false))
412 if (*this->p_
== ']')
420 // If we were parsing full JSON we would call read_value here,
424 if (!this->read_string(&s
))
427 val
->add_array_entry(s
);
429 if (!this->skip_whitespace(false))
431 if (*this->p_
== ']')
436 if (*this->p_
!= ',')
438 this->error("expected %<,%> or %<]%>");
445 // Read a JSON value into VAL. Return whether it succeeded.
448 Embedcfg_reader::read_value(Json_value
* val
)
450 if (!this->skip_whitespace(false))
457 if (!this->read_string(&s
))
464 return this->read_object(val
);
467 return this->read_array(val
);
470 this->error("invalid JSON syntax");
475 // Read a JSON string. Return whether it succeeded.
478 Embedcfg_reader::read_string(std::string
* str
)
480 if (!this->skip_whitespace(false))
482 if (*this->p_
!= '"')
484 this->error("expected %<\"%>");
490 while (this->p_
< this->pend_
&& *this->p_
!= '"')
492 if (*this->p_
!= '\\')
494 str
->push_back(*this->p_
);
500 if (this->p_
>= this->pend_
)
502 this->error("unterminated string");
507 case '"': case '\\': case '/':
508 str
->push_back(*this->p_
);
513 str
->push_back('\b');
518 str
->push_back('\f');
523 str
->push_back('\n');
528 str
->push_back('\r');
533 str
->push_back('\t');
540 unsigned int rune
= 0;
541 for (int i
= 0; i
< 4; i
++)
543 if (this->p_
>= this->pend_
)
545 this->error("unterminated string");
548 unsigned char c
= *this->p_
;
551 if (c
>= '0' && c
<= '9')
553 else if (c
>= 'A' && c
<= 'F')
554 rune
+= c
- 'A' + 10;
555 else if (c
>= 'a' && c
<= 'f')
556 rune
+= c
- 'a' + 10;
559 this->error("invalid hex digit");
563 Lex::append_char(rune
, false, str
, Linemap::unknown_location());
568 this->error("unrecognized string escape");
573 if (*this->p_
== '"')
579 this->error("unterminated string");
583 // Report an error if not at EOF.
586 Embedcfg_reader::check_eof()
588 if (this->skip_whitespace(true))
589 this->error("extraneous data at end of file");
592 // Skip whitespace. Return whether there is more to read.
595 Embedcfg_reader::skip_whitespace(bool eof_ok
)
597 while (this->p_
< this->pend_
)
601 case ' ': case '\t': case '\n': case '\r':
609 this->error("unexpected EOF");
616 Embedcfg_reader::error(const char* msg
)
618 if (!this->data_
.empty() && this->p_
!= NULL
)
619 go_error_at(Linemap::unknown_location(),
620 "%<-fgo-embedcfg%>: %s: %lu: %s",
622 static_cast<unsigned long>(this->p_
- this->data_
.data()),
625 go_error_at(Linemap::unknown_location(),
626 "%<-fgo-embedcfg%>: %s: %s",
627 this->filename_
, msg
);