Browse Source

Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop

cl-refactor
Gav Wood 10 years ago
parent
commit
2e2f90c3bb
  1. 2
      libp2p/Host.h
  2. 34
      libsolidity/AST.cpp
  3. 82
      libsolidity/AST.h
  4. 6
      libsolidity/AST_accept.h
  5. 2
      libsolidity/CompilerStack.cpp
  6. 9
      libsolidity/InterfaceHandler.cpp
  7. 24
      libsolidity/Parser.cpp
  8. 53
      libtestutils/BlockChainLoader.cpp
  9. 1
      mix/CodeModel.cpp
  10. 44
      mix/ContractCallDataEncoder.cpp
  11. 1
      mix/ContractCallDataEncoder.h
  12. 7
      mix/qml/TransactionDialog.qml
  13. 57
      test/SolidityNameAndTypeResolution.cpp
  14. 25
      test/SolidityNatspecJSON.cpp
  15. 8
      test/SolidityParser.cpp

2
libp2p/Host.h

@ -96,7 +96,7 @@ public:
static std::string pocHost();
/// Register a peer-capability; all new peer connections will have this capability.
template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr<T>(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; }
template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; std::shared_ptr<T> ret(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; }
bool haveCapability(CapDesc const& _name) const { return m_capabilities.count(_name) != 0; }
CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; }

34
libsolidity/AST.cpp

@ -21,6 +21,7 @@
*/
#include <algorithm>
#include <boost/range/adaptor/reversed.hpp>
#include <libsolidity/Utils.h>
#include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h>
@ -52,6 +53,7 @@ void ContractDefinition::checkTypeRequirements()
baseSpecifier->checkTypeRequirements();
checkIllegalOverrides();
checkAbstractFunctions();
FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty())
@ -60,6 +62,7 @@ void ContractDefinition::checkTypeRequirements()
FunctionDefinition const* fallbackFunction = nullptr;
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
{
if (function->getName().empty())
{
if (fallbackFunction)
@ -71,6 +74,9 @@ void ContractDefinition::checkTypeRequirements()
BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters."));
}
}
if (!function->isFullyImplemented())
setFullyImplemented(false);
}
for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
modifier->checkTypeRequirements();
@ -124,6 +130,28 @@ FunctionDefinition const* ContractDefinition::getFallbackFunction() const
return nullptr;
}
void ContractDefinition::checkAbstractFunctions()
{
map<string, bool> functions;
// Search from base to derived
for (ContractDefinition const* contract: boost::adaptors::reverse(getLinearizedBaseContracts()))
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
{
string const& name = function->getName();
if (!function->isFullyImplemented() && functions.count(name) && functions[name])
BOOST_THROW_EXCEPTION(function->createTypeError("Redeclaring an already implemented function as abstract"));
functions[name] = function->isFullyImplemented();
}
for (auto const& it: functions)
if (!it.second)
{
setFullyImplemented(false);
break;
}
}
void ContractDefinition::checkIllegalOverrides() const
{
// TODO unify this at a later point. for this we need to put the constness and the access specifier
@ -316,8 +344,8 @@ void FunctionDefinition::checkTypeRequirements()
modifier->checkTypeRequirements(isConstructor() ?
dynamic_cast<ContractDefinition const&>(*getScope()).getBaseContracts() :
vector<ASTPointer<InheritanceSpecifier>>());
m_body->checkTypeRequirements();
if (m_body)
m_body->checkTypeRequirements();
}
string FunctionDefinition::externalSignature() const
@ -649,6 +677,8 @@ void NewExpression::checkTypeRequirements()
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
if (!m_contract->isFullyImplemented())
BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an instance of an abstract contract."));
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},

82
libsolidity/AST.h

