Browse Source

Contract Interface Functions now return FunctionType

- Enchanced Function Type by declaration so that it can provide all the
  required information at each place interface functions are consumed

- Changed all places where interface functions was used.

- Simplified Mix's FunctionDefinition code
cl-refactor
Lefteris Karapetsas 10 years ago
parent
commit
122aada70f
  1. 2
      alethzero/MainWin.cpp
  2. 21
      libsolidity/AST.cpp
  3. 6
      libsolidity/AST.h
  4. 6
      libsolidity/Compiler.cpp
  5. 44
      libsolidity/InterfaceHandler.cpp
  6. 46
      libsolidity/Types.cpp
  7. 15
      libsolidity/Types.h
  8. 5
      mix/QContractDefinition.cpp
  9. 34
      mix/QFunctionDefinition.cpp
  10. 2
      mix/QFunctionDefinition.h
  11. 2
      mix/QVariableDeclaration.h
  12. 18
      test/SolidityNameAndTypeResolution.cpp

2
alethzero/MainWin.cpp

@ -1674,7 +1674,7 @@ string const Main::getFunctionHashes(dev::solidity::CompilerStack const &_compil
{
ret += it.first.abridged();
ret += " :";
ret += it.second.getName() + "\n";
ret += it.second->getDeclaration()->getName() + "\n";
}
return ret;
}

21
libsolidity/AST.cpp

@ -66,24 +66,25 @@ void ContractDefinition::checkTypeRequirements()
// check for hash collisions in function signatures
set<FixedHash<4>> hashes;
for (auto const& hashAndFunction: getInterfaceFunctionList())
for (auto const& it: getInterfaceFunctionList())
{
FixedHash<4> const& hash = std::get<0>(hashAndFunction);
FixedHash<4> const& hash = it.first;
if (hashes.count(hash))
BOOST_THROW_EXCEPTION(createTypeError(
std::string("Function signature hash collision for ") +
std::get<1>(hashAndFunction)->getCanonicalSignature(std::get<2>(hashAndFunction)->getName())));
it.second->getCanonicalSignature()));
hashes.insert(hash);
}
}
map<FixedHash<4>, FunctionDescription> ContractDefinition::getInterfaceFunctions() const
map<FixedHash<4>, std::shared_ptr<FunctionType const>> ContractDefinition::getInterfaceFunctions() const
{
auto exportedFunctionList = getInterfaceFunctionList();
map<FixedHash<4>, FunctionDescription> exportedFunctions;
map<FixedHash<4>, std::shared_ptr<FunctionType const>> exportedFunctions;
for (auto const& it: exportedFunctionList)
exportedFunctions.insert(make_pair(std::get<0>(it), FunctionDescription(std::get<1>(it), std::get<2>(it))));
// exportedFunctions.insert(make_pair(std::get<0>(it), FunctionDescription(std::get<1>(it), std::get<2>(it))));
exportedFunctions.insert(it);
solAssert(exportedFunctionList.size() == exportedFunctions.size(),
"Hash collision at Function Definition Hash calculation");
@ -138,12 +139,12 @@ void ContractDefinition::checkIllegalOverrides() const
}
}
vector<tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration const*>> const& ContractDefinition::getInterfaceFunctionList() const
vector<pair<FixedHash<4>, shared_ptr<FunctionType const>>> const& ContractDefinition::getInterfaceFunctionList() const
{
if (!m_interfaceFunctionList)
{
set<string> functionsSeen;
m_interfaceFunctionList.reset(new vector<tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration const*>>());
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, shared_ptr<FunctionType const>>>());
for (ContractDefinition const* contract: getLinearizedBaseContracts())
{
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
@ -151,7 +152,7 @@ vector<tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration cons
{
functionsSeen.insert(f->getName());
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
m_interfaceFunctionList->push_back(make_tuple(hash, make_shared<FunctionType>(*f, false), f.get()));
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*f, false)));
}
for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables())
@ -160,7 +161,7 @@ vector<tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration cons
FunctionType ftype(*v);
functionsSeen.insert(v->getName());
FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName())));
m_interfaceFunctionList->push_back(make_tuple(hash, make_shared<FunctionType>(*v), v.get()));
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v)));
}
}
}

6
libsolidity/AST.h

