Browse Source

Name resolution.

cl-refactor
Christian 10 years ago
parent
commit
a5a577d35c
  1. 17
      libsolidity/AST.h
  2. 1
      libsolidity/ASTForward.h
  3. 12
      libsolidity/ASTPrinter.cpp
  4. 116
      libsolidity/ASTPrinter.h
  5. 2
      libsolidity/ASTVisitor.h
  6. 142
      libsolidity/NameAndTypeResolver.cpp
  7. 40
      libsolidity/NameAndTypeResolver.h
  8. 9
      libsolidity/Parser.cpp
  9. 4
      libsolidity/Parser.h
  10. 39
      libsolidity/Scope.h
  11. 8
      solc/main.cpp
  12. 113
      test/solidityNameAndTypeResolution.cpp

17
libsolidity/AST.h

@ -72,6 +72,9 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
const ASTString& getName() const { return *m_name; }
vecptr<StructDefinition> const& getDefinedStructs() { return m_definedStructs; }
vecptr<VariableDeclaration> const& getStateVariables() { return m_stateVariables; }
vecptr<FunctionDefinition> const& getDefinedFunctions() { return m_definedFunctions; }
private:
ptr<ASTString> m_name;
vecptr<StructDefinition> m_definedStructs;
@ -105,6 +108,8 @@ public:
: ASTNode(_location), m_parameters(_parameters)
{}
virtual void accept(ASTVisitor& _visitor) override;
vecptr<VariableDeclaration> const& getParameters() { return m_parameters; }
private:
vecptr<VariableDeclaration> m_parameters;
};
@ -126,12 +131,16 @@ public:
const ASTString& getName() const { return *m_name; }
bool isPublic() const { return m_isPublic; }
bool isDeclaredConst() const { return m_isDeclaredConst; }
vecptr<VariableDeclaration> const& getParameters() { return m_parameters->getParameters(); }
bool hasReturnParameters() const { return m_returnParameters.get() != nullptr; }
vecptr<VariableDeclaration> const& getReturnParameters() { return m_returnParameters->getParameters(); }
Block& getBody() { return *m_body; }
private:
ptr<ASTString> m_name;
bool m_isPublic;
ptr<ParameterList> m_parameters;
bool m_isDeclaredConst;
ptr<ParameterList> m_returnParameters;
ptr<ParameterList> m_returnParameters; //< either "null"pointer or pointer to non-empty parameter list
ptr<Block> m_body;
};
@ -145,6 +154,7 @@ public:
{}
virtual void accept(ASTVisitor& _visitor) override;
TypeName* getTypeName() const { return m_type.get(); }
const ASTString& getName() const { return *m_name; }
private:
ptr<TypeName> m_type; ///< can be empty ("var")
@ -416,8 +426,13 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
ASTString const& getName() const { return *m_name; }
void setReferencedObject(ASTNode& _referencedObject) { m_referencedObject = &_referencedObject; }
ASTNode* getReferencedVariable() { return m_referencedObject; }
private:
ptr<ASTString> m_name;
//! Node the name refers to. Has to be a declaration of some sort.
ASTNode* m_referencedObject;
};
class ElementaryTypeNameExpression : public PrimaryExpression

1
libsolidity/ASTForward.h

@ -36,6 +36,7 @@ class FunctionCall;
class MemberAccess;
class IndexAccess;
class PrimaryExpression;
class Identifier;
class ElementaryTypeNameExpression;
class Literal;

12
libsolidity/ASTPrinter.cpp

