view gcc/go/gofrontend/import.h @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 1830386684a0
children
line wrap: on
line source

// import.h -- Go frontend import declarations.     -*- C++ -*-

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#ifndef GO_IMPORT_H
#define GO_IMPORT_H

#include "export.h"
#include "go-linemap.h"

class Gogo;
class Block;
class Package;
class Type;
class Named_object;
class Named_type;
class Expression;
class Import_function_body;
class Temporary_statement;
class Unnamed_label;
class Finalize_methods;

// Expressions can be imported either directly from import data (for
// simple constant expressions that can appear in a const declaration
// or as an array length in a type definition) or from an exported
// function body (for an inlinable function).  These two cases happen
// at different points in the compilation and have different
// requirements, so it's not easy to unify them.  Import_expression is
// an abstract interface that permits the expression import code to
// work at either point.  When importing expressions that only occur
// for an inlinable function, the ifb method is available to get the
// full Import_function_body.

class Import_expression
{
 public:
  // Return the import function body.  This should only be called for
  // expressions that can not appear outside of an inlinable function
  // body.
  virtual Import_function_body*
  ifb() = 0;

  // The location to report in an error message.
  virtual Location
  location() const = 0;

  // Peek at the next character in the input, returning a value from 0
  // to 0xff.  Returns -1 at end of stream.
  virtual int
  peek_char() = 0;

  // Return the next character and advance.
  virtual int
  get_char() = 0;

  // Return true if the next bytes match STR.
  virtual bool
  match_c_string(const char* str) = 0;

  // Require that the next bytes match STR.
  virtual void
  require_c_string(const char* str) = 0;

  // Advance the stream SKIP bytes.
  virtual void
  advance(size_t skip) = 0;

  // Read an identifier.
  virtual std::string
  read_identifier() = 0;

  // Read a type.
  virtual Type*
  read_type() = 0;

  // Return the maximum valid package index.
  virtual size_t
  max_package_index() const = 0;

  // Return the package for a package index.
  virtual Package*
  package_at_index(int index) = 0;

  // Return the version number of the export data we're reading.
  virtual Export_data_version
  version() const = 0;
};

// This class manages importing Go declarations.

class Import : public Import_expression
{
 public:
  // The Stream class is an interface used to read the data.  The
  // caller should instantiate a child of this class.
  class Stream
  {
   public:
    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
    { return this->saw_error_; }

    // Record that we've seen an error.
    void
    set_saw_error()
    { this->saw_error_ = true; }

    // Return the next character (a value from 0 to 0xff) without
    // advancing.  Returns -1 at end of stream.
    int
    peek_char();

    // Look for LENGTH characters, setting *BYTES to point to them.
    // Returns false if the bytes are not available.  Does not
    // advance.
    bool
    peek(size_t length, const char** bytes)
    { return this->do_peek(length, bytes); }

    // Return the next character (a value from 0 to 0xff) and advance
    // the read position by 1.  Returns -1 at end of stream.
    int
    get_char()
    {
      int c = this->peek_char();
      this->advance(1);
      return c;
    }

    // Return true if at the end of the stream.
    bool
    at_eof()
    { return this->peek_char() == -1; }

    // Return true if the next bytes match STR.
    bool
    match_c_string(const char* str)
    { return this->match_bytes(str, strlen(str)); }

    // Return true if the next LENGTH bytes match BYTES.
    bool
    match_bytes(const char* bytes, size_t length);

    // Give an error if the next bytes do not match STR.  Advance the
    // read position by the length of STR.
    void
    require_c_string(Location location, const char* str)
    { this->require_bytes(location, str, strlen(str)); }

    // Given an error if the next LENGTH bytes do not match BYTES.
    // Advance the read position by LENGTH.
    void
    require_bytes(Location, const char* bytes, size_t length);

    // Advance the read position by SKIP bytes.
    void
    advance(size_t skip)
    {
      this->do_advance(skip);
      this->pos_ += skip;
    }

    // Return the current read position.  This returns int because it
    // is more convenient in error reporting.  FIXME.
    int
    pos()
    { return static_cast<int>(this->pos_); }

