Simple aliasing during import.

This commit is contained in:
chriseth 2015-12-15 15:46:03 +01:00
parent d3c459b5a9
commit 603dc58040
10 changed files with 148 additions and 52 deletions

View File

@ -46,14 +46,16 @@ NameAndTypeResolver::NameAndTypeResolver(
bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit) bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
{ {
solAssert(!m_scopes[&_sourceUnit], ""); if (!m_scopes[&_sourceUnit])
m_scopes[&_sourceUnit].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get())); // By importing, it is possible that the container already exists.
m_scopes[&_sourceUnit].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get()));
m_currentScope = m_scopes[&_sourceUnit].get(); m_currentScope = m_scopes[&_sourceUnit].get();
// The helper registers all declarations in m_scopes as a side-effect of its construction. // The helper registers all declarations in m_scopes as a side-effect of its construction.
try try
{ {
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors); DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors);
_sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations();
} }
catch (FatalError const&) catch (FatalError const&)
{ {
@ -83,7 +85,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
); );
error = true; error = true;
} }
else else if (imp->name().empty())
{ {
auto scope = m_scopes.find(_sourceUnits.at(path)); auto scope = m_scopes.find(_sourceUnits.at(path));
solAssert(scope != end(m_scopes), ""); solAssert(scope != end(m_scopes), "");
@ -380,7 +382,7 @@ void NameAndTypeResolver::reportFatalTypeError(Error const& _e)
} }
DeclarationRegistrationHelper::DeclarationRegistrationHelper( DeclarationRegistrationHelper::DeclarationRegistrationHelper(
map<ASTNode const*, unique_ptr<DeclarationContainer>>& _scopes, map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
ASTNode& _astRoot, ASTNode& _astRoot,
ErrorList& _errors ErrorList& _errors
): ):
@ -392,6 +394,17 @@ DeclarationRegistrationHelper::DeclarationRegistrationHelper(
_astRoot.accept(*this); _astRoot.accept(*this);
} }
bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
{
SourceUnit const* importee = _import.annotation().sourceUnit;
solAssert(!!importee, "");
if (!m_scopes[importee])
m_scopes[importee].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get()));
m_scopes[&_import] = m_scopes[importee];
registerDeclaration(_import, false);
return true;
}
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract) bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
{ {
registerDeclaration(_contract, true); registerDeclaration(_contract, true);
@ -489,9 +502,9 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&)
void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration) void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
{ {
map<ASTNode const*, unique_ptr<DeclarationContainer>>::iterator iter; map<ASTNode const*, shared_ptr<DeclarationContainer>>::iterator iter;
bool newlyAdded; bool newlyAdded;
unique_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); shared_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get()));
tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container)); tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container));
solAssert(newlyAdded, "Unable to add new scope."); solAssert(newlyAdded, "Unable to add new scope.");
m_currentScope = &_declaration; m_currentScope = &_declaration;

View File