@ -202,6 +202,13 @@ bool ASTPrinter::visit(PrimaryExpression& _node)
return goDeeper();
}
bool ASTPrinter::visit(Identifier& _node)
{
writeLine(std::string("Identifier ") + _node.getName());
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(ElementaryTypeNameExpression& _node)
{
writeLine(std::string("ElementaryTypeNameExpression ") + Token::String(_node.getType()));
@ -356,6 +363,11 @@ void ASTPrinter::endVisit(PrimaryExpression&)
m_indentation--;
}
void ASTPrinter::endVisit(Identifier&)
{
m_indentation--;
}
void ASTPrinter::endVisit(ElementaryTypeNameExpression&)
{
m_indentation--;

116
libsolidity/ASTPrinter.h

@ -15,64 +15,66 @@ public:
/// Output the string representation of the AST to _stream.
void print(std::ostream& _stream);
bool visit(ContractDefinition& _node);
bool visit(StructDefinition& _node);
bool visit(ParameterList& _node);
bool visit(FunctionDefinition& _node);
bool visit(VariableDeclaration& _node);
bool visit(TypeName& _node);
bool visit(ElementaryTypeName& _node);
bool visit(UserDefinedTypeName& _node);
bool visit(Mapping& _node);
bool visit(Statement& _node);
bool visit(Block& _node);
bool visit(IfStatement& _node);
bool visit(BreakableStatement& _node);
bool visit(WhileStatement& _node);
bool visit(Continue& _node);
bool visit(Break& _node);
bool visit(Return& _node);
bool visit(VariableDefinition& _node);
bool visit(Expression& _node);
bool visit(Assignment& _node);
bool visit(UnaryOperation& _node);
bool visit(BinaryOperation& _node);
bool visit(FunctionCall& _node);
bool visit(MemberAccess& _node);
bool visit(IndexAccess& _node);
bool visit(PrimaryExpression& _node);
bool visit(ElementaryTypeNameExpression& _node);
bool visit(Literal& _node);
bool visit(ContractDefinition& _node) override;
bool visit(StructDefinition& _node) override;
bool visit(ParameterList& _node) override;
bool visit(FunctionDefinition& _node) override;
bool visit(VariableDeclaration& _node) override;
bool visit(TypeName& _node) override;
bool visit(ElementaryTypeName& _node) override;
bool visit(UserDefinedTypeName& _node) override;
bool visit(Mapping& _node) override;
bool visit(Statement& _node) override;
bool visit(Block& _node) override;
bool visit(IfStatement& _node) override;
bool visit(BreakableStatement& _node) override;
bool visit(WhileStatement& _node) override;
bool visit(Continue& _node) override;
bool visit(Break& _node) override;
bool visit(Return& _node) override;
bool visit(VariableDefinition& _node) override;
bool visit(Expression& _node) override;
bool visit(Assignment& _node) override;
bool visit(UnaryOperation& _node) override;
bool visit(BinaryOperation& _node) override;
bool visit(FunctionCall& _node) override;
bool visit(MemberAccess& _node) override;
bool visit(IndexAccess& _node) override;
bool visit(PrimaryExpression& _node) override;
bool visit(Identifier& _node) override;
bool visit(ElementaryTypeNameExpression& _node) override;
bool visit(Literal& _node) override;
void endVisit(ASTNode & _node);
void endVisit(ContractDefinition&);
void endVisit(StructDefinition&);
void endVisit(ParameterList&);
void endVisit(FunctionDefinition&);
void endVisit(VariableDeclaration&);
void endVisit(TypeName&);
void endVisit(ElementaryTypeName&);
void endVisit(UserDefinedTypeName&);
void endVisit(Mapping&);
void endVisit(Statement&);
void endVisit(Block&);
void endVisit(IfStatement&);
void endVisit(BreakableStatement&);
void endVisit(WhileStatement&);
void endVisit(Continue&);
void endVisit(Break&);
void endVisit(Return&);
void endVisit(VariableDefinition&);
void endVisit(Expression&);
void endVisit(Assignment&);
void endVisit(UnaryOperation&);
void endVisit(BinaryOperation&);
void endVisit(FunctionCall&);
void endVisit(MemberAccess&);
void endVisit(IndexAccess&);
void endVisit(PrimaryExpression&);
void endVisit(ElementaryTypeNameExpression&);
void endVisit(Literal&);
void endVisit(ASTNode & _node) override;
void endVisit(ContractDefinition&) override;
void endVisit(StructDefinition&) override;
void endVisit(ParameterList&) override;
void endVisit(FunctionDefinition&) override;
void endVisit(VariableDeclaration&) override;
void endVisit(TypeName&) override;
void endVisit(ElementaryTypeName&) override;
void endVisit(UserDefinedTypeName&) override;
void endVisit(Mapping&) override;
void endVisit(Statement&) override;
void endVisit(Block&) override;
void endVisit(IfStatement&) override;
void endVisit(BreakableStatement&) override;
void endVisit(WhileStatement&) override;
void endVisit(Continue&) override;
void endVisit(Break&) override;
void endVisit(Return&) override;
void endVisit(VariableDefinition&) override;
void endVisit(Expression&) override;
void endVisit(Assignment&) override;
void endVisit(UnaryOperation&) override;
void endVisit(BinaryOperation&) override;
void endVisit(FunctionCall&) override;
void endVisit(MemberAccess&) override;
void endVisit(IndexAccess&) override;
void endVisit(PrimaryExpression&) override;
void endVisit(Identifier&) override;
void endVisit(ElementaryTypeNameExpression&) override;
void endVisit(Literal&) override;
private:
void printSourcePart(ASTNode const& _node);

2
libsolidity/ASTVisitor.h

@ -39,6 +39,7 @@ public:
virtual bool visit(MemberAccess&) { return true; }
virtual bool visit(IndexAccess&) { return true; }
virtual bool visit(PrimaryExpression&) { return true; }
virtual bool visit(Identifier&) { return true; }
virtual bool visit(ElementaryTypeNameExpression&) { return true; }
virtual bool visit(Literal&) { return true; }
@ -69,6 +70,7 @@ public:
virtual void endVisit(MemberAccess&) { }
virtual void endVisit(IndexAccess&) { }
virtual void endVisit(PrimaryExpression&) { }
virtual void endVisit(Identifier&) { }
virtual void endVisit(ElementaryTypeNameExpression&) { }
virtual void endVisit(Literal&) { }
};

142
libsolidity/NameAndTypeResolver.cpp

@ -0,0 +1,142 @@
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/AST.h>
#include <boost/assert.hpp>
namespace dev {
namespace solidity {
class NameAndTypeResolver::ScopeHelper {
public:
ScopeHelper(NameAndTypeResolver& _resolver, ASTString const& _name, ASTNode& _declaration)
: m_resolver(_resolver)
{
m_resolver.registerName(_name, _declaration);
m_resolver.enterNewSubScope(_declaration);
}
~ScopeHelper()
{
m_resolver.closeCurrentScope();
}
private:
NameAndTypeResolver& m_resolver;
};
NameAndTypeResolver::NameAndTypeResolver()
{
}
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{
reset();
handleContract(_contract);
}
void NameAndTypeResolver::handleContract(ContractDefinition& _contract)
{
ScopeHelper scopeHelper(*this, _contract.getName(), _contract);
for (ptr<VariableDeclaration> const& variable : _contract.getStateVariables())
registerName(variable->getName(), *variable);
// @todo structs
for (ptr<FunctionDefinition> const& function : _contract.getDefinedFunctions())
handleFunction(*function);
// @todo resolve names used in mappings
}
void NameAndTypeResolver::reset()
{
m_scopes.clear();
m_globalScope = Scope();
m_currentScope = &m_globalScope;
}
void NameAndTypeResolver::handleFunction(FunctionDefinition& _function)
{
ScopeHelper scopeHelper(*this, _function.getName(), _function);
// @todo resolve names used in mappings
for (ptr<VariableDeclaration> const& variable : _function.getParameters())
registerName(variable->getName(), *variable);
if (_function.hasReturnParameters())
for (ptr<VariableDeclaration> const& variable : _function.getReturnParameters())
registerName(variable->getName(), *variable);
handleFunctionBody(_function.getBody());
}
void NameAndTypeResolver::handleFunctionBody(Block& _functionBody)
{
registerVariablesInFunction(_functionBody);
resolveReferencesInFunction(_functionBody);
}
void NameAndTypeResolver::registerVariablesInFunction(Block& _functionBody)
{
class VariableDeclarationFinder : public ASTVisitor {
public:
VariableDeclarationFinder(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {}
virtual bool visit(VariableDeclaration& _variable) override {
m_resolver.registerName(_variable.getName(), _variable);
return false;
}
private:
NameAndTypeResolver& m_resolver;
};
VariableDeclarationFinder declarationFinder(*this);
_functionBody.accept(declarationFinder);
}
void NameAndTypeResolver::resolveReferencesInFunction(Block& _functionBody)
{
class ReferencesResolver : public ASTVisitor {
public:
ReferencesResolver(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {}
virtual bool visit(Identifier& _identifier) override {
ASTNode* node = m_resolver.getNameFromCurrentScope(_identifier.getName());
if (node == nullptr)
throw std::exception(); // @todo
_identifier.setReferencedObject(*node);
return false;
}
private:
NameAndTypeResolver& m_resolver;
};
ReferencesResolver referencesResolver(*this);
_functionBody.accept(referencesResolver);
}
void NameAndTypeResolver::registerName(ASTString const& _name, ASTNode& _declaration)
{
if (!m_currentScope->registerName(_name, _declaration))
throw std::exception(); // @todo
}
ASTNode* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive)
{
return m_currentScope->resolveName(_name, _recursive);
}
void NameAndTypeResolver::enterNewSubScope(ASTNode& _node)
{
decltype(m_scopes)::iterator iter;
bool newlyAdded;
std::tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope));
BOOST_ASSERT(newlyAdded);
m_currentScope = &iter->second;
}
void NameAndTypeResolver::closeCurrentScope()
{
m_currentScope = m_currentScope->getOuterScope();
}
} }