   protected:
    // This function should set *BYTES to point to a buffer holding
    // the LENGTH bytes at the current read position.  It should
    // return false if the bytes are not available.  This should not
    // change the current read position.
    virtual bool
    do_peek(size_t length, const char** bytes) = 0;

    // This function should advance the current read position LENGTH
    // bytes.
    virtual void
    do_advance(size_t skip) = 0;

   private:
    // The current read position.
    size_t pos_;
    // True if we've seen an error reading from this stream.
    bool saw_error_;
  };

  // Find import data.  This searches the file system for FILENAME and
  // returns a pointer to a Stream object to read the data that it
  // exports.  LOCATION is the location of the import statement.
  // RELATIVE_IMPORT_PATH is used as a prefix for a relative import.
  static Stream*
  open_package(const std::string& filename, Location location,
	       const std::string& relative_import_path);

  // Constructor.
  Import(Stream*, Location);

  // Register the builtin types.
  void
  register_builtin_types(Gogo*);

  // Import everything defined in the stream.  LOCAL_NAME is the local
  // name to be used for bindings; if it is the string "." then
  // bindings should be inserted in the global scope.  If LOCAL_NAME
  // is the empty string then the name of the package itself is the
  // local name.  This returns the imported package, or NULL on error.
  Package*
  import(Gogo*, const std::string& local_name, bool is_local_name_exported);

  // The location of the import statement.
  Location
  location() const
  { return this->location_; }

  // Return the package we are importing.
  Package*
  package() const
  { return this->package_; }

  // Return the next character.
  int
  peek_char()
  { return this->stream_->peek_char(); }

  // Return the next character and advance.
  int
  get_char()
  { return this->stream_->get_char(); }

  // Read LENGTH characters into a string and advance past them.  On
  // EOF reports an error and returns an empty string.
  std::string
  read(size_t length);

  // Return true at the end of the stream.
  bool
  at_eof()
  { return this->stream_->at_eof(); }

  // Return whether the next bytes match STR.
  bool
  match_c_string(const char* str)
  { return this->stream_->match_c_string(str); }

  // Require that the next bytes match STR.
  void
  require_c_string(const char* str)
  { this->stream_->require_c_string(this->location_, str); }

  // Advance the stream SKIP bytes.
  void
  advance(size_t skip)
  { this->stream_->advance(skip); }

  // Stream position, for error reporting.
  int
  pos()
  { return this->stream_->pos(); }

  // Return the version number of the export data we're reading.
  Export_data_version
  version() const { return this->version_; }

  // Skip a semicolon if using an older version.
  void
  require_semicolon_if_old_version()
  {
    if (this->version_ == EXPORT_FORMAT_V1
	|| this->version_ == EXPORT_FORMAT_V2)
      this->require_c_string(";");
  }

  // Read an identifier.
  std::string
  read_identifier();

  // Read a name.  This is like read_identifier, except that a "?" is
  // returned as an empty string.  This matches Export::write_name.
  std::string
  read_name();

  // Return the maximum valid package index.  This is the size of
  // packages_ because we will subtract 1 in package_at_index.
  size_t
  max_package_index() const
  { return this->packages_.size(); }

  // Return the package at an index.  (We subtract 1 because package
  // index 0 is not used.)
  Package*
  package_at_index(int index)
  { return this->packages_.at(index - 1); }

  // Read a type.
  Type*
  read_type();

  // Return the type for a type index.  INPUT_NAME and INPUT_OFFSET
  // are only for error reporting.  PARSED is set to whether we parsed
  // the type information for a new type.
  Type*
  type_for_index(int index, const std::string& input_name,
		 size_t input_offset, bool* parsed);

  // Read an escape note.
  std::string
  read_escape();

  // Clear the stream when it is no longer accessible.
  void
  clear_stream()
  { this->stream_ = NULL; }

  // Just so that Import implements Import_expression.
  Import_function_body*
  ifb()
  { return NULL; }

