From a847d2b7b142a86b02296a7766a1bc29f36cf7a8 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 23 Oct 2018 02:46:41 +0000 Subject: [PATCH] compiler: export indexed type data, read unexported types lazily Introduce a new "types" command to the export data to record the number of types and the size of their export data. It is immediately followed by new "type" commands that can be indexed. Parse all the exported types immediately so that we register them, but parse other type data only as needed. Reviewed-on: https://go-review.googlesource.com/c/143022 From-SVN: r265409 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/export.cc | 333 +++++++++++++------ gcc/go/gofrontend/export.h | 44 ++- gcc/go/gofrontend/gogo.cc | 4 +- gcc/go/gofrontend/gogo.h | 2 - gcc/go/gofrontend/import.cc | 237 ++++++++++++- gcc/go/gofrontend/import.h | 60 ++++ gcc/go/gofrontend/types.cc | 21 +- gcc/go/gofrontend/types.h | 4 - libgo/go/go/internal/gccgoimporter/parser.go | 81 ++++- 10 files changed, 633 insertions(+), 155 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 1503672619f..a6b016ad710 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 6365d6440b7..a64c7fd80a6 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::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* 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::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::const_iterator p = exports->begin(); p != exports->end(); ++p) @@ -349,7 +425,8 @@ Export::prepare_types(const std::vector* 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* 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 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 types(static_cast(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 type_sizes(static_cast(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(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, "", 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, "", 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 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 55942818eec..84077a22c23 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* 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 bfbf682fc11..2472245ebe9 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 0864ee1d72a..9c469ca32b5 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 8d17df708fc..d30068f3b97 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(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; +} + // 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(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(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(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(- 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(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 dbdcc8fb489..cc7703bcc94 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 > type_offsets_; // Mapping from negated builtin type codes to Type structures. std::vector 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 94b332a25b2..2d348ba2c6f 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 c99b08a959d..4898e67d7b8 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 cd4e1d9288e..dc61e4cadc9 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) -- 2.30.2