|
|
@ -30,24 +30,6 @@ namespace dev { |
|
|
|
namespace solidity { |
|
|
|
|
|
|
|
|
|
|
|
class NameAndTypeResolver::ScopeHelper { |
|
|
|
public: |
|
|
|
ScopeHelper(NameAndTypeResolver& _resolver, Declaration& _declaration) |
|
|
|
: m_resolver(_resolver) |
|
|
|
{ |
|
|
|
m_resolver.registerDeclaration(_declaration); |
|
|
|
m_resolver.enterNewSubScope(_declaration); |
|
|
|
} |
|
|
|
~ScopeHelper() |
|
|
|
{ |
|
|
|
m_resolver.closeCurrentScope(); |
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
NameAndTypeResolver& m_resolver; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
NameAndTypeResolver::NameAndTypeResolver() |
|
|
|
{ |
|
|
|
} |
|
|
@ -55,149 +37,165 @@ NameAndTypeResolver::NameAndTypeResolver() |
|
|
|
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) |
|
|
|
{ |
|
|
|
reset(); |
|
|
|
DeclarationRegistrationHelper registrar(m_scopes, _contract); |
|
|
|
|
|
|
|
handleContract(_contract); |
|
|
|
} |
|
|
|
m_currentScope = &m_scopes[&_contract]; |
|
|
|
|
|
|
|
void NameAndTypeResolver::handleContract(ContractDefinition& _contract) |
|
|
|
{ |
|
|
|
ScopeHelper scopeHelper(*this, _contract); |
|
|
|
|
|
|
|
// @todo structs (definition and usage)
|
|
|
|
//@todo structs
|
|
|
|
|
|
|
|
for (ptr<VariableDeclaration> const& variable : _contract.getStateVariables()) |
|
|
|
registerVariableDeclarationAndResolveType(*variable); |
|
|
|
ReferencesResolver resolver(*variable, *this, nullptr); |
|
|
|
|
|
|
|
for (ptr<FunctionDefinition> const& function : _contract.getDefinedFunctions()) |
|
|
|
handleFunction(*function); |
|
|
|
for (ptr<FunctionDefinition> const& function : _contract.getDefinedFunctions()) { |
|
|
|
m_currentScope = &m_scopes[function.get()]; |
|
|
|
ReferencesResolver referencesResolver(*function, *this, |
|
|
|
function->getReturnParameterList().get()); |
|
|
|
} |
|
|
|
// First, all function parameter types need to be resolved before we can check
|
|
|
|
// the types, since it is possible to call functions that are only defined later
|
|
|
|
// in the source.
|
|
|
|
for (ptr<FunctionDefinition> const& function : _contract.getDefinedFunctions()) { |
|
|
|
m_currentScope = &m_scopes[function.get()]; |
|
|
|
function->getBody().checkTypeRequirements(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void NameAndTypeResolver::reset() |
|
|
|
{ |
|
|
|
m_scopes.clear(); |
|
|
|
m_globalScope = Scope(); |
|
|
|
m_currentScope = &m_globalScope; |
|
|
|
m_currentScope = nullptr; |
|
|
|
} |
|
|
|
|
|
|
|
void NameAndTypeResolver::handleFunction(FunctionDefinition& _function) |
|
|
|
Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) |
|
|
|
{ |
|
|
|
ScopeHelper scopeHelper(*this, _function); |
|
|
|
|
|
|
|
registerVariablesInFunction(_function); |
|
|
|
resolveReferencesInFunction(*_function.getReturnParameterList(), _function.getBody()); |
|
|
|
_function.getBody().checkTypeRequirements(); |
|
|
|
return m_currentScope->resolveName(_name, _recursive); |
|
|
|
} |
|
|
|
|
|
|
|
void NameAndTypeResolver::registerVariablesInFunction(FunctionDefinition& _function) |
|
|
|
|
|
|
|
DeclarationRegistrationHelper::DeclarationRegistrationHelper(std::map<ASTNode*, Scope>& _scopes, ASTNode& _astRoot) |
|
|
|
: m_scopes(_scopes), m_currentScope(&m_scopes[nullptr]) |
|
|
|
{ |
|
|
|
class VariableDeclarationFinder : public ASTVisitor { |
|
|
|
public: |
|
|
|
VariableDeclarationFinder(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {} |
|
|
|
virtual bool visit(VariableDeclaration& _variable) override { |
|
|
|
m_resolver.registerVariableDeclarationAndResolveType(_variable); |
|
|
|
return false; |
|
|
|
_astRoot.accept(*this); |
|
|
|
} |
|
|
|
private: |
|
|
|
NameAndTypeResolver& m_resolver; |
|
|
|
}; |
|
|
|
|
|
|
|
VariableDeclarationFinder declarationFinder(*this); |
|
|
|
_function.accept(declarationFinder); |
|
|
|
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract) |
|
|
|
{ |
|
|
|
registerDeclaration(_contract, true); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
void NameAndTypeResolver::resolveReferencesInFunction(ParameterList& _returnParameters, |
|
|
|
Block& _functionBody) |
|
|
|
void DeclarationRegistrationHelper::endVisit(ContractDefinition&) |
|
|
|
{ |
|
|
|
class ReferencesResolver : public ASTVisitor { |
|
|
|
public: |
|
|
|
ReferencesResolver(NameAndTypeResolver& _resolver, |
|
|
|
ParameterList& _returnParameters) |
|
|
|
: m_resolver(_resolver), m_returnParameters(_returnParameters) {} |
|
|
|
virtual bool visit(Identifier& _identifier) override { |
|
|
|
Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); |
|
|
|
if (declaration == nullptr) |
|
|
|
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); |
|
|
|
_identifier.setReferencedDeclaration(*declaration); |
|
|
|
return false; |
|
|
|
closeCurrentScope(); |
|
|
|
} |
|
|
|
virtual bool visit(Return& _return) override { |
|
|
|
_return.setFunctionReturnParameters(m_returnParameters); |
|
|
|
|
|
|
|
bool DeclarationRegistrationHelper::visit(StructDefinition& _struct) |
|
|
|
{ |
|
|
|
registerDeclaration(_struct, true); |
|
|
|
return true; |
|
|
|
} |
|
|
|
private: |
|
|
|
NameAndTypeResolver& m_resolver; |
|
|
|
ParameterList& m_returnParameters; |
|
|
|
}; |
|
|
|
|
|
|
|
ReferencesResolver referencesResolver(*this, _returnParameters); |
|
|
|
_functionBody.accept(referencesResolver); |
|
|
|
void DeclarationRegistrationHelper::endVisit(StructDefinition&) |
|
|
|
{ |
|
|
|
closeCurrentScope(); |
|
|
|
} |
|
|
|
|
|
|
|
void NameAndTypeResolver::registerVariableDeclarationAndResolveType(VariableDeclaration& _variable) |
|
|
|
bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function) |
|
|
|
{ |
|
|
|
registerDeclaration(_variable); |
|
|
|
TypeName* typeName = _variable.getTypeName(); |
|
|
|
if (typeName == nullptr) // unknown type, to be resolved by first assignment
|
|
|
|
return; |
|
|
|
|
|
|
|
// walk the AST to resolve user defined type references
|
|
|
|
// (walking is necessory because of mappings)
|
|
|
|
// @todo this could probably also be done at an earlier stage where we anyway
|
|
|
|
// walk the AST
|
|
|
|
registerDeclaration(_function, true); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
class UserDefinedTypeNameResolver : public ASTVisitor { |
|
|
|
public: |
|
|
|
UserDefinedTypeNameResolver(NameAndTypeResolver& _resolver) |
|
|
|
: m_resolver(_resolver) {} |
|
|
|
virtual bool visit(UserDefinedTypeName& _typeName) override { |
|
|
|
Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); |
|
|
|
if (declaration == nullptr) |
|
|
|
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); |
|
|
|
StructDefinition* referencedStruct = dynamic_cast<StructDefinition*>(declaration); |
|
|
|
//@todo later, contracts are also valid types
|
|
|
|
if (referencedStruct == nullptr) |
|
|
|
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Identifier does not name a type name.")); |
|
|
|
_typeName.setReferencedStruct(*referencedStruct); |
|
|
|
return false; |
|
|
|
void DeclarationRegistrationHelper::endVisit(FunctionDefinition&) |
|
|
|
{ |
|
|
|
closeCurrentScope(); |
|
|
|
} |
|
|
|
virtual bool visit(Mapping&) override { |
|
|
|
// @todo
|
|
|
|
|
|
|
|
bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration) |
|
|
|
{ |
|
|
|
registerDeclaration(_declaration, false); |
|
|
|
return true; |
|
|
|
} |
|
|
|
private: |
|
|
|
NameAndTypeResolver& m_resolver; |
|
|
|
}; |
|
|
|
|
|
|
|
UserDefinedTypeNameResolver resolver(*this); |
|
|
|
_variable.accept(resolver); |
|
|
|
void DeclarationRegistrationHelper::endVisit(VariableDeclaration&) |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
_variable.setType(typeName->toType()); |
|
|
|
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _node) |
|
|
|
{ |
|
|
|
std::map<ASTNode*, Scope>::iterator iter; |
|
|
|
bool newlyAdded; |
|
|
|
std::tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope)); |
|
|
|
BOOST_ASSERT(newlyAdded); |
|
|
|
m_currentScope = &iter->second; |
|
|
|
} |
|
|
|
|
|
|
|
void DeclarationRegistrationHelper::closeCurrentScope() |
|
|
|
{ |
|
|
|
BOOST_ASSERT(m_currentScope != nullptr); |
|
|
|
m_currentScope = m_currentScope->getOuterScope(); |
|
|
|
} |
|
|
|
|
|
|
|
void NameAndTypeResolver::registerDeclaration(Declaration& _declaration) |
|
|
|
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) |
|
|
|
{ |
|
|
|
BOOST_ASSERT(m_currentScope != nullptr); |
|
|
|
if (!m_currentScope->registerDeclaration(_declaration)) |
|
|
|
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Identifier already declared.")); |
|
|
|
|
|
|
|
if (_opensScope) |
|
|
|
enterNewSubScope(_declaration); |
|
|
|
} |
|
|
|
|
|
|
|
Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) |
|
|
|
ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, |
|
|
|
ParameterList* _returnParameters) |
|
|
|
: m_resolver(_resolver), m_returnParameters(_returnParameters) |
|
|
|
{ |
|
|
|
return m_currentScope->resolveName(_name, _recursive); |
|
|
|
_root.accept(*this); |
|
|
|
} |
|
|
|
|
|
|
|
void NameAndTypeResolver::enterNewSubScope(ASTNode& _node) |
|
|
|
void ReferencesResolver::endVisit(VariableDeclaration& _variable) |
|
|
|
{ |
|
|
|
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; |
|
|
|
// endVisit because the internal type needs resolving if it is a user defined type
|
|
|
|
// or mapping
|
|
|
|
if (_variable.getTypeName() != nullptr) |
|
|
|
_variable.setType(_variable.getTypeName()->toType()); |
|
|
|
// otherwise we have a "var"-declaration whose type is resolved by the first assignment
|
|
|
|
} |
|
|
|
|
|
|
|
void NameAndTypeResolver::closeCurrentScope() |
|
|
|
bool ReferencesResolver::visit(Return& _return) |
|
|
|
{ |
|
|
|
m_currentScope = m_currentScope->getOuterScope(); |
|
|
|
BOOST_ASSERT(m_returnParameters != nullptr); |
|
|
|
_return.setFunctionReturnParameters(*m_returnParameters); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool ReferencesResolver::visit(Mapping&) |
|
|
|
{ |
|
|
|
// @todo
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) |
|
|
|
{ |
|
|
|
Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); |
|
|
|
if (declaration == nullptr) |
|
|
|
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); |
|
|
|
StructDefinition* referencedStruct = dynamic_cast<StructDefinition*>(declaration); |
|
|
|
//@todo later, contracts are also valid types
|
|
|
|
if (referencedStruct == nullptr) |
|
|
|
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Identifier does not name a type name.")); |
|
|
|
_typeName.setReferencedStruct(*referencedStruct); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
bool ReferencesResolver::visit(Identifier& _identifier) |
|
|
|
{ |
|
|
|
Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); |
|
|
|
if (declaration == nullptr) |
|
|
|
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); |
|
|
|
_identifier.setReferencedDeclaration(*declaration); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} } |
|
|
|