diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 2d7146426..103dfbb33 100644 --- a/alethzero/MainWin.cpp +++ b/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.getName() + "\n"; } return ret; } diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 7620eeece..e4b5ed7d3 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -71,17 +71,19 @@ void ContractDefinition::checkTypeRequirements() FixedHash<4> const& hash = std::get<0>(hashAndFunction); if (hashes.count(hash)) BOOST_THROW_EXCEPTION(createTypeError( - "Function signature hash collision for " + - std::get<1>(hashAndFunction)>->getCanonicalSignature(std::get<2>(hashAndFunction)->getName()))); + std::string("Function signature hash collision for ") + + std::get<1>(hashAndFunction)->getCanonicalSignature(std::get<2>(hashAndFunction)->getName()))); hashes.insert(hash); } } -map, pair> ContractDefinition::getInterfaceFunctions() const +map, FunctionDescription> ContractDefinition::getInterfaceFunctions() const { - vector, FunctionType const*, Declaration const*>>> exportedFunctionList = getInterfaceFunctionList(); - map, pair> exportedFunctions(exportedFunctionList.begin(), - exportedFunctionList.end()); + auto exportedFunctionList = getInterfaceFunctionList(); + + map, FunctionDescription> exportedFunctions; + for (auto const& it: exportedFunctionList) + exportedFunctions[std::get<0>(it)] = std::move(FunctionDescription(std::get<1>(it), std::get<2>(it))); solAssert(exportedFunctionList.size() == exportedFunctions.size(), "Hash collision at Function Definition Hash calculation"); @@ -136,12 +138,12 @@ void ContractDefinition::checkIllegalOverrides() const } } -vector, FunctionType const*, Declaration const*>> const& ContractDefinition::getInterfaceFunctionList() const +vector, std::shared_ptr, Declaration const*>> const& ContractDefinition::getInterfaceFunctionList() const { if (!m_interfaceFunctionList) { set functionsSeen; - m_interfaceFunctionList.reset(new vector, FunctionType const*, Declaration const*>>()); + m_interfaceFunctionList.reset(new vector, std::shared_ptr, Declaration const*>>()); for (ContractDefinition const* contract: getLinearizedBaseContracts()) { for (ASTPointer const& f: contract->getDefinedFunctions()) @@ -149,7 +151,7 @@ vector, FunctionType const*, Declaration const*>> const& Cont { functionsSeen.insert(f->getName()); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); - m_interfaceFunctionList->push_back(make_tuple(hash, FunctionType(*f), f.get())); + m_interfaceFunctionList->push_back(make_tuple(hash, make_shared(*f), f.get())); } for (ASTPointer const& v: contract->getStateVariables()) @@ -157,8 +159,8 @@ vector, FunctionType const*, Declaration const*>> const& Cont { FunctionType ftype(*v); functionsSeen.insert(v->getName()); - FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName())); - m_interfaceFunctionList->push_back(make_tuple(hash, ftype, v.get())); + FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName()))); + m_interfaceFunctionList->push_back(make_tuple(hash, make_shared(*v), v.get())); } } } @@ -518,17 +520,104 @@ void Literal::checkTypeRequirements() } -ASTPointer FunctionDescription::getDocumentation() +bool ParamDescription::operator!=(ParamDescription const& _other) const +{ + return m_description.first == _other.getName() && m_description.second == _other.getType(); +} + +std::ostream& ParamDescription::operator<<(std::ostream& os) const +{ + return os << m_description.first << ":" << m_description.second; +} + +std::string ParamDescription::getName() const +{ + return m_description.first; +} + +std::string ParamDescription::getType() const +{ + return m_description.second; +} + + +ASTPointer FunctionDescription::getDocumentation() const { auto function = dynamic_cast(m_description.second); if (function) return function->getDocumentation(); + + return ASTPointer(); } -string FunctionDescription::getSignature() +string FunctionDescription::getSignature() const { return m_description.first->getCanonicalSignature(m_description.second->getName()); } +string FunctionDescription::getName() const +{ + return m_description.second->getName(); +} + +bool FunctionDescription::isConstant() const +{ + auto function = dynamic_cast(m_description.second); + if (function) + return function->isDeclaredConst(); + + return true; +} + +vector const FunctionDescription::getParameters() const +{ + auto function = dynamic_cast(m_description.second); + if (function) + { + vector paramsDescription; + for (auto const& param: function->getParameters()) + paramsDescription.push_back(ParamDescription(param->getName(), param->getType()->toString())); + + return paramsDescription; + } + + // else for now let's assume no parameters to accessors + // LTODO: fix this for mapping types + return {}; +} + +vector const FunctionDescription::getReturnParameters() const +{ + auto function = dynamic_cast(m_description.second); + if (function) + { + vector paramsDescription; + for (auto const& param: function->getParameters()) + paramsDescription.push_back(ParamDescription(param->getName(), param->getType()->toString())); + + return paramsDescription; + } + + auto vardecl = dynamic_cast(m_description.second); + return {ParamDescription(vardecl->getName(), vardecl->getType()->toString())}; +} + +Declaration const* FunctionDescription::getDeclaration() const +{ + return m_description.second; +} + +shared_ptr FunctionDescription::getFunctionTypeShared() const +{ + return m_description.first; +} + + +FunctionType const* FunctionDescription::getFunctionType() const +{ + return m_description.first.get(); +} + + } } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 695e504ac..89daf80fb 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -157,19 +157,59 @@ private: }; +/** +* Generic Parameter description used by @see FunctionDescription to return +* a descripton of its parameters. +*/ +struct ParamDescription +{ + ParamDescription(std::string const& _name, std::string const& _type): + m_description(_name, _type){} + + bool operator!=(ParamDescription const& _other) const; + std::ostream& operator<<(std::ostream& os) const; + + std::string getName() const; + std::string getType() const; + + std::pair m_description; +}; + + /** * Generic function description able to describe both normal functions and * functions that should be made as accessors to state variables */ struct FunctionDescription { - FunctionDescription(FunctionType const *_type, Declaration const* _decl): + FunctionDescription(std::shared_ptr _type, Declaration const* _decl): m_description(_type, _decl){} - ASTPointer getDocumentation(); - std::string getSignature(); + FunctionDescription(): + m_description(nullptr, nullptr){} - std::pair m_description; + /// @returns the natspec documentation of the function if existing. Accessor (for now) don't have natspec doc + ASTPointer getDocumentation() const; + /// @returns the canonical signature of the function + std::string getSignature() const; + /// @returns the name of the function, basically that of the declaration + std::string getName() const; + /// @returns whether the function is constant. IF it's an accessor this is always true + bool isConstant() const; + /// @returns the argument parameters of the function + std::vector const getParameters() const; + /// @returns the return parameters of the function + std::vector const getReturnParameters() const; + /// @returns the Declaration AST Node pointer + Declaration const* getDeclaration() const; + /// @returns a created shared pointer with the type of the function + std::shared_ptr makeFunctionType() const; + /// @returns a pointer to the function type + FunctionType const* getFunctionType() const; + /// @returns a shared pointer to the function type + std::shared_ptr getFunctionTypeShared() const; + + std::pair, Declaration const*> m_description; }; @@ -219,7 +259,7 @@ public: /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. - std::map, std::pair> getInterfaceFunctions() const; + std::map, FunctionDescription> getInterfaceFunctions() const; /// List of all (direct and indirect) base contracts in order from derived to base, including /// the contract itself. Available after name resolution @@ -232,7 +272,7 @@ public: private: void checkIllegalOverrides() const; - std::vector, FunctionType const*, Declaration const*>> const& getInterfaceFunctionList() const; + std::vector, std::shared_ptr, Declaration const*>> const& getInterfaceFunctionList() const; std::vector> m_baseContracts; std::vector> m_definedStructs; @@ -242,7 +282,7 @@ private: ASTPointer m_documentation; std::vector m_linearizedBaseContracts; - mutable std::unique_ptr, FunctionType const*, Declaration const*>>> m_interfaceFunctionList; + mutable std::unique_ptr, std::shared_ptr, Declaration const*>>> m_interfaceFunctionList; }; class InheritanceSpecifier: public ASTNode diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 13f8282e6..f6f48a8c2 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -177,13 +177,14 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor) unsigned argumentSize = 0; for (ASTPointer const& var: _constructor.getParameters()) argumentSize += CompilerUtils::getPaddedSize(var->getType()->getCalldataEncodedSize()); + if (argumentSize > 0) { m_context << u256(argumentSize); m_context.appendProgramSize(); m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls m_context << eth::Instruction::CODECOPY; - appendCalldataUnpacker(_constructor, true); + appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true); } m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor)); m_context << returnTag; @@ -201,7 +202,7 @@ set Compiler::getFunctionsCalled(set void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { - map, FunctionType const*, Declaration const*> interfaceFunctions = _contract.getInterfaceFunctions(); + map, FunctionDescription> interfaceFunctions = _contract.getInterfaceFunctions(); map, const eth::AssemblyItem> callDataUnpackerEntryPoints; // retrieve the function signature hash from the calldata @@ -219,11 +220,11 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) for (auto const& it: interfaceFunctions) { - FunctionType const* functionType = *it.second.first; + FunctionType const* functionType = it.second.getFunctionType(); m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); - m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second.second)); + m_context.appendJumpTo(m_context.getFunctionEntryLabel(*it.second.getDeclaration())); m_context << returnTag; appendReturnValuePacker(functionType->getReturnParameterTypes()); } diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 1adce8cb8..9b6327782 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -45,23 +45,23 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio Json::Value inputs(Json::arrayValue); Json::Value outputs(Json::arrayValue); - auto populateParameters = [](std::vector> const& _vars) + auto populateParameters = [](vector const& _params) { Json::Value params(Json::arrayValue); - for (ASTPointer const& var: _vars) + for (auto const& param: _params) { Json::Value input; - input["name"] = var->getName(); - input["type"] = var->getType()->toString(); + input["name"] = param.getName(); + input["type"] = param.getType(); params.append(input); } return params; }; - method["name"] = it.second->getName(); - method["constant"] = it.second->isDeclaredConst(); - method["inputs"] = populateParameters(it.second->getParameters()); - method["outputs"] = populateParameters(it.second->getReturnParameters()); + method["name"] = it.second.getName(); + method["constant"] = it.second.isConstant(); + method["inputs"] = populateParameters(it.second.getParameters()); + method["outputs"] = populateParameters(it.second.getReturnParameters()); methods.append(method); } return std::unique_ptr(new std::string(m_writer.write(methods))); @@ -72,17 +72,16 @@ unique_ptr InterfaceHandler::getABISolidityInterface(ContractDefinition string ret = "contract " + _contractDef.getName() + "{"; for (auto const& it: _contractDef.getInterfaceFunctions()) { - FunctionDefinition const* f = it.second; - auto populateParameters = [](vector> const& _vars) + auto populateParameters = [](vector const& _params) { string r = ""; - for (ASTPointer const& var: _vars) - r += (r.size() ? "," : "(") + var->getType()->toString() + " " + var->getName(); + for (auto const& param: _params) + r += (r.size() ? "," : "(") + param.getType() + " " + param.getName(); return r.size() ? r + ")" : "()"; }; - ret += "function " + f->getName() + populateParameters(f->getParameters()) + (f->isDeclaredConst() ? "constant " : ""); - if (f->getReturnParameters().size()) - ret += "returns" + populateParameters(f->getReturnParameters()); + ret += "function " + it.second.getName() + populateParameters(it.second.getParameters()) + (it.second.isConstant() ? "constant " : ""); + if (it.second.getReturnParameters().size()) + ret += "returns" + populateParameters(it.second.getReturnParameters()); else if (ret.back() == ' ') ret.pop_back(); ret += "{}"; @@ -98,7 +97,7 @@ std::unique_ptr 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(); @@ -106,7 +105,7 @@ std::unique_ptr 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->getCanonicalSignature()] = user; + methods[it.second.getSignature()] = user; } } } @@ -139,7 +138,7 @@ std::unique_ptr 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(); @@ -162,7 +161,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin method["return"] = m_return; if (!method.empty()) // add the function, only if we have any documentation to add - methods[it.second->getCanonicalSignature()] = method; + methods[it.second.getSignature()] = method; } } doc["methods"] = methods; diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index d5b001636..c5f1a3ae5 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -489,7 +489,7 @@ MemberList const& ContractType::getMembers() const map> members(IntegerType::AddressMemberList.begin(), IntegerType::AddressMemberList.end()); for (auto const& it: m_contract.getInterfaceFunctions()) - members[it.second.second->getName()] = make_shared(*it.second.second, false); + members[it.second.getName()] = it.second.getFunctionTypeShared(); m_members.reset(new MemberList(members)); } return *m_members; @@ -511,9 +511,9 @@ shared_ptr const& ContractType::getConstructorType() const u256 ContractType::getFunctionIdentifier(string const& _functionName) const { auto interfaceFunctions = m_contract.getInterfaceFunctions(); - for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it) - if (it->second.second->getName() == _functionName) - return FixedHash<4>::Arith(it->first); + for (auto const& it: m_contract.getInterfaceFunctions()) + if (it.second.getName() == _functionName) + return FixedHash<4>::Arith(it.first); return Invalid256; } @@ -582,28 +582,47 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL) { TypePointers params; + vector paramNames; TypePointers retParams; + vector retParamNames; params.reserve(_function.getParameters().size()); + paramNames.reserve(_function.getParameters().size()); for (ASTPointer const& var: _function.getParameters()) + { + paramNames.push_back(var->getName()); params.push_back(var->getType()); + } retParams.reserve(_function.getReturnParameters().size()); + retParamNames.reserve(_function.getReturnParameters().size()); for (ASTPointer const& var: _function.getReturnParameters()) + { + retParamNames.push_back(var->getName()); retParams.push_back(var->getType()); + } swap(params, m_parameterTypes); + swap(paramNames, m_parameterNames); swap(retParams, m_returnParameterTypes); + swap(retParamNames, m_returnParameterNames); } FunctionType::FunctionType(VariableDeclaration const& _varDecl): m_location(Location::INTERNAL) { TypePointers params; + vector paramNames; TypePointers retParams; + vector retParamNames; // for now, no input parameters LTODO: change for some things like mapping params.reserve(0); + paramNames.reserve(0); retParams.reserve(1); + retParamNames.reserve(1); retParams.push_back(_varDecl.getType()); + retParamNames.push_back(_varDecl.getName()); swap(params, m_parameterTypes); + swap(paramNames, m_parameterNames); swap(retParams, m_returnParameterTypes); + swap(retParamNames, m_returnParameterNames); } bool FunctionType::operator==(Type const& _other) const diff --git a/libsolidity/Types.h b/libsolidity/Types.h index b3a92021b..b26157b3e 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -365,7 +365,9 @@ public: m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet) {} TypePointers const& getParameterTypes() const { return m_parameterTypes; } + std::vector const& getParameterNames() const { return m_parameterNames; } TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; } + std::vector const& getReturnParameterNames() const { return m_returnParameterNames; } virtual bool operator==(Type const& _other) const override; virtual std::string toString() const override; @@ -390,6 +392,8 @@ private: TypePointers m_parameterTypes; TypePointers m_returnParameterTypes; + std::vector m_parameterNames; + std::vector m_returnParameterNames; 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 diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 7728414de..dbb95cf72 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -30,6 +30,8 @@ #include #include +using namespace std; + namespace dev { namespace solidity @@ -39,6 +41,7 @@ namespace test namespace { + ASTPointer parseTextAndResolveNames(std::string const& _source) { Parser parser; @@ -90,8 +93,8 @@ static ContractDefinition const* retrieveContract(ASTPointer _source return NULL; } -static FunctionDefinition const* retrieveFunctionBySignature(ContractDefinition const* _contract, - std::string const& _signature) +static FunctionDescription const& retrieveFunctionBySignature(ContractDefinition const* _contract, + std::string const& _signature) { FixedHash<4> hash(dev::sha3(_signature)); return _contract->getInterfaceFunctions()[hash]; @@ -629,19 +632,40 @@ BOOST_AUTO_TEST_CASE(modifier_returns_value) BOOST_AUTO_TEST_CASE(state_variable_accessors) { - char const* text = "contract base {\n" + char const* text = "contract test {\n" + " function fun() {\n" + " uint64(2);\n" + " }\n" + "uint256 foo;\n" + "}\n"; + + ASTPointer source; + 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, "Could not find the accessor function"); + // vector const expected({ParamDescription("", "uint256")}); + // BOOST_CHECK_EQUAL_COLLECTIONS(function.getReturnParameters().begin(), function.getReturnParameters().end(), + // expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(private_state_variable) +{ + char const* text = "contract test {\n" " function fun() {\n" " uint64(2);\n" " }\n" + "private:\n" "uint256 foo;\n" "}\n"; ASTPointer source; ContractDefinition const* contract; - FunctionDefinition const* function; BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text)); BOOST_CHECK((contract = retrieveContract(source, 0)) != nullptr); - BOOST_CHECK((function = retrieveFunctionBySignature(contract, "foo()")) != nullptr); + FunctionDescription function = retrieveFunctionBySignature(contract, "foo()"); + BOOST_CHECK_MESSAGE(function.getDeclaration() == nullptr, "Accessor function of a private variable should not exist"); } BOOST_AUTO_TEST_SUITE_END()