40
libsolidity/NameAndTypeResolver.h

@ -0,0 +1,40 @@
#pragma once
#include <map>
#include <libsolidity/Scope.h>
#include <libsolidity/ASTVisitor.h>
namespace dev {
namespace solidity {
class NameAndTypeResolver
{
public:
NameAndTypeResolver();
void resolveNamesAndTypes(ContractDefinition& _contract);
private:
class ScopeHelper; //< RIIA helper to open and close scopes
void reset();
void handleContract(ContractDefinition& _contract);
void handleFunction(FunctionDefinition& _function);
void handleFunctionBody(Block& _functionBody);
void registerVariablesInFunction(Block& _functionBody);
void resolveReferencesInFunction(Block& _functionBody);
void registerName(ASTString const& _name, ASTNode& _declaration);
ASTNode* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
void enterNewSubScope(ASTNode& _node);
void closeCurrentScope();
Scope m_globalScope; // not part of the map
std::map<ASTNode*, Scope> m_scopes;
Scope* m_currentScope;
};
} }

9
libsolidity/Parser.cpp

@ -28,7 +28,7 @@
namespace dev {
namespace solidity {
ptr<ASTNode> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
ptr<ContractDefinition> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
{
m_scanner = _scanner;
@ -132,8 +132,9 @@ ptr<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
}
ptr<ParameterList> returnParameters;
if (m_scanner->getCurrentToken() == Token::RETURNS) {
const bool permitEmptyParameterList = false;
m_scanner->next();
returnParameters = parseParameterList();
returnParameters = parseParameterList(permitEmptyParameterList);
}
ptr<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
@ -212,13 +213,13 @@ ptr<Mapping> Parser::parseMapping()
return nodeFactory.createNode<Mapping>(keyType, valueType);
}
ptr<ParameterList> Parser::parseParameterList()
ptr<ParameterList> Parser::parseParameterList(bool _permitEmpty)
{
ASTNodeFactory nodeFactory(*this);
vecptr<VariableDeclaration> parameters;
expectToken(Token::LPAREN);
if (m_scanner->getCurrentToken() != Token::RPAREN) {
if (!_permitEmpty || m_scanner->getCurrentToken() != Token::RPAREN) {
parameters.push_back(parseVariableDeclaration());
while (m_scanner->getCurrentToken() != Token::RPAREN) {
expectToken(Token::COMMA);

4
libsolidity/Parser.h

@ -32,7 +32,7 @@ class Scanner;
class Parser
{
public:
ptr<ASTNode> parse(std::shared_ptr<Scanner> const& _scanner);
ptr<ContractDefinition> parse(std::shared_ptr<Scanner> const& _scanner);
private:
class ASTNodeFactory;
@ -50,7 +50,7 @@ private:
ptr<VariableDeclaration> parseVariableDeclaration();
ptr<TypeName> parseTypeName();
ptr<Mapping> parseMapping();
ptr<ParameterList> parseParameterList();
ptr<ParameterList> parseParameterList(bool _permitEmpty = true);
ptr<Block> parseBlock();
ptr<Statement> parseStatement();
ptr<IfStatement> parseIfStatement();

39
libsolidity/Scope.h

@ -0,0 +1,39 @@
#pragma once
#include <map>
#include <libsolidity/ASTForward.h>
namespace dev {
namespace solidity {
class Scope
{
public:
explicit Scope(Scope* _outerScope = nullptr) : m_outerScope(_outerScope) {}
/// Registers the name _name in the scope unless it is already declared. Returns true iff
/// it was not yet declared.
bool registerName(ASTString const& _name, ASTNode& _declaration)
{
if (m_declaredNames.find(_name) != m_declaredNames.end())
return false;
m_declaredNames[_name] = &_declaration;
return true;
}
ASTNode* resolveName(ASTString const& _name, bool _recursive = false) const
{
auto result = m_declaredNames.find(_name);
if (result != m_declaredNames.end())
return result->second;
if (_recursive && m_outerScope != nullptr)
return m_outerScope->resolveName(_name, true);
return nullptr;
}
Scope* getOuterScope() const { return m_outerScope; }
private:
Scope* m_outerScope;
std::map<ASTString, ASTNode*> m_declaredNames;
};
} }

8
solc/main.cpp

@ -8,11 +8,12 @@
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/ASTPrinter.h>
#include <libsolidity/NameAndTypeResolver.h>
namespace dev {
namespace solidity {
ptr<ASTNode> parseAST(std::string const& _source)
ptr<ContractDefinition> parseAST(std::string const& _source)
{
ptr<Scanner> scanner = std::make_shared<Scanner>(CharStream(_source));
Parser parser;
@ -70,9 +71,12 @@ int main(int argc, char** argv)
std::cout << "Parsing..." << std::endl;
// @todo catch exception
dev::solidity::ptr<dev::solidity::ASTNode> ast = dev::solidity::parseAST(src);
dev::solidity::ptr<dev::solidity::ContractDefinition> ast = dev::solidity::parseAST(src);
std::cout << "Syntax tree for the contract:" << std::endl;
dev::solidity::ASTPrinter printer(ast, src);
printer.print(std::cout);
std::cout << "Resolving identifiers..." << std::endl;
dev::solidity::NameAndTypeResolver resolver;
resolver.resolveNamesAndTypes(*ast.get());
return 0;
}

113
test/solidityNameAndTypeResolution.cpp

@ -0,0 +1,113 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Unit tests for the name and type resolution of the solidity parser.
*/
#include <string>
#include <libdevcore/Log.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <boost/test/unit_test.hpp>
namespace dev {
namespace solidity {
namespace test {
namespace {
void parseTextAndResolveNames(const std::string& _source)
{
Parser parser;
ptr<ContractDefinition> contract = parser.parse(
std::make_shared<Scanner>(CharStream(_source)));
NameAndTypeResolver resolver;
resolver.resolveNamesAndTypes(*contract);
}
}
BOOST_AUTO_TEST_SUITE(SolidityNameAndTypeResolution)
BOOST_AUTO_TEST_CASE(smoke_test)
{
char const* text = "contract test {\n"
" uint256 stateVariable1;\n"
" function fun(uint256 arg1) { var x = 2; uint256 y = 3; x = 1; }"
"}\n";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(double_stateVariable_declaration)
{
char const* text = "contract test {\n"
" uint256 variable;\n"
" uint128 variable;\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception);
}
BOOST_AUTO_TEST_CASE(double_function_declaration)
{
char const* text = "contract test {\n"
" function fun() { var x = 2; }\n"
" function fun() { var y = 9; }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception);
}
BOOST_AUTO_TEST_CASE(double_variable_declaration)
{
char const* text = "contract test {\n"
" function f() { uint256 x = 9; if (true) { uint256 x = 2;} x = 3; }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception);
}
BOOST_AUTO_TEST_CASE(name_shadowing)
{
char const* text = "contract test {\n"
" uint256 variable;\n"
" function f() { uint8 variable = 2; }"
"}\n";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(name_references)
{
char const* text = "contract test {\n"
" uint256 variable;\n"
" function f() { variable = 2; f(); test; }"
"}\n";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(undeclared_name)
{
char const* text = "contract test {\n"
" uint256 variable;\n"
" function f() { notfound = 2; }"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception);
}
BOOST_AUTO_TEST_SUITE_END()
} } } // end namespaces
Loading…
Cancel
Save