@ -294,7 +294,7 @@ public:
/// @returns a map of canonical function signatures to FunctionDefinitions
/// as intended for use by the ABI.
std::map<FixedHash<4>, FunctionDescription> getInterfaceFunctions() const;
std::map<FixedHash<4>, std::shared_ptr<FunctionType const>> getInterfaceFunctions() const;
/// List of all (direct and indirect) base contracts in order from derived to base, including
/// the contract itself. Available after name resolution
@ -307,7 +307,7 @@ public:
private:
void checkIllegalOverrides() const;
std::vector<std::tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration const*>> const& getInterfaceFunctionList() const;
std::vector<std::pair<FixedHash<4>, std::shared_ptr<FunctionType const>>> const& getInterfaceFunctionList() const;
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
@ -316,7 +316,7 @@ private:
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
std::vector<ContractDefinition const*> m_linearizedBaseContracts;
mutable std::unique_ptr<std::vector<std::tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration const*>>> m_interfaceFunctionList;
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, std::shared_ptr<FunctionType const>>>> m_interfaceFunctionList;
};
class InheritanceSpecifier: public ASTNode

6
libsolidity/Compiler.cpp

@ -142,7 +142,7 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
{
map<FixedHash<4>, FunctionDescription> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
// retrieve the function signature hash from the calldata
@ -160,11 +160,11 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
for (auto const& it: interfaceFunctions)
{
FunctionType const* functionType = it.second.getFunctionType();
FunctionTypePointer functionType = it.second;
m_context << callDataUnpackerEntryPoints.at(it.first);
eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(functionType->getParameterTypes());
m_context.appendJumpTo(m_context.getFunctionEntryLabel(*it.second.getDeclaration()));
m_context.appendJumpTo(m_context.getFunctionEntryLabel(*it.second->getDeclaration()));
m_context << returnTag;
appendReturnValuePacker(functionType->getReturnParameterTypes());
}

44
libsolidity/InterfaceHandler.cpp

@ -45,23 +45,27 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio
Json::Value inputs(Json::arrayValue);
Json::Value outputs(Json::arrayValue);
auto populateParameters = [](vector<ParamDescription> const& _params)
auto populateParameters = [](vector<string> const& _paramNames,
vector<string> const& _paramTypes)
{
Json::Value params(Json::arrayValue);
for (auto const& param: _params)
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
for (unsigned i = 0; i < _paramNames.size(); ++i)
{
Json::Value input;
input["name"] = param.getName();
input["type"] = param.getType();
input["name"] = _paramNames[i];
input["type"] = _paramTypes[i];
params.append(input);
}
return params;
};
method["name"] = it.second.getName();
method["constant"] = it.second.isConstant();
method["inputs"] = populateParameters(it.second.getParameters());
method["outputs"] = populateParameters(it.second.getReturnParameters());
method["name"] = it.second->getDeclaration()->getName();
method["constant"] = it.second->isConstant();
method["inputs"] = populateParameters(it.second->getParameterNames(),
it.second->getParameterTypeNames());
method["outputs"] = populateParameters(it.second->getReturnParameterNames(),
it.second->getReturnParameterTypeNames());
methods.append(method);
}
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
@ -72,16 +76,20 @@ unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition
string ret = "contract " + _contractDef.getName() + "{";
for (auto const& it: _contractDef.getInterfaceFunctions())
{
auto populateParameters = [](vector<ParamDescription> const& _params)
auto populateParameters = [](vector<string> const& _paramNames,
vector<string> const& _paramTypes)
{
string r = "";
for (auto const& param: _params)
r += (r.size() ? "," : "(") + param.getType() + " " + param.getName();
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
for (unsigned i = 0; i < _paramNames.size(); ++i)
r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i];
return r.size() ? r + ")" : "()";
};
ret += "function " + it.second.getName() + populateParameters(it.second.getParameters()) + (it.second.isConstant() ? "constant " : "");
if (it.second.getReturnParameters().size())
ret += "returns" + populateParameters(it.second.getReturnParameters());
ret += "function " + it.second->getDeclaration()->getName() +
populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) +
(it.second->isConstant() ? "constant " : "");
if (it.second->getReturnParameterTypes().size())
ret += "returns" + populateParameters(it.second->getReturnParameterNames(), it.second->getReturnParameterTypeNames());
else if (ret.back() == ' ')
ret.pop_back();
ret += "{}";
@ -97,7 +105,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
for (auto const& it: _contractDef.getInterfaceFunctions())
{
Json::Value user;
auto strPtr = it.second.getDocumentation();
auto strPtr = it.second->getDocumentation();
if (strPtr)
{
resetUser();
@ -105,7 +113,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice);
methods[it.second.getSignature()] = user;
methods[it.second->getCanonicalSignature()] = user;
}
}
}
@ -138,7 +146,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
for (auto const& it: _contractDef.getInterfaceFunctions())
{
Json::Value method;
auto strPtr = it.second.getDocumentation();
auto strPtr = it.second->getDocumentation();
if (strPtr)
{
resetDev();
@ -161,7 +169,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
method["return"] = m_return;
if (!method.empty()) // add the function, only if we have any documentation to add
methods[it.second.getSignature()] = method;
methods[it.second->getCanonicalSignature()] = method;
}
}
doc["methods"] = methods;