  // Read a qualified identifier from an Import_expression.  Sets
  // *NAME, *PKG, and *IS_EXPORTED, and reports whether it succeeded.
  static bool
  read_qualified_identifier(Import_expression*, std::string* name,
			    Package** pkg, bool* is_exported);

 private:
  static Stream*
  try_package_in_directory(const std::string&, Location);

  static int
  try_suffixes(std::string*);

  static Stream*
  find_export_data(const std::string& filename, int fd, Location);

  static Stream*
  find_object_export_data(const std::string& filename, int fd,
			  off_t offset, Location);

  static const int archive_magic_len = 8;

  static bool
  is_archive_magic(const char*);

  static Stream*
  find_archive_export_data(const std::string& filename, int fd,
			   Location);

  // Read a package line.
  void
  read_one_package();

  // Read an import line.
  void
  read_one_import();

  // Read an indirectimport line.
  void
  read_one_indirect_import();

  // Read the import control functions and init graph.
  void
  read_import_init_fns(Gogo*);

  // Read the types.
  bool
  read_types();

  // Import a constant.
  void
  import_const();

  // Import a type.
  void
  import_type();

  // Import a variable.
  void
  import_var();

  // Import a function.
  void
  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);

  // Get an integer from a string.
  bool
  string_to_int(const std::string&, bool is_neg_ok, int* ret);

  // Get an unsigned integer from a string.
  bool
  string_to_unsigned(const std::string& s, unsigned* ret)
  {
    int ivalue;
    if (!this->string_to_int(s, false, &ivalue))
      return false;
    *ret = static_cast<unsigned>(ivalue);
    return true;
  }

  // Finalize methods for newly imported types.
  void
  finalize_methods();

  // The general IR.
  Gogo* gogo_;
  // The stream from which to read import data.
  Stream* stream_;
  // The location of the import statement we are processing.
  Location location_;
  // The package we are importing.
  Package* package_;
  // Whether to add new objects to the global scope, rather than to a
  // package scope.
  bool add_to_globals_;
  // Mapping from package index to package.
  std::vector<Package*> packages_;
  // 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.
  std::vector<Type*> types_;
  // Version of export data we're reading.
  Export_data_version version_;
};

// Read import data from a string.

class Stream_from_string : public Import::Stream
{
 public:
  Stream_from_string(const std::string& str)
    : str_(str), pos_(0)
  { }

 protected:
  bool
  do_peek(size_t length, const char** bytes)
  {
    if (this->pos_ + length > this->str_.length())
      return false;
    *bytes = this->str_.data() + this->pos_;
    return true;
  }

  void
  do_advance(size_t len)
  { this->pos_ += len; }

 private:
  // The string of data we are reading.
  std::string str_;
  // The current position within the string.
  size_t pos_;
};

// Read import data from a buffer allocated using malloc.

class Stream_from_buffer : public Import::Stream
{
 public:
  Stream_from_buffer(char* buf, size_t length)
    : buf_(buf), length_(length), pos_(0)
  { }

  ~Stream_from_buffer()
  { free(this->buf_); }

 protected:
  bool
  do_peek(size_t length, const char** bytes)
  {
    if (this->pos_ + length > this->length_)
      return false;
    *bytes = this->buf_ + this->pos_;
    return true;
  }

  void
  do_advance(size_t len)
  { this->pos_ += len; }

 private:
  // The data we are reading.
  char* buf_;
  // The length of the buffer.
  size_t length_;
  // The current position within the buffer.
  size_t pos_;
};

// Read import data from an open file descriptor.

class Stream_from_file : public Import::Stream
{
 public:
  Stream_from_file(int fd);

  ~Stream_from_file();

 protected:
  bool
  do_peek(size_t, const char**);

  void
  do_advance(size_t);

 private:
  // No copying.
  Stream_from_file(const Stream_from_file&);
  Stream_from_file& operator=(const Stream_from_file&);

  // The file descriptor.
  int fd_;
  // Data read from the file.
  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_;
};

// Class to manage importing a function body.  This is passed around
// to Statements and Expressions.  It parses the function into the IR.

class Import_function_body : public Import_expression
{
 public:
  Import_function_body(Gogo* gogo, Import* imp, Named_object* named_object,
		       const std::string& body, size_t off, Block* block,
		       int indent);
  ~Import_function_body();

