diff options
Diffstat (limited to 'gcc-4.9/gcc/go/gofrontend/import.cc')
-rw-r--r-- | gcc-4.9/gcc/go/gofrontend/import.cc | 960 |
1 files changed, 960 insertions, 0 deletions
diff --git a/gcc-4.9/gcc/go/gofrontend/import.cc b/gcc-4.9/gcc/go/gofrontend/import.cc new file mode 100644 index 000000000..4913100b5 --- /dev/null +++ b/gcc-4.9/gcc/go/gofrontend/import.cc @@ -0,0 +1,960 @@ +// 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 "simple-object.h" + +#include "go-c.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<std::string> 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 + fn = relative_import_path + '/' + fn; + is_local = false; + } + + if (!is_local) + { + for (std::vector<std::string>::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) + 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); + + 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::v1_magic_len, Import::archive_magic_len); + + if (lseek(fd, 0, SEEK_SET) < 0) + { + 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::v1_magic, Export::v1_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 a simple_object. + +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) + error_at(location, "%s: %s", filename.c_str(), errmsg); + else + 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), + builtin_types_((- SMALLEST_BUILTIN_CODE) + 1), + types_() +{ +} + +// 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(); + + stream->require_bytes(this->location_, Export::v1_magic, + Export::v1_magic_len); + + this->require_c_string("package "); + std::string package_name = this->read_identifier(); + this->require_c_string(";\n"); + + std::string pkgpath; + if (this->match_c_string("prefix ")) + { + this->advance(7); + std::string unique_prefix = this->read_identifier(); + this->require_c_string(";\n"); + pkgpath = unique_prefix + '.' + package_name; + } + else + { + this->require_c_string("pkgpath "); + pkgpath = this->read_identifier(); + this->require_c_string(";\n"); + } + + this->package_ = gogo->add_imported_package(package_name, local_name, + is_local_name_exported, + pkgpath, + this->location_, + &this->add_to_globals_); + if (this->package_ == NULL) + { + stream->set_saw_error(); + return NULL; + } + + 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->package_->set_priority(prio); + this->require_c_string(";\n"); + + while (stream->match_c_string("import")) + this->read_one_import(); + + if (stream->match_c_string("init")) + this->read_import_init_fns(gogo); + + // 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 + { + error_at(this->location_, + ("error in import data at %d: " + "expected %<const%>, %<type%>, %<var%>, " + "%<func%>, or %<checksum%>"), + 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::v1_checksum_len * 2); + this->require_c_string(";\n"); + } + + return this->package_; +} + +// Read an import line. We don't actually care about these. + +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("\";\n"); + + Package* p = this->gogo_->register_package(pkgpath, + Linemap::unknown_location()); + p->set_package_name(package_name, this->location()); +} + +// Read the list of import control functions. + +void +Import::read_import_init_fns(Gogo* gogo) +{ + this->require_c_string("init"); + while (!this->match_c_string(";")) + { + this->require_c_string(" "); + std::string package_name = this->read_identifier(); + this->require_c_string(" "); + std::string init_name = this->read_identifier(); + this->require_c_string(" "); + std::string prio_string = this->read_identifier(); + int prio; + if (!this->string_to_int(prio_string, false, &prio)) + return; + gogo->add_import_init_fn(package_name, init_name, prio); + } + this->require_c_string(";\n"); +} + +// 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_named_object(no); +} + +// Import a type. + +void +Import::import_type() +{ + 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; + Type* type; + Variable::import_var(this, &name, &type); + Variable* var = new Variable(type, NULL, true, false, false, + this->location_); + Named_object* no; + no = this->package_->add_variable(name, var); + if (this->add_to_globals_) + this->gogo_->add_named_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. + +Named_object* +Import::import_func(Package* package) +{ + std::string name; + Typed_identifier* receiver; + Typed_identifier_list* parameters; + Typed_identifier_list* results; + bool is_varargs; + Function::import_func(this, &name, &receiver, ¶meters, &results, + &is_varargs); + 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 NULL; + else if (rtype->named_type() != NULL) + no = rtype->named_type()->add_method_declaration(name, package, fntype, + loc); + else if (rtype->forward_declaration_type() != NULL) + no = rtype->forward_declaration_type()->add_method_declaration(name, + package, + fntype, + loc); + else + go_unreachable(); + } + else + { + no = package->add_function_declaration(name, fntype, loc); + if (this->add_to_globals_) + this->gogo_->add_named_object(no); + } + return no; +} + +// Read a type in the import stream. This records the type by the +// type index. If the type is named, it registers the name, but marks +// it as invisible. + +Type* +Import::read_type() +{ + Stream* stream = this->stream_; + this->require_c_string("<type "); + + std::string number; + int c; + while (true) + { + c = stream->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 == '>') + { + // This type was already defined. + if (index < 0 + ? (static_cast<size_t>(- index) >= this->builtin_types_.size() + || this->builtin_types_[- index] == NULL) + : (static_cast<size_t>(index) >= this->types_.size() + || this->types_[index] == NULL)) + { + error_at(this->location_, + "error in import data at %d: bad type index %d", + stream->pos(), index); + stream->set_saw_error(); + return Type::make_error_type(); + } + + return index < 0 ? this->builtin_types_[- index] : this->types_[index]; + } + + if (c != ' ') + { + if (!stream->saw_error()) + error_at(this->location_, + "error in import data at %d: expect %< %> or %<>%>'", + stream->pos()); + stream->set_saw_error(); + stream->advance(1); + return Type::make_error_type(); + } + + if (index <= 0 + || (static_cast<size_t>(index) < this->types_.size() + && this->types_[index] != NULL)) + { + 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<size_t>(index) >= this->types_.size()) + { + int newsize = std::max(static_cast<size_t>(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; + } + + // This type has a name. + + stream->advance(1); + std::string type_name; + 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(" "); + } + + // 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()) + { + 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(">")) + type = this->types_[index]; + else + { + type = this->read_type(); + + if (no->is_type_declaration()) + { + // We can define the type now. + + 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 (!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. FIXME: it would be a good + // idea to check that the two imported types are identical, + // but we have not finalized the methods yet, which means + // that we can not reliably compare interface types. + type = no->type_value(); + + // Don't change the visibility of the existing type. + } + + 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); + } + } + } + + this->require_c_string(">"); + + return type; +} + +// 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<int>(code); + go_assert(index > 0 + && static_cast<size_t>(index) < this->builtin_types_.size()); + this->builtin_types_[index] = named_object->type_value(); +} + +// 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 || c == ' ' || c == ';') + break; + ret += c; + stream->advance(1); + } + return ret; +} + +// Read a name from the stream. + +std::string +Import::read_name() +{ + std::string ret = this->read_identifier(); + if (ret == "?") + ret.clear(); + else if (!Lex::is_exported_name(ret)) + ret = '.' + this->package_->pkgpath() + '.' + ret; + return ret; +} + +// 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)) + { + 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_) + error_at(location, "import error at %d: expected %<%.*s%>", + this->pos(), static_cast<int>(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) + { + error("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; + } + // Don't bother to handle the general case, since we don't need it. + go_assert(length < 64); + char buf[64]; + ssize_t got = read(this->fd_, buf, length); + + if (got < 0) + { + if (!this->saw_error()) + error("read failed: %m"); + this->set_saw_error(); + return false; + } + + if (lseek(this->fd_, - got, SEEK_CUR) != 0) + { + if (!this->saw_error()) + error("lseek failed: %m"); + this->set_saw_error(); + return false; + } + + if (static_cast<size_t>(got) < length) + return false; + + this->data_.assign(buf, got); + + *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()) + error("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(); + } +} |