46
libsolidity/Types.cpp

@ -499,7 +499,7 @@ MemberList const& ContractType::getMembers() const
}
else
for (auto const& it: m_contract.getInterfaceFunctions())
members[it.second.getName()] = it.second.getFunctionTypeShared();
members[it.second->getDeclaration()->getName()] = it.second;
m_members.reset(new MemberList(members));
}
return *m_members;
@ -522,7 +522,7 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const
{
auto interfaceFunctions = m_contract.getInterfaceFunctions();
for (auto const& it: m_contract.getInterfaceFunctions())
if (it.second.getName() == _functionName)
if (it.second->getDeclaration()->getName() == _functionName)
return FixedHash<4>::Arith(it.first);
return Invalid256;
@ -589,12 +589,15 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
}
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL)
m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL),
m_isConstant(_function.isDeclaredConst()),
m_declaration(&_function)
{
TypePointers params;
vector<string> paramNames;
TypePointers retParams;
vector<string> retParamNames;
params.reserve(_function.getParameters().size());
paramNames.reserve(_function.getParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
@ -616,7 +619,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
}
FunctionType::FunctionType(VariableDeclaration const& _varDecl):
m_location(Location::EXTERNAL)
m_location(Location::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl)
{
TypePointers params({});
vector<string> paramNames({});
@ -711,7 +714,13 @@ MemberList const& FunctionType::getMembers() const
string FunctionType::getCanonicalSignature(std::string const& _name) const
{
string ret = _name + "(";
std::string funcName = _name;
if (_name == "")
{
solAssert(m_declaration != nullptr, "Function type without name needs a declaration");
funcName = m_declaration->getName();
}
string ret = funcName + "(";
for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it)
ret += (*it)->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ",");
@ -734,6 +743,33 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
m_gasSet || _setGas, m_valueSet || _setValue);
}
vector<string> const FunctionType::getParameterTypeNames() const
{
vector<string> names;
for (TypePointer const& t: m_parameterTypes)
names.push_back(t->toString());
return names;
}
vector<string> const FunctionType::getReturnParameterTypeNames() const
{
vector<string> names;
for (TypePointer const& t: m_returnParameterTypes)
names.push_back(t->toString());
return names;
}
ASTPointer<ASTString> FunctionType::getDocumentation() const
{
auto function = dynamic_cast<FunctionDefinition const*>(m_declaration);
if (function)
return function->getDocumentation();
return ASTPointer<ASTString>();
}
bool MappingType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())

15
libsolidity/Types.h