  // The IR.
  Gogo*
  gogo()
  { return this->gogo_; }

  // The location to report in an error message.
  Location
  location() const
  { return this->imp_->location(); }

  // The function we are importing.
  Named_object*
  function() const
  { return this->named_object_; }

  // A reference to the body we are reading.
  const std::string&
  body() const
  { return this->body_; }

  // The current offset into the body.
  size_t
  off()
  { return this->off_; }

  // Update the offset into the body.
  void
  set_off(size_t off)
  { this->off_ = off; }

  // Advance the offset by SKIP bytes.
  void
  advance(size_t skip)
  { this->off_ += skip; }

  // The current block.
  Block*
  block()
  { return this->blocks_.back(); }

  // Begin importing a new block BLOCK nested within the current block.
  void
  begin_block(Block *block)
  { this->blocks_.push_back(block); }

  // Record the fact that we're done importing the current block.
  void
  finish_block()
  { this->blocks_.pop_back(); }

  // The current indentation.
  int
  indent() const
  { return this->indent_; }

  // Increment the indentation level.
  void
  increment_indent()
  { ++this->indent_; }

  // Decrement the indentation level.
  void
  decrement_indent()
  { --this->indent_; }

  // The name of the function we are parsing.
  const std::string&
  name() const;

  // Return the next character in the input stream, or -1 at the end.
  int
  peek_char()
  {
    if (this->body_.length() <= this->off_)
      return -1;
    return static_cast<unsigned char>(this->body_[this->off_]);
  }

  // Return the next character and advance.
  int
  get_char()
  {
    if (this->body_.length() <= this->off_)
      return -1;
    int c = static_cast<unsigned char>(this->body_[this->off_]);
    this->off_++;
    return c;
  }

  // Return whether the C string matches the current body position.
  bool
  match_c_string(const char* str)
  {
    size_t len = strlen(str);
    return (this->body_.length() >= this->off_ + len
	    && this->body_.compare(this->off_, len, str) == 0);
  }

  // Give an error if the next bytes do not match STR.  Advance the
  // offset by the length of STR.
  void
  require_c_string(const char* str);

  // Read an identifier.
  std::string
  read_identifier();

  // Read a type.
  Type*
  read_type();

  Export_data_version
  version() const
  { return this->imp_->version(); }

  // Record the index of a temporary statement.
  void
  record_temporary(Temporary_statement*, unsigned int);

  // Return a temporary statement given an index.
  Temporary_statement*
  temporary_statement(unsigned int);

  // Return an unnamed label given an index, defining the label if we
  // haven't seen it already.
  Unnamed_label*
  unnamed_label(unsigned int, Location);

  // Implement Import_expression.
  Import_function_body*
  ifb()
  { return this; }

  // Return the maximum valid package index.
  size_t
  max_package_index() const
  { return this->imp_->max_package_index(); }

  // Return the package at an index.
  Package*
  package_at_index(int index)
  { return this->imp_->package_at_index(index); }

  // Return whether we have seen an error.
  bool
  saw_error() const
  { return this->saw_error_; }

  // Record that we have seen an error.
  void
  set_saw_error()
  { this->saw_error_ = true; }

 private:
  static size_t
  next_size(size_t);

  // The IR.
  Gogo* gogo_;
  // The importer.
  Import* imp_;
  // The function we are parsing.
  Named_object* named_object_;
  // The exported data we are parsing.  Note that this is a reference;
  // the body string must laster longer than this object.
  const std::string& body_;
  // The current offset into body_.
  size_t off_;
  // Stack to record nesting of blocks being imported.
  std::vector<Block *> blocks_;
  // Current expected indentation level.
  int indent_;
  // Temporary statements by index.
  std::vector<Temporary_statement*> temporaries_;
  // Unnamed labels by index.
  std::vector<Unnamed_label*> labels_;
  // Whether we've seen an error.  Used to avoid reporting excess
  // errors.
  bool saw_error_;
};

#endif // !defined(GO_IMPORT_H)