@ -196,6 +196,22 @@ protected:
ASTPointer<ASTString> m_documentation;
};
/**
* Abstract class that is added to AST nodes that can be marked as not being fully implemented
*/
class ImplementationOptional
{
public:
explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {}
/// @return whether this node is fully implemented or not
bool isFullyImplemented() const { return m_implemented; }
void setFullyImplemented(bool _implemented) { m_implemented = _implemented; }
protected:
bool m_implemented;
};
/// @}
/**
@ -203,20 +219,24 @@ protected:
* document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations.
*/
class ContractDefinition: public Declaration, public Documented
class ContractDefinition: public Declaration, public Documented, public ImplementationOptional
{
public:
ContractDefinition(SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<EnumDefinition>> const& _definedEnums,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
std::vector<ASTPointer<EventDefinition>> const& _events):
Declaration(_location, _name), Documented(_documentation),
ContractDefinition(
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<EnumDefinition>> const& _definedEnums,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
std::vector<ASTPointer<EventDefinition>> const& _events
):
Declaration(_location, _name),
Documented(_documentation),
ImplementationOptional(true),
m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs),
m_definedEnums(_definedEnums),
@ -263,6 +283,7 @@ public:
private:
void checkIllegalOverrides() const;
void checkAbstractFunctions();
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const;
@ -378,24 +399,29 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_parameters;
};
class FunctionDefinition: public Declaration, public VariableScope, public Documented
class FunctionDefinition: public Declaration, public VariableScope, public Documented, public ImplementationOptional
{
public:
FunctionDefinition(SourceLocation const& _location, ASTPointer<ASTString> const& _name,
Declaration::Visibility _visibility, bool _isConstructor,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body):
Declaration(_location, _name, _visibility), Documented(_documentation),
m_isConstructor(_isConstructor),
m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst),
m_functionModifiers(_modifiers),
m_returnParameters(_returnParameters),
m_body(_body)
FunctionDefinition(
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
Declaration::Visibility _visibility, bool _isConstructor,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body
):
Declaration(_location, _name, _visibility),
Documented(_documentation),
ImplementationOptional(_body != nullptr),
m_isConstructor(_isConstructor),
m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst),
m_functionModifiers(_modifiers),
m_returnParameters(_returnParameters),
m_body(_body)
{}
virtual void accept(ASTVisitor& _visitor) override;

6
libsolidity/AST_accept.h

@ -175,7 +175,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor)
if (m_returnParameters)
m_returnParameters->accept(_visitor);
listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor);
if (m_body)
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
@ -188,7 +189,8 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
if (m_returnParameters)
m_returnParameters->accept(_visitor);
listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor);
if (m_body)
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}

2
libsolidity/CompilerStack.cpp

@ -138,6 +138,8 @@ void CompilerStack::compile(bool _optimize)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
if (!contract->isFullyImplemented())
continue;
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()];

9
libsolidity/InterfaceHandler.cpp

@ -175,8 +175,17 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
method["author"] = m_author;
Json::Value params(Json::objectValue);
std::vector<std::string> paramNames = it.second->getParameterNames();
for (auto const& pair: m_params)
{
if (find(paramNames.begin(), paramNames.end(), pair.first) == paramNames.end())
// LTODO: mismatching parameter name, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(
DocstringParsingError() <<
errinfo_comment("documented parameter \"" + pair.first + "\" not found found in the function")
);
params[pair.first] = pair.second;
}
if (!m_params.empty())
method["params"] = params;

24
libsolidity/Parser.cpp

@ -164,8 +164,17 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
}
nodeFactory.markEndPosition();
expectToken(Token::RBrace);
return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs, enums,
stateVariables, functions, modifiers, events);
return nodeFactory.createNode<ContractDefinition>(
name,
docString,
baseContracts,
structs,
enums,
stateVariables,
functions,
modifiers,
events
);
}
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
@ -247,8 +256,15 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
}
else
returnParameters = createEmptyParameterList();
ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
ASTPointer<Block> block = ASTPointer<Block>();
nodeFactory.markEndPosition();
if (m_scanner->getCurrentToken() != Token::Semicolon)
{
block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
}
else
m_scanner->next(); // just consume the ';'
bool const c_isConstructor = (_contractName && *name == *_contractName);
return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring,
parameters, isDeclaredConst, modifiers,

53
libtestutils/BlockChainLoader.cpp

