| // 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) |