compiler: export indexed type data, read unexported types lazily
authorIan Lance Taylor <ian@gcc.gnu.org>
Tue, 23 Oct 2018 02:46:41 +0000 (02:46 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Tue, 23 Oct 2018 02:46:41 +0000 (02:46 +0000)
    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
gcc/go/gofrontend/export.cc
gcc/go/gofrontend/export.h
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/gogo.h
gcc/go/gofrontend/import.cc
gcc/go/gofrontend/import.h
gcc/go/gofrontend/types.cc
gcc/go/gofrontend/types.h
libgo/go/go/internal/gccgoimporter/parser.go

index 1503672619f151a87a2a703278bc1dd8b6467985..a6b016ad710c530fbaa41009d5c05258cfe7c121 100644 (file)
@@ -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.
index 6365d6440b73d9cf59c16dfef416c2bf9c779c37..a64c7fd80a603577cb94f0fa48b863aaeb493af3 100644 (file)
@@ -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.
index 55942818eec931994815b174dd41d3eab4628efb..84077a22c23b254832fcfe6fb510831c77f208a1 100644 (file)
@@ -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)
index bfbf682fc113ef35ccf87ebf599af9c000a9c4b2..2472245ebe95d35f96ada08e3ef9282a657b428d 100644 (file)
@@ -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(),
index 0864ee1d72a8d41fee3a851ba4e073bad8595b75..9c469ca32b5ece8a5a74fb20c8536c509a362096 100644 (file)
@@ -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;
index 8d17df708fc189a54017d9078b971c186ab4b24d..d30068f3b97e99fd495c70ad6464b470ab0bc71a 100644 (file)
@@ -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;
 }
index dbdcc8fb4896c8e6e9e357b220c4c8ec42236ad9..cc7703bcc943871f7ec98586f999dc73309042f1 100644 (file)
@@ -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)
index 94b332a25b2b9195e63949e66e00a8e6862782b3..2d348ba2c6f1539fdfa0dbdcf78ad6b350f7d5c3 100644 (file)
@@ -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)
index c99b08a959d526d6dc587d0e49e28d741a2f6822..4898e67d7b809d533b4323e23998d42ef5398270 100644 (file)
@@ -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**);
index cd4e1d9288e041ef78ad40d28cf11d336245caa1..dc61e4cadc9e8349d6918603442fda7c3b99809a 100644 (file)
@@ -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)