@ -111,7 +111,8 @@ private:
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration, /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
/// where nullptr denotes the global scope. Note that structs are not scope since they do /// where nullptr denotes the global scope. Note that structs are not scope since they do
/// not contain code. /// not contain code.
std::map<ASTNode const*, std::unique_ptr<DeclarationContainer>> m_scopes; /// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope.
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
DeclarationContainer* m_currentScope = nullptr; DeclarationContainer* m_currentScope = nullptr;
ErrorList& m_errors; ErrorList& m_errors;
@ -125,12 +126,13 @@ class DeclarationRegistrationHelper: private ASTVisitor
{ {
public: public:
DeclarationRegistrationHelper( DeclarationRegistrationHelper(
std::map<ASTNode const*, std::unique_ptr<DeclarationContainer>>& _scopes, std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
ASTNode& _astRoot, ASTNode& _astRoot,
ErrorList& _errors ErrorList& _errors
); );
private: private:
bool visit(ImportDirective& _declaration) override;
bool visit(ContractDefinition& _contract) override; bool visit(ContractDefinition& _contract) override;
void endVisit(ContractDefinition& _contract) override; void endVisit(ContractDefinition& _contract) override;
bool visit(StructDefinition& _struct) override; bool visit(StructDefinition& _struct) override;
@ -166,7 +168,7 @@ private:
// creates the Declaration error and adds it in the errors list and throws FatalError // creates the Declaration error and adds it in the errors list and throws FatalError
void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description); void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
std::map<ASTNode const*, std::unique_ptr<DeclarationContainer>>& m_scopes; std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
ASTNode const* m_currentScope = nullptr; ASTNode const* m_currentScope = nullptr;
VariableScope* m_currentFunction = nullptr; VariableScope* m_currentFunction = nullptr;
ErrorList& m_errors; ErrorList& m_errors;

View File

@ -56,6 +56,13 @@ Error ASTNode::createTypeError(string const& _description) const
return Error(Error::Type::TypeError) << errinfo_sourceLocation(location()) << errinfo_comment(_description); return Error(Error::Type::TypeError) << errinfo_sourceLocation(location()) << errinfo_comment(_description);
} }
SourceUnitAnnotation& SourceUnit::annotation() const
{
if (!m_annotation)
m_annotation = new SourceUnitAnnotation();
return static_cast<SourceUnitAnnotation&>(*m_annotation);
}
ImportAnnotation& ImportDirective::annotation() const ImportAnnotation& ImportDirective::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
@ -63,6 +70,12 @@ ImportAnnotation& ImportDirective::annotation() const
return static_cast<ImportAnnotation&>(*m_annotation); return static_cast<ImportAnnotation&>(*m_annotation);
} }
TypePointer ImportDirective::type() const
{
solAssert(!!annotation().sourceUnit, "");
return make_shared<ModuleType>(*annotation().sourceUnit);
}
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
{ {
auto exportedFunctionList = interfaceFunctionList(); auto exportedFunctionList = interfaceFunctionList();

View File

@ -120,6 +120,7 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual SourceUnitAnnotation& annotation() const override;
std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; } std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; }
@ -128,44 +129,7 @@ private:
}; };
/** /**
* Import directive for referencing other files / source objects. * Abstract AST class for a declaration (contract, function, struct, variable, import directive).
* Example: import "abc.sol" // imports all symbols of "abc.sol" into current scope
* Source objects are identified by a string which can be a file name but does not have to be.
* Other ways to use it:
* import "abc" as x; // creates symbol "x" that contains all symbols in "abc"
* import * as x from "abc"; // same as above
* import {a as b, c} from "abc"; // creates new symbols "b" and "c" referencing "a" and "c" in "abc", respectively.
*/
class ImportDirective: public ASTNode
{
public:
ImportDirective(
SourceLocation const& _location,
ASTPointer<ASTString> const& _path,
ASTPointer<ASTString> const& _unitAlias,
std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>>&& _symbolAliases
):
ASTNode(_location), m_path(_path), m_unitAlias(_unitAlias), m_symbolAliases(_symbolAliases) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
ASTString const& path() const { return *m_path; }
virtual ImportAnnotation& annotation() const override;
private:
ASTPointer<ASTString> m_path;
/// The alias for the module itself. If present, import the whole unit under that name and
/// ignore m_symbolAlias.
ASTPointer<ASTString> m_unitAlias;
/// The aliases for the specific symbols to import. If non-empty import the specific symbols.
/// If the second component is empty, import the identifier unchanged.
/// If both m_unitAlias and m_symbolAlias are empty, import all symbols into the current scope.
std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> m_symbolAliases;
};
/**
* Abstract AST class for a declaration (contract, function, struct, variable).
*/ */
class Declaration: public ASTNode class Declaration: public ASTNode
{ {
@ -210,6 +174,45 @@ private:
ASTNode const* m_scope; ASTNode const* m_scope;
}; };
/**
* Import directive for referencing other files / source objects.
* Example: import "abc.sol" // imports all symbols of "abc.sol" into current scope
* Source objects are identified by a string which can be a file name but does not have to be.
* Other ways to use it:
* import "abc" as x; // creates symbol "x" that contains all symbols in "abc"
* import * as x from "abc"; // same as above
* import {a as b, c} from "abc"; // creates new symbols "b" and "c" referencing "a" and "c" in "abc", respectively.
*/
class ImportDirective: public Declaration
{
public:
ImportDirective(
SourceLocation const& _location,
ASTPointer<ASTString> const& _path,
ASTPointer<ASTString> const& _unitAlias,
std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>>&& _symbolAliases
):
Declaration(_location, _unitAlias),
m_path(_path),
m_symbolAliases(_symbolAliases)
{ }
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
ASTString const& path() const { return *m_path; }
virtual ImportAnnotation& annotation() const override;
virtual TypePointer type() const override;
private:
ASTPointer<ASTString> m_path;
/// The aliases for the specific symbols to import. If non-empty import the specific symbols.
/// If the second component is empty, import the identifier unchanged.
/// If both m_unitAlias and m_symbolAlias are empty, import all symbols into the current scope.
std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> m_symbolAliases;
};
/** /**
* Abstract class that is added to each AST node that can store local variables. * Abstract class that is added to each AST node that can store local variables.
*/ */

View File

@ -54,10 +54,20 @@ struct DocumentedAnnotation
std::multimap<std::string, DocTag> docTags; std::multimap<std::string, DocTag> docTags;
}; };
struct SourceUnitAnnotation: ASTAnnotation
{
/// The "absolute" (in the compiler sense) path of this source unit.
std::string path;
/// The exported symbols (all global symbols).
std::map<ASTString, std::vector<Declaration const*>> exportedSymbols;
};
struct ImportAnnotation: ASTAnnotation struct ImportAnnotation: ASTAnnotation
{ {
/// The absolute path of the source unit to import. /// The absolute path of the source unit to import.
std::string absolutePath; std::string absolutePath;
/// The actual source unit.
SourceUnit const* sourceUnit = nullptr;
}; };
struct TypeDeclarationAnnotation: ASTAnnotation struct TypeDeclarationAnnotation: ASTAnnotation

