From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- gcc/go/gofrontend/import.cc | 892 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 892 insertions(+) create mode 100644 gcc/go/gofrontend/import.cc (limited to 'gcc/go/gofrontend/import.cc') diff --git a/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc new file mode 100644 index 000000000..d926edf44 --- /dev/null +++ b/gcc/go/gofrontend/import.cc @@ -0,0 +1,892 @@ +// 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 "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)); +} + +// The name used for parameters, receivers, and results in imported +// function types. + +const char* const Import::import_marker = "*imported*"; + +// 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, we use the search path +// provided by -I and -L options. + +// 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, source_location location) +{ + if (!IS_ABSOLUTE_PATH(filename)) + { + 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 += filename; + Stream* s = Import::try_package_in_directory(indir, location); + if (s != NULL) + return s; + } + } + + Stream* s = Import::try_package_in_directory(filename, 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, + source_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, + source_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, + source_location location) +{ + const char* errmsg; + int err; + simple_object_read* sobj = simple_object_start_read(fd, offset, + "__GNU_GO", + &errmsg, &err); + if (sobj == NULL) + return NULL; + + off_t sec_offset; + off_t sec_length; + int found = simple_object_find_section(sobj, ".go_export", &sec_offset, + &sec_length, &errmsg, &err); + + simple_object_release_read(sobj); + + if (!found) + return NULL; + + if (lseek(fd, offset + sec_offset, SEEK_SET) < 0) + { + error_at(location, "lseek %s failed: %m", filename.c_str()); + return NULL; + } + + char* buf = new char[sec_length]; + ssize_t c = read(fd, buf, sec_length); + if (c < 0) + { + error_at(location, "read %s failed: %m", filename.c_str()); + delete[] buf; + return NULL; + } + if (c < sec_length) + { + error_at(location, "%s: short read", filename.c_str()); + delete[] buf; + return NULL; + } + + return new Stream_from_buffer(buf, sec_length); +} + +// Class Import. + +// Construct an Import object. We make the builtin_types_ vector +// large enough to hold all the builtin types. + +Import::Import(Stream* stream, source_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"); + + this->require_c_string("prefix "); + std::string unique_prefix = this->read_identifier(); + this->require_c_string(";\n"); + + this->package_ = gogo->add_imported_package(package_name, local_name, + is_local_name_exported, + unique_prefix, + 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"); + + if (stream->match_c_string("import ")) + 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 %, %, %, " + "%, 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::v1_checksum_len * 2); + this->require_c_string(";\n"); + } + + return this->package_; +} + +// Read the list of import control functions. + +void +Import::read_import_init_fns(Gogo* gogo) +{ + this->require_c_string("import"); + 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(); + + source_location loc = this->location_; + Named_object* no; + if (fntype->is_method()) + { + Type* rtype = receiver->type()->deref(); + if (rtype->is_error_type()) + return NULL; + Named_type* named_rtype = rtype->named_type(); + gcc_assert(named_rtype != NULL); + no = named_rtype->add_method_declaration(name, package, fntype, loc); + } + 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("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(- index) >= this->builtin_types_.size() + || this->builtin_types_[- index] == NULL) + : (static_cast(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(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(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; + } + + // 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 current package, the name will be + // .PREFIX.PACKAGE.NAME or simply NAME with no dots. Otherwise, a + // non-hidden symbol will be PREFIX.PACKAGE.NAME and a hidden symbol + // will be .PREFIX.PACKAGE.NAME. + std::string package_name; + std::string unique_prefix; + if (type_name.find('.') != std::string::npos) + { + bool is_hidden = false; + size_t start = 0; + if (type_name[0] == '.') + { + ++start; + is_hidden = true; + } + size_t dot1 = type_name.find('.', start); + size_t dot2; + if (dot1 == std::string::npos) + dot2 = std::string::npos; + else + dot2 = type_name.find('.', dot1 + 1); + if (dot1 == std::string::npos || dot2 == std::string::npos) + { + error_at(this->location_, + ("error at import data at %d: missing dot in type name"), + stream->pos()); + stream->set_saw_error(); + } + else + { + unique_prefix = type_name.substr(start, dot1 - start); + package_name = type_name.substr(dot1 + 1, dot2 - (dot1 + 1)); + } + if (!is_hidden) + type_name.erase(0, dot2 + 1); + } + + 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 (package_name.empty()) + package = this->package_; + else + package = this->gogo_->register_package(package_name, unique_prefix, + UNKNOWN_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", + Gogo::message_name(package->name()).c_str(), + Gogo::message_name(type_name).c_str()); + stream->set_saw_error(); + return Type::make_error_type(); + } + else + gcc_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 + { + gcc_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(); + + 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 methds yet, which means + // that we can nt 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); +} + +// 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); + gcc_assert(named_object != NULL && named_object->is_type()); + int index = - static_cast(code); + gcc_assert(index > 0 + && static_cast(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; +} + +// 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(source_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(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. + gcc_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(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(); + } +} -- cgit v1.2.3