@ -28,57 +28,6 @@ using namespace dev;
using namespace dev::test;
using namespace dev::eth;
namespace dev
{
namespace test
{
dev::eth::BlockInfo toBlockInfo(Json::Value const& _json);
bytes toGenesisBlock(Json::Value const& _json);
}
}
dev::eth::BlockInfo dev::test::toBlockInfo(Json::Value const& _json)
{
RLPStream rlpStream;
auto size = _json.getMemberNames().size();
rlpStream.appendList(_json["hash"].empty() ? size : (size - 1));
rlpStream << fromHex(_json["parentHash"].asString());
rlpStream << fromHex(_json["uncleHash"].asString());
rlpStream << fromHex(_json["coinbase"].asString());
rlpStream << fromHex(_json["stateRoot"].asString());
rlpStream << fromHex(_json["transactionsTrie"].asString());
rlpStream << fromHex(_json["receiptTrie"].asString());
rlpStream << fromHex(_json["bloom"].asString());
rlpStream << bigint(_json["difficulty"].asString());
rlpStream << bigint(_json["number"].asString());
rlpStream << bigint(_json["gasLimit"].asString());
rlpStream << bigint(_json["gasUsed"].asString());
rlpStream << bigint(_json["timestamp"].asString());
rlpStream << fromHex(_json["extraData"].asString());
rlpStream << fromHex(_json["mixHash"].asString());
rlpStream << fromHex(_json["nonce"].asString());
BlockInfo result;
RLP rlp(rlpStream.out());
result.populateFromHeader(rlp, IgnoreNonce);
return result;
}
bytes dev::test::toGenesisBlock(Json::Value const& _json)
{
BlockInfo bi = toBlockInfo(_json);
RLPStream rlpStream;
bi.streamRLP(rlpStream, WithNonce);
RLPStream fullStream(3);
fullStream.appendRaw(rlpStream.out());
fullStream.appendRaw(RLPEmptyList);
fullStream.appendRaw(RLPEmptyList);
bi.verifyInternals(&fullStream.out());
return fullStream.out();
}
BlockChainLoader::BlockChainLoader(Json::Value const& _json)
{
// load pre state
@ -86,7 +35,7 @@ BlockChainLoader::BlockChainLoader(Json::Value const& _json)
m_state = sl.state();
// load genesisBlock
m_bc.reset(new BlockChain(toGenesisBlock(_json["genesisBlockHeader"]), m_dir.path(), true));
m_bc.reset(new BlockChain(fromHex(_json["genesisRLP"].asString()), m_dir.path(), true));
// load blocks
for (auto const& block: _json["blocks"])

1
mix/CodeModel.cpp

@ -342,6 +342,7 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
r.type = SolidityType::Type::Bytes;
r.size = static_cast<unsigned>(b->getNumBytes());
}
break;
case Type::Category::Contract:
r.type = SolidityType::Type::Address;
break;

44
mix/ContractCallDataEncoder.cpp

@ -80,28 +80,32 @@ unsigned ContractCallDataEncoder::encodeSingleItem(QVariant const& _data, Solidi
if ((src.startsWith("\"") && src.endsWith("\"")) || (src.startsWith("\'") && src.endsWith("\'")))
src = src.remove(src.length() - 1, 1).remove(0, 1);
QRegExp rx("[a-z]+");
if (src.startsWith("0x"))
{
result = fromHex(src.toStdString().substr(2));
if (_type.type != SolidityType::Type::Bytes)
result = padded(result, alignSize);
}
else if (rx.indexIn(src.toLower(), 0) != -1)
{
QByteArray bytesAr = src.toLocal8Bit();
result = bytes(bytesAr.begin(), bytesAr.end());
}
else
{
bigint i(src.toStdString());
result = bytes(alignSize);
toBigEndian((u256)i, result);
try
{
bigint i(src.toStdString());
result = bytes(alignSize);
toBigEndian((u256)i, result);
}
catch (std::exception const& ex)
{
// manage input as a string.
QByteArray bytesAr = src.toLocal8Bit();
result = bytes(bytesAr.begin(), bytesAr.end());
result = paddedRight(result, alignSize);
}
}
unsigned dataSize = _type.dynamicSize ? result.size() : alignSize;
_dest.insert(_dest.end(), result.begin(), result.end());
if (_dest.size() % alignSize != 0)
if ((_dest.size() - 4) % alignSize != 0)
_dest.resize((_dest.size() & ~(alignSize - 1)) + alignSize);
return dataSize;
}
@ -158,7 +162,11 @@ dev::bytes ContractCallDataEncoder::decodeBytes(dev::bytes const& _rawValue)
QString ContractCallDataEncoder::toString(dev::bytes const& _b)
{
return QString::fromStdString(dev::toJS(_b));
QString str;
if (asString(_b, str))
return "\"" + str + "\" " + QString::fromStdString(dev::toJS(_b));
else
return QString::fromStdString(dev::toJS(_b));
}
@ -196,3 +204,17 @@ QStringList ContractCallDataEncoder::decode(QList<QVariableDeclaration*> const&
}
return r;
}
bool ContractCallDataEncoder::asString(dev::bytes const& _b, QString& _str)
{
dev::bytes bunPad = unpadded(_b);
for (unsigned i = 0; i < bunPad.size(); i++)
{
if (bunPad.at(i) < 9 || bunPad.at(i) > 127)
return false;
else
_str += QString::fromStdString(dev::toJS(bunPad.at(i))).replace("0x", "");
}
return true;
}