View File

@ -1925,9 +1925,25 @@ string ModifierType::toString(bool _short) const
return name + ")"; return name + ")";
} }
MagicType::MagicType(MagicType::Kind _kind): bool ModuleType::operator==(Type const& _other) const
m_kind(_kind)
{ {
if (_other.category() != category())
return false;
return &m_sourceUnit == &dynamic_cast<ModuleType const&>(_other).m_sourceUnit;
}
MemberList::MemberMap ModuleType::nativeMembers(ContractDefinition const*) const
{
MemberList::MemberMap symbols;
for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols)
for (Declaration const* symbol: symbolName.second)
symbols.push_back(MemberList::Member(symbolName.first, symbol->type(), symbol));
return symbols;
}
string ModuleType::toString(bool) const
{
return string("module \"") + m_sourceUnit.annotation().path + string("\"");
} }
bool MagicType::operator==(Type const& _other) const bool MagicType::operator==(Type const& _other) const

View File

@ -134,7 +134,7 @@ public:
{ {
Integer, IntegerConstant, StringLiteral, Bool, Real, Array, Integer, IntegerConstant, StringLiteral, Bool, Real, Array,
FixedBytes, Contract, Struct, Function, Enum, Tuple, FixedBytes, Contract, Struct, Function, Enum, Tuple,
Mapping, TypeType, Modifier, Magic Mapping, TypeType, Modifier, Magic, Module
}; };
/// @{ /// @{
@ -969,6 +969,34 @@ private:
}; };
/**
* Special type for imported modules. These mainly give access to their scope via members.
*/
class ModuleType: public Type
{
public:
virtual Category category() const override { return Category::Module; }
explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
{
return TypePointer();
}
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return true; }
virtual unsigned sizeOnStack() const override { return 0; }
virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
virtual std::string toString(bool _short) const override;
private:
SourceUnit const& m_sourceUnit;
};
/** /**
* Special type for magic variables (block, msg, tx), similar to a struct but without any reference * Special type for magic variables (block, msg, tx), similar to a struct but without any reference
* (it always references a global singleton by name). * (it always references a global singleton by name).
@ -979,7 +1007,7 @@ public:
enum class Kind { Block, Message, Transaction }; enum class Kind { Block, Message, Transaction };
virtual Category category() const override { return Category::Magic; } virtual Category category() const override { return Category::Magic; }
explicit MagicType(Kind _kind); explicit MagicType(Kind _kind): m_kind(_kind) {}
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
{ {

View File

@ -111,6 +111,8 @@ bool CompilerStack::parse()
sourcePair.second.ast = Parser(m_errors).parse(sourcePair.second.scanner); sourcePair.second.ast = Parser(m_errors).parse(sourcePair.second.scanner);
if (!sourcePair.second.ast) if (!sourcePair.second.ast)
solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error."); solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error.");
else
sourcePair.second.ast->annotation().path = sourcePair.first;
sourceUnitsByName[sourcePair.first] = sourcePair.second.ast.get(); sourceUnitsByName[sourcePair.first] = sourcePair.second.ast.get();
} }
if (!Error::containsOnlyWarnings(m_errors)) if (!Error::containsOnlyWarnings(m_errors))
@ -384,6 +386,7 @@ void CompilerStack::resolveImports()
<< errinfo_sourceLocation(import->location()) << errinfo_sourceLocation(import->location())
<< errinfo_comment("Source not found.") << errinfo_comment("Source not found.")
); );
import->annotation().sourceUnit = m_sources.at(path).ast.get();
toposort(path, &m_sources[path]); toposort(path, &m_sources[path]);
} }

View File

@ -119,7 +119,7 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::Import); expectToken(Token::Import);
ASTPointer<ASTString> path; ASTPointer<ASTString> path;
ASTPointer<ASTString> unitAlias; ASTPointer<ASTString> unitAlias = make_shared<string>();
vector<pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> symbolAliases; vector<pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> symbolAliases;
if (m_scanner->currentToken() == Token::StringLiteral) if (m_scanner->currentToken() == Token::StringLiteral)

View File

@ -93,6 +93,14 @@ BOOST_AUTO_TEST_CASE(relative_import_multiplex)
BOOST_CHECK(c.compile()); BOOST_CHECK(c.compile());
} }
BOOST_AUTO_TEST_CASE(simple_alias)
{
CompilerStack c;
c.addSource("a", "contract A {}");
c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B { function() { x.A r = x.A(20); } }");
BOOST_CHECK(c.compile());
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }