Browse Source

Merge pull request #1802 from LianaHus/sol_Deny_overwriting_attribute_by_function_via_inheritance

Proper overwriting of attribute and functions.
cl-refactor
chriseth 10 years ago
parent
commit
420ac9c0bf
  1. 6
      libsolidity/AST.h
  2. 10
      libsolidity/DeclarationContainer.cpp
  3. 8
      libsolidity/DeclarationContainer.h
  4. 83
      libsolidity/NameAndTypeResolver.cpp
  5. 17
      libsolidity/NameAndTypeResolver.h
  6. 1
      libsolidity/Types.h
  7. 84
      test/libsolidity/SolidityEndToEndTest.cpp

6
libsolidity/AST.h

@ -1220,7 +1220,7 @@ public:
/// Stores a set of possible declarations referenced by this identifier. Has to be resolved
/// providing argument types using overloadResolution before the referenced declaration
/// is accessed.
void setOverloadedDeclarations(std::set<Declaration const*> const& _declarations)
void setOverloadedDeclarations(std::vector<Declaration const*> const& _declarations)
{
m_overloadedDeclarations = _declarations;
}
@ -1237,8 +1237,8 @@ private:
/// Stores a reference to the current contract. This is needed because types of base contracts
/// change depending on the context.
ContractDefinition const* m_currentContract = nullptr;
/// A set of overloaded declarations, right now only FunctionDefinition has overloaded declarations.
std::set<Declaration const*> m_overloadedDeclarations;
/// A vector of overloaded declarations, right now only FunctionDefinition has overloaded declarations.
std::vector<Declaration const*> m_overloadedDeclarations;
};
/**

10
libsolidity/DeclarationContainer.cpp

@ -37,6 +37,7 @@ Declaration const* DeclarationContainer::conflictingDeclaration(Declaration cons
declarations += m_declarations.at(name);
if (m_invisibleDeclarations.count(name))
declarations += m_invisibleDeclarations.at(name);
if (dynamic_cast<FunctionDefinition const*>(&_declaration))
{
// check that all other declarations with the same name are functions
@ -66,14 +67,13 @@ bool DeclarationContainer::registerDeclaration(Declaration const& _declaration,
return false;
if (_invisible)
m_invisibleDeclarations[name].insert(&_declaration);
m_invisibleDeclarations[name].push_back(&_declaration);
else
m_declarations[name].insert(&_declaration);
m_declarations[name].push_back(&_declaration);
return true;
}
set<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
std::vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
{
solAssert(!_name.empty(), "Attempt to resolve empty name.");
auto result = m_declarations.find(_name);
@ -81,5 +81,5 @@ set<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name
return result->second;
if (_recursive && m_enclosingContainer)
return m_enclosingContainer->resolveName(_name, true);
return set<Declaration const*>({});
return vector<Declaration const*>({});
}

8
libsolidity/DeclarationContainer.h

@ -48,17 +48,17 @@ public:
/// @param _update if true, replaces a potential declaration that is already present
/// @returns false if the name was already declared.
bool registerDeclaration(Declaration const& _declaration, bool _invisible = false, bool _update = false);
std::set<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const;
std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const;
Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; }
std::map<ASTString, std::set<Declaration const*>> const& getDeclarations() const { return m_declarations; }
std::map<ASTString, std::vector<Declaration const*>> const& getDeclarations() const { return m_declarations; }
/// @returns whether declaration is valid, and if not also returns previous declaration.
Declaration const* conflictingDeclaration(Declaration const& _declaration) const;
private:
Declaration const* m_enclosingDeclaration;
DeclarationContainer const* m_enclosingContainer;
std::map<ASTString, std::set<Declaration const*>> m_declarations;
std::map<ASTString, std::set<Declaration const*>> m_invisibleDeclarations;
std::map<ASTString, std::vector<Declaration const*>> m_declarations;
std::map<ASTString, std::vector<Declaration const*>> m_invisibleDeclarations;
};
}

83
libsolidity/NameAndTypeResolver.cpp

@ -53,9 +53,13 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
m_currentScope = &m_scopes[&_contract];
linearizeBaseContracts(_contract);
// we first import non-functions only as we do not yet know the argument types
for (ContractDefinition const* base: _contract.getLinearizedBaseContracts())
importInheritedScope(*base, false); // import non-functions
std::vector<ContractDefinition const*> properBases(
++_contract.getLinearizedBaseContracts().begin(),
_contract.getLinearizedBaseContracts().end()
);
for (ContractDefinition const* base: properBases)
importInheritedScope(*base);
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
ReferencesResolver resolver(*structDef, *this, &_contract, nullptr);
@ -80,8 +84,6 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
}
m_currentScope = &m_scopes[&_contract];
for (ContractDefinition const* base: _contract.getLinearizedBaseContracts())
importInheritedScope(*base, true); // import functions
// now resolve references inside the code
for (ASTPointer<ModifierDefinition> const& modifier: _contract.getFunctionModifiers())
@ -115,20 +117,55 @@ void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
solAssert(_declaration.getScope() == nullptr, "Updated declaration outside global scope.");
}
set<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
{
auto iterator = m_scopes.find(_scope);
if (iterator == end(m_scopes))
return set<Declaration const*>({});
return vector<Declaration const*>({});
return iterator->second.resolveName(_name, false);
}
set<Declaration const*> NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive)
vector<Declaration const*> NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive)
{
return m_currentScope->resolveName(_name, _recursive);
}
void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base, bool _importFunctions)
vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
Identifier const& _identifier,
vector<Declaration const*> const& _declarations
)
{
solAssert(_declarations.size() > 1, "");
vector<Declaration const*> uniqueFunctions;
for (auto it = _declarations.begin(); it != _declarations.end(); ++it)
{
solAssert(*it, "");
// the declaration is functionDefinition while declarations > 1
FunctionDefinition const& functionDefinition = dynamic_cast<FunctionDefinition const&>(**it);
FunctionType functionType(functionDefinition);
for (auto parameter: functionType.getParameterTypes() + functionType.getReturnParameterTypes())
if (!parameter)
BOOST_THROW_EXCEPTION(
DeclarationError() <<
errinfo_sourceLocation(_identifier.getLocation()) <<
errinfo_comment("Function type can not be used in this context")
);
if (uniqueFunctions.end() == find_if(
uniqueFunctions.begin(),
uniqueFunctions.end(),
[&](Declaration const* d)
{
FunctionType newFunctionType(dynamic_cast<FunctionDefinition const&>(*d));
return functionType.hasEqualArgumentTypes(newFunctionType);
}
))
uniqueFunctions.push_back(*it);
}
return uniqueFunctions;
}
void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
{
auto iterator = m_scopes.find(&_base);
solAssert(iterator != end(m_scopes), "");
@ -136,31 +173,8 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base,
for (auto const& declaration: nameAndDeclaration.second)
// Import if it was declared in the base, is not the constructor and is visible in derived classes
if (declaration->getScope() == &_base && declaration->isVisibleInDerivedContracts())
{
auto function = dynamic_cast<FunctionDefinition const*>(declaration);
if ((function == nullptr) == _importFunctions)
continue;
if (!!function)
{
FunctionType functionType(*function);
// only import if a function with the same arguments does not exist yet
bool functionWithEqualArgumentsFound = false;
for (auto knownDeclaration: m_currentScope->resolveName(nameAndDeclaration.first))
{
auto knownFunction = dynamic_cast<FunctionDefinition const*>(knownDeclaration);
if (!knownFunction)
continue; // this is not legal, but will be caught later
if (!FunctionType(*knownFunction).hasEqualArgumentTypes(functionType))
continue;
functionWithEqualArgumentsFound = true;
break;
}
if (functionWithEqualArgumentsFound)
continue;
}
m_currentScope->registerDeclaration(*declaration);
}
}
void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) const
{
@ -465,10 +479,9 @@ bool ReferencesResolver::visit(Identifier& _identifier)
errinfo_comment("Undeclared identifier.")
);
else if (declarations.size() == 1)
_identifier.setReferencedDeclaration(**declarations.begin(), m_currentContract);
_identifier.setReferencedDeclaration(*declarations.front(), m_currentContract);
else
// Duplicate declaration will be checked in checkTypeRequirements()
_identifier.setOverloadedDeclarations(declarations);
_identifier.setOverloadedDeclarations(m_resolver.cleanedDeclarations(_identifier, declarations));
return false;
}

17
libsolidity/NameAndTypeResolver.h

@ -56,19 +56,24 @@ public:
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
/// the global scope is used (i.e. the one containing only the contract).
/// @returns a pointer to the declaration on success or nullptr on failure.
std::set<Declaration const*> resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const;
std::vector<Declaration const*> resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const;
/// Resolves a name in the "current" scope. Should only be called during the initial
/// resolving phase.
std::set<Declaration const*> getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
std::vector<Declaration const*> getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
/// returns the vector of declarations without repetitions
static std::vector<Declaration const*> cleanedDeclarations(
Identifier const& _identifier,
std::vector<Declaration const*> const& _declarations
);
private:
void reset();
/// Either imports all non-function members or all function members declared directly in the
/// given contract (i.e. does not import inherited members) into the current scope if they are
///not present already.
void importInheritedScope(ContractDefinition const& _base, bool _importFunctions);
/// Imports all members declared directly in the given contract (i.e. does not import inherited members)
/// into the current scope if they are not present already.
void importInheritedScope(ContractDefinition const& _base);
/// Computes "C3-Linearization" of base contracts and stores it inside the contract.
void linearizeBaseContracts(ContractDefinition& _contract) const;

1
libsolidity/Types.h

@ -617,6 +617,7 @@ public:
/// @returns true if this function can take the given argument types (possibly
/// after implicit conversion).
bool canTakeArguments(TypePointers const& _arguments) const;
/// @returns true if the types of parameters are equal (does't check return parameter types)
bool hasEqualArgumentTypes(FunctionType const& _other) const;
Location const& getLocation() const { return m_location; }

84
test/libsolidity/SolidityEndToEndTest.cpp

@ -3910,6 +3910,90 @@ BOOST_AUTO_TEST_CASE(external_types_in_calls)
BOOST_CHECK(callContractFunction("nonexisting") == encodeArgs(u256(9)));
}
BOOST_AUTO_TEST_CASE(proper_order_of_overwriting_of_attributes)
{
// bug #1798
char const* sourceCode = R"(
contract init {
function isOk() returns (bool) { return false; }
bool public ok = false;
}
contract fix {
function isOk() returns (bool) { return true; }
bool public ok = true;
}
contract init_fix is init, fix {
function checkOk() returns (bool) { return ok; }
}
contract fix_init is fix, init {
function checkOk() returns (bool) { return ok; }
}
)";
compileAndRun(sourceCode, 0, "init_fix");
BOOST_CHECK(callContractFunction("isOk()") == encodeArgs(true));
BOOST_CHECK(callContractFunction("ok()") == encodeArgs(true));
compileAndRun(sourceCode, 0, "fix_init");
BOOST_CHECK(callContractFunction("isOk()") == encodeArgs(false));
BOOST_CHECK(callContractFunction("ok()") == encodeArgs(false));
}
BOOST_AUTO_TEST_CASE(proper_overwriting_accessor_by_function)
{
// bug #1798
char const* sourceCode = R"(
contract attribute {
bool ok = false;
}
contract func {
function ok() returns (bool) { return true; }
}
contract attr_func is attribute, func {
function checkOk() returns (bool) { return ok(); }
}
contract func_attr is func, attribute {
function checkOk() returns (bool) { return ok; }
}
)";
compileAndRun(sourceCode, 0, "attr_func");
BOOST_CHECK(callContractFunction("ok()") == encodeArgs(true));
compileAndRun(sourceCode, 0, "func_attr");
BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(false));
}
BOOST_AUTO_TEST_CASE(overwriting_inheritance)
{
// bug #1798
char const* sourceCode = R"(
contract A {
function ok() returns (uint) { return 1; }
}
contract B {
function ok() returns (uint) { return 2; }
}
contract C {
uint ok = 6;
}
contract AB is A, B {
function ok() returns (uint) { return 4; }
}
contract reversedE is C, AB {
function checkOk() returns (uint) { return ok(); }
}
contract E is AB, C {
function checkOk() returns (uint) { return ok; }
}
)";
compileAndRun(sourceCode, 0, "reversedE");
BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(4));
compileAndRun(sourceCode, 0, "E");
BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(6));
}
BOOST_AUTO_TEST_SUITE_END()
}

Loading…
Cancel
Save