1
mix/ContractCallDataEncoder.h

@ -66,6 +66,7 @@ private:
dev::bytes encodeBytes(QString const& _str);
dev::bytes decodeBytes(dev::bytes const& _rawValue);
QString toString(dev::bytes const& _b);
bool asString(dev::bytes const& _b, QString& _str);
private:
bytes m_encodedData;

7
mix/qml/TransactionDialog.qml

@ -81,6 +81,7 @@ Window {
loadParameter(params[p]);
}
}
initTypeLoader();
modalTransactionDialog.setX((Screen.width - width) / 2);
modalTransactionDialog.setY((Screen.height - height) / 2);
@ -126,11 +127,15 @@ Window {
}
}
}
initTypeLoader();
}
function initTypeLoader()
{
typeLoader.value = {}
typeLoader.members = []
typeLoader.value = paramValues;
typeLoader.members = paramsModel;
}
function close()

57
test/SolidityNameAndTypeResolution.cpp

@ -359,6 +359,63 @@ BOOST_AUTO_TEST_CASE(comparison_bitop_precedence)
ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed");
}
BOOST_AUTO_TEST_CASE(function_no_implementation)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = "contract test {\n"
" function functionName(bytes32 input) returns (bytes32 out);\n"
"}\n";
ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed");
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->getNodes();
ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[0].get());
BOOST_CHECK(contract);
BOOST_CHECK(!contract->isFullyImplemented());
BOOST_CHECK(!contract->getDefinedFunctions()[0]->isFullyImplemented());
}
BOOST_AUTO_TEST_CASE(abstract_contract)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract base { function foo(); }
contract derived is base { function foo() {} }
)";
ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed");
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->getNodes();
ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[0].get());
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[1].get());
BOOST_CHECK(base);
BOOST_CHECK(!base->isFullyImplemented());
BOOST_CHECK(!base->getDefinedFunctions()[0]->isFullyImplemented());
BOOST_CHECK(derived);
BOOST_CHECK(derived->isFullyImplemented());
BOOST_CHECK(derived->getDefinedFunctions()[0]->isFullyImplemented());
}
BOOST_AUTO_TEST_CASE(create_abstract_contract)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract base { function foo(); }
contract derived {
base b;
function foo() { b = new base();}
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract base { function foo(); }
contract derived is base { function foo() {} }
contract wrong is derived { function foo(); }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(function_canonical_signature)
{
ASTPointer<SourceUnit> sourceUnit;

25
test/SolidityNatspecJSON.cpp

@ -176,7 +176,6 @@ BOOST_AUTO_TEST_CASE(dev_and_user_no_doc)
"}\n";
char const* devNatspec = "{\"methods\":{}}";
char const* userNatspec = "{\"methods\":{}}";
checkNatspec(sourceCode, devNatspec, false);
@ -230,6 +229,18 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params)
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_documenting_nonexistant_param)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter\n"
" /// @param not_existing Documentation for the second parameter\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
BOOST_CHECK_THROW(checkNatspec(sourceCode, "", false), DocstringParsingError);
}
BOOST_AUTO_TEST_CASE(dev_mutiline_param_description)
{
char const* sourceCode = "contract test {\n"
@ -487,17 +498,7 @@ BOOST_AUTO_TEST_CASE(dev_title_at_function_error)
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
" \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\","
" \"methods\":{"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Mul function\"\n"
" }\n"
" }\n"
"}";
BOOST_CHECK_THROW(checkNatspec(sourceCode, natspec, false), DocstringParsingError);
BOOST_CHECK_THROW(checkNatspec(sourceCode, "", false), DocstringParsingError);
}
BOOST_AUTO_TEST_CASE(natspec_notice_without_tag)

8
test/SolidityParser.cpp

@ -108,6 +108,14 @@ BOOST_AUTO_TEST_CASE(single_function_param)
ETH_TEST_CHECK_NO_THROW(parseText(text), "Parsing failed.");
}
BOOST_AUTO_TEST_CASE(function_no_body)
{
char const* text = "contract test {\n"
" function functionName(bytes32 input) returns (bytes32 out);\n"
"}\n";
ETH_TEST_CHECK_NO_THROW(parseText(text), "Parsing failed.");
}
BOOST_AUTO_TEST_CASE(missing_parameter_name_in_named_args)
{
char const* text = "contract test {\n"

Loading…
Cancel
Save