blob: b12b3b843df6166b9dc4cc24655096f47312796e [file] [log] [blame]
// 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)