diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 1503672619f151a87a2a703278bc1dd8b6467985..a6b016ad710c530fbaa41009d5c05258cfe7c121 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -e1dc92a6037a3f81ea1b8ea8fb6207af33505f0c +6db7e35d3bcd75ab3cb15296a5ddc5178038c9c1 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/export.cc b/gcc/go/gofrontend/export.cc index 6365d6440b73d9cf59c16dfef416c2bf9c779c37..a64c7fd80a603577cb94f0fa48b863aaeb493af3 100644 --- a/gcc/go/gofrontend/export.cc +++ b/gcc/go/gofrontend/export.cc @@ -44,11 +44,49 @@ const int Export::checksum_len; // Constructor. Export::Export(Stream* stream) - : stream_(stream), type_refs_(), type_index_(1), packages_() + : stream_(stream), type_index_(1), packages_() { go_assert(Export::checksum_len == Go_sha1_helper::checksum_len); } +// Type hash table operations, treating aliases as distinct. + +class Type_hash_alias_identical +{ + public: + unsigned int + operator()(const Type* type) const + { + return type->hash_for_method(NULL, + (Type::COMPARE_ERRORS + | Type::COMPARE_TAGS + | Type::COMPARE_ALIASES)); + } +}; + +class Type_alias_identical +{ + public: + bool + operator()(const Type* t1, const Type* t2) const + { + return Type::are_identical(t1, t2, + (Type::COMPARE_ERRORS + | Type::COMPARE_TAGS + | Type::COMPARE_ALIASES), + NULL); + } +}; + +// Mapping from Type objects to a constant index. This would be nicer +// as a field in Export, but then export.h would have to #include +// types.h. + +typedef Unordered_map_hash(const Type*, int, Type_hash_alias_identical, + Type_alias_identical) Type_refs; + +static Type_refs type_refs; + // A functor to sort Named_object pointers by name. struct Sort_bindings @@ -139,9 +177,10 @@ Export::export_globals(const std::string& package_name, std::sort(exports.begin(), exports.end(), Sort_bindings()); - // Find all packages not explicitly imported but mentioned by types. + // Assign indexes to all exported types and types referenced by + // exported types, and collect all packages mentioned. Unordered_set(const Package*) type_imports; - this->prepare_types(&exports, &type_imports); + int unexported_type_index = this->prepare_types(&exports, &type_imports); // Although the export data is readable, at least this version is, // it is conceptually a binary format. Start with a four byte @@ -178,10 +217,17 @@ Export::export_globals(const std::string& package_name, // and ABI being used, although ideally any problems in that area // would be caught by the linker. + // Write out all the types, both exported and not. + this->write_types(unexported_type_index); + + // Write out the non-type export data. for (std::vector<Named_object*>::const_iterator p = exports.begin(); p != exports.end(); ++p) - (*p)->export_named_object(this); + { + if (!(*p)->is_type()) + (*p)->export_named_object(this); + } std::string checksum = this->stream_->checksum(); std::string s = "checksum "; @@ -204,9 +250,10 @@ Export::export_globals(const std::string& package_name, class Find_types_to_prepare : public Traverse { public: - Find_types_to_prepare(Unordered_set(const Package*)* imports) + Find_types_to_prepare(Export* exp, + Unordered_set(const Package*)* imports) : Traverse(traverse_types), - imports_(imports) + exp_(exp), imports_(imports) { } int @@ -221,19 +268,34 @@ class Find_types_to_prepare : public Traverse traverse_named_type(Named_type*); private: + // Exporters. + Export* exp_; // List of packages we are building. Unordered_set(const Package*)* imports_; }; -// Traverse a type. +// Set type index of referenced type, record package imports, and make +// sure we traverse methods of named types. int Find_types_to_prepare::type(Type* type) { - // Skip forwarders. + // Skip forwarders; don't try to give them a type index. if (type->forward_declaration_type() != NULL) return TRAVERSE_CONTINUE; + // Skip the void type, which we'll see when exporting + // unsafe.Pointer. The void type is not itself exported, because + // Pointer_type::do_export checks for it. + if (type->is_void_type()) + return TRAVERSE_SKIP_COMPONENTS; + + if (!this->exp_->set_type_index(type)) + { + // We've already seen this type. + return TRAVERSE_SKIP_COMPONENTS; + } + // At this stage of compilation traversing interface types traverses // the final list of methods, but we export the locally defined // methods. If there is an embedded interface type we need to make @@ -267,7 +329,7 @@ Find_types_to_prepare::type(Type* type) } // Traverse the types in a function type. We don't need the function -// type tself, just the receiver, parameter, and result types. +// type itself, just the receiver, parameter, and result types. void Find_types_to_prepare::traverse_function(Function_type* type) @@ -319,20 +381,34 @@ Find_types_to_prepare::traverse_named_type(Named_type* nt) } } -// Collect all the pacakges we see in types, so that if we refer to -// any types from indirectly importe packages we can tell the importer -// about the package. +// Prepare to export types by assigning a type index to every exported +// type and every type referenced by an exported type. Also collect +// all the packages we see in types, so that if we refer to any types +// from indirectly imported packages we can tell the importer about +// the package. This returns the number of exported types. -void +int Export::prepare_types(const std::vector<Named_object*>* exports, Unordered_set(const Package*)* imports) { - // Use a single index of the traversal class because traversal + // Assign indexes to all the exported types. + for (std::vector<Named_object*>::const_iterator p = exports->begin(); + p != exports->end(); + ++p) + { + if (!(*p)->is_type()) + continue; + this->set_type_index((*p)->type_value()); + } + + int ret = this->type_index_; + + // Use a single instance of the traversal class because traversal // classes keep track of which types they've already seen. That // lets us avoid type reference loops. - Find_types_to_prepare find(imports); + Find_types_to_prepare find(this, imports); - // Traverse all the exported objects. + // Traverse all the exported objects and assign indexes to all types. for (std::vector<Named_object*>::const_iterator p = exports->begin(); p != exports->end(); ++p) @@ -349,7 +425,8 @@ Export::prepare_types(const std::vector<Named_object*>* exports, break; case Named_object::NAMED_OBJECT_TYPE: - Type::traverse(no->type_value(), &find); + Type::traverse(no->type_value()->real_type(), &find); + find.traverse_named_type(no->type_value()); break; case Named_object::NAMED_OBJECT_VAR: @@ -370,6 +447,31 @@ Export::prepare_types(const std::vector<Named_object*>* exports, break; } } + + return ret; +} + +// Give a type an index if it doesn't already have one. Return true +// if we set the type index, false if it was already known. + +bool +Export::set_type_index(Type* type) +{ + type = type->forwarded(); + + std::pair<Type_refs::iterator, bool> ins = + type_refs.insert(std::make_pair(type, 0)); + if (!ins.second) + { + // We've already seen this type. + return false; + } + + int index = this->type_index_; + ++this->type_index_; + ins.first->second = index; + + return true; } // Sort packages. @@ -705,6 +807,104 @@ Export::write_imported_init_fns(const std::string& package_name, this->write_c_string("\n"); } +// Write the types to the export stream. + +void +Export::write_types(int unexported_type_index) +{ + // Map from type index to type. + std::vector<const Type*> types(static_cast<size_t>(this->type_index_)); + for (Type_refs::const_iterator p = type_refs.begin(); + p != type_refs.end(); + ++p) + { + if (p->second >= 0) + types.at(p->second) = p->first; + } + + // Write the type information to a buffer. + Stream_to_string type_data; + Export::Stream* orig_stream = this->stream_; + this->stream_ = &type_data; + + std::vector<size_t> type_sizes(static_cast<size_t>(this->type_index_)); + type_sizes[0] = 0; + + // Start at 1 because type index 0 is not used. + size_t start_size = 0; + for (int i = 1; i < this->type_index_; ++i) + { + this->write_type_definition(types[i], i); + + size_t cur_size = type_data.string().size(); + type_sizes[i] = cur_size - start_size; + start_size = cur_size; + } + + // Back to original stream. + this->stream_ = orig_stream; + + // The line "types MAXP1 EXPORTEDP1 SIZES..." appears before the + // types. MAXP1 is one more than the maximum type index used; that + // is, it is the size of the array we need to allocate to hold all + // the values. Indexes 1 up to but not including EXPORTEDP1 are the + // exported types. The other types are not exported. SIZES... is a + // list of MAXP1-1 entries listing the size of the type definition + // for each type, starting at index 1. + char buf[100]; + snprintf(buf, sizeof buf, "types %d %d", this->type_index_, + unexported_type_index); + this->write_c_string(buf); + + // Start at 1 because type index 0 is not used. + for (int i = 1; i < this->type_index_; ++i) + { + snprintf(buf, sizeof buf, " %lu", + static_cast<unsigned long>(type_sizes[i])); + this->write_c_string(buf); + } + this->write_c_string("\n"); + this->write_string(type_data.string()); +} + +// Write a single type to the export stream. + +void +Export::write_type_definition(const Type* type, int index) +{ + this->write_c_string("type "); + + char buf[30]; + snprintf(buf, sizeof buf, "%d ", index); + this->write_c_string(buf); + + const Named_type* nt = type->named_type(); + if (nt != NULL) + { + const Named_object* no = nt->named_object(); + const Package* package = no->package(); + + this->write_c_string("\""); + if (package != NULL && !Gogo::is_hidden_name(no->name())) + { + this->write_string(package->pkgpath()); + this->write_c_string("."); + } + this->write_string(nt->named_object()->name()); + this->write_c_string("\" "); + + if (nt->is_alias()) + this->write_c_string("= "); + } + + type->export_type(this); + + // Type::export_type will print a newline for a named type, but not + // otherwise. + if (nt == NULL) + this->write_c_string("\n"); +} + // Write a name to the export stream. void @@ -736,91 +936,19 @@ Export::write_unsigned(unsigned value) this->write_c_string(buf); } -// Export a type. We have to ensure that on import we create a single -// Named_type node for each named type. We do this by keeping a hash -// table mapping named types to reference numbers. The first time we -// see a named type we assign it a reference number by making an entry -// in the hash table. If we see it again, we just refer to the -// reference number. - -// Named types are, of course, associated with packages. Note that we -// may see a named type when importing one package, and then later see -// the same named type when importing a different package. The home -// package may or may not be imported during this compilation. The -// reference number scheme has to get this all right. Basic approach -// taken from "On the Linearization of Graphs and Writing Symbol -// Files" by Robert Griesemer. +// Export a type. void Export::write_type(const Type* type) { - // We don't want to assign a reference number to a forward - // declaration to a type which was defined later. type = type->forwarded(); - - Type_refs::const_iterator p = this->type_refs_.find(type); - if (p != this->type_refs_.end()) - { - // This type was already in the table. - int index = p->second; - go_assert(index != 0); - char buf[30]; - snprintf(buf, sizeof buf, "<type %d>", index); - this->write_c_string(buf); - return; - } - - const Named_type* named_type = type->named_type(); - const Forward_declaration_type* forward = type->forward_declaration_type(); - - int index = this->type_index_; - ++this->type_index_; - + Type_refs::const_iterator p = type_refs.find(type); + go_assert(p != type_refs.end()); + int index = p->second; + go_assert(index != 0); char buf[30]; - snprintf(buf, sizeof buf, "<type %d ", index); + snprintf(buf, sizeof buf, "<type %d>", index); this->write_c_string(buf); - - if (named_type != NULL || forward != NULL) - { - const Named_object* named_object; - if (named_type != NULL) - { - // The builtin types should have been predefined. - go_assert(!Linemap::is_predeclared_location(named_type->location()) - || (named_type->named_object()->package()->package_name() - == "unsafe")); - named_object = named_type->named_object(); - } - else - named_object = forward->named_object(); - - const Package* package = named_object->package(); - - std::string s = "\""; - if (package != NULL && !Gogo::is_hidden_name(named_object->name())) - { - s += package->pkgpath(); - s += '.'; - } - s += named_object->name(); - s += "\" "; - this->write_string(s); - - // We must add a named type to the table now, since the - // definition of the type may refer to the named type via a - // pointer. - this->type_refs_[type] = index; - - if (named_type != NULL && named_type->is_alias()) - this->write_c_string("= "); - } - - type->export_type(this); - - this->write_c_string(">"); - - if (named_type == NULL) - this->type_refs_[type] = index; } // Export escape note. @@ -873,18 +1001,15 @@ Export::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()); std::pair<Type_refs::iterator, bool> ins = - this->type_refs_.insert(std::make_pair(named_object->type_value(), code)); + type_refs.insert(std::make_pair(named_object->type_value(), code)); go_assert(ins.second); // We also insert the underlying type. We can see the underlying - // type at least for string and bool. We skip the type aliases byte - // and rune here. - if (code != BUILTIN_BYTE && code != BUILTIN_RUNE) - { - Type* real_type = named_object->type_value()->real_type(); - ins = this->type_refs_.insert(std::make_pair(real_type, code)); - go_assert(ins.second); - } + // type at least for string and bool. It's OK if this insert + // fails--we expect duplications here, and it doesn't matter when + // they occur. + Type* real_type = named_object->type_value()->real_type(); + type_refs.insert(std::make_pair(real_type, code)); } // Class Export::Stream. diff --git a/gcc/go/gofrontend/export.h b/gcc/go/gofrontend/export.h index 55942818eec931994815b174dd41d3eab4628efb..84077a22c23b254832fcfe6fb510831c77f208a1 100644 --- a/gcc/go/gofrontend/export.h +++ b/gcc/go/gofrontend/export.h @@ -11,6 +11,7 @@ class Go_sha1_helper; class Gogo; +class Named_object; class Import_init; class Named_object; class Bindings; @@ -154,6 +155,10 @@ class Export : public String_dump const Import_init_set& imported_init_fns, const Bindings* bindings); + // Set the index of a type. + bool + set_type_index(Type*); + // Write a string to the export stream. void write_string(const std::string& s) @@ -196,7 +201,7 @@ class Export : public String_dump Export& operator=(const Export&); // Prepare types for exporting. - void + int prepare_types(const std::vector<Named_object*>* exports, Unordered_set(const Package*)* imports); @@ -224,24 +229,27 @@ class Export : public String_dump write_imported_init_fns(const std::string& package_name, const std::string&, const Import_init_set&); + // Write out all types. + void + write_types(int unexported_type_index); + + // Write out one type definition. + void + write_type_definition(const Type* type, int index); + // Register one builtin type. void register_builtin_type(Gogo*, const char* name, Builtin_code); - // Mapping from Type objects to a constant index. - typedef Unordered_map(const Type*, int) Type_refs; - // The stream to which we are writing data. Stream* stream_; - // Type mappings. - Type_refs type_refs_; // Index number of next type. int type_index_; // Packages we have written out. Unordered_set(const Package*) packages_; }; -// An export streamer which puts the export stream in a named section. +// An export streamer that puts the export stream in a named section. class Stream_to_section : public Export::Stream { @@ -256,4 +264,26 @@ class Stream_to_section : public Export::Stream Backend* backend_; }; +// An export streamer that puts the export stream in a string. + +class Stream_to_string : public Export::Stream +{ + public: + Stream_to_string() + : string_() + {} + + const std::string& + string() const + { return this->string_; } + + protected: + void + do_write(const char* s, size_t len) + { this->string_.append(s, len); } + + private: + std::string string_; +}; + #endif // !defined(GO_EXPORT_H) diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index bfbf682fc113ef35ccf87ebf599af9c000a9c4b2..2472245ebe95d35f96ada08e3ef9282a657b428d 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -7511,8 +7511,8 @@ Named_object::export_named_object(Export* exp) const break; case NAMED_OBJECT_TYPE: - this->type_value()->export_named_type(exp, this->name_); - break; + // Types are handled by export::write_types. + go_unreachable(); case NAMED_OBJECT_TYPE_DECLARATION: go_error_at(this->type_declaration_value()->location(), diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 0864ee1d72a8d41fee3a851ba4e073bad8595b75..9c469ca32b5ece8a5a74fb20c8536c509a362096 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -12,9 +12,7 @@ class Traverse; class Statement_inserter; class Type; -class Type_hash_identical; class Type_equal; -class Type_identical; class Typed_identifier; class Typed_identifier_list; class Function_type; diff --git a/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc index 8d17df708fc189a54017d9078b971c186ab4b24d..d30068f3b97e99fd495c70ad6464b470ab0bc71a 100644 --- a/gcc/go/gofrontend/import.cc +++ b/gcc/go/gofrontend/import.cc @@ -236,7 +236,7 @@ Import::find_export_data(const std::string& filename, int fd, Location location) } char buf[len]; - ssize_t c = read(fd, buf, len); + ssize_t c = ::read(fd, buf, len); if (c < len) return NULL; @@ -288,7 +288,7 @@ Import::find_object_export_data(const std::string& filename, Import::Import(Stream* stream, Location location) : gogo_(NULL), stream_(stream), location_(location), package_(NULL), - add_to_globals_(false), + add_to_globals_(false), type_data_(), type_pos_(0), type_offsets_(), builtin_types_((- SMALLEST_BUILTIN_CODE) + 1), types_(), version_(EXPORT_FORMAT_UNKNOWN) { @@ -403,6 +403,12 @@ Import::import(Gogo* gogo, const std::string& local_name, 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()) { @@ -585,6 +591,86 @@ Import::read_import_init_fns(Gogo* gogo) } } +// 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<size_t, size_t>(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<size_t>(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; +} + // Import a constant. void @@ -605,6 +691,18 @@ Import::import_const() 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); @@ -694,9 +792,73 @@ Import::import_func(Package* package) return no; } +// 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<size_t>(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, it registers the name, but marks -// it as invisible. +// 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() @@ -720,7 +882,28 @@ Import::read_type() if (c == '>') { - // This type was already defined. + // A reference to a type defined earlier. + + if (index >= 0 && !this->type_data_.empty()) + { + if (static_cast<size_t>(index) >= this->type_offsets_.size()) + { + go_error_at(this->location_, + ("error in import data at %d: " + "bad type index %d >= %d"), + stream->pos(), index, + static_cast<int>(this->type_offsets_.size())); + stream->set_saw_error(); + return Type::make_error_type(); + } + + if (this->types_[index] == NULL) + { + if (!this->parse_type(index)) + return Type::make_error_type(); + } + } + if (index < 0 ? (static_cast<size_t>(- index) >= this->builtin_types_.size() || this->builtin_types_[- index] == NULL) @@ -737,11 +920,21 @@ Import::read_type() return index < 0 ? this->builtin_types_[- index] : this->types_[index]; } + 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: expect %< %> or %<>%>'", + "error in import data at %d: expected %< %> or %<>%>'", stream->pos()); stream->set_saw_error(); stream->advance(1); @@ -774,10 +967,25 @@ Import::read_type() return type; } - // This type has a name. - 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; @@ -863,7 +1071,7 @@ Import::read_type() // 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(">")) + if (this->match_c_string(">") || this->match_c_string("\n")) type = this->types_[index]; else { @@ -912,8 +1120,6 @@ Import::read_type() } } - this->require_c_string(">"); - return type; } @@ -1125,10 +1331,9 @@ Stream_from_file::do_peek(size_t length, const char** bytes) *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); + + this->data_.resize(length); + ssize_t got = ::read(this->fd_, &this->data_[0], length); if (got < 0) { @@ -1149,8 +1354,6 @@ Stream_from_file::do_peek(size_t length, const char** bytes) if (static_cast<size_t>(got) < length) return false; - this->data_.assign(buf, got); - *bytes = this->data_.data(); return true; } diff --git a/gcc/go/gofrontend/import.h b/gcc/go/gofrontend/import.h index dbdcc8fb4896c8e6e9e357b220c4c8ec42236ad9..cc7703bcc943871f7ec98586f999dc73309042f1 100644 --- a/gcc/go/gofrontend/import.h +++ b/gcc/go/gofrontend/import.h @@ -30,6 +30,11 @@ class Import Stream(); virtual ~Stream(); + // Set the position, for error messages. + void + set_pos(int pos) + { this->pos_ = pos; } + // Return whether we have seen an error. bool saw_error() const @@ -249,6 +254,10 @@ class Import void read_import_init_fns(Gogo*); + // Read the types. + bool + read_types(); + // Import a constant. void import_const(); @@ -265,6 +274,14 @@ class Import Named_object* import_func(Package*); + // Parse a type definition. + bool + parse_type(int index); + + // Read a named type and store it at this->type_[index]. + Type* + read_named_type(int index); + // Register a single builtin type. void register_builtin_type(Gogo*, const char* name, Builtin_code); @@ -299,6 +316,12 @@ class Import // Whether to add new objects to the global scope, rather than to a // package scope. bool add_to_globals_; + // All type data. + std::string type_data_; + // Position of type data in the stream. + int type_pos_; + // Mapping from type code to offset/length in type_data_. + std::vector<std::pair<size_t, size_t> > type_offsets_; // Mapping from negated builtin type codes to Type structures. std::vector<Named_type*> builtin_types_; // Mapping from exported type codes to Type structures. @@ -399,4 +422,41 @@ class Stream_from_file : public Import::Stream std::string data_; }; +// Read import data from an offset into a std::string. This uses a +// reference to the string, to avoid copying, so the string must be +// kept alive through some other mechanism. + +class Stream_from_string_ref : public Import::Stream +{ + public: + Stream_from_string_ref(const std::string& str, size_t offset, size_t length) + : str_(str), pos_(offset), end_(offset + length) + { } + + ~Stream_from_string_ref() + {} + + protected: + bool + do_peek(size_t length, const char** bytes) + { + if (this->pos_ + length > this->end_) + return false; + *bytes = &this->str_[this->pos_]; + return true; + } + + void + do_advance(size_t length) + { this->pos_ += length; } + + private: + // A reference to the string we are reading from. + const std::string& str_; + // The current offset into the string. + size_t pos_; + // The index after the last byte we can read. + size_t end_; +}; + #endif // !defined(GO_IMPORT_H) diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 94b332a25b2b9195e63949e66e00a8e6862782b3..2d348ba2c6f1539fdfa0dbdcf78ad6b350f7d5c3 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -10865,19 +10865,8 @@ Named_type::append_reflection_type_name(Gogo* gogo, bool use_alias, ret->append(Gogo::unpack_hidden_name(this->named_object_->name())); } -// Export the type. This is called to export a global type. - -void -Named_type::export_named_type(Export* exp, const std::string&) const -{ - // We don't need to write the name of the type here, because it will - // be written by Export::write_type anyhow. - exp->write_c_string("type "); - exp->write_type(this); - exp->write_c_string("\n"); -} - -// Import a named type. +// Import a named type. This is only used for export format versions +// before version 3. void Named_type::import_named_type(Import* imp, Named_type** ptype) @@ -10891,12 +10880,15 @@ Named_type::import_named_type(Import* imp, Named_type** ptype) } // Export the type when it is referenced by another type. In this -// case Export::export_type will already have issued the name. +// case Export::export_type will already have issued the name. The +// output always ends with a newline, since that is convenient if +// there are methods. void Named_type::do_export(Export* exp) const { exp->write_type(this->type_); + exp->write_c_string("\n"); // To save space, we only export the methods directly attached to // this type. @@ -10904,7 +10896,6 @@ Named_type::do_export(Export* exp) const if (methods == NULL) return; - exp->write_c_string("\n"); for (Bindings::const_definitions_iterator p = methods->begin_definitions(); p != methods->end_definitions(); ++p) diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index c99b08a959d526d6dc587d0e49e28d741a2f6822..4898e67d7b809d533b4323e23998d42ef5398270 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -3445,10 +3445,6 @@ class Named_type : public Type void append_mangled_type_name(Gogo*, bool use_alias, std::string*) const; - // Export the type. - void - export_named_type(Export*, const std::string& name) const; - // Import a named type. static void import_named_type(Import*, Named_type**); diff --git a/libgo/go/go/internal/gccgoimporter/parser.go b/libgo/go/go/internal/gccgoimporter/parser.go index cd4e1d9288e041ef78ad40d28cf11d336245caa1..dc61e4cadc9e8349d6918603442fda7c3b99809a 100644 --- a/libgo/go/go/internal/gccgoimporter/parser.go +++ b/libgo/go/go/internal/gccgoimporter/parser.go @@ -18,7 +18,7 @@ import ( ) type parser struct { - scanner scanner.Scanner + scanner *scanner.Scanner version string // format version tok rune // current token lit string // literal string; only valid for Ident, Int, String tokens @@ -27,18 +27,24 @@ type parser struct { pkg *types.Package // reference to imported package imports map[string]*types.Package // package path -> package object typeMap map[int]types.Type // type number -> type + typeData []string // unparsed type data initdata InitData // package init priority data } func (p *parser) init(filename string, src io.Reader, imports map[string]*types.Package) { + p.scanner = new(scanner.Scanner) + p.initScanner(filename, src) + p.imports = imports + p.typeMap = make(map[int]types.Type) +} + +func (p *parser) initScanner(filename string, src io.Reader) { p.scanner.Init(src) p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) } p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments p.scanner.Whitespace = 1<<'\t' | 1<<' ' p.scanner.Filename = filename // for good error messages p.next() - p.imports = imports - p.typeMap = make(map[int]types.Type) } type importError struct { @@ -720,6 +726,9 @@ func (p *parser) parseType(pkg *types.Package) (t types.Type) { n := p.parseInt() if p.tok == '>' { + if len(p.typeData) > 0 && p.typeMap[int(n)] == nil { + p.parseSavedType(pkg, int(n)) + } t = p.typeMap[int(n)] } else { t = p.parseTypeDefinition(pkg, int(n)) @@ -739,6 +748,67 @@ func (p *parser) parseType(pkg *types.Package) (t types.Type) { return } +// Types = "types" maxp1 exportedp1 (offset length)* . +func (p *parser) parseTypes(pkg *types.Package) { + maxp1 := p.parseInt() + exportedp1 := p.parseInt() + + type typeOffset struct { + offset int + length int + } + var typeOffsets []typeOffset + + total := 0 + for i := 1; i < int(maxp1); i++ { + len := int(p.parseInt()) + typeOffsets = append(typeOffsets, typeOffset{total, len}) + total += len + } + + // We should now have p.tok pointing to the final newline. + // The next runes from the scanner should be the type data. + + var sb strings.Builder + for sb.Len() < total { + r := p.scanner.Next() + if r == scanner.EOF { + p.error("unexpected EOF") + } + sb.WriteRune(r) + } + allTypeData := sb.String() + + p.typeData = []string{""} // type 0, unused + for _, to := range typeOffsets { + p.typeData = append(p.typeData, allTypeData[to.offset:to.offset+to.length]) + } + + for i := 1; i < int(exportedp1); i++ { + p.parseSavedType(pkg, i) + } +} + +// parseSavedType parses one saved type definition. +func (p *parser) parseSavedType(pkg *types.Package, i int) { + defer func(s *scanner.Scanner, tok rune, lit string) { + p.scanner = s + p.tok = tok + p.lit = lit + }(p.scanner, p.tok, p.lit) + + p.scanner = new(scanner.Scanner) + p.initScanner(p.scanner.Filename, strings.NewReader(p.typeData[i])) + p.expectKeyword("type") + id := int(p.parseInt()) + if id != i { + p.errorf("type ID mismatch: got %d, want %d", id, i) + } + if p.typeMap[i] == nil { + p.typeMap[i] = p.parseTypeDefinition(pkg, i) + } +} + // PackageInit = unquotedString unquotedString int . func (p *parser) parsePackageInit() PackageInit { name := p.parseUnquotedString() @@ -883,6 +953,11 @@ func (p *parser) parseDirective() { p.getPkg(pkgpath, pkgname) p.expectEOL() + case "types": + p.next() + p.parseTypes(p.pkg) + p.expectEOL() + case "func": p.next() fun := p.parseFunc(p.pkg)