@ -41,6 +41,7 @@ namespace solidity
class Type; // forward
class FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>;
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>;
/**
@ -371,8 +372,10 @@ public:
TypePointers const& getParameterTypes() const { return m_parameterTypes; }
std::vector<std::string> const& getParameterNames() const { return m_parameterNames; }
std::vector<std::string> const getParameterTypeNames() const;
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
std::vector<std::string> const& getReturnParameterNames() const { return m_returnParameterNames; }
std::vector<std::string> const getReturnParameterTypeNames() const;
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
@ -383,7 +386,15 @@ public:
virtual MemberList const& getMembers() const override;
Location const& getLocation() const { return m_location; }
std::string getCanonicalSignature(std::string const& _name) const;
/// @returns the canonical signature of this function type given the function name
/// If @a _name is not provided (empty string) then the @c m_declaration member of the
/// function type is used
std::string getCanonicalSignature(std::string const& _name = "") const;
Declaration const* getDeclaration() const { return m_declaration; }
bool isConstant() const { return m_isConstant; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> getDocumentation() const;
bool gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; }
@ -402,7 +413,9 @@ private:
Location const m_location;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
bool m_isConstant;
mutable std::unique_ptr<MemberList> m_members;
Declaration const* m_declaration = nullptr;
};
/**

5
mix/QContractDefinition.cpp

@ -34,10 +34,7 @@ using namespace dev::mix;
QContractDefinition::QContractDefinition(dev::solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_contract)
{
if (_contract->getConstructor() != nullptr)
{
FunctionDescription desc(_contract->getConstructor());
m_constructor = new QFunctionDefinition(desc);
}
m_constructor = new QFunctionDefinition(ContractType(*_contract).getConstructorType());
else
m_constructor = new QFunctionDefinition();

34
mix/QFunctionDefinition.cpp

@ -28,29 +28,15 @@
using namespace dev::solidity;
using namespace dev::mix;
QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionDescription const& _f): QBasicNodeDefinition(_f.getDeclaration()), m_hash(dev::sha3(_f.getSignature()))
QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_f->getDeclaration()), m_hash(dev::sha3(_f->getCanonicalSignature()))
{
FunctionDefinition const* funcDef;
VariableDeclaration const* varDecl;
if ((funcDef = _f.getFunctionDefinition()))
{
std::vector<std::shared_ptr<VariableDeclaration>> parameters = funcDef->getParameterList().getParameters();
for (unsigned i = 0; i < parameters.size(); i++)
m_parameters.append(new QVariableDeclaration(parameters.at(i).get()));
std::vector<std::shared_ptr<VariableDeclaration>> returnParameters = funcDef->getReturnParameters();
for (unsigned i = 0; i < returnParameters.size(); i++)
m_returnParameters.append(new QVariableDeclaration(returnParameters.at(i).get()));
}
else
{
if (!(varDecl = _f.getVariableDeclaration()))
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Malformed FunctionDescription. Should never happen."));
// only the return parameter for now.
// TODO: change this for other state variables like mapping and maybe abstract this inside solidity and not here
auto returnParams = _f.getReturnParameters();
m_returnParameters.append(new QVariableDeclaration(returnParams[0]));
}
auto paramNames = _f->getParameterNames();
auto paramTypes = _f->getParameterTypeNames();
auto returnNames = _f->getReturnParameterNames();
auto returnTypes = _f->getReturnParameterTypeNames();
for (unsigned i = 0; i < paramNames.size(); ++i)
m_parameters.append(new QVariableDeclaration(paramNames[i], paramTypes[i]));
for (unsigned i = 0; i < returnNames.size(); ++i)
m_returnParameters.append(new QVariableDeclaration(returnNames[i], returnTypes[i]));
}

2
mix/QFunctionDefinition.h

@ -39,7 +39,7 @@ class QFunctionDefinition: public QBasicNodeDefinition
public:
QFunctionDefinition() {}
QFunctionDefinition(solidity::FunctionDescription const& _f);
QFunctionDefinition(solidity::FunctionTypePointer const& _f);
/// Get all input parameters of this function.
QList<QVariableDeclaration*> const& parametersList() const { return m_parameters; }
/// Get all input parameters of this function as QML property.

2
mix/QVariableDeclaration.h

@ -37,7 +37,7 @@ class QVariableDeclaration: public QBasicNodeDefinition
public:
QVariableDeclaration() {}
QVariableDeclaration(solidity::VariableDeclaration const* _v): QBasicNodeDefinition(_v), m_type(QString::fromStdString(_v->getType()->toString())) {}
QVariableDeclaration(solidity::ParamDescription const& _v): QBasicNodeDefinition(_v.getName()), m_type(QString::fromStdString(_v.getType())) {}
QVariableDeclaration(std::string const& _name, std::string const& _type): QBasicNodeDefinition(_name), m_type(QString::fromStdString(_type)) {}
QString type() const { return m_type; }
private:
QString m_type;

18
test/SolidityNameAndTypeResolution.cpp

@ -93,8 +93,8 @@ static ContractDefinition const* retrieveContract(ASTPointer<SourceUnit> _source
return NULL;
}
static FunctionDescription const& retrieveFunctionBySignature(ContractDefinition const* _contract,
std::string const& _signature)
static std::shared_ptr<FunctionType const> const& retrieveFunctionBySignature(ContractDefinition const* _contract,
std::string const& _signature)
{
FixedHash<4> hash(dev::sha3(_signature));
return _contract->getInterfaceFunctions()[hash];
@ -643,11 +643,11 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors)
ContractDefinition const* contract;
BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text));
BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr);
FunctionDescription function = retrieveFunctionBySignature(contract, "foo()");
BOOST_REQUIRE(function.getDeclaration() != nullptr);
auto returnParams = function.getReturnParameters();
BOOST_CHECK_EQUAL(returnParams.at(0).getType(), "uint256");
BOOST_CHECK(function.isConstant());
std::shared_ptr<FunctionType const> function = retrieveFunctionBySignature(contract, "foo()");
BOOST_CHECK_MESSAGE(function->getDeclaration() != nullptr, "Could not find the accessor function");
auto returnParams = function->getReturnParameterTypeNames();
BOOST_CHECK_EQUAL(returnParams.at(0), "uint256");
BOOST_CHECK(function->isConstant());
}
BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor)
@ -676,8 +676,8 @@ BOOST_AUTO_TEST_CASE(private_state_variable)
ContractDefinition const* contract;
BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text));
BOOST_CHECK((contract = retrieveContract(source, 0)) != nullptr);
FunctionDescription function = retrieveFunctionBySignature(contract, "foo()");
BOOST_CHECK_MESSAGE(function.getDeclaration() == nullptr, "Accessor function of a private variable should not exist");
FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()");
BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist");
}
BOOST_AUTO_TEST_SUITE_END()

Loading…
Cancel
Save