// import.cc -- Go frontend import declarations. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "go-system.h" #include "filenames.h" #include "go-c.h" #include "go-diagnostics.h" #include "gogo.h" #include "lex.h" #include "types.h" #include "export.h" #include "import.h" #ifndef O_BINARY #define O_BINARY 0 #endif // The list of paths we search for import files. static std::vector search_path; // Add a directory to the search path. This is called from the option // handling language hook. GO_EXTERN_C void go_add_search_path(const char* path) { search_path.push_back(std::string(path)); } // Find import data. This searches the file system for FILENAME and // returns a pointer to a Stream object to read the data that it // exports. If the file is not found, it returns NULL. // When FILENAME is not an absolute path and does not start with ./ or // ../, we use the search path provided by -I and -L options. // When FILENAME does start with ./ or ../, we use // RELATIVE_IMPORT_PATH as a prefix. // When FILENAME does not exist, we try modifying FILENAME to find the // file. We use the first of these which exists: // * We append ".gox". // * We turn the base of FILENAME into libFILENAME.so. // * We turn the base of FILENAME into libFILENAME.a. // * We append ".o". // When using a search path, we apply each of these transformations at // each entry on the search path before moving on to the next entry. // If the file exists, but does not contain any Go export data, we // stop; we do not keep looking for another file with the same name // later in the search path. Import::Stream* Import::open_package(const std::string& filename, Location location, const std::string& relative_import_path) { bool is_local; if (IS_ABSOLUTE_PATH(filename)) is_local = true; else if (filename[0] == '.' && (filename[1] == '\0' || IS_DIR_SEPARATOR(filename[1]))) is_local = true; else if (filename[0] == '.' && filename[1] == '.' && (filename[2] == '\0' || IS_DIR_SEPARATOR(filename[2]))) is_local = true; else is_local = false; std::string fn = filename; if (is_local && !IS_ABSOLUTE_PATH(filename) && !relative_import_path.empty()) { if (fn == ".") { // A special case. fn = relative_import_path; } else if (fn[0] == '.' && fn[1] == '.' && (fn[2] == '\0' || IS_DIR_SEPARATOR(fn[2]))) { // We are going to join relative_import_path and fn, and it // will look like DIR/../PATH. But DIR does not necessarily // exist in this case, and if it doesn't the use of .. will // fail although it shouldn't. The gc compiler uses // path.Join here, which cleans up the .., so we need to do // the same. size_t index; for (index = relative_import_path.length() - 1; index > 0 && !IS_DIR_SEPARATOR(relative_import_path[index]); index--) ; if (index > 0) fn = relative_import_path.substr(0, index) + fn.substr(2); else fn = relative_import_path + '/' + fn; } else fn = relative_import_path + '/' + fn; is_local = false; } if (!is_local) { for (std::vector::const_iterator p = search_path.begin(); p != search_path.end(); ++p) { std::string indir = *p; if (!indir.empty() && indir[indir.size() - 1] != '/') indir += '/'; indir += fn; Stream* s = Import::try_package_in_directory(indir, location); if (s != NULL) return s; } } Stream* s = Import::try_package_in_directory(fn, location); if (s != NULL) return s; return NULL; } // Try to find the export data for FILENAME. Import::Stream* Import::try_package_in_directory(const std::string& filename, Location location) { std::string found_filename = filename; int fd = open(found_filename.c_str(), O_RDONLY | O_BINARY); if (fd >= 0) { struct stat s; if (fstat(fd, &s) >= 0 && S_ISDIR(s.st_mode)) { close(fd); fd = -1; errno = EISDIR; } } if (fd < 0) { if (errno != ENOENT && errno != EISDIR) go_warning_at(location, 0, "%s: %m", filename.c_str()); fd = Import::try_suffixes(&found_filename); if (fd < 0) return NULL; } // The export data may not be in this file. Stream* s = Import::find_export_data(found_filename, fd, location); if (s != NULL) return s; close(fd); go_error_at(location, "%s exists but does not contain any Go export data", found_filename.c_str()); return NULL; } // Given import "*PFILENAME", where *PFILENAME does not exist, try // various suffixes. If we find one, set *PFILENAME to the one we // found. Return the open file descriptor. int Import::try_suffixes(std::string* pfilename) { std::string filename = *pfilename + ".gox"; int fd = open(filename.c_str(), O_RDONLY | O_BINARY); if (fd >= 0) { *pfilename = filename; return fd; } const char* basename = lbasename(pfilename->c_str()); size_t basename_pos = basename - pfilename->c_str(); filename = pfilename->substr(0, basename_pos) + "lib" + basename + ".so"; fd = open(filename.c_str(), O_RDONLY | O_BINARY); if (fd >= 0) { *pfilename = filename; return fd; } filename = pfilename->substr(0, basename_pos) + "lib" + basename + ".a"; fd = open(filename.c_str(), O_RDONLY | O_BINARY); if (fd >= 0) { *pfilename = filename; return fd; } filename = *pfilename + ".o"; fd = open(filename.c_str(), O_RDONLY | O_BINARY); if (fd >= 0) { *pfilename = filename; return fd; } return -1; } // Look for export data in the file descriptor FD. Import::Stream* Import::find_export_data(const std::string& filename, int fd, Location location) { // See if we can read this as an object file. Import::Stream* stream = Import::find_object_export_data(filename, fd, 0, location); if (stream != NULL) return stream; const int len = MAX(Export::magic_len, Import::archive_magic_len); if (lseek(fd, 0, SEEK_SET) < 0) { go_error_at(location, "lseek %s failed: %m", filename.c_str()); return NULL; } char buf[len]; ssize_t c = ::read(fd, buf, len); if (c < len) return NULL; // Check for a file containing nothing but Go export data. if (memcmp(buf, Export::cur_magic, Export::magic_len) == 0 || memcmp(buf, Export::v1_magic, Export::magic_len) == 0 || memcmp(buf, Export::v2_magic, Export::magic_len) == 0) return new Stream_from_file(fd); // See if we can read this as an archive. if (Import::is_archive_magic(buf)) return Import::find_archive_export_data(filename, fd, location); return NULL; } // Look for export data in an object file. Import::Stream* Import::find_object_export_data(const std::string& filename, int fd, off_t offset, Location location) { char *buf; size_t len; int err; const char *errmsg = go_read_export_data(fd, offset, &buf, &len, &err); if (errmsg != NULL) { if (err == 0) go_error_at(location, "%s: %s", filename.c_str(), errmsg); else go_error_at(location, "%s: %s: %s", filename.c_str(), errmsg, xstrerror(err)); return NULL; } if (buf == NULL) return NULL; return new Stream_from_buffer(buf, len); } // Class Import. // Construct an Import object. We make the builtin_types_ vector // large enough to hold all the builtin types. Import::Import(Stream* stream, Location location) : gogo_(NULL), stream_(stream), location_(location), package_(NULL), add_to_globals_(false), packages_(), type_data_(), type_pos_(0), type_offsets_(), builtin_types_((- SMALLEST_BUILTIN_CODE) + 1), types_(), version_(EXPORT_FORMAT_UNKNOWN) { } // Import the data in the associated stream. Package* Import::import(Gogo* gogo, const std::string& local_name, bool is_local_name_exported) { // Hold on to the Gogo structure. Otherwise we need to pass it // through all the import functions, because we need it when reading // a type. this->gogo_ = gogo; // A stream of export data can include data from more than one input // file. Here we loop over each input file. Stream* stream = this->stream_; while (!stream->at_eof() && !stream->saw_error()) { // The vector of types is package specific. this->types_.clear(); // Check magic string / version number. if (stream->match_bytes(Export::cur_magic, Export::magic_len)) { stream->require_bytes(this->location_, Export::cur_magic, Export::magic_len); this->version_ = EXPORT_FORMAT_CURRENT; } else if (stream->match_bytes(Export::v1_magic, Export::magic_len)) { stream->require_bytes(this->location_, Export::v1_magic, Export::magic_len); this->version_ = EXPORT_FORMAT_V1; } else if (stream->match_bytes(Export::v2_magic, Export::magic_len)) { stream->require_bytes(this->location_, Export::v2_magic, Export::magic_len); this->version_ = EXPORT_FORMAT_V2; } else { go_error_at(this->location_, ("error in import data at %d: invalid magic string"), stream->pos()); return NULL; } this->require_c_string("package "); std::string package_name = this->read_identifier(); this->require_semicolon_if_old_version(); this->require_c_string("\n"); std::string pkgpath; std::string pkgpath_symbol; if (this->match_c_string("prefix ")) { this->advance(7); std::string unique_prefix = this->read_identifier(); this->require_semicolon_if_old_version(); this->require_c_string("\n"); pkgpath = unique_prefix + '.' + package_name; pkgpath_symbol = (Gogo::pkgpath_for_symbol(unique_prefix) + '.' + Gogo::pkgpath_for_symbol(package_name)); } else { this->require_c_string("pkgpath "); pkgpath = this->read_identifier(); this->require_semicolon_if_old_version(); this->require_c_string("\n"); pkgpath_symbol = Gogo::pkgpath_for_symbol(pkgpath); } if (stream->saw_error()) return NULL; this->package_ = gogo->add_imported_package(package_name, local_name, is_local_name_exported, pkgpath, pkgpath_symbol, this->location_, &this->add_to_globals_); if (this->package_ == NULL) { stream->set_saw_error(); return NULL; } // Read and discard priority if older V1 export data format. if (version() == EXPORT_FORMAT_V1) { this->require_c_string("priority "); std::string priority_string = this->read_identifier(); int prio; if (!this->string_to_int(priority_string, false, &prio)) return NULL; this->require_c_string(";\n"); } while (stream->match_c_string("package")) this->read_one_package(); while (stream->match_c_string("import")) this->read_one_import(); while (stream->match_c_string("indirectimport")) this->read_one_indirect_import(); if (stream->match_c_string("init")) this->read_import_init_fns(gogo); if (stream->match_c_string("types ")) { if (!this->read_types()) return NULL; } // Loop over all the input data for this package. while (!stream->saw_error()) { if (stream->match_c_string("const ")) this->import_const(); else if (stream->match_c_string("type ")) this->import_type(); else if (stream->match_c_string("var ")) this->import_var(); else if (stream->match_c_string("func ")) this->import_func(this->package_); else if (stream->match_c_string("checksum ")) break; else { go_error_at(this->location_, ("error in import data at %d: " "expected %, %, %, " "%, or %"), stream->pos()); stream->set_saw_error(); return NULL; } } // We currently ignore the checksum. In the future we could // store the checksum somewhere in the generated object and then // verify that the checksum matches at link time or at dynamic // load time. this->require_c_string("checksum "); stream->advance(Export::checksum_len * 2); this->require_semicolon_if_old_version(); this->require_c_string("\n"); } // Finalize methods for any imported types. This call is made late in the // import process so as to A) avoid finalization of a type whose methods // refer to types that are only partially read in, and B) capture both the // types imported by read_types() directly, and those imported indirectly // because they are referenced by an imported function or variable. // See issues #33013 and #33219 for more on why this is needed. this->finalize_methods(); return this->package_; } // Read a package line. This let us reliably determine the pkgpath // symbol, even if the package was compiled with a -fgo-prefix option. void Import::read_one_package() { this->require_c_string("package "); std::string package_name = this->read_identifier(); this->require_c_string(" "); std::string pkgpath = this->read_identifier(); this->require_c_string(" "); std::string pkgpath_symbol = this->read_identifier(); this->require_semicolon_if_old_version(); this->require_c_string("\n"); Package* p = this->gogo_->register_package(pkgpath, pkgpath_symbol, Linemap::unknown_location()); p->set_package_name(package_name, this->location()); } // Read an import line. void Import::read_one_import() { this->require_c_string("import "); std::string package_name = this->read_identifier(); this->require_c_string(" "); std::string pkgpath = this->read_identifier(); this->require_c_string(" \""); Stream* stream = this->stream_; while (stream->peek_char() != '"') stream->advance(1); this->require_c_string("\""); this->require_semicolon_if_old_version(); this->require_c_string("\n"); Package* p = this->gogo_->register_package(pkgpath, "", Linemap::unknown_location()); p->set_package_name(package_name, this->location()); this->packages_.push_back(p); } // Read an indirectimport line. void Import::read_one_indirect_import() { this->require_c_string("indirectimport "); std::string package_name = this->read_identifier(); this->require_c_string(" "); std::string pkgpath = this->read_identifier(); this->require_c_string("\n"); Package* p = this->gogo_->register_package(pkgpath, "", Linemap::unknown_location()); p->set_package_name(package_name, this->location()); this->packages_.push_back(p); } // Read the list of import control functions and/or init graph. void Import::read_import_init_fns(Gogo* gogo) { this->require_c_string("init"); // Maps init function to index in the "init" clause; needed // to read the init_graph section. std::map init_idx; while (!this->match_c_string("\n") && !this->match_c_string(";")) { int priority = -1; this->require_c_string(" "); std::string package_name = this->read_identifier(); this->require_c_string(" "); std::string init_name = this->read_identifier(); if (this->version_ == EXPORT_FORMAT_V1) { // Older version 1 init fcn export data format is: // // this->require_c_string(" "); std::string prio_string = this->read_identifier(); if (!this->string_to_int(prio_string, false, &priority)) return; } gogo->add_import_init_fn(package_name, init_name, priority); // Record the index of this init fcn so that we can look it // up by index in the subsequent init_graph section. unsigned idx = init_idx.size(); init_idx[init_name] = idx; } this->require_semicolon_if_old_version(); this->require_c_string("\n"); if (this->match_c_string("init_graph")) { this->require_c_string("init_graph"); // Build a vector mapping init fcn slot to Import_init pointer. go_assert(init_idx.size() > 0); std::vector import_initvec; import_initvec.resize(init_idx.size()); for (std::map::const_iterator it = init_idx.begin(); it != init_idx.end(); ++it) { const std::string& init_name = it->first; Import_init* ii = gogo->lookup_init(init_name); import_initvec[it->second] = ii; } // Init graph format is: // // init_graph ... ; // // where src + sink are init functions indices. while (!this->match_c_string("\n") && !this->match_c_string(";")) { this->require_c_string(" "); std::string src_string = this->read_identifier(); unsigned src; if (!this->string_to_unsigned(src_string, &src)) return; this->require_c_string(" "); std::string sink_string = this->read_identifier(); unsigned sink; if (!this->string_to_unsigned(sink_string, &sink)) return; go_assert(src < import_initvec.size()); Import_init* ii_src = import_initvec[src]; go_assert(sink < import_initvec.size()); Import_init* ii_sink = import_initvec[sink]; ii_src->record_precursor_fcn(ii_sink->init_name()); } this->require_semicolon_if_old_version(); this->require_c_string("\n"); } } // Import the types. Starting in export format version 3 all the // types are listed first. bool Import::read_types() { this->require_c_string("types "); std::string str = this->read_identifier(); int maxp1; if (!this->string_to_int(str, false, &maxp1)) return false; this->require_c_string(" "); str = this->read_identifier(); int exportedp1; if (!this->string_to_int(str, false, &exportedp1)) return false; this->type_offsets_.resize(maxp1, std::make_pair(0, 0)); size_t total_type_size = 0; // Start at 1 because type index 0 not used. for (int i = 1; i < maxp1; i++) { this->require_c_string(" "); str = this->read_identifier(); int v; if (!this->string_to_int(str, false, &v)) return false; size_t vs = static_cast(v); this->type_offsets_[i] = std::make_pair(total_type_size, vs); total_type_size += vs; } this->require_c_string("\n"); // Types can refer to each other in an unpredictable order. Read // all the type data into type_data_. The type_offsets_ vector we // just initialized provides indexes into type_data_. this->type_pos_ = this->stream_->pos(); const char* type_data; if (!this->stream_->peek(total_type_size, &type_data)) return false; this->type_data_ = std::string(type_data, total_type_size); this->advance(total_type_size); this->types_.resize(maxp1, NULL); // Parse all the exported types now, so that the names are properly // bound and visible to the parser. Parse unexported types lazily. // Start at 1 because there is no type 0. for (int i = 1; i < exportedp1; i++) { // We may have already parsed this type when we parsed an // earlier type. Type* type = this->types_[i]; if (type == NULL) { if (!this->parse_type(i)) return false; type = this->types_[i]; go_assert(type != NULL); } Named_type* nt = type->named_type(); if (nt == NULL) { go_error_at(this->location_, "error in import data: exported unnamed type %d", i); return false; } nt->set_is_visible(); if (this->add_to_globals_) this->gogo_->add_named_type(nt); } return true; } void Import::finalize_methods() { Finalize_methods finalizer(this->gogo_); Unordered_set(Type*) real_for_named; for (size_t i = 1; i < this->types_.size(); i++) { Type* type = this->types_[i]; if (type != NULL && type->named_type() != NULL) { finalizer.type(type); // If the real type is a struct type, we don't want to // finalize its methods. For a named type defined as a // struct type, we only want to finalize the methods of the // named type. This is like Finalize_methods::type. Type* real_type = type->named_type()->real_type(); if (real_type->struct_type() != NULL) real_for_named.insert(real_type); } } for (size_t i = 1; i < this->types_.size(); i++) { Type* type = this->types_[i]; if (type != NULL && type->named_type() == NULL && real_for_named.find(type) == real_for_named.end()) finalizer.type(type); } } // Import a constant. void Import::import_const() { std::string name; Type* type; Expression* expr; Named_constant::import_const(this, &name, &type, &expr); Typed_identifier tid(name, type, this->location_); Named_object* no = this->package_->add_constant(tid, expr); if (this->add_to_globals_) this->gogo_->add_dot_import_object(no); } // Import a type. void Import::import_type() { if (this->version_ >= EXPORT_FORMAT_V3) { if (!this->stream_->saw_error()) { go_error_at(this->location_, "error in import data at %d: old type syntax", this->stream_->pos()); this->stream_->set_saw_error(); } return; } Named_type* type; Named_type::import_named_type(this, &type); // The named type has been added to the package by the type import // process. Here we need to make it visible to the parser, and it // to the global bindings if necessary. type->set_is_visible(); if (this->add_to_globals_) this->gogo_->add_named_type(type); } // Import a variable. void Import::import_var() { std::string name; Package* vpkg; bool is_exported; Type* type; if (!Variable::import_var(this, &name, &vpkg, &is_exported, &type)) return; if (vpkg == NULL) vpkg = this->package_; if (!is_exported) name = '.' + vpkg->pkgpath() + '.' + name; Variable* var = new Variable(type, NULL, true, false, false, this->location_); Named_object* no; no = vpkg->add_variable(name, var); if (this->add_to_globals_ && vpkg == this->package_) this->gogo_->add_dot_import_object(no); } // Import a function into PACKAGE. PACKAGE is normally // THIS->PACKAGE_, but it will be different for a method associated // with a type defined in a different package. void Import::import_func(Package* package) { std::string name; Package* fpkg; bool is_exported; Typed_identifier* receiver; Typed_identifier_list* parameters; Typed_identifier_list* results; bool is_varargs; bool nointerface; std::string asm_name; std::string body; if (!Function::import_func(this, &name, &fpkg, &is_exported, &receiver, ¶meters, &results, &is_varargs, &nointerface, &asm_name, &body)) return; if (fpkg == NULL) fpkg = package; if (!is_exported) name = '.' + fpkg->pkgpath() + '.' + name; Function_type *fntype = Type::make_function_type(receiver, parameters, results, this->location_); if (is_varargs) fntype->set_is_varargs(); Location loc = this->location_; Named_object* no; if (fntype->is_method()) { Type* rtype = receiver->type(); // We may still be reading the definition of RTYPE, so we have // to be careful to avoid calling base or convert. If RTYPE is // a named type or a forward declaration, then we know that it // is not a pointer, because we are reading a method on RTYPE // and named pointers can't have methods. if (rtype->classification() == Type::TYPE_POINTER) rtype = rtype->points_to(); if (rtype->is_error_type()) return; else if (rtype->named_type() != NULL) no = rtype->named_type()->add_method_declaration(name, fpkg, fntype, loc); else if (rtype->forward_declaration_type() != NULL) no = rtype->forward_declaration_type()->add_method_declaration(name, fpkg, fntype, loc); else go_unreachable(); } else { no = fpkg->add_function_declaration(name, fntype, loc); if (this->add_to_globals_ && fpkg == package) this->gogo_->add_dot_import_object(no); } if (nointerface) no->func_declaration_value()->set_nointerface(); if (!asm_name.empty()) no->func_declaration_value()->set_asm_name(asm_name); if (!body.empty() && !no->func_declaration_value()->has_imported_body()) no->func_declaration_value()->set_imported_body(this, body); } // Read a type definition and initialize the entry in this->types_. // This parses the type definition saved by read_types earlier. This // returns true on success, false on failure. bool Import::parse_type(int i) { go_assert(i >= 0 && static_cast(i) < this->types_.size()); go_assert(this->types_[i] == NULL); size_t offset = this->type_offsets_[i].first; size_t len = this->type_offsets_[i].second; Stream* orig_stream = this->stream_; Stream_from_string_ref stream(this->type_data_, offset, len); stream.set_pos(this->type_pos_ + offset); this->stream_ = &stream; this->require_c_string("type "); std::string str = this->read_identifier(); int id; if (!this->string_to_int(str, false, &id)) { this->stream_ = orig_stream; return false; } if (i != id) { go_error_at(this->location_, ("error in import data at %d: " "type ID mismatch: got %d, want %d"), stream.pos(), id, i); this->stream_ = orig_stream; return false; } this->require_c_string(" "); if (stream.peek_char() == '"') { stream.advance(1); Type* type = this->read_named_type(i); if (type->is_error_type()) { this->stream_ = orig_stream; return false; } } else { Type* type = Type::import_type(this); if (type->is_error_type()) { this->stream_ = orig_stream; return false; } this->types_[i] = type; this->require_c_string("\n"); } this->stream_ = orig_stream; return true; } // Read a type in the import stream. This records the type by the // type index. If the type is named (which can only happen with older // export formats), it registers the name, but marks it as invisible. Type* Import::read_type() { Stream* stream = this->stream_; this->require_c_string("get_char(); if (c != '-' && (c < '0' || c > '9')) break; number += c; } int index; if (!this->string_to_int(number, true, &index)) return Type::make_error_type(); if (c == '>') { // A reference to a type defined earlier. bool parsed; return this->type_for_index(index, "import data", stream->pos(), &parsed); } if (this->version_ >= EXPORT_FORMAT_V3) { if (!stream->saw_error()) go_error_at(this->location_, "error in import data at %d: expected %<>%>", stream->pos()); stream->set_saw_error(); return Type::make_error_type(); } if (c != ' ') { if (!stream->saw_error()) go_error_at(this->location_, "error in import data at %d: expected %< %> or %<>%>", stream->pos()); stream->set_saw_error(); stream->advance(1); return Type::make_error_type(); } if (index <= 0 || (static_cast(index) < this->types_.size() && this->types_[index] != NULL)) { go_error_at(this->location_, "error in import data at %d: type index already defined", stream->pos()); stream->set_saw_error(); return Type::make_error_type(); } if (static_cast(index) >= this->types_.size()) { int newsize = std::max(static_cast(index) + 1, this->types_.size() * 2); this->types_.resize(newsize, NULL); } if (stream->peek_char() != '"') { Type* type = Type::import_type(this); this->require_c_string(">"); this->types_[index] = type; return type; } stream->advance(1); Type* type = this->read_named_type(index); this->require_c_string(">"); return type; } // Read a named type from the import stream and store it in // this->types_[index]. The stream should be positioned immediately // after the '"' that starts the name. Type* Import::read_named_type(int index) { Stream* stream = this->stream_; std::string type_name; int c; while ((c = stream->get_char()) != '"') type_name += c; // If this type is in the package we are currently importing, the // name will be .PKGPATH.NAME or simply NAME with no dots. // Otherwise, a non-hidden symbol will be PKGPATH.NAME and a hidden // symbol will be .PKGPATH.NAME. std::string pkgpath; if (type_name.find('.') != std::string::npos) { size_t start = 0; if (type_name[0] == '.') start = 1; size_t dot = type_name.rfind('.'); pkgpath = type_name.substr(start, dot - start); if (type_name[0] != '.') type_name.erase(0, dot + 1); } this->require_c_string(" "); // The package name may follow. This is the name of the package in // the package clause of that package. The type name will include // the pkgpath, which may be different. std::string package_name; if (stream->peek_char() == '"') { stream->advance(1); while ((c = stream->get_char()) != '"') package_name += c; this->require_c_string(" "); } bool in_heap = true; if (this->match_c_string("notinheap")) { this->require_c_string("notinheap "); in_heap = false; } bool is_alias = false; if (this->match_c_string("= ")) { stream->advance(2); is_alias = true; } // Declare the type in the appropriate package. If we haven't seen // it before, mark it as invisible. We declare it before we read // the actual definition of the type, since the definition may refer // to the type itself. Package* package; if (pkgpath.empty() || pkgpath == this->gogo_->pkgpath()) package = this->package_; else { package = this->gogo_->register_package(pkgpath, "", Linemap::unknown_location()); if (!package_name.empty()) package->set_package_name(package_name, this->location()); } Named_object* no = package->bindings()->lookup(type_name); if (no == NULL) no = package->add_type_declaration(type_name, this->location_); else if (!no->is_type_declaration() && !no->is_type()) { go_error_at(this->location_, "imported %<%s.%s%> both type and non-type", pkgpath.c_str(), Gogo::message_name(type_name).c_str()); stream->set_saw_error(); return Type::make_error_type(); } else go_assert(no->package() == package); if (this->types_[index] == NULL) { if (no->is_type_declaration()) { // FIXME: It's silly to make a forward declaration every time. this->types_[index] = Type::make_forward_declaration(no); } else { go_assert(no->is_type()); this->types_[index] = no->type_value(); } } // If there is no type definition, then this is just a forward // declaration of a type defined in some other file. Type* type; if (this->match_c_string(">") || this->match_c_string("\n")) { type = this->types_[index]; if (!in_heap) go_error_at(this->location_, ("import error at %d for type index %d: " "forward declaration marked notinheap"), this->pos(), index); } else { if (no->is_type_declaration()) { // We can define the type now. type = this->read_type(); no = package->add_type(type_name, type, this->location_); Named_type* ntype = no->type_value(); // This type has not yet been imported. ntype->clear_is_visible(); if (!in_heap) ntype->set_not_in_heap(); if (is_alias) ntype->set_is_alias(); if (!type->is_undefined() && type->interface_type() != NULL) this->gogo_->record_interface_type(type->interface_type()); type = ntype; } else if (no->is_type()) { // We have seen this type before. type = no->type_value(); // Don't change the visibility of the existing type. // For older export versions, we need to skip the type // definition in the stream. if (this->version_ < EXPORT_FORMAT_V3) this->read_type(); } else go_unreachable(); this->types_[index] = type; // Read the type methods. if (this->match_c_string("\n")) { this->advance(1); while (this->match_c_string(" func")) { this->advance(1); this->import_func(package); } } } return type; } // Return the type given an index. Set *PARSED if we parsed it here. Type* Import::type_for_index(int index, const std::string& input_name, size_t input_offset, bool* parsed) { *parsed = false; if (index >= 0 && !this->type_data_.empty()) { if (static_cast(index) >= this->type_offsets_.size()) { go_error_at(this->location_, "error in %s at %lu: bad type index %d, max %d", input_name.c_str(), static_cast(input_offset), index, static_cast(this->type_offsets_.size())); return Type::make_error_type(); } if (this->types_[index] == NULL) { if (!this->parse_type(index)) return Type::make_error_type(); *parsed = true; } } if (index < 0 ? (static_cast(- index) >= this->builtin_types_.size() || this->builtin_types_[- index] == NULL) : (static_cast(index) >= this->types_.size() || this->types_[index] == NULL)) { go_error_at(this->location_, "error in %s at %lu: bad type index %d", input_name.c_str(), static_cast(input_offset), index); return Type::make_error_type(); } return index < 0 ? this->builtin_types_[- index] : this->types_[index]; } // Read an escape note. std::string Import::read_escape() { if (this->match_c_string(" stream_; this->require_c_string(" get_char(); if (c != 'x' && !ISXDIGIT(c)) break; escape += c; } if (c != '>') { go_error_at(this->location(), ("error in import data at %d: " "expect %< %> or %<>%>, got %c"), stream->pos(), c); stream->set_saw_error(); stream->advance(1); escape = Escape_note::make_tag(Node::ESCAPE_UNKNOWN); } return escape; } else return Escape_note::make_tag(Node::ESCAPE_UNKNOWN); } // Register the builtin types. void Import::register_builtin_types(Gogo* gogo) { this->register_builtin_type(gogo, "int8", BUILTIN_INT8); this->register_builtin_type(gogo, "int16", BUILTIN_INT16); this->register_builtin_type(gogo, "int32", BUILTIN_INT32); this->register_builtin_type(gogo, "int64", BUILTIN_INT64); this->register_builtin_type(gogo, "uint8", BUILTIN_UINT8); this->register_builtin_type(gogo, "uint16", BUILTIN_UINT16); this->register_builtin_type(gogo, "uint32", BUILTIN_UINT32); this->register_builtin_type(gogo, "uint64", BUILTIN_UINT64); this->register_builtin_type(gogo, "float32", BUILTIN_FLOAT32); this->register_builtin_type(gogo, "float64", BUILTIN_FLOAT64); this->register_builtin_type(gogo, "complex64", BUILTIN_COMPLEX64); this->register_builtin_type(gogo, "complex128", BUILTIN_COMPLEX128); this->register_builtin_type(gogo, "int", BUILTIN_INT); this->register_builtin_type(gogo, "uint", BUILTIN_UINT); this->register_builtin_type(gogo, "uintptr", BUILTIN_UINTPTR); this->register_builtin_type(gogo, "bool", BUILTIN_BOOL); this->register_builtin_type(gogo, "string", BUILTIN_STRING); this->register_builtin_type(gogo, "error", BUILTIN_ERROR); this->register_builtin_type(gogo, "byte", BUILTIN_BYTE); this->register_builtin_type(gogo, "rune", BUILTIN_RUNE); } // Register a single builtin type. void Import::register_builtin_type(Gogo* gogo, const char* name, Builtin_code code) { Named_object* named_object = gogo->lookup_global(name); go_assert(named_object != NULL && named_object->is_type()); int index = - static_cast(code); go_assert(index > 0 && static_cast(index) < this->builtin_types_.size()); this->builtin_types_[index] = named_object->type_value(); } // Characters that stop read_identifier. We base this on the // characters that stop an identifier, without worrying about // characters that are permitted in an identifier. That lets us skip // UTF-8 parsing. static const char * const identifier_stop = " \n;:,()[]"; // Read an identifier from the stream. std::string Import::read_identifier() { std::string ret; Stream* stream = this->stream_; int c; while (true) { c = stream->peek_char(); if (c == -1 || strchr(identifier_stop, c) != NULL) break; // FIXME: Probably we shouldn't accept '.', but that might break // some existing imports. if (c == '.' && stream->match_c_string("...")) break; ret += c; stream->advance(1); } return ret; } // Read a possibly qualified identifier from IMP. The qualification // is , where ID is a package number. If the name has a leading // '.', it is not exported; otherwise, it is. Set *NAME, *PKG and // *IS_EXPORTED. Reports whether the read succeeded. bool Import::read_qualified_identifier(Import_expression* imp, std::string* name, Package** pkg, bool* is_exported) { *pkg = NULL; if (imp->match_c_string("advance(2); char buf[50]; char *pbuf = &buf[0]; while (true) { int next = imp->peek_char(); if (next == -1 || static_cast(pbuf - buf) >= sizeof buf - 1) return false; if (next == '>') { imp->advance(1); break; } *pbuf = static_cast(next); ++pbuf; imp->advance(1); } *pbuf = '\0'; char *end; long index = strtol(buf, &end, 10); if (*end != '\0' || index <= 0 || static_cast(index) > imp->max_package_index()) return false; *pkg = imp->package_at_index(index); go_assert(*pkg != NULL); } *is_exported = true; if (imp->match_c_string(".")) { imp->advance(1); *is_exported = false; } *name = imp->read_identifier(); return !name->empty(); } // Read a name from the stream. std::string Import::read_name() { std::string ret = this->read_identifier(); if (ret == "?") ret.clear(); return ret; } // Read LENGTH bytes from the stream. void Import::read(size_t length, std::string* out) { const char* data; if (!this->stream_->peek(length, &data)) { if (!this->stream_->saw_error()) go_error_at(this->location_, "import error at %d: expected %d bytes", this->stream_->pos(), static_cast(length)); this->stream_->set_saw_error(); *out = std::string(""); return; } *out = std::string(data, length); this->advance(length); } // Turn a string into a integer with appropriate error handling. bool Import::string_to_int(const std::string &s, bool is_neg_ok, int* ret) { char* end; long prio = strtol(s.c_str(), &end, 10); if (*end != '\0' || prio > 0x7fffffff || (prio < 0 && !is_neg_ok)) { go_error_at(this->location_, "invalid integer in import data at %d", this->stream_->pos()); this->stream_->set_saw_error(); return false; } *ret = prio; return true; } // Class Import::Stream. Import::Stream::Stream() : pos_(0), saw_error_(false) { } Import::Stream::~Stream() { } // Return the next character to come from the stream. int Import::Stream::peek_char() { const char* read; if (!this->do_peek(1, &read)) return -1; // Make sure we return an unsigned char, so that we don't get // confused by \xff. unsigned char ret = *read; return ret; } // Return true if the next LENGTH characters from the stream match // BYTES bool Import::Stream::match_bytes(const char* bytes, size_t length) { const char* read; if (!this->do_peek(length, &read)) return false; return memcmp(bytes, read, length) == 0; } // Require that the next LENGTH bytes from the stream match BYTES. void Import::Stream::require_bytes(Location location, const char* bytes, size_t length) { const char* read; if (!this->do_peek(length, &read) || memcmp(bytes, read, length) != 0) { if (!this->saw_error_) go_error_at(location, "import error at %d: expected %<%.*s%>", this->pos(), static_cast(length), bytes); this->saw_error_ = true; return; } this->advance(length); } // Class Stream_from_file. Stream_from_file::Stream_from_file(int fd) : fd_(fd), data_() { if (lseek(fd, 0, SEEK_SET) != 0) { go_fatal_error(Linemap::unknown_location(), "lseek failed: %m"); this->set_saw_error(); } } Stream_from_file::~Stream_from_file() { close(this->fd_); } // Read next bytes. bool Stream_from_file::do_peek(size_t length, const char** bytes) { if (this->data_.length() >= length) { *bytes = this->data_.data(); return true; } this->data_.resize(length); ssize_t got = ::read(this->fd_, &this->data_[0], length); if (got < 0) { if (!this->saw_error()) go_fatal_error(Linemap::unknown_location(), "read failed: %m"); this->set_saw_error(); return false; } if (lseek(this->fd_, - got, SEEK_CUR) < 0) { if (!this->saw_error()) go_fatal_error(Linemap::unknown_location(), "lseek failed: %m"); this->set_saw_error(); return false; } if (static_cast(got) < length) return false; *bytes = this->data_.data(); return true; } // Advance. void Stream_from_file::do_advance(size_t skip) { if (lseek(this->fd_, skip, SEEK_CUR) < 0) { if (!this->saw_error()) go_fatal_error(Linemap::unknown_location(), "lseek failed: %m"); this->set_saw_error(); } if (!this->data_.empty()) { if (this->data_.length() > skip) this->data_.erase(0, skip); else this->data_.clear(); } } // Class Import_function_body. Import_function_body::Import_function_body(Gogo* gogo, Import* imp, Named_object* named_object, const std::string& body, size_t off, Block* block, int indent) : gogo_(gogo), imp_(imp), named_object_(named_object), body_(body), off_(off), indent_(indent), temporaries_(), labels_(), saw_error_(false) { this->blocks_.push_back(block); } Import_function_body::~Import_function_body() { // At this point we should be left with the original outer block only. go_assert(saw_errors() || this->blocks_.size() == 1); } // The name of the function we are parsing. const std::string& Import_function_body::name() const { return this->named_object_->name(); } // Class Import_function_body. // Require that the next bytes match STR, issuing an error if not. // Advance past the string. void Import_function_body::require_c_string(const char* str) { if (!this->match_c_string(str)) { if (!this->saw_error_) go_error_at(this->location(), "invalid export data for %qs: expected %qs at %lu", this->name().c_str(), str, static_cast(this->off_)); this->saw_error_ = true; return; } this->advance(strlen(str)); } // Read an identifier. std::string Import_function_body::read_identifier() { size_t start = this->off_; for (size_t i = start; i < this->body_.length(); i++) { int c = static_cast(this->body_[i]); if (strchr(identifier_stop, c) != NULL) { this->off_ = i; return this->body_.substr(start, i - start); } // FIXME: Probably we shouldn't accept '.', but that might break // some existing imports. if (c == '.' && i + 2 < this->body_.length() && this->body_[i + 1] == '.' && this->body_[i + 2] == '.') { this->off_ = i; return this->body_.substr(start, i - start); } } this->off_ = this->body_.length(); return this->body_.substr(start); } // Read a type. Type* Import_function_body::read_type() { this->require_c_string("off_; size_t i; int c = '\0'; for (i = start; i < this->body_.length(); ++i) { c = static_cast(this->body_[i]); if (c != '-' && (c < '0' || c > '9')) break; } this->off_ = i + 1; char *end; std::string num = this->body_.substr(start, i - start); long val = strtol(num.c_str(), &end, 10); if (*end != '\0' || val > 0x7fffffff) { if (!this->saw_error_) go_error_at(this->location(), "invalid export data for %qs: expected integer at %lu", this->name().c_str(), static_cast(start)); this->saw_error_ = true; return Type::make_error_type(); } if (c != '>') { if (!this->saw_error_) go_error_at(this->location(), "invalid export data for %qs: expected %<>%> at %lu", this->name().c_str(), static_cast(i)); this->saw_error_ = true; return Type::make_error_type(); } bool parsed; Type* type = this->imp_->type_for_index(static_cast(val), this->name(), static_cast(start), &parsed); // If we just read this type's information, its methods will not // have been finalized. Do that now. if (parsed) this->gogo_->finalize_methods_for_type(type); return type; } // Return the next size to use for a vector mapping indexes to values. size_t Import_function_body::next_size(size_t have) { if (have == 0) return 8; else if (have < 256) return have * 2; else return have + 64; } // Record the index of a temporary statement. void Import_function_body::record_temporary(Temporary_statement* temp, unsigned int idx) { size_t have = this->temporaries_.size(); while (static_cast(idx) >= have) { size_t want = Import_function_body::next_size(have); this->temporaries_.resize(want, NULL); have = want; } this->temporaries_[idx] = temp; } // Return a temporary statement given an index. Temporary_statement* Import_function_body::temporary_statement(unsigned int idx) { if (static_cast(idx) >= this->temporaries_.size()) return NULL; return this->temporaries_[idx]; } // Return an unnamed label given an index, defining the label if we // haven't seen it already. Unnamed_label* Import_function_body::unnamed_label(unsigned int idx, Location loc) { size_t have = this->labels_.size(); while (static_cast(idx) >= have) { size_t want = Import_function_body::next_size(have); this->labels_.resize(want, NULL); have = want; } Unnamed_label* label = this->labels_[idx]; if (label == NULL) { label = new Unnamed_label(loc); this->labels_[idx] = label; } return label; }