From 2a48091ff2cc98cda310912cf3a814f39d725180 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Sat, 28 Feb 2015 16:10:19 +0800 Subject: [PATCH 01/28] add two parsing test cases related to overloaded functions --- test/SolidityParser.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index 88b86e638..414ac5256 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -126,6 +126,31 @@ BOOST_AUTO_TEST_CASE(missing_argument_in_named_args) BOOST_CHECK_THROW(parseText(text), ParserError); } +BOOST_AUTO_TEST_CASE(two_exact_functions) +{ + char const* text = R"( + contract test { + function fun(uint a) returns(uint r) { return a; } + function fun(uint a) returns(uint r) { return a; } + } + )"; + // with support of overloaded functions, during parsing, + // we can't determine whether they match exactly, however + // it will throw DeclarationError in following stage. + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(overloaded_functions) +{ + char const* text = R"( + contract test { + function fun(uint a) returns(uint r) { return a; } + function fun(uint a, uint b) returns(uint r) { return a + b; } + } + )"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + BOOST_AUTO_TEST_CASE(function_natspec_documentation) { ASTPointer contract; From defd6cfa21b053d2cf24695ffae1c9c886fe57e6 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Sat, 28 Feb 2015 16:11:59 +0800 Subject: [PATCH 02/28] mark an identifier as callable if its next token is '(' --- libsolidity/AST.h | 7 +++++-- libsolidity/Parser.cpp | 7 ++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index eab53153f..b21e505e9 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -1134,8 +1134,8 @@ public: class Identifier: public PrimaryExpression { public: - Identifier(SourceLocation const& _location, ASTPointer const& _name): - PrimaryExpression(_location), m_name(_name) {} + Identifier(SourceLocation const& _location, ASTPointer const& _name, bool _isCallable): + PrimaryExpression(_location), m_name(_name), m_isCallable(_isCallable) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; @@ -1151,6 +1151,8 @@ public: Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; } ContractDefinition const* getCurrentContract() const { return m_currentContract; } + bool isCallable() const { return m_isCallable; } + private: ASTPointer m_name; @@ -1159,6 +1161,7 @@ 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; + bool m_isCallable = false; }; /** diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 44d111591..cecf772da 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -837,9 +837,14 @@ ASTPointer Parser::parsePrimaryExpression() expression = nodeFactory.createNode(token, getLiteralAndAdvance()); break; case Token::Identifier: + { nodeFactory.markEndPosition(); - expression = nodeFactory.createNode(getLiteralAndAdvance()); + // if the next token is '(', this identifier looks like function call, + // it could be a contract, event etc. + bool isCallable = m_scanner->peekNextToken() == Token::LParen; + expression = nodeFactory.createNode(getLiteralAndAdvance(), isCallable); break; + } case Token::LParen: { m_scanner->next(); From 522f4288490d2955e47a3211b561d4a12fe58414 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Sat, 28 Feb 2015 16:13:11 +0800 Subject: [PATCH 03/28] add end to end test cases related to overloaded functions --- test/SolidityEndToEndTest.cpp | 43 ++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index ae2417052..da5b29ad4 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -3170,8 +3170,49 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base_with_gap) BOOST_CHECK(callContractFunction("m_i()") == encodeArgs(4)); } +BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_first) +{ + char const* sourceCode = R"( + contract test { + function f(uint k) returns(uint d) { return k; } + function f(uint a, uint b) returns(uint d) { return a + b; } + function g() returns(uint d) { return f(3); } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(3)); +} -BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_second) +{ + char const* sourceCode = R"( + contract test { + function f(uint a, uint b) returns(uint d) { return a + b; } + function f(uint k) returns(uint d) { return k; } + function g() returns(uint d) { return f(3, 7); } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(10)); +} + +BOOST_AUTO_TEST_CASE(overloaded_function_call_with_if_else) +{ + char const* sourceCode = R"( + contract test { + function f(uint a, uint b) returns(uint d) { return a + b; } + function f(uint k) returns(uint d) { return k; } + function g(bool flag) returns(uint d) { + if (flag) + return f(3); + else + return f(3, 7); + } + } + )"; + BOOST_CHECK(callContractFunction("g(bool)", true) == encodeArgs(3)); + BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs(10)); +} } } From f2da0e249caacba2ed407bfc957d568ad5d82752 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Sat, 28 Feb 2015 16:15:27 +0800 Subject: [PATCH 04/28] enhance DeclarationContainer to use `std::set` so that it can handle overloaded function names --- libsolidity/DeclarationContainer.cpp | 29 +++++++++++++++++++++++----- libsolidity/DeclarationContainer.h | 6 +++--- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/libsolidity/DeclarationContainer.cpp b/libsolidity/DeclarationContainer.cpp index 2594d4281..226b9d680 100644 --- a/libsolidity/DeclarationContainer.cpp +++ b/libsolidity/DeclarationContainer.cpp @@ -22,6 +22,7 @@ #include #include +#include namespace dev { @@ -34,17 +35,35 @@ bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, if (name.empty()) return true; - if (!_update && (m_declarations.count(name) || m_invisibleDeclarations.count(name))) - return false; + if (!_update) + { + if (dynamic_cast(&_declaration)) + { + // other declarations must be FunctionDefinition, otherwise clash with other declarations. + for (auto&& declaration: m_declarations[_declaration.getName()]) + if (dynamic_cast(declaration) == nullptr) + return false; + } + else if (m_declarations.count(_declaration.getName()) != 0) + return false; + } + else + { + // update declaration + solAssert(dynamic_cast(&_declaration) == nullptr, "cannot be FunctionDefinition"); + + m_declarations[_declaration.getName()].clear(); + } if (_invisible) m_invisibleDeclarations.insert(name); else - m_declarations[name] = &_declaration; + m_declarations[_declaration.getName()].insert(&_declaration); + return true; } -Declaration const* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const +std::set DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const { solAssert(!_name.empty(), "Attempt to resolve empty name."); auto result = m_declarations.find(_name); @@ -52,7 +71,7 @@ Declaration const* DeclarationContainer::resolveName(ASTString const& _name, boo return result->second; if (_recursive && m_enclosingContainer) return m_enclosingContainer->resolveName(_name, true); - return nullptr; + return std::set({}); } } diff --git a/libsolidity/DeclarationContainer.h b/libsolidity/DeclarationContainer.h index f70881f5b..42784ec2a 100644 --- a/libsolidity/DeclarationContainer.h +++ b/libsolidity/DeclarationContainer.h @@ -48,14 +48,14 @@ 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); - Declaration const* resolveName(ASTString const& _name, bool _recursive = false) const; + std::set resolveName(ASTString const& _name, bool _recursive = false) const; Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; } - std::map const& getDeclarations() const { return m_declarations; } + std::map> const& getDeclarations() const { return m_declarations; } private: Declaration const* m_enclosingDeclaration; DeclarationContainer const* m_enclosingContainer; - std::map m_declarations; + std::map> m_declarations; std::set m_invisibleDeclarations; }; From 0f2f0195bec48f771569d32e60812a779e4dba5d Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Sat, 28 Feb 2015 16:20:11 +0800 Subject: [PATCH 05/28] fix test code due to API changes in DeclarationContainer --- test/SolidityExpressionCompiler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/SolidityExpressionCompiler.cpp b/test/SolidityExpressionCompiler.cpp index 7034085ef..fa01ce1c9 100644 --- a/test/SolidityExpressionCompiler.cpp +++ b/test/SolidityExpressionCompiler.cpp @@ -79,7 +79,9 @@ Declaration const& resolveDeclaration( // bracers are required, cause msvc couldnt handle this macro in for statement for (string const& namePart: _namespacedName) { - BOOST_REQUIRE(declaration = _resolver.resolveName(namePart, declaration)); + auto declarations = _resolver.resolveName(namePart, declaration); + BOOST_REQUIRE(!declarations.empty()); + BOOST_REQUIRE(declaration = *declarations.begin()); } BOOST_REQUIRE(declaration); return *declaration; From 774d677aaca7bd180095669a4e937134e2b79219 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Sat, 28 Feb 2015 16:29:32 +0800 Subject: [PATCH 06/28] add test cases for functions in derived overload functions in base --- test/SolidityEndToEndTest.cpp | 24 ++++++++++++++++++++++++ test/SolidityNameAndTypeResolution.cpp | 8 ++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index da5b29ad4..fcd59e5d1 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -3214,6 +3214,30 @@ BOOST_AUTO_TEST_CASE(overloaded_function_call_with_if_else) BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs(10)); } +BOOST_AUTO_TEST_CASE(derived_overload_base_function_direct) +{ + char const* sourceCode = R"( + contract B { function f() returns(uint) { return 10; } } + contract C is B { function f(uint i) returns(uint) { return 2 * i; } } + )"; + compileAndRun(sourceCode, "C"); + BOOST_CHECK(callContractFunction("f(uint)", 1) == encodeArgs(2)); +} + +BOOST_AUTO_TEST_CASE(derived_overload_base_function_indirect) +{ + char const* sourceCode = R"( + contract A { function f(uint a) returns(uint) { return 2 * a; } } + contract B { function f() returns(uint) { return 10; } } + contract C is A, B { } + )"; + compileAndRun(sourceCode, "C"); + BOOST_CHECK(callContractFunction("f(uint)", 1) == encodeArgs(2)); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(10)); +} + +BOOST_AUTO_TEST_SUITE_END() + } } } // end namespaces diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index c3a4a3377..d2ae3f739 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -424,23 +424,23 @@ BOOST_AUTO_TEST_CASE(cyclic_inheritance) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } -BOOST_AUTO_TEST_CASE(illegal_override_direct) +BOOST_AUTO_TEST_CASE(legal_override_direct) { char const* text = R"( contract B { function f() {} } contract C is B { function f(uint i) {} } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } -BOOST_AUTO_TEST_CASE(illegal_override_indirect) +BOOST_AUTO_TEST_CASE(legal_override_indirect) { char const* text = R"( contract A { function f(uint a) {} } contract B { function f() {} } contract C is A, B { } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } BOOST_AUTO_TEST_CASE(illegal_override_visibility) From 29ec4453b5429051932277c0c5bb25323893c93e Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Sun, 1 Mar 2015 11:33:38 +0800 Subject: [PATCH 07/28] add two overloaded function test cases --- test/SolidityEndToEndTest.cpp | 20 +++++++++++++------- test/SolidityNameAndTypeResolution.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index fcd59e5d1..2beca7848 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -3218,10 +3218,13 @@ BOOST_AUTO_TEST_CASE(derived_overload_base_function_direct) { char const* sourceCode = R"( contract B { function f() returns(uint) { return 10; } } - contract C is B { function f(uint i) returns(uint) { return 2 * i; } } + contract C is B { + function f(uint i) returns(uint) { return 2 * i; } + function g() returns(uint) { return f(1); } + } )"; - compileAndRun(sourceCode, "C"); - BOOST_CHECK(callContractFunction("f(uint)", 1) == encodeArgs(2)); + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(2)); } BOOST_AUTO_TEST_CASE(derived_overload_base_function_indirect) @@ -3229,11 +3232,14 @@ BOOST_AUTO_TEST_CASE(derived_overload_base_function_indirect) char const* sourceCode = R"( contract A { function f(uint a) returns(uint) { return 2 * a; } } contract B { function f() returns(uint) { return 10; } } - contract C is A, B { } + contract C is A, B { + function g() returns(uint) { return f(); } + function h() returns(uint) { return f(1); } + } )"; - compileAndRun(sourceCode, "C"); - BOOST_CHECK(callContractFunction("f(uint)", 1) == encodeArgs(2)); - BOOST_CHECK(callContractFunction("f()") == encodeArgs(10)); + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(10)); + BOOST_CHECK(callContractFunction("h()") == encodeArgs(2)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index d2ae3f739..d764019ef 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -1287,6 +1287,31 @@ BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_string) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } +BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve) +{ + char const* sourceCode = R"( + contract test { + function f() returns(uint) { return 1; } + function f(uint a) returns(uint) { return a; } + function g() returns(uint) { return f(3, 5); } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function) +{ + // literal 1 can be both converted to uint8 and uint8, so it's ambiguous. + char const* sourceCode = R"( + contract test { + function f(uint8 a) returns(uint) { return a; } + function f(uint a) returns(uint) { return 2*a; } + function g() returns(uint) { return f(1); } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } From fcc31b7a7fe4405f22dbb937fbcb5e12ecfeae6a Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Sun, 1 Mar 2015 11:34:12 +0800 Subject: [PATCH 08/28] the contract interfaces should take overloaded function into consideration TODO: --- libsolidity/CompilerContext.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index ee8d3e00c..f787db7fc 100644 --- a/libsolidity/CompilerContext.cpp +++ b/libsolidity/CompilerContext.cpp @@ -106,8 +106,12 @@ eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefiniti solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); for (ContractDefinition const* contract: m_inheritanceHierarchy) for (ASTPointer const& function: contract->getDefinedFunctions()) - if (!function->isConstructor() && function->getName() == _function.getName()) + { + if (!function->isConstructor() && + dynamic_cast(*function->getType()).getCanonicalSignature() == + dynamic_cast(*_function.getType()).getCanonicalSignature()) return getFunctionEntryLabel(*function); + } solAssert(false, "Virtual function " + _function.getName() + " not found."); return m_asm.newTag(); // not reached } @@ -117,7 +121,7 @@ eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(string const& _nam auto it = getSuperContract(_base); for (; it != m_inheritanceHierarchy.end(); ++it) for (ASTPointer const& function: (*it)->getDefinedFunctions()) - if (!function->isConstructor() && function->getName() == _name) + if (!function->isConstructor() && function->getName() == _name) // TODO: add a test case for this! return getFunctionEntryLabel(*function); solAssert(false, "Super function " + _name + " not found."); return m_asm.newTag(); // not reached From 17f79a5c6ee1f769a53e333eb2f6ac175e22eda7 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Sun, 1 Mar 2015 11:34:39 +0800 Subject: [PATCH 09/28] implement overload resolution --- libsolidity/AST.cpp | 159 ++++++++++++++++++++++++++-- libsolidity/AST.h | 15 ++- libsolidity/CompilerContext.cpp | 4 +- libsolidity/ExpressionCompiler.cpp | 6 +- libsolidity/NameAndTypeResolver.cpp | 39 ++++--- libsolidity/NameAndTypeResolver.h | 4 +- libsolidity/Parser.cpp | 7 +- libsolidity/Types.h | 15 ++- 8 files changed, 210 insertions(+), 39 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 79b755e97..428c82f21 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -76,6 +76,15 @@ void ContractDefinition::checkTypeRequirements() for (ASTPointer const& function: getDefinedFunctions()) function->checkTypeRequirements(); + // check for duplicate declaration + set functions; + for (ASTPointer const& function: getDefinedFunctions()) + { + string signature = function->getCanonicalSignature(); + if (functions.count(signature)) + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Duplicate functions are not allowed.")); + functions.insert(signature); + } for (ASTPointer const& variable: m_stateVariables) variable->checkTypeRequirements(); @@ -129,6 +138,7 @@ void ContractDefinition::checkIllegalOverrides() const // TODO unify this at a later point. for this we need to put the constness and the access specifier // into the types map functions; + set functionNames; map modifiers; // We search from derived to base, so the stored item causes the error. @@ -141,7 +151,8 @@ void ContractDefinition::checkIllegalOverrides() const string const& name = function->getName(); if (modifiers.count(name)) BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier.")); - FunctionDefinition const*& override = functions[name]; + FunctionDefinition const*& override = functions[function->getCanonicalSignature()]; + functionNames.insert(name); if (!override) override = function.get(); else if (override->getVisibility() != function->getVisibility() || @@ -152,13 +163,13 @@ void ContractDefinition::checkIllegalOverrides() const for (ASTPointer const& modifier: contract->getFunctionModifiers()) { string const& name = modifier->getName(); - if (functions.count(name)) - BOOST_THROW_EXCEPTION(functions[name]->createTypeError("Override changes modifier to function.")); ModifierDefinition const*& override = modifiers[name]; if (!override) override = modifier.get(); else if (ModifierType(*override) != ModifierType(*modifier)) BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature.")); + if (functionNames.count(name)) + BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier to function.")); } } } @@ -185,16 +196,21 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn if (!m_interfaceFunctionList) { set functionsSeen; + set signaturesSeen; m_interfaceFunctionList.reset(new vector, FunctionTypePointer>>()); for (ContractDefinition const* contract: getLinearizedBaseContracts()) { for (ASTPointer const& f: contract->getDefinedFunctions()) - if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && functionsSeen.count(f->getName()) == 0) + { + string functionSignature = f->getCanonicalSignature(); + if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && signaturesSeen.count(functionSignature) == 0) { functionsSeen.insert(f->getName()); - FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); + signaturesSeen.insert(functionSignature); + FixedHash<4> hash(dev::sha3(functionSignature)); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*f, false))); } + } for (ASTPointer const& v: contract->getStateVariables()) if (v->isPublic() && functionsSeen.count(v->getName()) == 0) @@ -467,7 +483,43 @@ void Return::checkTypeRequirements() void VariableDeclarationStatement::checkTypeRequirements() { +<<<<<<< HEAD m_variable->checkTypeRequirements(); +======= + // Variables can be declared without type (with "var"), in which case the first assignment + // sets the type. + // Note that assignments before the first declaration are legal because of the special scoping + // rules inherited from JavaScript. + if (m_variable->getValue()) + { + if (m_variable->getType()) + { + std::cout << "getType() ok" << std::endl; + m_variable->getValue()->expectType(*m_variable->getType()); + } + else + { + // no type declared and no previous assignment, infer the type + std::cout << "here's where called...." << std::endl; + Identifier* identifier = dynamic_cast(m_variable->getValue().get()); + if (identifier) + identifier->checkTypeRequirementsFromVariableDeclaration(); + else + m_variable->getValue()->checkTypeRequirements(); + TypePointer type = m_variable->getValue()->getType(); + if (type->getCategory() == Type::Category::IntegerConstant) + { + auto intType = dynamic_pointer_cast(type)->getIntegerType(); + if (!intType) + BOOST_THROW_EXCEPTION(m_variable->getValue()->createTypeError("Invalid integer constant " + type->toString())); + type = intType; + } + else if (type->getCategory() == Type::Category::Void) + BOOST_THROW_EXCEPTION(m_variable->createTypeError("var cannot be void type")); + m_variable->setType(type); + } + } +>>>>>>> implement overload resolution } void Assignment::checkTypeRequirements() @@ -544,10 +596,16 @@ void BinaryOperation::checkTypeRequirements() void FunctionCall::checkTypeRequirements() { - m_expression->checkTypeRequirements(); + // we need to check arguments' type first as their info will be used by m_express(Identifier). for (ASTPointer const& argument: m_arguments) argument->checkTypeRequirements(); + auto identifier = dynamic_cast(m_expression.get()); + if (identifier) + identifier->checkTypeRequirementsWithFunctionCall(*this); + else + m_expression->checkTypeRequirements(); + Type const* expressionType = m_expression->getType().get(); if (isTypeConversion()) { @@ -617,6 +675,19 @@ void FunctionCall::checkTypeRequirements() else m_type = functionType->getReturnParameterTypes().front(); } + else if (OverloadedFunctionType const* overloadedTypes = dynamic_cast(expressionType)) + { + // this only applies to "x(3)" where x is assigned by "var x = f;" where f is an overloaded functions. + overloadedTypes->m_identifier->overloadResolution(*this); + FunctionType const* functionType = dynamic_cast(overloadedTypes->m_identifier->getType().get()); + + // @todo actually the return type should be an anonymous struct, + // but we change it to the type of the first return value until we have structs + if (functionType->getReturnParameterTypes().empty()) + m_type = make_shared(); + else + m_type = functionType->getReturnParameterTypes().front(); + } else BOOST_THROW_EXCEPTION(createTypeError("Type is not callable.")); } @@ -709,16 +780,92 @@ void IndexAccess::checkTypeRequirements() } } +void Identifier::checkTypeRequirementsWithFunctionCall(FunctionCall const& _functionCall) +{ + solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved."); + + if (!m_referencedDeclaration) + overloadResolution(_functionCall); + + checkTypeRequirements(); +} + +void Identifier::checkTypeRequirementsFromVariableDeclaration() +{ + solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved."); + + if (!m_referencedDeclaration) + m_type = make_shared(m_overloadedDeclarations, this); + else + checkTypeRequirements(); + + m_isLValue = true; +} + void Identifier::checkTypeRequirements() { + // var x = f; TODO! solAssert(m_referencedDeclaration, "Identifier not resolved."); m_isLValue = m_referencedDeclaration->isLValue(); + if (m_isLValue) + std::cout << "Identifier: " << string(getName()) << " -> true" << std::endl; + else + std::cout << "Identifier: " << string(getName()) << " -> true" << std::endl; m_type = m_referencedDeclaration->getType(m_currentContract); if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined.")); } +void Identifier::overloadResolution(FunctionCall const& _functionCall) +{ + solAssert(m_overloadedDeclarations.size() > 1, "FunctionIdentifier not resolved."); + solAssert(!m_referencedDeclaration, "Referenced declaration should be null before overload resolution."); + + bool resolved = false; + + std::vector> arguments = _functionCall.getArguments(); + std::vector> const& argumentNames = _functionCall.getNames(); + + if (argumentNames.empty()) + { + // positional arguments + std::vector possibles; + for (Declaration const* declaration: m_overloadedDeclarations) + { + TypePointer const& function = declaration->getType(); + auto const& functionType = dynamic_cast(*function); + TypePointers const& parameterTypes = functionType.getParameterTypes(); + + if (functionType.takesArbitraryParameters() || + (arguments.size() == parameterTypes.size() && + std::equal(arguments.cbegin(), arguments.cend(), parameterTypes.cbegin(), + [](ASTPointer const& argument, TypePointer const& parameterType) + { + return argument->getType()->isImplicitlyConvertibleTo(*parameterType); + }))) + possibles.push_back(declaration); + } + std::cout << "possibles: " << possibles.size() << std::endl; + if (possibles.empty()) + BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier")); + else if (std::none_of(possibles.cbegin() + 1, possibles.cend(), + [&possibles](Declaration const* declaration) + { + return declaration->getScope() == possibles.front()->getScope(); + })) + setReferencedDeclaration(*possibles.front()); + else + BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier")); + } + else + { + // named arguments + // TODO: don't support right now + // BOOST_THROW_EXCEPTION(createTypeError("Named arguments with overloaded functions are not supported yet.")); + } +} + void ElementaryTypeNameExpression::checkTypeRequirements() { m_type = make_shared(Type::fromElementaryTypeName(m_typeToken)); diff --git a/libsolidity/AST.h b/libsolidity/AST.h index b21e505e9..fa1d4a92e 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -1134,8 +1134,8 @@ public: class Identifier: public PrimaryExpression { public: - Identifier(SourceLocation const& _location, ASTPointer const& _name, bool _isCallable): - PrimaryExpression(_location), m_name(_name), m_isCallable(_isCallable) {} + Identifier(SourceLocation const& _location, ASTPointer const& _name): + PrimaryExpression(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; @@ -1151,9 +1151,15 @@ public: Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; } ContractDefinition const* getCurrentContract() const { return m_currentContract; } - bool isCallable() const { return m_isCallable; } + void setOverloadedDeclarations(std::set const& _declarations) { m_overloadedDeclarations = _declarations; } + std::set getOverloadedDeclarations() const { return m_overloadedDeclarations; } + void checkTypeRequirementsWithFunctionCall(FunctionCall const& _functionCall); + void checkTypeRequirementsFromVariableDeclaration(); + + void overloadResolution(FunctionCall const& _functionCall); private: + ASTPointer m_name; /// Declaration the name refers to. @@ -1161,7 +1167,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; - bool m_isCallable = false; + /// A set of overloaded declarations, right now only FunctionDefinition has overloaded declarations. + std::set m_overloadedDeclarations; }; /** diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index f787db7fc..b12e01923 100644 --- a/libsolidity/CompilerContext.cpp +++ b/libsolidity/CompilerContext.cpp @@ -108,8 +108,8 @@ eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefiniti for (ASTPointer const& function: contract->getDefinedFunctions()) { if (!function->isConstructor() && - dynamic_cast(*function->getType()).getCanonicalSignature() == - dynamic_cast(*_function.getType()).getCanonicalSignature()) + dynamic_cast(*function->getType(contract)).getCanonicalSignature() == + dynamic_cast(*_function.getType(contract)).getCanonicalSignature()) return getFunctionEntryLabel(*function); } solAssert(false, "Virtual function " + _function.getName() + " not found."); diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 3d7a25311..5e5442ba3 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -822,7 +822,11 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) void ExpressionCompiler::endVisit(Identifier const& _identifier) { Declaration const* declaration = _identifier.getReferencedDeclaration(); - if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) + if (declaration == nullptr) + { + // no-op + } + else if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) { if (magicVar->getType()->getCategory() == Type::Category::Contract) // "this" or "super" diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index f6ee2f1d0..c787ae6b0 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -90,15 +90,15 @@ void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) solAssert(_declaration.getScope() == nullptr, "Updated declaration outside global scope."); } -Declaration const* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const +std::set NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const { auto iterator = m_scopes.find(_scope); if (iterator == end(m_scopes)) - return nullptr; + return std::set({}); return iterator->second.resolveName(_name, false); } -Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) +std::set NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) { return m_currentScope->resolveName(_name, _recursive); } @@ -108,13 +108,11 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base) auto iterator = m_scopes.find(&_base); solAssert(iterator != end(m_scopes), ""); for (auto const& nameAndDeclaration: iterator->second.getDeclarations()) - { - Declaration 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->getName() != _base.getName() && - declaration->isVisibleInDerivedContracts()) - m_currentScope->registerDeclaration(*declaration); - } + 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->getName() != _base.getName() && + declaration->isVisibleInDerivedContracts()) + m_currentScope->registerDeclaration(*declaration); } void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) const @@ -361,24 +359,31 @@ bool ReferencesResolver::visit(Mapping&) bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) { - Declaration const* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); - if (!declaration) + auto declarations = m_resolver.getNameFromCurrentScope(_typeName.getName()); + if (declarations.empty()) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation()) << errinfo_comment("Undeclared identifier.")); - _typeName.setReferencedDeclaration(*declaration); + else if (declarations.size() > 1) + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation()) + << errinfo_comment("Duplicate identifier.")); + else + _typeName.setReferencedDeclaration(**declarations.begin()); return false; } bool ReferencesResolver::visit(Identifier& _identifier) { - Declaration const* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); - if (!declaration) + auto declarations = m_resolver.getNameFromCurrentScope(_identifier.getName()); + if (declarations.empty()) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation()) << errinfo_comment("Undeclared identifier.")); - _identifier.setReferencedDeclaration(*declaration, m_currentContract); + else if (declarations.size() == 1) + _identifier.setReferencedDeclaration(**declarations.begin(), m_currentContract); + else + // Duplicate declaration will be checked in checkTypeRequirements() + _identifier.setOverloadedDeclarations(declarations); return false; } - } } diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 63b8ab637..828776179 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -56,11 +56,11 @@ 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. - Declaration const* resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const; + std::set 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. - Declaration const* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); + std::set getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); private: void reset(); diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index cecf772da..44d111591 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -837,14 +837,9 @@ ASTPointer Parser::parsePrimaryExpression() expression = nodeFactory.createNode(token, getLiteralAndAdvance()); break; case Token::Identifier: - { nodeFactory.markEndPosition(); - // if the next token is '(', this identifier looks like function call, - // it could be a contract, event etc. - bool isCallable = m_scanner->peekNextToken() == Token::LParen; - expression = nodeFactory.createNode(getLiteralAndAdvance(), isCallable); + expression = nodeFactory.createNode(getLiteralAndAdvance()); break; - } case Token::LParen: { m_scanner->next(); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 6cef8d64a..5dd742ada 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -77,7 +77,7 @@ public: enum class Category { Integer, IntegerConstant, Bool, Real, Array, - String, Contract, Struct, Function, Enum, + String, Contract, Struct, Function, OverloadedFunctions, Enum, Mapping, Void, TypeType, Modifier, Magic }; @@ -524,6 +524,19 @@ private: Declaration const* m_declaration = nullptr; }; +class OverloadedFunctionType: public Type +{ +public: + explicit OverloadedFunctionType(std::set const& _overloadedDeclarations, Identifier* _identifier): + m_overloadedDeclarations(_overloadedDeclarations), m_identifier(_identifier) {} + virtual Category getCategory() const override { return Category::OverloadedFunctions; } + virtual std::string toString() const override { return "OverloadedFunctions"; } + +// private: + std::set m_overloadedDeclarations; + Identifier * m_identifier; +}; + /** * The type of a mapping, there is one distinct type per key/value type pair. */ From b441305234bbc7a2984791964fb3a538a2854dd5 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Thu, 5 Mar 2015 21:32:48 -0600 Subject: [PATCH 10/28] remove debug stuff --- libsolidity/AST.cpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 428c82f21..2aa57d7e3 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -493,14 +493,10 @@ void VariableDeclarationStatement::checkTypeRequirements() if (m_variable->getValue()) { if (m_variable->getType()) - { - std::cout << "getType() ok" << std::endl; m_variable->getValue()->expectType(*m_variable->getType()); - } else { // no type declared and no previous assignment, infer the type - std::cout << "here's where called...." << std::endl; Identifier* identifier = dynamic_cast(m_variable->getValue().get()); if (identifier) identifier->checkTypeRequirementsFromVariableDeclaration(); @@ -804,14 +800,9 @@ void Identifier::checkTypeRequirementsFromVariableDeclaration() void Identifier::checkTypeRequirements() { - // var x = f; TODO! solAssert(m_referencedDeclaration, "Identifier not resolved."); m_isLValue = m_referencedDeclaration->isLValue(); - if (m_isLValue) - std::cout << "Identifier: " << string(getName()) << " -> true" << std::endl; - else - std::cout << "Identifier: " << string(getName()) << " -> true" << std::endl; m_type = m_referencedDeclaration->getType(m_currentContract); if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined.")); @@ -846,7 +837,6 @@ void Identifier::overloadResolution(FunctionCall const& _functionCall) }))) possibles.push_back(declaration); } - std::cout << "possibles: " << possibles.size() << std::endl; if (possibles.empty()) BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier")); else if (std::none_of(possibles.cbegin() + 1, possibles.cend(), @@ -859,11 +849,9 @@ void Identifier::overloadResolution(FunctionCall const& _functionCall) BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier")); } else - { // named arguments // TODO: don't support right now - // BOOST_THROW_EXCEPTION(createTypeError("Named arguments with overloaded functions are not supported yet.")); - } + BOOST_THROW_EXCEPTION(createTypeError("Named arguments with overloaded functions are not supported yet.")); } void ElementaryTypeNameExpression::checkTypeRequirements() From eeaf97ae7f408261eac44a6085742451d30b1ac1 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Thu, 5 Mar 2015 21:37:51 -0600 Subject: [PATCH 11/28] add another var x = f; overloaded function test case --- test/SolidityEndToEndTest.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 2beca7848..dd34627ac 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -3210,6 +3210,27 @@ BOOST_AUTO_TEST_CASE(overloaded_function_call_with_if_else) } } )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("g(bool)", true) == encodeArgs(3)); + BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs(10)); +} + +BOOST_AUTO_TEST_CASE(overloaded_function_with_var) +{ + char const* sourceCode = R"( + contract test { + function f(uint k) returns(uint d) { return k; } + function f(uint a, uint b) returns(uint d) { return a + b; } + function g(bool flag) returns(uint d) { + var x = f; + if (flag) + return x(3); + else + return x(3, 7); + } + } + )"; + compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("g(bool)", true) == encodeArgs(3)); BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs(10)); } From aa68913dd2325932a4ed35044987f590e63b1ab8 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Thu, 5 Mar 2015 23:02:35 -0600 Subject: [PATCH 12/28] make it work for var x = f; --- libsolidity/AST.cpp | 29 ++++++++++++++++++----------- libsolidity/AST.h | 2 +- libsolidity/ExpressionCompiler.cpp | 13 ++++++++----- libsolidity/Types.h | 9 +++++---- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 2aa57d7e3..2fd60f7b0 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -674,15 +674,23 @@ void FunctionCall::checkTypeRequirements() else if (OverloadedFunctionType const* overloadedTypes = dynamic_cast(expressionType)) { // this only applies to "x(3)" where x is assigned by "var x = f;" where f is an overloaded functions. - overloadedTypes->m_identifier->overloadResolution(*this); - FunctionType const* functionType = dynamic_cast(overloadedTypes->m_identifier->getType().get()); + auto identifier = dynamic_cast(m_expression.get()); + solAssert(identifier, "only applies to 'var x = f;'"); + + Declaration const* function = overloadedTypes->getIdentifier()->overloadResolution(*this); + if (!function) + BOOST_THROW_EXCEPTION(createTypeError("Can't resolve declarations")); + + identifier->setReferencedDeclaration(*function); + identifier->checkTypeRequirements(); + + TypePointer type = identifier->getType(); + FunctionType const* functionType = dynamic_cast(type.get()); - // @todo actually the return type should be an anonymous struct, - // but we change it to the type of the first return value until we have structs if (functionType->getReturnParameterTypes().empty()) m_type = make_shared(); else - m_type = functionType->getReturnParameterTypes().front(); + m_type = functionType->getReturnParameterTypes().front(); } else BOOST_THROW_EXCEPTION(createTypeError("Type is not callable.")); @@ -781,7 +789,7 @@ void Identifier::checkTypeRequirementsWithFunctionCall(FunctionCall const& _func solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved."); if (!m_referencedDeclaration) - overloadResolution(_functionCall); + setReferencedDeclaration(*overloadResolution(_functionCall)); checkTypeRequirements(); } @@ -791,7 +799,7 @@ void Identifier::checkTypeRequirementsFromVariableDeclaration() solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved."); if (!m_referencedDeclaration) - m_type = make_shared(m_overloadedDeclarations, this); + m_type = make_shared(this); else checkTypeRequirements(); @@ -808,13 +816,11 @@ void Identifier::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined.")); } -void Identifier::overloadResolution(FunctionCall const& _functionCall) +Declaration const* Identifier::overloadResolution(FunctionCall const& _functionCall) { solAssert(m_overloadedDeclarations.size() > 1, "FunctionIdentifier not resolved."); solAssert(!m_referencedDeclaration, "Referenced declaration should be null before overload resolution."); - bool resolved = false; - std::vector> arguments = _functionCall.getArguments(); std::vector> const& argumentNames = _functionCall.getNames(); @@ -844,7 +850,7 @@ void Identifier::overloadResolution(FunctionCall const& _functionCall) { return declaration->getScope() == possibles.front()->getScope(); })) - setReferencedDeclaration(*possibles.front()); + return possibles.front(); else BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier")); } @@ -852,6 +858,7 @@ void Identifier::overloadResolution(FunctionCall const& _functionCall) // named arguments // TODO: don't support right now BOOST_THROW_EXCEPTION(createTypeError("Named arguments with overloaded functions are not supported yet.")); + return nullptr; } void ElementaryTypeNameExpression::checkTypeRequirements() diff --git a/libsolidity/AST.h b/libsolidity/AST.h index fa1d4a92e..6cc7f742f 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -1157,7 +1157,7 @@ public: void checkTypeRequirementsWithFunctionCall(FunctionCall const& _functionCall); void checkTypeRequirementsFromVariableDeclaration(); - void overloadResolution(FunctionCall const& _functionCall); + Declaration const* overloadResolution(FunctionCall const& _functionCall); private: ASTPointer m_name; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 5e5442ba3..4be461b25 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -822,11 +822,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) void ExpressionCompiler::endVisit(Identifier const& _identifier) { Declaration const* declaration = _identifier.getReferencedDeclaration(); - if (declaration == nullptr) - { - // no-op - } - else if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) + if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) { if (magicVar->getType()->getCategory() == Type::Category::Contract) // "this" or "super" @@ -849,6 +845,13 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) { // no-op } + else if (declaration == nullptr && _identifier.getOverloadedDeclarations().size() > 1) + { + // var x = f; + declaration = *_identifier.getOverloadedDeclarations().begin(); + FunctionDefinition const* functionDef = dynamic_cast(declaration); + m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag(); + } else { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 5dd742ada..b19f75406 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -527,13 +527,14 @@ private: class OverloadedFunctionType: public Type { public: - explicit OverloadedFunctionType(std::set const& _overloadedDeclarations, Identifier* _identifier): - m_overloadedDeclarations(_overloadedDeclarations), m_identifier(_identifier) {} + explicit OverloadedFunctionType(Identifier* _identifier): m_identifier(_identifier) {} + virtual Category getCategory() const override { return Category::OverloadedFunctions; } virtual std::string toString() const override { return "OverloadedFunctions"; } -// private: - std::set m_overloadedDeclarations; + Identifier* getIdentifier() const { return m_identifier; } + +private: Identifier * m_identifier; }; From 3eeac6a21b16837852b4ebb7c09169054878a5cb Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Sun, 8 Mar 2015 09:42:29 -0500 Subject: [PATCH 13/28] add the location of a duplicated function --- libsolidity/AST.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 2fd60f7b0..9d19b30c9 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -82,7 +82,8 @@ void ContractDefinition::checkTypeRequirements() { string signature = function->getCanonicalSignature(); if (functions.count(signature)) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Duplicate functions are not allowed.")); + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(function->getLocation()) + << errinfo_comment("Duplicate functions are not allowed.")); functions.insert(signature); } From 19220b9fe5c78a5e68724c0def4f187955a5bc76 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Sun, 8 Mar 2015 17:26:36 -0500 Subject: [PATCH 14/28] fix rebase errors --- libsolidity/AST.cpp | 39 ++++++--------------------------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 9d19b30c9..09c03ef6e 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -356,7 +356,12 @@ void VariableDeclaration::checkTypeRequirements() else { // no type declared and no previous assignment, infer the type - m_value->checkTypeRequirements(); + Identifier* identifier = dynamic_cast(m_value.get()); + if (identifier) + identifier->checkTypeRequirementsFromVariableDeclaration(); + else + m_value->checkTypeRequirements(); + TypePointer type = m_value->getType(); if (type->getCategory() == Type::Category::IntegerConstant) { @@ -484,39 +489,7 @@ void Return::checkTypeRequirements() void VariableDeclarationStatement::checkTypeRequirements() { -<<<<<<< HEAD m_variable->checkTypeRequirements(); -======= - // Variables can be declared without type (with "var"), in which case the first assignment - // sets the type. - // Note that assignments before the first declaration are legal because of the special scoping - // rules inherited from JavaScript. - if (m_variable->getValue()) - { - if (m_variable->getType()) - m_variable->getValue()->expectType(*m_variable->getType()); - else - { - // no type declared and no previous assignment, infer the type - Identifier* identifier = dynamic_cast(m_variable->getValue().get()); - if (identifier) - identifier->checkTypeRequirementsFromVariableDeclaration(); - else - m_variable->getValue()->checkTypeRequirements(); - TypePointer type = m_variable->getValue()->getType(); - if (type->getCategory() == Type::Category::IntegerConstant) - { - auto intType = dynamic_pointer_cast(type)->getIntegerType(); - if (!intType) - BOOST_THROW_EXCEPTION(m_variable->getValue()->createTypeError("Invalid integer constant " + type->toString())); - type = intType; - } - else if (type->getCategory() == Type::Category::Void) - BOOST_THROW_EXCEPTION(m_variable->createTypeError("var cannot be void type")); - m_variable->setType(type); - } - } ->>>>>>> implement overload resolution } void Assignment::checkTypeRequirements() From 7f2ac996792ff43662b1f655a71a53f2281ae0d3 Mon Sep 17 00:00:00 2001 From: debris Date: Sat, 11 Apr 2015 19:53:45 +0200 Subject: [PATCH 15/28] fixed #1590, msvc build with cmake 3.2.1 --- CMakeLists.txt | 17 +++++++++++++---- test/CMakeLists.txt | 8 ++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 100ef9139..eb6dc9d46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,22 @@ # cmake global cmake_minimum_required(VERSION 2.8.12) + +project(ethereum) + +set(CMAKE_AUTOMOC ON) + +# link_directories interprate relative paths with respect to CMAKE_CURRENT_SOURCE_DIR +cmake_policy(SET CMP0015 NEW) + # let cmake autolink dependencies on windows # it's specified globally, cause qt libraries requires that on windows and they are also found globally cmake_policy(SET CMP0020 NEW) -project(ethereum) +# 3.1 and above +if ((${CMAKE_MAJOR_VERSION} GREATER 2) AND (${CMAKE_MINOR_VERSION} GREATER 0)) + # implicitly dereference variables (deprecated in 3.1) + cmake_policy(SET CMP0054 OLD) +endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -118,9 +130,6 @@ endfunction() ###################################################################################################### -set(CMAKE_AUTOMOC ON) -cmake_policy(SET CMP0015 NEW) - # Clear invalid option if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release") if (PARANOID) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 90af5122e..1df4c7cc3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -34,10 +34,10 @@ endforeach(file) file(GLOB HEADERS "*.h") add_executable(testeth ${SRC_LIST} ${HEADERS}) -add_executable(createRandomVMTest createRandomVMTest.cpp vm.cpp TestHelper.cpp Stats.cpp) -add_executable(createRandomStateTest createRandomStateTest.cpp TestHelper.cpp Stats.cpp) -add_executable(checkRandomVMTest checkRandomVMTest.cpp vm.cpp TestHelper.cpp Stats.cpp) -add_executable(checkRandomStateTest checkRandomStateTest.cpp TestHelper.cpp Stats.cpp) +add_executable(createRandomVMTest "./createRandomVMTest.cpp" "./vm.cpp" "./TestHelper.cpp" "./Stats.cpp") +add_executable(createRandomStateTest "./createRandomStateTest.cpp" "./TestHelper.cpp" "./Stats.cpp") +add_executable(checkRandomVMTest "./checkRandomVMTest.cpp" "./vm.cpp" "./TestHelper.cpp" "./Stats.cpp") +add_executable(checkRandomStateTest "./checkRandomStateTest.cpp" "./TestHelper.cpp" "./Stats.cpp") target_link_libraries(testeth ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) target_link_libraries(testeth ${CURL_LIBRARIES}) From 0b36ca86d758b1be39c5db36e0398f436556a9e0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 15 Apr 2015 17:40:50 +0200 Subject: [PATCH 16/28] Fixed function overloads. Added tests, disallowed non-calling usage of non-unique function references. --- libsolidity/AST.cpp | 330 ++++++++++++++----------- libsolidity/AST.h | 63 +++-- libsolidity/Compiler.cpp | 6 +- libsolidity/CompilerContext.cpp | 42 ++-- libsolidity/CompilerContext.h | 11 +- libsolidity/DeclarationContainer.cpp | 29 ++- libsolidity/DeclarationContainer.h | 2 +- libsolidity/ExpressionCompiler.cpp | 46 ++-- libsolidity/LValue.cpp | 8 +- libsolidity/NameAndTypeResolver.cpp | 80 +++++- libsolidity/NameAndTypeResolver.h | 21 +- libsolidity/Types.cpp | 117 ++++++--- libsolidity/Types.h | 57 +++-- mix/CodeModel.cpp | 4 +- test/SolidityEndToEndTest.cpp | 154 +++++------- test/SolidityNameAndTypeResolution.cpp | 74 +++++- 16 files changed, 646 insertions(+), 398 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index ada4a01d1..1c1983d98 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -52,6 +52,7 @@ void ContractDefinition::checkTypeRequirements() for (ASTPointer const& baseSpecifier: getBaseContracts()) baseSpecifier->checkTypeRequirements(); + checkDuplicateFunctions(); checkIllegalOverrides(); checkAbstractFunctions(); @@ -82,20 +83,11 @@ void ContractDefinition::checkTypeRequirements() for (ASTPointer const& function: getDefinedFunctions()) function->checkTypeRequirements(); - // check for duplicate declaration - set functions; - for (ASTPointer const& function: getDefinedFunctions()) - { - string signature = function->getCanonicalSignature(); - if (functions.count(signature)) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(function->getLocation()) - << errinfo_comment("Duplicate functions are not allowed.")); - functions.insert(signature); - } for (ASTPointer const& variable: m_stateVariables) variable->checkTypeRequirements(); + checkExternalTypeClashes(); // check for hash collisions in function signatures set> hashes; for (auto const& it: getInterfaceFunctionList()) @@ -140,6 +132,33 @@ FunctionDefinition const* ContractDefinition::getFallbackFunction() const return nullptr; } +void ContractDefinition::checkDuplicateFunctions() const +{ + /// Checks that two functions with the same name defined in this contract have different + /// argument types and that there is at most one constructor. + map> functions; + for (ASTPointer const& function: getDefinedFunctions()) + functions[function->getName()].push_back(function.get()); + if (functions[getName()].size() > 1) + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(getLocation()) << + errinfo_comment("More than one constructor defined.") + ); + for (auto const& it: functions) + { + vector const& overloads = it.second; + for (size_t i = 0; i < overloads.size(); ++i) + for (size_t j = i + 1; j < overloads.size(); ++j) + if (FunctionType(*overloads[i]).hasEqualArgumentTypes(FunctionType(*overloads[j]))) + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(overloads[j]->getLocation()) << + errinfo_comment("Function with same name and arguments already defined.") + ); + } +} + void ContractDefinition::checkAbstractFunctions() { map functions; @@ -166,8 +185,7 @@ void ContractDefinition::checkIllegalOverrides() const { // TODO unify this at a later point. for this we need to put the constness and the access specifier // into the types - map functions; - set functionNames; + map> functions; map modifiers; // We search from derived to base, so the stored item causes the error. @@ -180,14 +198,21 @@ void ContractDefinition::checkIllegalOverrides() const string const& name = function->getName(); if (modifiers.count(name)) BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier.")); - FunctionDefinition const*& override = functions[function->getCanonicalSignature()]; - functionNames.insert(name); - if (!override) - override = function.get(); - else if (override->getVisibility() != function->getVisibility() || - override->isDeclaredConst() != function->isDeclaredConst() || - FunctionType(*override) != FunctionType(*function)) - BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature.")); + FunctionType functionType(*function); + // function should not change the return type + for (FunctionDefinition const* overriding: functions[name]) + { + FunctionType overridingType(*overriding); + if (!overridingType.hasEqualArgumentTypes(functionType)) + continue; + if ( + overriding->getVisibility() != function->getVisibility() || + overriding->isDeclaredConst() != function->isDeclaredConst() || + overridingType != functionType + ) + BOOST_THROW_EXCEPTION(overriding->createTypeError("Override changes extended function signature.")); + } + functions[name].push_back(function.get()); } for (ASTPointer const& modifier: contract->getFunctionModifiers()) { @@ -197,12 +222,43 @@ void ContractDefinition::checkIllegalOverrides() const override = modifier.get(); else if (ModifierType(*override) != ModifierType(*modifier)) BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature.")); - if (functionNames.count(name)) + if (!functions[name].empty()) BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier to function.")); } } } +void ContractDefinition::checkExternalTypeClashes() const +{ + map>>> externalDeclarations; + for (ContractDefinition const* contract: getLinearizedBaseContracts()) + { + for (ASTPointer const& f: contract->getDefinedFunctions()) + if (f->isPartOfExternalInterface()) + { + auto functionType = make_shared(*f); + externalDeclarations[functionType->externalSignature(f->getName())].push_back( + make_pair(f.get(), functionType) + ); + } + for (ASTPointer const& v: contract->getStateVariables()) + if (v->isPartOfExternalInterface()) + { + auto functionType = make_shared(*v); + externalDeclarations[functionType->externalSignature(v->getName())].push_back( + make_pair(v.get(), functionType) + ); + } + } + for (auto const& it: externalDeclarations) + for (size_t i = 0; i < it.second.size(); ++i) + for (size_t j = i + 1; j < it.second.size(); ++j) + if (!it.second[i].second->hasEqualArgumentTypes(*it.second[j].second)) + BOOST_THROW_EXCEPTION(it.second[j].first->createTypeError( + "Function overload clash during conversion to external types for arguments." + )); +} + std::vector> const& ContractDefinition::getInterfaceEvents() const { if (!m_interfaceEvents) @@ -291,11 +347,11 @@ TypePointer EnumValue::getType(ContractDefinition const*) const void InheritanceSpecifier::checkTypeRequirements() { - m_baseName->checkTypeRequirements(); + m_baseName->checkTypeRequirements(nullptr); for (ASTPointer const& argument: m_arguments) - argument->checkTypeRequirements(); + argument->checkTypeRequirements(nullptr); - ContractDefinition const* base = dynamic_cast(m_baseName->getReferencedDeclaration()); + ContractDefinition const* base = dynamic_cast(&m_baseName->getReferencedDeclaration()); solAssert(base, "Base contract not available."); TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes(); if (parameterTypes.size() != m_arguments.size()) @@ -409,11 +465,7 @@ void VariableDeclaration::checkTypeRequirements() else { // no type declared and no previous assignment, infer the type - Identifier* identifier = dynamic_cast(m_value.get()); - if (identifier) - identifier->checkTypeRequirementsFromVariableDeclaration(); - else - m_value->checkTypeRequirements(); + m_value->checkTypeRequirements(nullptr); TypePointer type = m_value->getType(); if (type->getCategory() == Type::Category::IntegerConstant) @@ -452,11 +504,15 @@ void ModifierDefinition::checkTypeRequirements() void ModifierInvocation::checkTypeRequirements(vector> const& _bases) { - m_modifierName->checkTypeRequirements(); + TypePointers argumentTypes; for (ASTPointer const& argument: m_arguments) - argument->checkTypeRequirements(); + { + argument->checkTypeRequirements(nullptr); + argumentTypes.push_back(argument->getType()); + } + m_modifierName->checkTypeRequirements(&argumentTypes); - auto declaration = m_modifierName->getReferencedDeclaration(); + auto const* declaration = &m_modifierName->getReferencedDeclaration(); vector> emptyParameterList; vector> const* parameters = nullptr; if (auto modifier = dynamic_cast(declaration)) @@ -464,7 +520,7 @@ void ModifierInvocation::checkTypeRequirements(vectorgetName()->getReferencedDeclaration()) + if (declaration == &base->getName()->getReferencedDeclaration()) { if (auto referencedConstructor = dynamic_cast(*declaration).getConstructor()) parameters = &referencedConstructor->getParameters(); @@ -547,9 +603,9 @@ void VariableDeclarationStatement::checkTypeRequirements() m_variable->checkTypeRequirements(); } -void Assignment::checkTypeRequirements() +void Assignment::checkTypeRequirements(TypePointers const*) { - m_leftHandSide->checkTypeRequirements(); + m_leftHandSide->checkTypeRequirements(nullptr); m_leftHandSide->requireLValue(); if (m_leftHandSide->getType()->getCategory() == Type::Category::Mapping) BOOST_THROW_EXCEPTION(createTypeError("Mappings cannot be assigned to.")); @@ -559,7 +615,7 @@ void Assignment::checkTypeRequirements() else { // compound assignment - m_rightHandSide->checkTypeRequirements(); + m_rightHandSide->checkTypeRequirements(nullptr); TypePointer resultType = m_type->binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator), m_rightHandSide->getType()); if (!resultType || *resultType != *m_type) @@ -572,7 +628,7 @@ void Assignment::checkTypeRequirements() void ExpressionStatement::checkTypeRequirements() { - m_expression->checkTypeRequirements(); + m_expression->checkTypeRequirements(nullptr); if (m_expression->getType()->getCategory() == Type::Category::IntegerConstant) if (!dynamic_pointer_cast(m_expression->getType())->getIntegerType()) BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant.")); @@ -580,7 +636,7 @@ void ExpressionStatement::checkTypeRequirements() void Expression::expectType(Type const& _expectedType) { - checkTypeRequirements(); + checkTypeRequirements(nullptr); Type const& type = *getType(); if (!type.isImplicitlyConvertibleTo(_expectedType)) BOOST_THROW_EXCEPTION(createTypeError("Type " + type.toString() + @@ -595,10 +651,10 @@ void Expression::requireLValue() m_lvalueRequested = true; } -void UnaryOperation::checkTypeRequirements() +void UnaryOperation::checkTypeRequirements(TypePointers const*) { // Inc, Dec, Add, Sub, Not, BitNot, Delete - m_subExpression->checkTypeRequirements(); + m_subExpression->checkTypeRequirements(nullptr); if (m_operator == Token::Value::Inc || m_operator == Token::Value::Dec || m_operator == Token::Value::Delete) m_subExpression->requireLValue(); m_type = m_subExpression->getType()->unaryOperatorResult(m_operator); @@ -606,10 +662,10 @@ void UnaryOperation::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type.")); } -void BinaryOperation::checkTypeRequirements() +void BinaryOperation::checkTypeRequirements(TypePointers const*) { - m_left->checkTypeRequirements(); - m_right->checkTypeRequirements(); + m_left->checkTypeRequirements(nullptr); + m_right->checkTypeRequirements(nullptr); m_commonType = m_left->getType()->binaryOperatorResult(m_operator, m_right->getType()); if (!m_commonType) BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) + @@ -619,17 +675,22 @@ void BinaryOperation::checkTypeRequirements() m_type = Token::isCompareOp(m_operator) ? make_shared() : m_commonType; } -void FunctionCall::checkTypeRequirements() +void FunctionCall::checkTypeRequirements(TypePointers const*) { - // we need to check arguments' type first as their info will be used by m_express(Identifier). + bool isPositionalCall = m_names.empty(); + + // we need to check arguments' type first as they will be forwarded to + // m_expression->checkTypeRequirements + TypePointers argumentTypes; for (ASTPointer const& argument: m_arguments) - argument->checkTypeRequirements(); + { + argument->checkTypeRequirements(nullptr); + // only store them for positional calls + if (isPositionalCall) + argumentTypes.push_back(argument->getType()); + } - auto identifier = dynamic_cast(m_expression.get()); - if (identifier) - identifier->checkTypeRequirementsWithFunctionCall(*this); - else - m_expression->checkTypeRequirements(); + m_expression->checkTypeRequirements(isPositionalCall ? &argumentTypes : nullptr); Type const* expressionType = m_expression->getType().get(); if (isTypeConversion()) @@ -639,7 +700,7 @@ void FunctionCall::checkTypeRequirements() // number of non-mapping members if (m_arguments.size() != 1) BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion.")); - if (!m_names.empty()) + if (!isPositionalCall) BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments.")); if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType())) BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); @@ -654,8 +715,9 @@ void FunctionCall::checkTypeRequirements() if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size()) BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call.")); - if (m_names.empty()) + if (isPositionalCall) { + // call by positional arguments for (size_t i = 0; i < m_arguments.size(); ++i) if (!functionType->takesArbitraryParameters() && !m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])) @@ -663,6 +725,7 @@ void FunctionCall::checkTypeRequirements() } else { + // call by named arguments if (functionType->takesArbitraryParameters()) BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions " "that take arbitrary parameters.")); @@ -700,27 +763,6 @@ void FunctionCall::checkTypeRequirements() else m_type = functionType->getReturnParameterTypes().front(); } - else if (OverloadedFunctionType const* overloadedTypes = dynamic_cast(expressionType)) - { - // this only applies to "x(3)" where x is assigned by "var x = f;" where f is an overloaded functions. - auto identifier = dynamic_cast(m_expression.get()); - solAssert(identifier, "only applies to 'var x = f;'"); - - Declaration const* function = overloadedTypes->getIdentifier()->overloadResolution(*this); - if (!function) - BOOST_THROW_EXCEPTION(createTypeError("Can't resolve declarations")); - - identifier->setReferencedDeclaration(*function); - identifier->checkTypeRequirements(); - - TypePointer type = identifier->getType(); - FunctionType const* functionType = dynamic_cast(type.get()); - - if (functionType->getReturnParameterTypes().empty()) - m_type = make_shared(); - else - m_type = functionType->getReturnParameterTypes().front(); - } else BOOST_THROW_EXCEPTION(createTypeError("Type is not callable.")); } @@ -730,10 +772,10 @@ bool FunctionCall::isTypeConversion() const return m_expression->getType()->getCategory() == Type::Category::TypeType; } -void NewExpression::checkTypeRequirements() +void NewExpression::checkTypeRequirements(TypePointers const*) { - m_contractName->checkTypeRequirements(); - m_contract = dynamic_cast(m_contractName->getReferencedDeclaration()); + m_contractName->checkTypeRequirements(nullptr); + m_contract = dynamic_cast(&m_contractName->getReferencedDeclaration()); if (!m_contract) BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); if (!m_contract->isFullyImplemented()) @@ -744,15 +786,37 @@ void NewExpression::checkTypeRequirements() FunctionType::Location::Creation); } -void MemberAccess::checkTypeRequirements() +void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes) { - m_expression->checkTypeRequirements(); + m_expression->checkTypeRequirements(nullptr); Type const& type = *m_expression->getType(); - m_type = type.getMemberType(*m_memberName); - if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " - "visible in " + type.toString())); - // This should probably move somewhere else. + + MemberList::MemberMap possibleMembers = type.getMembers().membersByName(*m_memberName); + if (possibleMembers.size() > 1 && _argumentTypes) + { + // do override resolution + for (auto it = possibleMembers.begin(); it != possibleMembers.end();) + if ( + it->type->getCategory() == Type::Category::Function && + !dynamic_cast(*it->type).canTakeArguments(*_argumentTypes) + ) + it = possibleMembers.erase(it); + else + ++it; + } + if (possibleMembers.size() == 0) + BOOST_THROW_EXCEPTION(createTypeError( + "Member \"" + *m_memberName + "\" not found or not visible " + "after argument-dependent lookup in " + type.toString() + )); + else if (possibleMembers.size() > 1) + BOOST_THROW_EXCEPTION(createTypeError( + "Member \"" + *m_memberName + "\" not unique " + "after argument-dependent lookup in " + type.toString() + )); + + m_referencedDeclaration = possibleMembers.front().declaration; + m_type = possibleMembers.front().type; if (type.getCategory() == Type::Category::Struct) m_isLValue = true; else if (type.getCategory() == Type::Category::Array) @@ -765,9 +829,9 @@ void MemberAccess::checkTypeRequirements() m_isLValue = false; } -void IndexAccess::checkTypeRequirements() +void IndexAccess::checkTypeRequirements(TypePointers const*) { - m_base->checkTypeRequirements(); + m_base->checkTypeRequirements(nullptr); switch (m_base->getType()->getCategory()) { case Type::Category::Array: @@ -800,7 +864,7 @@ void IndexAccess::checkTypeRequirements() m_type = make_shared(make_shared(ArrayType::Location::Memory, type.getActualType())); else { - m_index->checkTypeRequirements(); + m_index->checkTypeRequirements(nullptr); auto length = dynamic_cast(m_index->getType().get()); if (!length) BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected.")); @@ -815,89 +879,57 @@ void IndexAccess::checkTypeRequirements() } } -void Identifier::checkTypeRequirementsWithFunctionCall(FunctionCall const& _functionCall) -{ - solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved."); - - if (!m_referencedDeclaration) - setReferencedDeclaration(*overloadResolution(_functionCall)); - - checkTypeRequirements(); -} - -void Identifier::checkTypeRequirementsFromVariableDeclaration() +void Identifier::checkTypeRequirements(TypePointers const* _argumentTypes) { - solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved."); - if (!m_referencedDeclaration) - m_type = make_shared(this); - else - checkTypeRequirements(); - - m_isLValue = true; -} - -void Identifier::checkTypeRequirements() -{ - solAssert(m_referencedDeclaration, "Identifier not resolved."); - + { + if (!_argumentTypes) + BOOST_THROW_EXCEPTION(createTypeError("Unable to determine overloaded type.")); + overloadResolution(*_argumentTypes); + } + solAssert(!!m_referencedDeclaration, "Referenced declaration is null after overload resolution."); m_isLValue = m_referencedDeclaration->isLValue(); m_type = m_referencedDeclaration->getType(m_currentContract); if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined.")); } -Declaration const* Identifier::overloadResolution(FunctionCall const& _functionCall) +Declaration const& Identifier::getReferencedDeclaration() const +{ + solAssert(!!m_referencedDeclaration, "Identifier not resolved."); + return *m_referencedDeclaration; +} + +void Identifier::overloadResolution(TypePointers const& _argumentTypes) { - solAssert(m_overloadedDeclarations.size() > 1, "FunctionIdentifier not resolved."); solAssert(!m_referencedDeclaration, "Referenced declaration should be null before overload resolution."); + solAssert(!m_overloadedDeclarations.empty(), "No candidates for overload resolution found."); - std::vector> arguments = _functionCall.getArguments(); - std::vector> const& argumentNames = _functionCall.getNames(); + std::vector possibles; + if (m_overloadedDeclarations.size() == 1) + m_referencedDeclaration = *m_overloadedDeclarations.begin(); - if (argumentNames.empty()) + for (Declaration const* declaration: m_overloadedDeclarations) { - // positional arguments - std::vector possibles; - for (Declaration const* declaration: m_overloadedDeclarations) - { - TypePointer const& function = declaration->getType(); - auto const& functionType = dynamic_cast(*function); - TypePointers const& parameterTypes = functionType.getParameterTypes(); - - if (functionType.takesArbitraryParameters() || - (arguments.size() == parameterTypes.size() && - std::equal(arguments.cbegin(), arguments.cend(), parameterTypes.cbegin(), - [](ASTPointer const& argument, TypePointer const& parameterType) - { - return argument->getType()->isImplicitlyConvertibleTo(*parameterType); - }))) - possibles.push_back(declaration); - } - if (possibles.empty()) - BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier")); - else if (std::none_of(possibles.cbegin() + 1, possibles.cend(), - [&possibles](Declaration const* declaration) - { - return declaration->getScope() == possibles.front()->getScope(); - })) - return possibles.front(); - else - BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier")); + TypePointer const& function = declaration->getType(); + auto const* functionType = dynamic_cast(function.get()); + if (functionType && functionType->canTakeArguments(_argumentTypes)) + possibles.push_back(declaration); } + if (possibles.size() == 1) + m_referencedDeclaration = possibles.front(); + else if (possibles.empty()) + BOOST_THROW_EXCEPTION(createTypeError("No matching declaration found after argument-dependent lookup.")); else - // named arguments - // TODO: don't support right now - BOOST_THROW_EXCEPTION(createTypeError("Named arguments with overloaded functions are not supported yet.")); - return nullptr; + BOOST_THROW_EXCEPTION(createTypeError("No unique declaration found after argument-dependent lookup.")); } -void ElementaryTypeNameExpression::checkTypeRequirements() +void ElementaryTypeNameExpression::checkTypeRequirements(TypePointers const*) { m_type = make_shared(Type::fromElementaryTypeName(m_typeToken)); } -void Literal::checkTypeRequirements() +void Literal::checkTypeRequirements(TypePointers const*) { m_type = Type::forLiteral(*this); if (!m_type) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index fd50eb956..f0144c7ec 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -282,8 +282,14 @@ public: FunctionDefinition const* getFallbackFunction() const; private: + /// Checks that two functions defined in this contract with the same name have different + /// arguments and that there is at most one constructor. + void checkDuplicateFunctions() const; void checkIllegalOverrides() const; void checkAbstractFunctions(); + /// Checks that different functions with external visibility end up having different + /// external argument types (i.e. different signature). + void checkExternalTypeClashes() const; std::vector, FunctionTypePointer>> const& getInterfaceFunctionList() const; @@ -967,7 +973,10 @@ class Expression: public ASTNode { public: Expression(SourceLocation const& _location): ASTNode(_location) {} - virtual void checkTypeRequirements() = 0; + /// Performs type checking after which m_type should be set. + /// @arg _argumentTypes if set, provides the argument types for the case that this expression + /// is used in the context of a call, used for function overload resolution. + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) = 0; std::shared_ptr const& getType() const { return m_type; } bool isLValue() const { return m_isLValue; } @@ -1006,7 +1015,7 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Expression const& getLeftHandSide() const { return *m_leftHandSide; } Token::Value getAssignmentOperator() const { return m_assigmentOperator; } @@ -1034,7 +1043,7 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Token::Value getOperator() const { return m_operator; } bool isPrefixOperation() const { return m_isPrefix; } @@ -1061,7 +1070,7 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Expression const& getLeftExpression() const { return *m_left; } Expression const& getRightExpression() const { return *m_right; } @@ -1089,7 +1098,7 @@ public: Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Expression const& getExpression() const { return *m_expression; } std::vector> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; } @@ -1115,7 +1124,7 @@ public: Expression(_location), m_contractName(_contractName) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; /// Returns the referenced contract. Can only be called after type checking. ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; } @@ -1139,11 +1148,18 @@ public: virtual void accept(ASTConstVisitor& _visitor) const override; Expression const& getExpression() const { return *m_expression; } ASTString const& getMemberName() const { return *m_memberName; } - virtual void checkTypeRequirements() override; + /// @returns the declaration referenced by this expression. Might return nullptr even if the + /// expression is valid, e.g. if the member does not correspond to an AST node. + Declaration const* referencedDeclaration() const { return m_referencedDeclaration; } + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; private: ASTPointer m_expression; ASTPointer m_memberName; + + /// Pointer to the referenced declaration, this is sometimes needed to resolve function over + /// loads in the type-checking phase. + Declaration const* m_referencedDeclaration = nullptr; }; /** @@ -1157,7 +1173,7 @@ public: Expression(_location), m_base(_base), m_index(_index) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Expression const& getBaseExpression() const { return *m_base; } Expression const* getIndexExpression() const { return m_index.get(); } @@ -1187,28 +1203,33 @@ public: PrimaryExpression(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; ASTString const& getName() const { return *m_name; } - void setReferencedDeclaration(Declaration const& _referencedDeclaration, - ContractDefinition const* _currentContract = nullptr) + void setReferencedDeclaration( + Declaration const& _referencedDeclaration, + ContractDefinition const* _currentContract = nullptr + ) { m_referencedDeclaration = &_referencedDeclaration; m_currentContract = _currentContract; } - Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; } - ContractDefinition const* getCurrentContract() const { return m_currentContract; } + Declaration const& getReferencedDeclaration() const; - void setOverloadedDeclarations(std::set const& _declarations) { m_overloadedDeclarations = _declarations; } - std::set getOverloadedDeclarations() const { return m_overloadedDeclarations; } + /// 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 const& _declarations) + { + m_overloadedDeclarations = _declarations; + } - void checkTypeRequirementsWithFunctionCall(FunctionCall const& _functionCall); - void checkTypeRequirementsFromVariableDeclaration(); + /// Tries to find exactly one of the possible referenced declarations provided the given + /// argument types in a call context. + void overloadResolution(TypePointers const& _argumentTypes); - Declaration const* overloadResolution(FunctionCall const& _functionCall); private: - ASTPointer m_name; /// Declaration the name refers to. @@ -1235,7 +1256,7 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Token::Value getTypeToken() const { return m_typeToken; } @@ -1269,7 +1290,7 @@ public: PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Token::Value getToken() const { return m_token; } /// @returns the non-parsed value of the literal diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 8e2634499..a8d0a5910 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -90,7 +90,7 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp for (auto const& modifier: constructor->getModifiers()) { auto baseContract = dynamic_cast( - modifier->getName()->getReferencedDeclaration()); + &modifier->getName()->getReferencedDeclaration()); if (baseContract) if (m_baseArguments.count(baseContract->getConstructor()) == 0) m_baseArguments[baseContract->getConstructor()] = &modifier->getArguments(); @@ -99,7 +99,7 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp for (ASTPointer const& base: contract->getBaseContracts()) { ContractDefinition const* baseContract = dynamic_cast( - base->getName()->getReferencedDeclaration()); + &base->getName()->getReferencedDeclaration()); solAssert(baseContract, ""); if (m_baseArguments.count(baseContract->getConstructor()) == 0) @@ -545,7 +545,7 @@ void Compiler::appendModifierOrFunctionCode() ASTPointer const& modifierInvocation = m_currentFunction->getModifiers()[m_modifierDepth]; // constructor call should be excluded - if (dynamic_cast(modifierInvocation->getName()->getReferencedDeclaration())) + if (dynamic_cast(&modifierInvocation->getName()->getReferencedDeclaration())) { ++m_modifierDepth; appendModifierOrFunctionCode(); diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index 0afda1367..f373fdfb0 100644 --- a/libsolidity/CompilerContext.cpp +++ b/libsolidity/CompilerContext.cpp @@ -102,27 +102,13 @@ eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _dec eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function) { solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - for (ContractDefinition const* contract: m_inheritanceHierarchy) - for (ASTPointer const& function: contract->getDefinedFunctions()) - { - if (!function->isConstructor() && - dynamic_cast(*function->getType(contract)).getCanonicalSignature() == - dynamic_cast(*_function.getType(contract)).getCanonicalSignature()) - return getFunctionEntryLabel(*function); - } - solAssert(false, "Virtual function " + _function.getName() + " not found."); - return m_asm.newTag(); // not reached + return getVirtualFunctionEntryLabel(_function, m_inheritanceHierarchy.begin()); } -eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(string const& _name, ContractDefinition const& _base) +eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(FunctionDefinition const& _function, ContractDefinition const& _base) { - auto it = getSuperContract(_base); - for (; it != m_inheritanceHierarchy.end(); ++it) - for (ASTPointer const& function: (*it)->getDefinedFunctions()) - if (!function->isConstructor() && function->getName() == _name) // TODO: add a test case for this! - return getFunctionEntryLabel(*function); - solAssert(false, "Super function " + _name + " not found."); - return m_asm.newTag(); // not reached + solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); + return getVirtualFunctionEntryLabel(_function, getSuperContract(_base)); } FunctionDefinition const* CompilerContext::getNextConstructor(ContractDefinition const& _contract) const @@ -194,6 +180,26 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node) updateSourceLocation(); } +eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel( + FunctionDefinition const& _function, + vector::const_iterator _searchStart +) +{ + string name = _function.getName(); + FunctionType functionType(_function); + auto it = _searchStart; + for (; it != m_inheritanceHierarchy.end(); ++it) + for (ASTPointer const& function: (*it)->getDefinedFunctions()) + if ( + function->getName() == name && + !function->isConstructor() && + FunctionType(*function).hasEqualArgumentTypes(functionType) + ) + return getFunctionEntryLabel(*function); + solAssert(false, "Super function " + name + " not found."); + return m_asm.newTag(); // not reached +} + vector::const_iterator CompilerContext::getSuperContract(ContractDefinition const& _contract) const { solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index 87f90d4c4..67dd3c94b 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -63,9 +63,9 @@ public: void setInheritanceHierarchy(std::vector const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; } /// @returns the entry label of the given function and takes overrides into account. eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function); - /// @returns the entry label of function with the given name from the most derived class just + /// @returns the entry label of a function that overrides the given declaration from the most derived class just /// above _base in the current inheritance hierarchy. - eth::AssemblyItem getSuperFunctionEntryLabel(std::string const& _name, ContractDefinition const& _base); + eth::AssemblyItem getSuperFunctionEntryLabel(FunctionDefinition const& _function, ContractDefinition const& _base); FunctionDefinition const* getNextConstructor(ContractDefinition const& _contract) const; /// @returns the set of functions for which we still need to generate code @@ -136,6 +136,13 @@ public: }; private: + /// @returns the entry label of the given function - searches the inheritance hierarchy + /// startig from the given point towards the base. + eth::AssemblyItem getVirtualFunctionEntryLabel( + FunctionDefinition const& _function, + std::vector::const_iterator _searchStart + ); + /// @returns an iterator to the contract directly above the given contract. std::vector::const_iterator getSuperContract(const ContractDefinition &_contract) const; /// Updates source location set in the assembly. void updateSourceLocation(); diff --git a/libsolidity/DeclarationContainer.cpp b/libsolidity/DeclarationContainer.cpp index 226b9d680..565f71dfd 100644 --- a/libsolidity/DeclarationContainer.cpp +++ b/libsolidity/DeclarationContainer.cpp @@ -35,30 +35,29 @@ bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, if (name.empty()) return true; - if (!_update) + if (_update) + { + solAssert(!dynamic_cast(&_declaration), "Attempt to update function definition."); + m_declarations[name].clear(); + m_invisibleDeclarations[name].clear(); + } + else { if (dynamic_cast(&_declaration)) { - // other declarations must be FunctionDefinition, otherwise clash with other declarations. - for (auto&& declaration: m_declarations[_declaration.getName()]) - if (dynamic_cast(declaration) == nullptr) + // check that all other declarations with the same name are functions + for (auto&& declaration: m_invisibleDeclarations[name] + m_declarations[name]) + if (!dynamic_cast(declaration)) return false; } - else if (m_declarations.count(_declaration.getName()) != 0) - return false; - } - else - { - // update declaration - solAssert(dynamic_cast(&_declaration) == nullptr, "cannot be FunctionDefinition"); - - m_declarations[_declaration.getName()].clear(); + else if (m_declarations.count(name) > 0 || m_invisibleDeclarations.count(name) > 0) + return false; } if (_invisible) - m_invisibleDeclarations.insert(name); + m_invisibleDeclarations[name].insert(&_declaration); else - m_declarations[_declaration.getName()].insert(&_declaration); + m_declarations[name].insert(&_declaration); return true; } diff --git a/libsolidity/DeclarationContainer.h b/libsolidity/DeclarationContainer.h index 42784ec2a..35a6ea077 100644 --- a/libsolidity/DeclarationContainer.h +++ b/libsolidity/DeclarationContainer.h @@ -56,7 +56,7 @@ private: Declaration const* m_enclosingDeclaration; DeclarationContainer const* m_enclosingContainer; std::map> m_declarations; - std::set m_invisibleDeclarations; + std::map> m_invisibleDeclarations; }; } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 3ca8de89e..8c8c3ee0d 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -601,13 +601,25 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) bool alsoSearchInteger = false; ContractType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); if (type.isSuper()) - m_context << m_context.getSuperFunctionEntryLabel(member, type.getContractDefinition()).pushTag(); + { + solAssert(!!_memberAccess.referencedDeclaration(), "Referenced declaration not resolved."); + m_context << m_context.getSuperFunctionEntryLabel( + dynamic_cast(*_memberAccess.referencedDeclaration()), + type.getContractDefinition() + ).pushTag(); + } else { // ordinary contract type - u256 identifier = type.getFunctionIdentifier(member); - if (identifier != Invalid256) + if (Declaration const* declaration = _memberAccess.referencedDeclaration()) { + u256 identifier; + if (auto const* variable = dynamic_cast(declaration)) + identifier = FunctionType(*variable).externalIdentifier(); + else if (auto const* function = dynamic_cast(declaration)) + identifier = FunctionType(*function).externalIdentifier(); + else + solAssert(false, "Contract member is neither variable nor function."); appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::Address), true); m_context << identifier; } @@ -683,19 +695,16 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) case Type::Category::TypeType: { TypeType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - if (!type.getMembers().getMemberType(member)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to " + type.toString())); + solAssert( + !type.getMembers().membersByName(_memberAccess.getMemberName()).empty(), + "Invalid member access to " + type.toString() + ); - if (auto contractType = dynamic_cast(type.getActualType().get())) + if (dynamic_cast(type.getActualType().get())) { - ContractDefinition const& contract = contractType->getContractDefinition(); - for (ASTPointer const& function: contract.getDefinedFunctions()) - if (function->getName() == member) - { - m_context << m_context.getFunctionEntryLabel(*function).pushTag(); - return; - } - solAssert(false, "Function not found in member access."); + auto const* function = dynamic_cast(_memberAccess.referencedDeclaration()); + solAssert(!!function, "Function not found in member access"); + m_context << m_context.getFunctionEntryLabel(*function).pushTag(); } else if (auto enumType = dynamic_cast(type.getActualType().get())) m_context << enumType->getMemberValue(_memberAccess.getMemberName()); @@ -780,7 +789,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) void ExpressionCompiler::endVisit(Identifier const& _identifier) { CompilerContext::LocationSetter locationSetter(m_context, _identifier); - Declaration const* declaration = _identifier.getReferencedDeclaration(); + Declaration const* declaration = &_identifier.getReferencedDeclaration(); if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) { switch (magicVar->getType()->getCategory()) @@ -819,13 +828,6 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) { // no-op } - else if (declaration == nullptr && _identifier.getOverloadedDeclarations().size() > 1) - { - // var x = f; - declaration = *_identifier.getOverloadedDeclarations().begin(); - FunctionDefinition const* functionDef = dynamic_cast(declaration); - m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag(); - } else { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); diff --git a/libsolidity/LValue.cpp b/libsolidity/LValue.cpp index 234072bce..fc990b0aa 100644 --- a/libsolidity/LValue.cpp +++ b/libsolidity/LValue.cpp @@ -177,10 +177,10 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc for (auto const& member: structType.getMembers()) { // assign each member that is not a mapping - TypePointer const& memberType = member.second; + TypePointer const& memberType = member.type; if (memberType->getCategory() == Type::Category::Mapping) continue; - pair const& offsets = structType.getStorageOffsetsOfMember(member.first); + pair const& offsets = structType.getStorageOffsetsOfMember(member.name); m_context << offsets.first << u256(offsets.second) << eth::Instruction::DUP6 << eth::Instruction::DUP3 @@ -230,10 +230,10 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const for (auto const& member: structType.getMembers()) { // zero each member that is not a mapping - TypePointer const& memberType = member.second; + TypePointer const& memberType = member.type; if (memberType->getCategory() == Type::Category::Mapping) continue; - pair const& offsets = structType.getStorageOffsetsOfMember(member.first); + pair const& offsets = structType.getStorageOffsetsOfMember(member.name); m_context << offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD << u256(offsets.second); diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index c787ae6b0..1c527b89c 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -53,8 +53,9 @@ 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); + importInheritedScope(*base, false); // import non-functions for (ASTPointer const& structDef: _contract.getDefinedStructs()) ReferencesResolver resolver(*structDef, *this, &_contract, nullptr); @@ -64,6 +65,8 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) ReferencesResolver resolver(*variable, *this, &_contract, nullptr); for (ASTPointer const& event: _contract.getEvents()) ReferencesResolver resolver(*event, *this, &_contract, nullptr); + + // these can contain code, only resolve parameters for now for (ASTPointer const& modifier: _contract.getFunctionModifiers()) { m_currentScope = &m_scopes[modifier.get()]; @@ -75,6 +78,28 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) ReferencesResolver referencesResolver(*function, *this, &_contract, function->getReturnParameterList().get()); } + + m_currentScope = &m_scopes[&_contract]; + for (ContractDefinition const* base: _contract.getLinearizedBaseContracts()) + importInheritedScope(*base, true); // import functions + + // now resolve references inside the code + for (ASTPointer const& modifier: _contract.getFunctionModifiers()) + { + m_currentScope = &m_scopes[modifier.get()]; + ReferencesResolver resolver(*modifier, *this, &_contract, nullptr, true); + } + for (ASTPointer const& function: _contract.getDefinedFunctions()) + { + m_currentScope = &m_scopes[function.get()]; + ReferencesResolver referencesResolver( + *function, + *this, + &_contract, + function->getReturnParameterList().get(), + true + ); + } } void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract) @@ -90,7 +115,7 @@ void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) solAssert(_declaration.getScope() == nullptr, "Updated declaration outside global scope."); } -std::set NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const +set NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const { auto iterator = m_scopes.find(_scope); if (iterator == end(m_scopes)) @@ -98,21 +123,43 @@ std::set NameAndTypeResolver::resolveName(ASTString const& _ return iterator->second.resolveName(_name, false); } -std::set NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) +set NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) { return m_currentScope->resolveName(_name, _recursive); } -void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base) +void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base, bool _importFunctions) { auto iterator = m_scopes.find(&_base); solAssert(iterator != end(m_scopes), ""); for (auto const& nameAndDeclaration: iterator->second.getDeclarations()) 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->getName() != _base.getName() && - declaration->isVisibleInDerivedContracts()) + if (declaration->getScope() == &_base && declaration->isVisibleInDerivedContracts()) + { + auto function = dynamic_cast(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(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 @@ -123,8 +170,7 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) for (ASTPointer const& baseSpecifier: _contract.getBaseContracts()) { ASTPointer baseName = baseSpecifier->getName(); - ContractDefinition const* base = dynamic_cast( - baseName->getReferencedDeclaration()); + auto base = dynamic_cast(&baseName->getReferencedDeclaration()); if (!base) BOOST_THROW_EXCEPTION(baseName->createTypeError("Contract expected.")); // "push_front" has the effect that bases mentioned later can overwrite members of bases @@ -316,11 +362,19 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio enterNewSubScope(_declaration); } -ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, - ContractDefinition const* _currentContract, - ParameterList const* _returnParameters, bool _allowLazyTypes): - m_resolver(_resolver), m_currentContract(_currentContract), - m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes) +ReferencesResolver::ReferencesResolver( + ASTNode& _root, + NameAndTypeResolver& _resolver, + ContractDefinition const* _currentContract, + ParameterList const* _returnParameters, + bool _resolveInsideCode, + bool _allowLazyTypes +): + m_resolver(_resolver), + m_currentContract(_currentContract), + m_returnParameters(_returnParameters), + m_resolveInsideCode(_resolveInsideCode), + m_allowLazyTypes(_allowLazyTypes) { _root.accept(*this); } diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 828776179..6528bbef2 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -65,9 +65,10 @@ public: private: void reset(); - /// 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); + /// 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); /// Computes "C3-Linearization" of base contracts and stores it inside the contract. void linearizeBaseContracts(ContractDefinition& _contract) const; @@ -126,13 +127,18 @@ private: class ReferencesResolver: private ASTVisitor { public: - ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, - ContractDefinition const* _currentContract, - ParameterList const* _returnParameters, - bool _allowLazyTypes = true); + ReferencesResolver( + ASTNode& _root, + NameAndTypeResolver& _resolver, + ContractDefinition const* _currentContract, + ParameterList const* _returnParameters, + bool _resolveInsideCode = false, + bool _allowLazyTypes = true + ); private: virtual void endVisit(VariableDeclaration& _variable) override; + virtual bool visit(Block&) override { return m_resolveInsideCode; } virtual bool visit(Identifier& _identifier) override; virtual bool visit(UserDefinedTypeName& _typeName) override; virtual bool visit(Mapping&) override; @@ -141,6 +147,7 @@ private: NameAndTypeResolver& m_resolver; ContractDefinition const* m_currentContract; ParameterList const* m_returnParameters; + bool m_resolveInsideCode; bool m_allowLazyTypes; }; diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 78649cc95..e35dc0b6f 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -92,13 +93,13 @@ std::pair const* MemberList::getMemberStorageOffset(string const { TypePointers memberTypes; memberTypes.reserve(m_memberTypes.size()); - for (auto const& nameAndType: m_memberTypes) - memberTypes.push_back(nameAndType.second); + for (auto const& member: m_memberTypes) + memberTypes.push_back(member.type); m_storageOffsets.reset(new StorageOffsets()); m_storageOffsets->computeOffsets(memberTypes); } for (size_t index = 0; index < m_memberTypes.size(); ++index) - if (m_memberTypes[index].first == _name) + if (m_memberTypes[index].name == _name) return m_storageOffsets->getOffset(index); return nullptr; } @@ -189,7 +190,7 @@ TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length if (_length) { if (!_length->getType()) - _length->checkTypeRequirements(); + _length->checkTypeRequirements(nullptr); auto const* length = dynamic_cast(_length->getType().get()); if (!length) BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length.")); @@ -793,18 +794,46 @@ MemberList const& ContractType::getMembers() const if (!m_members) { // All address members and all interface functions - vector> members(IntegerType::AddressMemberList.begin(), - IntegerType::AddressMemberList.end()); + MemberList::MemberMap members( + IntegerType::AddressMemberList.begin(), + IntegerType::AddressMemberList.end() + ); if (m_super) { + // add the most derived of all functions which are visible in derived contracts for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts()) for (ASTPointer const& function: base->getDefinedFunctions()) - if (function->isVisibleInDerivedContracts()) - members.push_back(make_pair(function->getName(), make_shared(*function, true))); + { + if (!function->isVisibleInDerivedContracts()) + continue; + auto functionType = make_shared(*function, true); + bool functionWithEqualArgumentsFound = false; + for (auto const& member: members) + { + if (member.name != function->getName()) + continue; + auto memberType = dynamic_cast(member.type.get()); + solAssert(!!memberType, "Override changes type."); + if (!memberType->hasEqualArgumentTypes(*functionType)) + continue; + functionWithEqualArgumentsFound = true; + break; + } + if (!functionWithEqualArgumentsFound) + members.push_back(MemberList::Member( + function->getName(), + functionType, + function.get() + )); + } } else for (auto const& it: m_contract.getInterfaceFunctions()) - members.push_back(make_pair(it.second->getDeclaration().getName(), it.second)); + members.push_back(MemberList::Member( + it.second->getDeclaration().getName(), + it.second, + &it.second->getDeclaration() + )); m_members.reset(new MemberList(members)); } return *m_members; @@ -823,16 +852,6 @@ shared_ptr const& ContractType::getConstructorType() const return m_constructorType; } -u256 ContractType::getFunctionIdentifier(string const& _functionName) const -{ - auto interfaceFunctions = m_contract.getInterfaceFunctions(); - for (auto const& it: m_contract.getInterfaceFunctions()) - if (it.second->getDeclaration().getName() == _functionName) - return FixedHash<4>::Arith(it.first); - - return Invalid256; -} - vector> ContractType::getStateVariables() const { vector variables; @@ -873,8 +892,8 @@ u256 StructType::getStorageSize() const bool StructType::canLiveOutsideStorage() const { - for (pair const& member: getMembers()) - if (!member.second->canLiveOutsideStorage()) + for (auto const& member: getMembers()) + if (!member.type->canLiveOutsideStorage()) return false; return true; } @@ -891,7 +910,7 @@ MemberList const& StructType::getMembers() const { MemberList::MemberMap members; for (ASTPointer const& variable: m_struct.getMembers()) - members.push_back(make_pair(variable->getName(), variable->getType())); + members.push_back(MemberList::Member(variable->getName(), variable->getType(), variable.get())); m_members.reset(new MemberList(members)); } return *m_members; @@ -996,11 +1015,11 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): vector retParamNames; if (auto structType = dynamic_cast(returnType.get())) { - for (pair const& member: structType->getMembers()) - if (member.second->canLiveOutsideStorage()) + for (auto const& member: structType->getMembers()) + if (member.type->canLiveOutsideStorage()) { - retParamNames.push_back(member.first); - retParams.push_back(member.second); + retParamNames.push_back(member.name); + retParams.push_back(member.type); } } else @@ -1130,12 +1149,12 @@ MemberList const& FunctionType::getMembers() const case Location::Bare: if (!m_members) { - vector> members{ + MemberList::MemberMap members{ {"value", make_shared(parseElementaryTypeVector({"uint"}), TypePointers{copyAndSetGasOrValue(false, true)}, Location::SetValue, false, m_gasSet, m_valueSet)}}; if (m_location != Location::Creation) - members.push_back(make_pair("gas", make_shared( + members.push_back(MemberList::Member("gas", make_shared( parseElementaryTypeVector({"uint"}), TypePointers{copyAndSetGasOrValue(true, false)}, Location::SetGas, false, m_gasSet, m_valueSet))); @@ -1147,6 +1166,37 @@ MemberList const& FunctionType::getMembers() const } } +bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes) const +{ + TypePointers const& parameterTypes = getParameterTypes(); + if (takesArbitraryParameters()) + return true; + else if (_argumentTypes.size() != parameterTypes.size()) + return false; + else + return std::equal( + _argumentTypes.cbegin(), + _argumentTypes.cend(), + parameterTypes.cbegin(), + [](TypePointer const& argumentType, TypePointer const& parameterType) + { + return argumentType->isImplicitlyConvertibleTo(*parameterType); + } + ); +} + +bool FunctionType::hasEqualArgumentTypes(FunctionType const& _other) const +{ + if (m_parameterTypes.size() != _other.m_parameterTypes.size()) + return false; + return equal( + m_parameterTypes.cbegin(), + m_parameterTypes.cend(), + _other.m_parameterTypes.cbegin(), + [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; } + ); +} + string FunctionType::externalSignature(std::string const& _name) const { std::string funcName = _name; @@ -1167,6 +1217,11 @@ string FunctionType::externalSignature(std::string const& _name) const return ret + ")"; } +u256 FunctionType::externalIdentifier() const +{ + return FixedHash<4>::Arith(FixedHash<4>(dev::sha3(externalSignature()))); +} + TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) { TypePointers pointers; @@ -1250,7 +1305,7 @@ MemberList const& TypeType::getMembers() const // We need to lazy-initialize it because of recursive references. if (!m_members) { - vector> members; + MemberList::MemberMap members; if (m_actualType->getCategory() == Category::Contract && m_currentContract != nullptr) { ContractDefinition const& contract = dynamic_cast(*m_actualType).getContractDefinition(); @@ -1259,14 +1314,14 @@ MemberList const& TypeType::getMembers() const // We are accessing the type of a base contract, so add all public and protected // members. Note that this does not add inherited functions on purpose. for (Declaration const* decl: contract.getInheritableMembers()) - members.push_back(make_pair(decl->getName(), decl->getType())); + members.push_back(MemberList::Member(decl->getName(), decl->getType(), decl)); } else if (m_actualType->getCategory() == Category::Enum) { EnumDefinition const& enumDef = dynamic_cast(*m_actualType).getEnumDefinition(); auto enumType = make_shared(enumDef); for (ASTPointer const& enumValue: enumDef.getMembers()) - members.push_back(make_pair(enumValue->getName(), enumType)); + members.push_back(MemberList::Member(enumValue->getName(), enumType)); } m_members.reset(new MemberList(members)); } diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 5fa09f690..446737faa 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -69,17 +69,43 @@ private: class MemberList { public: - using MemberMap = std::vector>; + struct Member + { + Member(std::string const& _name, TypePointer const& _type, Declaration const* _declaration = nullptr): + name(_name), + type(_type), + declaration(_declaration) + { + } + + std::string name; + TypePointer type; + Declaration const* declaration = nullptr; + }; + + using MemberMap = std::vector; MemberList() {} explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {} MemberList& operator=(MemberList&& _other); TypePointer getMemberType(std::string const& _name) const { + TypePointer type; for (auto const& it: m_memberTypes) - if (it.first == _name) - return it.second; - return TypePointer(); + if (it.name == _name) + { + solAssert(!type, "Requested member type by non-unique name."); + type = it.type; + } + return type; + } + MemberMap membersByName(std::string const& _name) const + { + MemberMap members; + for (auto const& it: m_memberTypes) + if (it.name == _name) + members.push_back(it); + return members; } /// @returns the offset of the given member in storage slots and bytes inside a slot or /// a nullptr if the member is not part of storage. @@ -104,7 +130,7 @@ public: enum class Category { Integer, IntegerConstant, Bool, Real, Array, - FixedBytes, Contract, Struct, Function, OverloadedFunctions, Enum, + FixedBytes, Contract, Struct, Function, Enum, Mapping, Void, TypeType, Modifier, Magic }; @@ -554,11 +580,18 @@ public: virtual unsigned getSizeOnStack() const override; virtual MemberList const& getMembers() const override; + /// @returns true if this function can take the given argument types (possibly + /// after implicit conversion). + bool canTakeArguments(TypePointers const& _arguments) const; + bool hasEqualArgumentTypes(FunctionType const& _other) const; + Location const& getLocation() const { return m_location; } /// @returns the external 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 externalSignature(std::string const& _name = "") const; + /// @returns the external identifier of this function (the hash of the signature). + u256 externalIdentifier() const; Declaration const& getDeclaration() const { solAssert(m_declaration, "Requested declaration from a FunctionType that has none"); @@ -597,20 +630,6 @@ private: Declaration const* m_declaration = nullptr; }; -class OverloadedFunctionType: public Type -{ -public: - explicit OverloadedFunctionType(Identifier* _identifier): m_identifier(_identifier) {} - - virtual Category getCategory() const override { return Category::OverloadedFunctions; } - virtual std::string toString() const override { return "OverloadedFunctions"; } - - Identifier* getIdentifier() const { return m_identifier; } - -private: - Identifier * m_identifier; -}; - /** * The type of a mapping, there is one distinct type per key/value type pair. * Mappings always occupy their own storage slot, but do not actually use it. diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 3c4972fcf..6c54f4c02 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -397,8 +397,8 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) StructType const* s = dynamic_cast(_type); for(auto const& structMember: s->getMembers()) { - auto slotAndOffset = s->getStorageOffsetsOfMember(structMember.first); - r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.first), nodeType(structMember.second.get()), slotAndOffset.first, slotAndOffset.second }); + auto slotAndOffset = s->getStorageOffsetsOfMember(structMember.name); + r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.name), nodeType(structMember.type.get()), slotAndOffset.first, slotAndOffset.second }); } } break; diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 8218a2524..c18b3c562 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -304,7 +304,6 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr) BOOST_AUTO_TEST_CASE(calling_other_functions) { - // note that the index of a function is its index in the sorted sequence of functions char const* sourceCode = "contract collatz {\n" " function run(uint x) returns(uint y) {\n" " while ((y = x) > 1) {\n" @@ -1095,26 +1094,6 @@ BOOST_AUTO_TEST_CASE(now) BOOST_CHECK(callContractFunction("someInfo()") == encodeArgs(true)); } -BOOST_AUTO_TEST_CASE(function_types) -{ - char const* sourceCode = "contract test {\n" - " function a(bool selector) returns (uint b) {\n" - " var f = fun1;\n" - " if (selector) f = fun2;\n" - " return f(9);\n" - " }\n" - " function fun1(uint x) returns (uint b) {\n" - " return 11;\n" - " }\n" - " function fun2(uint x) returns (uint b) {\n" - " return 12;\n" - " }\n" - "}\n"; - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("a(bool)", false) == encodeArgs(11)); - BOOST_CHECK(callContractFunction("a(bool)", true) == encodeArgs(12)); -} - BOOST_AUTO_TEST_CASE(type_conversions_cleanup) { // 22-byte integer converted to a contract (i.e. address, 20 bytes), converted to a 32 byte @@ -3571,6 +3550,61 @@ BOOST_AUTO_TEST_CASE(packed_storage_structs_bytes) BOOST_CHECK(callContractFunction("test()") == encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(packed_storage_structs_delete) +{ + char const* sourceCode = R"( + contract C { + struct str { uint8 a; uint16 b; uint8 c; } + uint8 x; + uint16 y; + str data; + function test() returns (uint) { + x = 1; + y = 2; + data.a = 2; + data.b = 0xabcd; + data.c = 0xfa; + if (x != 1 || y != 2 || data.a != 2 || data.b != 0xabcd || data.c != 0xfa) + return 2; + delete y; + delete data.b; + if (x != 1 || y != 0 || data.a != 2 || data.b != 0 || data.c != 0xfa) + return 3; + delete x; + delete data; + return 1; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(1)); + BOOST_CHECK(m_state.storage(m_contractAddress).empty()); +} + +BOOST_AUTO_TEST_CASE(packed_storage_structs_with_bytes0) +{ + char const* sourceCode = R"( + contract C { + struct str { uint8 a; bytes0 b; uint8 c; } + uint8 a; + bytes0 x; + uint8 b; + str data; + function test() returns (bool) { + a = 2; + b = 3; + data.a = 4; + data.c = 5; + delete x; + delete data.b; + return a == 2 && b == 3 && data.a == 4 && data.c == 5; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(true)); +} + BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_first) { char const* sourceCode = R"( @@ -3616,26 +3650,6 @@ BOOST_AUTO_TEST_CASE(overloaded_function_call_with_if_else) BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs(10)); } -BOOST_AUTO_TEST_CASE(overloaded_function_with_var) -{ - char const* sourceCode = R"( - contract test { - function f(uint k) returns(uint d) { return k; } - function f(uint a, uint b) returns(uint d) { return a + b; } - function g(bool flag) returns(uint d) { - var x = f; - if (flag) - return x(3); - else - return x(3, 7); - } - } - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("g(bool)", true) == encodeArgs(3)); - BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs(10)); -} - BOOST_AUTO_TEST_CASE(derived_overload_base_function_direct) { char const* sourceCode = R"( @@ -3664,59 +3678,19 @@ BOOST_AUTO_TEST_CASE(derived_overload_base_function_indirect) BOOST_CHECK(callContractFunction("h()") == encodeArgs(2)); } -BOOST_AUTO_TEST_CASE(packed_storage_structs_delete) +BOOST_AUTO_TEST_CASE(super_overload) { char const* sourceCode = R"( - contract C { - struct str { uint8 a; uint16 b; uint8 c; } - uint8 x; - uint16 y; - str data; - function test() returns (uint) { - x = 1; - y = 2; - data.a = 2; - data.b = 0xabcd; - data.c = 0xfa; - if (x != 1 || y != 2 || data.a != 2 || data.b != 0xabcd || data.c != 0xfa) - return 2; - delete y; - delete data.b; - if (x != 1 || y != 0 || data.a != 2 || data.b != 0 || data.c != 0xfa) - return 3; - delete x; - delete data; - return 1; - } - } - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("test()") == encodeArgs(1)); - BOOST_CHECK(m_state.storage(m_contractAddress).empty()); -} - -BOOST_AUTO_TEST_CASE(packed_storage_structs_with_bytes0) -{ - char const* sourceCode = R"( - contract C { - struct str { uint8 a; bytes0 b; uint8 c; } - uint8 a; - bytes0 x; - uint8 b; - str data; - function test() returns (bool) { - a = 2; - b = 3; - data.a = 4; - data.c = 5; - delete x; - delete data.b; - return a == 2 && b == 3 && data.a == 4 && data.c == 5; - } + contract A { function f(uint a) returns(uint) { return 2 * a; } } + contract B { function f(bool b) returns(uint) { return 10; } } + contract C is A, B { + function g() returns(uint) { return super.f(true); } + function h() returns(uint) { return super.f(1); } } )"; - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("test()") == encodeArgs(true)); + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(10)); + BOOST_CHECK(callContractFunction("h()") == encodeArgs(2)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 6bde9fd24..fd439fc84 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -1637,7 +1637,7 @@ BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve) BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function) { - // literal 1 can be both converted to uint8 and uint8, so it's ambiguous. + // literal 1 can be both converted to uint and uint8, so the call is ambiguous. char const* sourceCode = R"( contract test { function f(uint8 a) returns(uint) { return a; } @@ -1648,6 +1648,78 @@ BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function) BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); } +BOOST_AUTO_TEST_CASE(assignment_of_nonoverloaded_function) +{ + char const* sourceCode = R"( + contract test { + function f(uint a) returns(uint) { return 2 * a; } + function g() returns(uint) { var x = f; return x(7); } + } + )"; + ETH_TEST_REQUIRE_NO_THROW(parseTextAndResolveNames(sourceCode), "Type resolving failed"); +} + +BOOST_AUTO_TEST_CASE(assignment_of_overloaded_function) +{ + char const* sourceCode = R"( + contract test { + function f() returns(uint) { return 1; } + function f(uint a) returns(uint) { return 2 * a; } + function g() returns(uint) { var x = f; return x(7); } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(external_types_clash) +{ + char const* sourceCode = R"( + contract base { + enum a { X } + function f(a) { } + } + contract test is base { + function f(uint8 a) { } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(override_changes_return_types) +{ + char const* sourceCode = R"( + contract base { + function f(uint a) returns (uint) { } + } + contract test is base { + function f(uint a) returns (uint8) { } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(multiple_constructors) +{ + char const* sourceCode = R"( + contract test { + function test(uint a) { } + function test() {} + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), DeclarationError); +} + +BOOST_AUTO_TEST_CASE(equal_overload) +{ + char const* sourceCode = R"( + contract test { + function test(uint a) returns (uint b) { } + function test(uint a) external {} + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), DeclarationError); +} + BOOST_AUTO_TEST_SUITE_END() } From 46182cd82f55679cfa6e4146217feb047af15b20 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 20 Apr 2015 14:57:56 +0200 Subject: [PATCH 17/28] Styling. --- libsolidity/DeclarationContainer.cpp | 14 +++++--------- libsolidity/NameAndTypeResolver.cpp | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/libsolidity/DeclarationContainer.cpp b/libsolidity/DeclarationContainer.cpp index 565f71dfd..5f8d24e34 100644 --- a/libsolidity/DeclarationContainer.cpp +++ b/libsolidity/DeclarationContainer.cpp @@ -24,10 +24,9 @@ #include #include -namespace dev -{ -namespace solidity -{ +using namespace std; +using namespace dev; +using namespace dev::solidity; bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _invisible, bool _update) { @@ -62,7 +61,7 @@ bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, return true; } -std::set DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const +set DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const { solAssert(!_name.empty(), "Attempt to resolve empty name."); auto result = m_declarations.find(_name); @@ -70,8 +69,5 @@ std::set DeclarationContainer::resolveName(ASTString const& return result->second; if (_recursive && m_enclosingContainer) return m_enclosingContainer->resolveName(_name, true); - return std::set({}); -} - -} + return set({}); } diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 1c527b89c..a286934a9 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -31,7 +31,7 @@ namespace dev namespace solidity { -NameAndTypeResolver::NameAndTypeResolver(std::vector const& _globals) +NameAndTypeResolver::NameAndTypeResolver(vector const& _globals) { for (Declaration const* declaration: _globals) m_scopes[nullptr].registerDeclaration(*declaration); @@ -119,7 +119,7 @@ set NameAndTypeResolver::resolveName(ASTString const& _name, { auto iterator = m_scopes.find(_scope); if (iterator == end(m_scopes)) - return std::set({}); + return set({}); return iterator->second.resolveName(_name, false); } @@ -415,11 +415,17 @@ bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) { auto declarations = m_resolver.getNameFromCurrentScope(_typeName.getName()); if (declarations.empty()) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation()) - << errinfo_comment("Undeclared identifier.")); + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(_typeName.getLocation()) << + errinfo_comment("Undeclared identifier.") + ); else if (declarations.size() > 1) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation()) - << errinfo_comment("Duplicate identifier.")); + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(_typeName.getLocation()) << + errinfo_comment("Duplicate identifier.") + ); else _typeName.setReferencedDeclaration(**declarations.begin()); return false; From 50d78468f012b8f4b72066b5b17ba9a32cceae68 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 21 Apr 2015 08:25:45 +0200 Subject: [PATCH 18/28] policy CMP0054 set to new --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77180e56f..b3773861e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ cmake_policy(SET CMP0020 NEW) # 3.1 and above if ((${CMAKE_MAJOR_VERSION} GREATER 2) AND (${CMAKE_MINOR_VERSION} GREATER 0)) # implicitly dereference variables (deprecated in 3.1) - cmake_policy(SET CMP0054 OLD) + cmake_policy(SET CMP0054 NEW) endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") From 6f1bfcb23230bd2e340d407260f3b83f216c6bc9 Mon Sep 17 00:00:00 2001 From: Jan Willem Penterman Date: Tue, 21 Apr 2015 10:34:07 +0200 Subject: [PATCH 19/28] added some missing "color" definitions and uisng basic charset for logger "icons" --- libdevcore/Log.cpp | 9 +++ libdevcore/Terminal.h | 131 ++++++++++++++++++++---------------------- 2 files changed, 72 insertions(+), 68 deletions(-) diff --git a/libdevcore/Log.cpp b/libdevcore/Log.cpp index c4cacad26..5515fd8da 100644 --- a/libdevcore/Log.cpp +++ b/libdevcore/Log.cpp @@ -34,12 +34,21 @@ using namespace dev; int dev::g_logVerbosity = 5; map dev::g_logOverride; +#ifdef _WIN32 +const char* LogChannel::name() { return EthGray "..."; } +const char* LeftChannel::name() { return EthNavy "<--"; } +const char* RightChannel::name() { return EthGreen "-->"; } +const char* WarnChannel::name() { return EthOnRed EthBlackBold " X"; } +const char* NoteChannel::name() { return EthBlue " i"; } +const char* DebugChannel::name() { return EthWhite " D"; } +#else const char* LogChannel::name() { return EthGray "···"; } const char* LeftChannel::name() { return EthNavy "◀▬▬"; } const char* RightChannel::name() { return EthGreen "▬▬▶"; } const char* WarnChannel::name() { return EthOnRed EthBlackBold " ✘"; } const char* NoteChannel::name() { return EthBlue " ℹ"; } const char* DebugChannel::name() { return EthWhite " ◇"; } +#endif LogOutputStreamBase::LogOutputStreamBase(char const* _id, std::type_info const* _info, unsigned _v) { diff --git a/libdevcore/Terminal.h b/libdevcore/Terminal.h index f4e31e7e4..a30a527f2 100644 --- a/libdevcore/Terminal.h +++ b/libdevcore/Terminal.h @@ -9,75 +9,70 @@ namespace con #define EthReset "" // Text Reset -// Regular Colors -#define EthBlack "" // Black -#define EthRed "" // Red -#define EthGreen "" // Green -#define EthYellow "" // Yellow -#define EthBlue "" // Blue -#define EthPurple "" // Purple -#define EthCyan "" // Cyan -#define EthWhite "" // White - -// Bold -#define EthBlackB "" // Black -#define EthRedB "" // Red -#define EthGreenB "" // Green -#define EthYellowB "" // Yellow -#define EthBlueB "" // Blue -#define EthPurpleB "" // Purple -#define EthCyanB "" // Cyan -#define EthWhiteB "" // White - -// Underline -#define EthBlackU "" // Black -#define EthRedU "" // Red -#define EthGreenU "" // Green -#define EthYellowU "" // Yellow -#define EthBlueU "" // Blue -#define EthPurpleU "" // Purple -#define EthCyanU "" // Cyan -#define EthWhiteU "" // White +#define EthReset "" // Text Reset -// Background -#define EthBlackOn "" // Black -#define EthRedOn "" // Red -#define EthGreenOn "" // Green -#define EthYellowOn "" // Yellow -#define EthBlueOn "" // Blue -#define EthPurpleOn "" // Purple -#define EthCyanOn "" // Cyan -#define EthWhiteOn "" // White - -// High Intensity -#define EthCoal "" // Black -#define EthRedI "" // Red -#define EthLime "" // Green -#define EthYellowI "" // Yellow -#define EthBlueI "" // Blue -#define EthPurpleI "" // Purple -#define EthCyanI "" // Cyan -#define EthWhiteI "" // White - -// Bold High Intensity -#define EthBlackBI "" // Black -#define EthRedBI "" // Red -#define EthGreenBI "" // Green -#define EthYellowBI "" // Yellow -#define EthBlueBI "" // Blue -#define EthPurpleBI "" // Purple -#define EthCyanBI "" // Cyan -#define EthWhiteBI "" // White - -// High Intensity backgrounds -#define EthBlackOnI "" // Black -#define EthRedOnI "" // Red -#define EthGreenOnI "" // Green -#define EthYellowOnI "" // Yellow -#define EthBlueOnI "" // Blue -#define EthPurpleOnI "" // Purple -#define EthCyanOnI "" // Cyan -#define EthWhiteOnI "" // White + // Regular Colors +#define EthBlack "" // Black +#define EthCoal "" // Black +#define EthGray "" // White +#define EthWhite "" // White +#define EthMaroon "" // Red +#define EthRed "" // Red +#define EthGreen "" // Green +#define EthLime "" // Green +#define EthOrange "" // Yellow +#define EthYellow "" // Yellow +#define EthNavy "" // Blue +#define EthBlue "" // Blue +#define EthViolet "" // Purple +#define EthPurple "" // Purple +#define EthTeal "" // Cyan +#define EthCyan "" // Cyan + +#define EthBlackBold "" // Black +#define EthCoalBold "" // Black +#define EthGrayBold "" // White +#define EthWhiteBold "" // White +#define EthMaroonBold "" // Red +#define EthRedBold "" // Red +#define EthGreenBold "" // Green +#define EthLimeBold "" // Green +#define EthOrangeBold "" // Yellow +#define EthYellowBold "" // Yellow +#define EthNavyBold "" // Blue +#define EthBlueBold "" // Blue +#define EthVioletBold "" // Purple +#define EthPurpleBold "" // Purple +#define EthTealBold "" // Cyan +#define EthCyanBold "" // Cyan + + // Background +#define EthOnBlack "" // Black +#define EthOnCoal "" // Black +#define EthOnGray "" // White +#define EthOnWhite "" // White +#define EthOnMaroon "" // Red +#define EthOnRed "" // Red +#define EthOnGreen "" // Green +#define EthOnLime "" // Green +#define EthOnOrange "" // Yellow +#define EthOnYellow "" // Yellow +#define EthOnNavy "" // Blue +#define EthOnBlue "" // Blue +#define EthOnViolet "" // Purple +#define EthOnPurple "" // Purple +#define EthOnTeal "" // Cyan +#define EthOnCyan "" // Cyan + + // Underline +#define EthBlackUnder "" // Black +#define EthGrayUnder "" // White +#define EthMaroonUnder "" // Red +#define EthGreenUnder "" // Green +#define EthOrangeUnder "" // Yellow +#define EthNavyUnder "" // Blue +#define EthVioletUnder "" // Purple +#define EthTealUnder "" // Cyan #else From aa9583a14b8c545793f4ace95ec2161a767e2c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 21 Apr 2015 10:37:47 +0200 Subject: [PATCH 20/28] testeth: Handle command line options for user defined tests --- test/TestHelper.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index ed844e961..92d38c008 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -542,6 +542,8 @@ void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _e void userDefinedTest(string testTypeFlag, std::function doTests) { + Options::get(); // parse command line options, e.g. to enable JIT + for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) { string arg = boost::unit_test::framework::master_test_suite().argv[i]; From 69db1c7181249a2b70fd39f8f5185166dc192815 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 21 Apr 2015 11:45:33 +0200 Subject: [PATCH 21/28] Removed (and added) some #ifs (but removed more than I added). --- alethzero/CMakeLists.txt | 4 +++- alethzero/Transact.cpp | 6 ++++++ test/CMakeLists.txt | 11 +++++------ test/libethereum/state.cpp | 2 ++ test/libevmcore/CMakeLists.txt | 5 ----- test/{libevmcore => libsolidity}/Assembly.cpp | 4 ---- test/libsolidity/SolidityABIJSON.cpp | 3 --- test/libsolidity/SolidityCompiler.cpp | 4 ---- test/libsolidity/SolidityEndToEndTest.cpp | 4 ---- test/libsolidity/SolidityExpressionCompiler.cpp | 4 ---- test/libsolidity/SolidityInterface.cpp | 4 ---- test/libsolidity/SolidityNameAndTypeResolution.cpp | 4 ---- test/libsolidity/SolidityNatspecJSON.cpp | 4 ---- test/libsolidity/SolidityOptimizer.cpp | 4 ---- test/libsolidity/SolidityParser.cpp | 4 ---- test/libsolidity/SolidityScanner.cpp | 4 ---- test/libsolidity/SolidityTypes.cpp | 4 ---- 17 files changed, 16 insertions(+), 59 deletions(-) delete mode 100644 test/libevmcore/CMakeLists.txt rename test/{libevmcore => libsolidity}/Assembly.cpp (99%) diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index fb134a82d..d9e8dff6f 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -51,7 +51,9 @@ target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} lll) -target_link_libraries(${EXECUTABLE} solidity) +if (SOLIDITY) + target_link_libraries(${EXECUTABLE} solidity) +endif () target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} web3jsonrpc) diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 1ebdf9e23..d3e5650bf 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -168,6 +168,7 @@ static std::string toString(TransactionException _te) } } +#if ETH_SOLIDITY static string getFunctionHashes(dev::solidity::CompilerStack const& _compiler, string const& _contractName) { string ret = ""; @@ -182,6 +183,7 @@ static string getFunctionHashes(dev::solidity::CompilerStack const& _compiler, s } return ret; } +#endif static tuple, bytes, string> userInputToCode(string const& _user, bool _opt) { @@ -197,6 +199,7 @@ static tuple, bytes, string> userInputToCode(string const& _user, boost::replace_all_copy(u, " ", ""); data = fromHex(u); } +#if ETH_SOLIDITY else if (sourceIsSolidity(_user)) { dev::solidity::CompilerStack compiler(true); @@ -220,6 +223,7 @@ static tuple, bytes, string> userInputToCode(string const& _user, errors.push_back("Solidity: Uncaught exception"); } } +#endif #if ETH_SERPENT else if (sourceIsSerpent(_user)) { @@ -394,6 +398,7 @@ void Transact::on_send_clicked() // If execution is a contract creation, add Natspec to // a local Natspec LEVELDB ethereum()->submitTransaction(s, value(), m_data, ui->gas->value(), gasPrice()); +#if ETH_SOLIDITY string src = ui->data->toPlainText().toStdString(); if (sourceIsSolidity(src)) try @@ -407,6 +412,7 @@ void Transact::on_send_clicked() } } catch (...) {} +#endif } else ethereum()->submitTransaction(s, value(), m_context->fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b46ab06e5..a97bb86fc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,19 +23,18 @@ add_subdirectory(libdevcrypto) add_subdirectory(libethcore) add_subdirectory(libethereum) add_subdirectory(libevm) -add_subdirectory(libevmcore) add_subdirectory(libnatspec) add_subdirectory(libp2p) -add_subdirectory(libsolidity) +if (SOLIDITY) + add_subdirectory(libsolidity) +endif () +if (JSONRPC) add_subdirectory(libweb3jsonrpc) +endif () add_subdirectory(libwhisper) set(SRC_LIST ${SRC_LIST} ${SRC}) -if (NOT JSONRPC) - list(REMOVE_ITEM SRC_LIST "libweb3jsonrpc/./AccountHolder.cpp") -endif() - include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ..) include_directories(${Boost_INCLUDE_DIRS}) diff --git a/test/libethereum/state.cpp b/test/libethereum/state.cpp index e6811d972..670ed0330 100644 --- a/test/libethereum/state.cpp +++ b/test/libethereum/state.cpp @@ -171,10 +171,12 @@ BOOST_AUTO_TEST_CASE(stMemoryStressTest) dev::test::executeTests("stMemoryStressTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); } +#if ETH_SOLIDITY BOOST_AUTO_TEST_CASE(stSolidityTest) { dev::test::executeTests("stSolidityTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); } +#endif BOOST_AUTO_TEST_CASE(stMemoryTest) { diff --git a/test/libevmcore/CMakeLists.txt b/test/libevmcore/CMakeLists.txt deleted file mode 100644 index 3ceda13b0..000000000 --- a/test/libevmcore/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -cmake_policy(SET CMP0015 NEW) - -aux_source_directory(. SRCS) - -add_sources(${SRCS}) diff --git a/test/libevmcore/Assembly.cpp b/test/libsolidity/Assembly.cpp similarity index 99% rename from test/libevmcore/Assembly.cpp rename to test/libsolidity/Assembly.cpp index fab260a9d..8dcee7fb5 100644 --- a/test/libevmcore/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -20,8 +20,6 @@ * Unit tests for Assembly Items from evmcore/Assembly.h */ -#if ETH_SOLIDITY - #include #include #include @@ -120,5 +118,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index b0633cca1..f0e54a940 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -19,7 +19,6 @@ * @date 2014 * Unit tests for the solidity compiler JSON Interface output. */ -#if ETH_SOLIDITY #include "../TestHelper.h" #include @@ -501,5 +500,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } - -#endif diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp index bb16c88cd..7b0ceedb6 100644 --- a/test/libsolidity/SolidityCompiler.cpp +++ b/test/libsolidity/SolidityCompiler.cpp @@ -20,8 +20,6 @@ * Unit tests for the solidity compiler. */ -#if ETH_SOLIDITY - #include #include #include @@ -193,5 +191,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index c345f5204..db58c3441 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -21,8 +21,6 @@ * Unit tests for the solidity expression compiler, testing the behaviour of the code. */ -#if ETH_SOLIDITY - #include #include #include @@ -3704,5 +3702,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 505cac991..613b0b1f7 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -20,8 +20,6 @@ * Unit tests for the solidity expression compiler. */ -#if ETH_SOLIDITY - #include #include @@ -491,5 +489,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityInterface.cpp b/test/libsolidity/SolidityInterface.cpp index ab6cb9029..c8f74e3aa 100644 --- a/test/libsolidity/SolidityInterface.cpp +++ b/test/libsolidity/SolidityInterface.cpp @@ -20,8 +20,6 @@ * Unit tests for generating source interfaces for Solidity contracts. */ -#if ETH_SOLIDITY - #include "../TestHelper.h" #include #include @@ -149,5 +147,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } - -#endif diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 917ea0007..076ea595b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -20,8 +20,6 @@ * Unit tests for the name and type resolution of the solidity parser. */ -#if ETH_SOLIDITY - #include #include @@ -1671,5 +1669,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 99adcf199..d2c1ec186 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -20,8 +20,6 @@ * Unit tests for the solidity compiler JSON Interface output. */ -#if ETH_SOLIDITY - #include "../TestHelper.h" #include #include @@ -539,5 +537,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } - -#endif diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 8ab1de8f1..af9b51467 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -20,8 +20,6 @@ * Tests for the Solidity optimizer. */ -#if ETH_SOLIDITY - #include #include #include @@ -816,5 +814,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 7baa12921..ad17c40a6 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -20,8 +20,6 @@ * Unit tests for the solidity parser. */ -#if ETH_SOLIDITY - #include #include #include @@ -855,5 +853,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 20b946ee0..8d3e53929 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -20,8 +20,6 @@ * Unit tests for the solidity scanner. */ -#if ETH_SOLIDITY - #include #include @@ -288,5 +286,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index da8b48303..6b6306479 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -20,8 +20,6 @@ * Unit tests for the type system of Solidity. */ -#if ETH_SOLIDITY - #include #include @@ -93,5 +91,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } - -#endif From c4ad72f29211ea07043c3c290a86cb7055ac5731 Mon Sep 17 00:00:00 2001 From: Jan Willem Penterman Date: Tue, 21 Apr 2015 12:54:32 +0200 Subject: [PATCH 22/28] removed special characters from Net Log messages (Win only) --- libp2p/Common.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp index 1853679cf..f4394b146 100644 --- a/libp2p/Common.cpp +++ b/libp2p/Common.cpp @@ -34,6 +34,19 @@ bool dev::p2p::NodeIPEndpoint::test_allowLocal = false; //⊳⊲◀▶■▣▢□▷◁▧▨▩▲◆◉◈◇◎●◍◌○◼☑☒☎☢☣☰☀♽♥♠✩✭❓✔✓✖✕✘✓✔✅⚒⚡⦸⬌∅⁕«««»»»⚙━┅┉▬ +#ifdef _WIN32 +const char* NetWarn::name() { return EthYellow "N" EthRed " X"; } +const char* NetImpolite::name() { return EthYellow "N" EthRed " !"; } +const char* NetNote::name() { return EthYellow "N" EthBlue " i"; } +const char* NetConnect::name() { return EthYellow "N" EthYellow " C"; } +const char* NetMessageSummary::name() { return EthYellow "N" EthWhite " ."; } +const char* NetMessageDetail::name() { return EthYellow "N" EthGray " o"; } +const char* NetTriviaSummary::name() { return EthYellow "N" EthGray " O"; } +const char* NetTriviaDetail::name() { return EthYellow "N" EthCoal " 0"; } +const char* NetAllDetail::name() { return EthYellow "N" EthCoal " A"; } +const char* NetRight::name() { return EthYellow "N" EthGreen "->"; } +const char* NetLeft::name() { return EthYellow "N" EthNavy "<-"; } +#else const char* NetWarn::name() { return EthYellow "⧎" EthRed " ✘"; } const char* NetImpolite::name() { return EthYellow "⧎" EthRed " !"; } const char* NetNote::name() { return EthYellow "⧎" EthBlue " ℹ"; } @@ -45,6 +58,7 @@ const char* NetTriviaDetail::name() { return EthYellow "⧎" EthCoal " ◍"; } const char* NetAllDetail::name() { return EthYellow "⧎" EthCoal " ●"; } const char* NetRight::name() { return EthYellow "⧎" EthGreen "▬▶"; } const char* NetLeft::name() { return EthYellow "⧎" EthNavy "◀▬"; } +#endif bool p2p::isPublicAddress(std::string const& _addressToCheck) { From 7386869de60855429fa2587b2ad51b0cee9ab9d2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 20 Apr 2015 14:09:41 +0200 Subject: [PATCH 23/28] Fixed byte alignment for return type of ripemd160 built-in contract. --- libsolidity/ExpressionCompiler.cpp | 8 +++++++- test/libsolidity/SolidityEndToEndTest.cpp | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 7ea71a7a4..02f22593a 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -1066,7 +1066,13 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio m_context << eth::Instruction::POP; m_context << eth::Instruction::POP; // pop contract address - if (firstType) + if (_functionType.getLocation() == FunctionType::Location::RIPEMD160) + { + // fix: built-in contract returns right-aligned data + CompilerUtils(m_context).loadFromMemory(0, IntegerType(160), false, true); + appendTypeConversion(IntegerType(160), FixedBytesType(20)); + } + else if (firstType) CompilerUtils(m_context).loadFromMemory(0, *firstType, false, true); } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index c345f5204..1d437ee5f 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1499,7 +1499,7 @@ BOOST_AUTO_TEST_CASE(ripemd) { h256 ret; dev::ripemd160(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32)); - return u256(ret) >> (256 - 160); + return u256(ret); }; testSolidityAgainstCpp("a(bytes32)", f, u256(4)); testSolidityAgainstCpp("a(bytes32)", f, u256(5)); @@ -1814,7 +1814,7 @@ BOOST_AUTO_TEST_CASE(gas_for_builtin) )"; compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("test(uint256)", 500) == bytes()); - BOOST_CHECK(callContractFunction("test(uint256)", 800) == encodeArgs(u256("0x8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"), true)); + BOOST_CHECK(callContractFunction("test(uint256)", 800) == encodeArgs(u256("0x8eb208f7e05d987a9b044a8e98c6b087f15a0bfc000000000000000000000000"), true)); } BOOST_AUTO_TEST_CASE(value_complex) From ea5e343e4d33d22da7d2763a01954a0eaaa6bdbd Mon Sep 17 00:00:00 2001 From: Jan Willem Penterman Date: Tue, 21 Apr 2015 14:42:14 +0200 Subject: [PATCH 24/28] more ascii art for windows logging --- libethereum/BlockChain.cpp | 7 +++++++ libethereum/BlockQueue.cpp | 4 ++++ libethereum/Client.cpp | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 8bba5ff1b..b1c519858 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -48,10 +48,17 @@ namespace js = json_spirit; #define ETH_CATCH 1 #define ETH_TIMED_IMPORTS 0 +#ifdef _WIN32 +const char* BlockChainDebug::name() { return EthBlue "8" EthWhite " <>"; } +const char* BlockChainWarn::name() { return EthBlue "8" EthOnRed EthBlackBold " X"; } +const char* BlockChainNote::name() { return EthBlue "8" EthBlue " i"; } +const char* BlockChainChat::name() { return EthBlue "8" EthWhite " o"; } +#else const char* BlockChainDebug::name() { return EthBlue "☍" EthWhite " ◇"; } const char* BlockChainWarn::name() { return EthBlue "☍" EthOnRed EthBlackBold " ✘"; } const char* BlockChainNote::name() { return EthBlue "☍" EthBlue " ℹ"; } const char* BlockChainChat::name() { return EthBlue "☍" EthWhite " ◌"; } +#endif std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) { diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index b056dc74f..0c40521e2 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -29,7 +29,11 @@ using namespace std; using namespace dev; using namespace dev::eth; +#ifdef _WIN32 +const char* BlockQueueChannel::name() { return EthOrange "[]->"; } +#else const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; } +#endif ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, bool _isOurs) { diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index b9ddcdbaa..cbd650c75 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -124,10 +124,17 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, ActivityReport const& _r) return _out; } +#ifdef _WIN32 +const char* ClientNote::name() { return EthTeal "^" EthBlue " i"; } +const char* ClientChat::name() { return EthTeal "^" EthWhite " o"; } +const char* ClientTrace::name() { return EthTeal "^" EthGray " O"; } +const char* ClientDetail::name() { return EthTeal "^" EthCoal " 0"; } +#else const char* ClientNote::name() { return EthTeal "⧫" EthBlue " ℹ"; } const char* ClientChat::name() { return EthTeal "⧫" EthWhite " ◌"; } const char* ClientTrace::name() { return EthTeal "⧫" EthGray " ◎"; } const char* ClientDetail::name() { return EthTeal "⧫" EthCoal " ●"; } +#endif Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId): Worker("eth"), From 3146a7aeb75b91d78d7a3027ec3812e8346cdb9b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 21 Apr 2015 13:35:38 +0200 Subject: [PATCH 25/28] Fix for Contract and Enum types as external function arguments. --- libsolidity/AST.h | 11 ++++----- libsolidity/Types.cpp | 8 +++---- libsolidity/Types.h | 29 +++++++++++++++++++---- test/libsolidity/SolidityEndToEndTest.cpp | 19 +++++++++++++++ 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 0c133ff1a..a75bdf7dc 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -143,8 +143,8 @@ public: ASTString const& getName() const { return *m_name; } Visibility getVisibility() const { return m_visibility == Visibility::Default ? getDefaultVisibility() : m_visibility; } bool isPublic() const { return getVisibility() >= Visibility::Public; } - bool isVisibleInContract() const { return getVisibility() != Visibility::External; } - virtual bool isVisibleInDerivedContracts() const { return isVisibleInContract() && getVisibility() >= Visibility::Internal; } + virtual bool isVisibleInContract() const { return getVisibility() != Visibility::External; } + bool isVisibleInDerivedContracts() const { return isVisibleInContract() && getVisibility() >= Visibility::Internal; } /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. /// Available only after name and type resolution step. @@ -156,7 +156,7 @@ public: /// contract types. virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0; virtual bool isLValue() const { return false; } - virtual bool isPartOfExternalInterface() const { return false; }; + virtual bool isPartOfExternalInterface() const { return false; } protected: virtual Visibility getDefaultVisibility() const { return Visibility::Public; } @@ -437,10 +437,9 @@ public: ASTPointer const& getReturnParameterList() const { return m_returnParameters; } Block const& getBody() const { return *m_body; } - virtual bool isVisibleInDerivedContracts() const override + virtual bool isVisibleInContract() const override { - return !isConstructor() && !getName().empty() && isVisibleInContract() && - getVisibility() >= Visibility::Internal; + return Declaration::isVisibleInContract() && !isConstructor() && !getName().empty(); } virtual TypePointer getType(ContractDefinition const*) const override; virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !getName().empty(); } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index bfde7187a..eb24a4629 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -1109,7 +1109,7 @@ unsigned FunctionType::getSizeOnStack() const return size; } -TypePointer FunctionType::externalType() const +FunctionTypePointer FunctionType::externalFunctionType() const { TypePointers paramTypes; TypePointers retParamTypes; @@ -1117,13 +1117,13 @@ TypePointer FunctionType::externalType() const for (auto type: m_parameterTypes) { if (!type->externalType()) - return TypePointer(); + return FunctionTypePointer(); paramTypes.push_back(type->externalType()); } for (auto type: m_returnParameterTypes) { if (!type->externalType()) - return TypePointer(); + return FunctionTypePointer(); retParamTypes.push_back(type->externalType()); } return make_shared(paramTypes, retParamTypes, m_location, m_arbitraryParameters); @@ -1168,7 +1168,7 @@ string FunctionType::externalSignature(std::string const& _name) const } string ret = funcName + "("; - TypePointers externalParameterTypes = dynamic_cast(*externalType()).getParameterTypes(); + TypePointers externalParameterTypes = externalFunctionType()->getParameterTypes(); for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it) { solAssert(!!(*it), "Parameter should have external type"); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index e6d32d175..493fde545 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -404,12 +404,20 @@ public: virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; + virtual unsigned getCalldataEncodedSize(bool _padded = true) const override + { + return externalType()->getCalldataEncodedSize(_padded); + } virtual unsigned getStorageBytes() const override { return 20; } + virtual bool canLiveOutsideStorage() const override { return true; } virtual bool isValueType() const override { return true; } virtual std::string toString() const override; virtual MemberList const& getMembers() const override; - virtual TypePointer externalType() const override { return std::make_shared(160, IntegerType::Modifier::Address); } + virtual TypePointer externalType() const override + { + return std::make_shared(160, IntegerType::Modifier::Address); + } bool isSuper() const { return m_super; } ContractDefinition const& getContractDefinition() const { return m_contract; } @@ -472,13 +480,21 @@ public: explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; + virtual unsigned getCalldataEncodedSize(bool _padded = true) const override + { + return externalType()->getCalldataEncodedSize(_padded); + } virtual unsigned getSizeOnStack() const override { return 1; } virtual unsigned getStorageBytes() const override; + virtual bool canLiveOutsideStorage() const override { return true; } virtual std::string toString() const override; virtual bool isValueType() const override { return true; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer externalType() const override { return std::make_shared(8 * int(getStorageBytes())); } + virtual TypePointer externalType() const override + { + return std::make_shared(8 * int(getStorageBytes())); + } EnumDefinition const& getEnumDefinition() const { return m_enum; } /// @returns the value that the string has in the Enum @@ -512,9 +528,12 @@ public: virtual Category getCategory() const override { return Category::Function; } - /// @returns TypePointer of a new FunctionType object. All input/return parameters are an appropriate external types of input/return parameters of current function. - /// Returns an empty shared pointer if one of the input/return parameters does not have an externaltype. - virtual TypePointer externalType() const override; + /// @returns TypePointer of a new FunctionType object. All input/return parameters are an + /// appropriate external types of input/return parameters of current function. + /// Returns an empty shared pointer if one of the input/return parameters does not have an + /// external type. + virtual FunctionTypePointer externalFunctionType() const; + virtual TypePointer externalType() const override { return externalFunctionType(); } explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); explicit FunctionType(VariableDeclaration const& _varDecl); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index c345f5204..f01e4c851 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -3699,6 +3699,25 @@ BOOST_AUTO_TEST_CASE(packed_storage_signed) BOOST_CHECK( callContractFunction("test()") == encodeArgs(u256(-2), u256(4), u256(-112), u256(0))); } +BOOST_AUTO_TEST_CASE(external_types_in_calls) +{ + char const* sourceCode = R"( + contract C1 { C1 public bla; function C1(C1 x) { bla = x; } } + contract C { + function test() returns (C1 x, C1 y) { + C1 c = new C1(C1(9)); + x = c.bla(); + y = this.t1(C1(7)); + } + function t1(C1 a) returns (C1) { return a; } + function() returns (C1) { return C1(9); } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(9), u256(7))); + BOOST_CHECK(callContractFunction("nonexisting") == encodeArgs(u256(9))); +} + BOOST_AUTO_TEST_SUITE_END() } From e2c147f6887a3db0d6333a9507962dc55dc91ee0 Mon Sep 17 00:00:00 2001 From: Jan Willem Penterman Date: Tue, 21 Apr 2015 17:00:02 +0200 Subject: [PATCH 26/28] multiple gpu support (and option to use less than maximum cpu threads) --- eth/main.cpp | 38 ++++++++++++++++++++++++-------- libethash-cl/ethash_cl_miner.cpp | 21 ++++++++++++++++++ libethash-cl/ethash_cl_miner.h | 2 ++ libethcore/Ethash.cpp | 10 ++++++++- libethcore/Ethash.h | 14 +++++++----- 5 files changed, 70 insertions(+), 15 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index da8943e46..eabcc2a4d 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -136,6 +136,7 @@ void help() << " -G,--opencl When mining use the GPU via OpenCL." << endl << " --opencl-platform When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl << " --opencl-device When mining using -G/--opencl use OpenCL device n (default: 0)." << endl + << " -t, --mining-threads Limit number of CPU/GPU miners to n (default: use everything available on selected platform)" << endl << "Client networking:" << endl << " --client-name Add a name to your client's version string (default: blank)." << endl << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl @@ -481,7 +482,8 @@ int main(int argc, char** argv) /// Mining options MinerType minerType = MinerType::CPU; unsigned openclPlatform = 0; - unsigned openclDevice = 0; + unsigned openclDevice = 0; + unsigned miningThreads = UINT_MAX; /// File name for import/export. string filename; @@ -601,12 +603,12 @@ int main(int argc, char** argv) else if (arg == "--opencl-platform" && i + 1 < argc) try { openclPlatform= stol(argv[++i]); - } - catch (...) - { - cerr << "Bad " << arg << " option: " << argv[i] << endl; - return -1; - } + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; + } else if (arg == "--opencl-device" && i + 1 < argc) try { openclDevice = stol(argv[++i]); @@ -850,6 +852,17 @@ int main(int argc, char** argv) return -1; } } + else if ((arg == "-t" || arg == "--mining-threads") && i + 1 < argc) + { + try { + miningThreads = stol(argv[++i]); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; + } + } else if (arg == "-b" || arg == "--bootstrap") bootstrap = true; else if (arg == "-f" || arg == "--force-mining") @@ -905,9 +918,16 @@ int main(int argc, char** argv) if (sessionSecret) sigKey = KeyPair(sessionSecret); - ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform); - ProofOfWork::GPUMiner::setDefaultDevice(openclDevice); + + if (minerType == MinerType::CPU) { + ProofOfWork::CPUMiner::setNumInstances(miningThreads); + } + else if (minerType == MinerType::GPU) { + ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform); + ProofOfWork::GPUMiner::setDefaultDevice(openclDevice); + ProofOfWork::GPUMiner::setNumInstances(miningThreads); + } // Two codepaths is necessary since named block require database, but numbered // blocks are superuseful to have when database is already open in another process. if (mode == OperationMode::DAGInit && !(initDAG == LatestBlock || initDAG == PendingBlock)) diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index 53eabe349..891d3f97d 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -85,6 +85,27 @@ std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _devic return "{ \"platform\": \"" + platforms[platform_num].getInfo() + "\", \"device\": \"" + device.getInfo() + "\", \"version\": \"" + device_version + "\" }"; } +unsigned ethash_cl_miner::get_num_devices(unsigned _platformId) +{ + std::vector platforms; + cl::Platform::get(&platforms); + if (platforms.empty()) + { + debugf("No OpenCL platforms found.\n"); + return 0; + } + + std::vector devices; + unsigned platform_num = std::min(_platformId, platforms.size() - 1); + platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices); + if (devices.empty()) + { + debugf("No OpenCL devices found.\n"); + return 0; + } + return devices.size(); +} + void ethash_cl_miner::finish() { if (m_queue()) diff --git a/libethash-cl/ethash_cl_miner.h b/libethash-cl/ethash_cl_miner.h index 3046f037b..21635df91 100644 --- a/libethash-cl/ethash_cl_miner.h +++ b/libethash-cl/ethash_cl_miner.h @@ -33,6 +33,8 @@ public: bool init(ethash_params const& params, std::function _fillDAG, unsigned workgroup_size = 64, unsigned _platformId = 0, unsigned _deviceId = 0); static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0); + static unsigned get_num_devices(unsigned _platformId = 0); + void finish(); void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count); diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 7ff35fd2b..91ba00710 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -264,10 +264,12 @@ private: unsigned Ethash::GPUMiner::s_platformId = 0; unsigned Ethash::GPUMiner::s_deviceId = 0; +unsigned Ethash::GPUMiner::s_numInstances = 1; +unsigned Ethash::CPUMiner::s_numInstances = 1; Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): Miner(_ci), - Worker("gpuminer"), + Worker("gpuminer" + toString(index())), m_hook(new EthashCLHook(this)) { } @@ -308,6 +310,7 @@ void Ethash::GPUMiner::workLoop() auto p = EthashAux::params(m_minerSeed); auto cb = [&](void* d) { EthashAux::full(m_minerSeed, bytesRef((byte*)d, p.full_size)); }; + unsigned device = instances() > 0 ? index() : s_deviceId; m_miner->init(p, cb, 32, s_platformId, s_deviceId); } @@ -331,6 +334,11 @@ std::string Ethash::GPUMiner::platformInfo() return ethash_cl_miner::platform_info(s_platformId, s_deviceId); } +unsigned Ethash::GPUMiner::getNumDevices() +{ + return ethash_cl_miner::get_num_devices(s_platformId); +} + #endif } diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index 2bbe7d649..9a66e9865 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -78,17 +78,18 @@ public: static bool preVerify(BlockInfo const& _header); static WorkPackage package(BlockInfo const& _header); static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } + class CPUMiner: public Miner, Worker { public: CPUMiner(ConstructionInfo const& _ci): Miner(_ci), Worker("miner" + toString(index())) {} - static unsigned instances() { return std::thread::hardware_concurrency(); } + static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); } static std::string platformInfo(); static void setDefaultPlatform(unsigned) {} static void setDefaultDevice(unsigned) {} - + static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, std::thread::hardware_concurrency()); } protected: void kickOff() override { @@ -100,7 +101,7 @@ public: private: void workLoop() override; - static unsigned s_deviceId; + static unsigned s_numInstances; }; #if ETH_ETHASHCL || !ETH_TRUE @@ -112,11 +113,13 @@ public: GPUMiner(ConstructionInfo const& _ci); ~GPUMiner(); - static unsigned instances() { return 1; } + static unsigned instances() { return s_numInstances > 0 ? s_numInstances : 1; } static std::string platformInfo(); + static unsigned getNumDevices(); static void setDefaultPlatform(unsigned _id) { s_platformId = _id; } static void setDefaultDevice(unsigned _id) { s_deviceId = _id; } - + static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, getNumDevices()); } + protected: void kickOff() override; void pause() override; @@ -133,6 +136,7 @@ public: h256 m_minerSeed; ///< Last seed in m_miner static unsigned s_platformId; static unsigned s_deviceId; + static unsigned s_numInstances; }; #else using GPUMiner = CPUMiner; From 55b9038d6f24ebaef2273e0ab70c8b97cf7ca639 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 21 Apr 2015 17:22:46 +0200 Subject: [PATCH 27/28] More awesome logging stuff. --- alethzero/NatspecHandler.cpp | 2 +- eth/main.cpp | 6 +- libdevcore/Log.cpp | 4 +- libdevcore/Log.h | 111 ++++++++++++++++++++++++++++++++--- libdevcrypto/MemoryDB.cpp | 10 ++-- libdevcrypto/OverlayDB.cpp | 2 +- libdevcrypto/TrieDB.h | 12 ++-- libethcore/EthashAux.cpp | 6 +- libethereum/BlockChain.cpp | 28 +++------ libethereum/BlockQueue.cpp | 4 +- libethereum/Client.cpp | 16 +++-- libevmcore/Assembly.cpp | 2 +- libp2p/Network.cpp | 2 +- libp2p/NodeTable.cpp | 2 +- 14 files changed, 152 insertions(+), 55 deletions(-) diff --git a/alethzero/NatspecHandler.cpp b/alethzero/NatspecHandler.cpp index 27cf00341..ffab6db58 100644 --- a/alethzero/NatspecHandler.cpp +++ b/alethzero/NatspecHandler.cpp @@ -51,7 +51,7 @@ NatspecHandler::~NatspecHandler() void NatspecHandler::add(dev::h256 const& _contractHash, string const& _doc) { m_db->Put(m_writeOptions, _contractHash.ref(), _doc); - cdebug << "Registering NatSpec: " << _contractHash.abridged() << _doc; + cdebug << "Registering NatSpec: " << _contractHash << _doc; } string NatspecHandler::retrieve(dev::h256 const& _contractHash) const diff --git a/eth/main.cpp b/eth/main.cpp index da8943e46..b51a76142 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -370,7 +370,7 @@ void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod) for (unsigned i = 0; !completed; ++i) { if (current) - cnote << "Mining on PoWhash" << current.headerHash.abridged() << ": " << f.miningProgress(); + cnote << "Mining on PoWhash" << current.headerHash << ": " << f.miningProgress(); else cnote << "Getting work package..."; Json::Value v = rpc.eth_getWork(); @@ -380,12 +380,12 @@ void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod) current.headerHash = hh; current.seedHash = h256(v[1].asString()); current.boundary = h256(fromHex(v[2].asString()), h256::AlignRight); - cnote << "Got work package:" << current.headerHash.abridged() << " < " << current.boundary; + cnote << "Got work package:" << current.headerHash << " < " << current.boundary; f.setWork(current); } this_thread::sleep_for(chrono::milliseconds(_recheckPeriod)); } - cnote << "Solution found; submitting [" << solution.nonce << "," << current.headerHash.abridged() << "," << solution.mixHash.abridged() << "] to" << _remote << "..."; + cnote << "Solution found; submitting [" << solution.nonce << "," << current.headerHash << "," << solution.mixHash << "] to" << _remote << "..."; bool ok = rpc.eth_submitWork("0x" + toString(solution.nonce), "0x" + toString(current.headerHash), "0x" + toString(solution.mixHash)); if (ok) clog(HappyChannel) << "Submitted and accepted."; diff --git a/libdevcore/Log.cpp b/libdevcore/Log.cpp index c4cacad26..c71b28001 100644 --- a/libdevcore/Log.cpp +++ b/libdevcore/Log.cpp @@ -41,7 +41,9 @@ const char* WarnChannel::name() { return EthOnRed EthBlackBold " ✘"; } const char* NoteChannel::name() { return EthBlue " ℹ"; } const char* DebugChannel::name() { return EthWhite " ◇"; } -LogOutputStreamBase::LogOutputStreamBase(char const* _id, std::type_info const* _info, unsigned _v) +LogOutputStreamBase::LogOutputStreamBase(char const* _id, std::type_info const* _info, unsigned _v, bool _autospacing): + m_autospacing(_autospacing), + m_verbosity(_v) { auto it = g_logOverride.find(_info); if ((it != g_logOverride.end() && it->second == true) || (it == g_logOverride.end() && (int)_v <= g_logVerbosity)) diff --git a/libdevcore/Log.h b/libdevcore/Log.h index c8924724b..71c2b3450 100644 --- a/libdevcore/Log.h +++ b/libdevcore/Log.h @@ -26,8 +26,12 @@ #include #include #include +#include #include "vector_ref.h" +#include "Common.h" #include "CommonIO.h" +#include "CommonData.h" +#include "FixedHash.h" #include "Terminal.h" namespace dev @@ -76,35 +80,128 @@ std::string getThreadName(); /// The default logging channels. Each has an associated verbosity and three-letter prefix (name() ). /// Channels should inherit from LogChannel and define name() and verbosity. struct LogChannel { static const char* name(); static const int verbosity = 1; }; -struct LeftChannel: public LogChannel { static const char* name(); }; +struct LeftChannel: public LogChannel { static const char* name(); }; struct RightChannel: public LogChannel { static const char* name(); }; -struct WarnChannel: public LogChannel { static const char* name(); static const int verbosity = 0; }; -struct NoteChannel: public LogChannel { static const char* name(); }; +struct WarnChannel: public LogChannel { static const char* name(); static const int verbosity = 0; }; +struct NoteChannel: public LogChannel { static const char* name(); }; struct DebugChannel: public LogChannel { static const char* name(); static const int verbosity = 0; }; +enum LogTag +{ + None, + url, + error +}; + class LogOutputStreamBase { public: - LogOutputStreamBase(char const* _id, std::type_info const* _info, unsigned _v); + LogOutputStreamBase(char const* _id, std::type_info const* _info, unsigned _v, bool _autospacing); + + void comment(std::string const& _t) + { + switch (m_logTag) + { + case url: m_sstr << EthNavyUnder; break; + case error: m_sstr << EthRedBold; break; + default:; + } + m_sstr << _t << EthReset; + m_logTag = None; + } + + void append(unsigned long _t) { m_sstr << EthBlue << _t << EthReset; } + void append(long _t) { m_sstr << EthBlue << _t << EthReset; } + void append(unsigned int _t) { m_sstr << EthBlue << _t << EthReset; } + void append(int _t) { m_sstr << EthBlue << _t << EthReset; } + void append(bigint const& _t) { m_sstr << EthNavy << _t << EthReset; } + void append(u256 const& _t) { m_sstr << EthNavy << _t << EthReset; } + void append(u160 const& _t) { m_sstr << EthNavy << _t << EthReset; } + void append(double _t) { m_sstr << EthBlue << _t << EthReset; } + template void append(FixedHash const& _t) { m_sstr << EthTeal "#" << _t.abridged() << EthReset; } + void append(h160 const& _t) { m_sstr << EthRed "@" << _t.abridged() << EthReset; } + void append(h256 const& _t) { m_sstr << EthCyan "#" << _t.abridged() << EthReset; } + void append(h512 const& _t) { m_sstr << EthTeal "##" << _t.abridged() << EthReset; } + void append(std::string const& _t) { m_sstr << EthGreen "\"" + _t + "\"" EthReset; } + void append(bytes const& _t) { m_sstr << EthYellow "%" << toHex(_t) << EthReset; } + void append(bytesConstRef _t) { m_sstr << EthYellow "%" << toHex(_t) << EthReset; } + template void append(std::vector const& _t) + { + m_sstr << EthWhite "[" EthReset; + int n = 0; + for (auto const& i: _t) + { + m_sstr << (n++ ? EthWhite ", " EthReset : ""); + append(i); + } + m_sstr << EthWhite "]" EthReset; + } + template void append(std::set const& _t) + { + m_sstr << EthYellow "{" EthReset; + int n = 0; + for (auto const& i: _t) + { + m_sstr << (n++ ? EthYellow ", " EthReset : ""); + append(i); + } + m_sstr << EthYellow "}" EthReset; + } + template void append(std::map const& _t) + { + m_sstr << EthLime "{" EthReset; + int n = 0; + for (auto const& i: _t) + { + m_sstr << (n++ ? EthLime ", " EthReset : ""); + append(i.first); + m_sstr << (n++ ? EthLime ": " EthReset : ""); + append(i.second); + } + m_sstr << EthLime "}" EthReset; + } + template void append(std::pair const& _t) + { + m_sstr << EthPurple "(" EthReset; + append(_t.first); + m_sstr << EthPurple ", " EthReset; + append(_t.second); + m_sstr << EthPurple ")" EthReset; + } + template void append(T const& _t) + { + m_sstr << toString(_t); + } + template void append(boost::asio::ip::tcp::endpoint const& _t) + { + m_sstr << EthNavyUnder "tcp://" << _t << EthReset; + } protected: + bool m_autospacing = false; + unsigned m_verbosity = 0; std::stringstream m_sstr; ///< The accrued log entry. + LogTag m_logTag; }; /// Logging class, iostream-like, that can be shifted to. template -class LogOutputStream: private LogOutputStreamBase +class LogOutputStream: LogOutputStreamBase { public: /// Construct a new object. /// If _term is true the the prefix info is terminated with a ']' character; if not it ends only with a '|' character. - LogOutputStream(): LogOutputStreamBase(Id::name(), &typeid(Id), Id::verbosity) {} + LogOutputStream(): LogOutputStreamBase(Id::name(), &typeid(Id), Id::verbosity, _AutoSpacing) {} /// Destructor. Posts the accrued log entry to the g_logPost function. ~LogOutputStream() { if (Id::verbosity <= g_logVerbosity) g_logPost(m_sstr.str(), Id::name()); } + LogOutputStream& operator<<(std::string const& _t) { if (Id::verbosity <= g_logVerbosity) { if (_AutoSpacing && m_sstr.str().size() && m_sstr.str().back() != ' ') m_sstr << " "; comment(_t); } return *this; } + + LogOutputStream& operator<<(LogTag _t) { m_logTag = _t; return *this; } + /// Shift arbitrary data to the log. Spaces will be added between items as required. - template LogOutputStream& operator<<(T const& _t) { if (Id::verbosity <= g_logVerbosity) { if (_AutoSpacing && m_sstr.str().size() && m_sstr.str().back() != ' ') m_sstr << " "; m_sstr << _t; } return *this; } + template LogOutputStream& operator<<(T const& _t) { if (Id::verbosity <= g_logVerbosity) { if (_AutoSpacing && m_sstr.str().size() && m_sstr.str().back() != ' ') m_sstr << " "; append(_t); } return *this; } }; // Simple cout-like stream objects for accessing common log channels. diff --git a/libdevcrypto/MemoryDB.cpp b/libdevcrypto/MemoryDB.cpp index ab74173c4..a207bb7d6 100644 --- a/libdevcrypto/MemoryDB.cpp +++ b/libdevcrypto/MemoryDB.cpp @@ -49,7 +49,7 @@ std::string MemoryDB::lookup(h256 _h) const if (!m_enforceRefs || (m_refCount.count(it->first) && m_refCount.at(it->first))) return it->second; // else if (m_enforceRefs && m_refCount.count(it->first) && !m_refCount.at(it->first)) -// cnote << "Lookup required for value with no refs. Let's hope it's in the DB." << _h.abridged(); +// cnote << "Lookup required for value with no refs. Let's hope it's in the DB." << _h; } return std::string(); } @@ -67,7 +67,7 @@ void MemoryDB::insert(h256 _h, bytesConstRef _v) m_over[_h] = _v.toString(); m_refCount[_h]++; #if ETH_PARANOIA - dbdebug << "INST" << _h.abridged() << "=>" << m_refCount[_h]; + dbdebug << "INST" << _h << "=>" << m_refCount[_h]; #endif } @@ -82,15 +82,15 @@ bool MemoryDB::kill(h256 _h) { // If we get to this point, then there was probably a node in the level DB which we need to remove and which we have previously // used as part of the memory-based MemoryDB. Nothing to be worried about *as long as the node exists in the DB*. - dbdebug << "NOKILL-WAS" << _h.abridged(); + dbdebug << "NOKILL-WAS" << _h; return false; } - dbdebug << "KILL" << _h.abridged() << "=>" << m_refCount[_h]; + dbdebug << "KILL" << _h << "=>" << m_refCount[_h]; return true; } else { - dbdebug << "NOKILL" << _h.abridged(); + dbdebug << "NOKILL" << _h; return false; } #else diff --git a/libdevcrypto/OverlayDB.cpp b/libdevcrypto/OverlayDB.cpp index 5f8aea667..91f73ad49 100644 --- a/libdevcrypto/OverlayDB.cpp +++ b/libdevcrypto/OverlayDB.cpp @@ -103,7 +103,7 @@ void OverlayDB::kill(h256 _h) if (m_db) m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret); if (ret.empty()) - cnote << "Decreasing DB node ref count below zero with no DB node. Probably have a corrupt Trie." << _h.abridged(); + cnote << "Decreasing DB node ref count below zero with no DB node. Probably have a corrupt Trie." << _h; } #else MemoryDB::kill(_h); diff --git a/libdevcrypto/TrieDB.h b/libdevcrypto/TrieDB.h index 68a2c248d..a707c30f0 100644 --- a/libdevcrypto/TrieDB.h +++ b/libdevcrypto/TrieDB.h @@ -120,14 +120,14 @@ public: if (_r.isList() && _r.itemCount() == 2 && (!_wasExt || _out)) { if (_out) - (*_out) << std::string(_indent * 2, ' ') << (_wasExt ? "!2 " : "2 ") << sha3(_r.data()).abridged() << ": " << _r << "\n"; + (*_out) << std::string(_indent * 2, ' ') << (_wasExt ? "!2 " : "2 ") << sha3(_r.data()) << ": " << _r << "\n"; if (!isLeaf(_r)) // don't go down leaves descendEntry(_r[1], _keyMask, true, _out, _indent + 1); } else if (_r.isList() && _r.itemCount() == 17) { if (_out) - (*_out) << std::string(_indent * 2, ' ') << "17 " << sha3(_r.data()).abridged() << ": " << _r << "\n"; + (*_out) << std::string(_indent * 2, ' ') << "17 " << sha3(_r.data()) << ": " << _r << "\n"; for (unsigned i = 0; i < 16; ++i) if (!_r[i].isEmpty()) // 16 branches are allowed to be empty descendEntry(_r[i], _keyMask, false, _out, _indent + 1); @@ -779,7 +779,7 @@ template std::string GenericTrieDB::atAux(RLP const& _here, Nibbl template bytes GenericTrieDB::mergeAt(RLP const& _orig, NibbleSlice _k, bytesConstRef _v, bool _inLine) { #if ETH_PARANOIA - tdebug << "mergeAt " << _orig << _k << sha3(_orig.data()).abridged(); + tdebug << "mergeAt " << _orig << _k << sha3(_orig.data()); #endif // The caller will make sure that the bytes are inserted properly. @@ -854,7 +854,7 @@ template bytes GenericTrieDB::mergeAt(RLP const& _orig, NibbleSli template void GenericTrieDB::mergeAtAux(RLPStream& _out, RLP const& _orig, NibbleSlice _k, bytesConstRef _v) { #if ETH_PARANOIA - tdebug << "mergeAtAux " << _orig << _k << sha3(_orig.data()).abridged() << ((_orig.isData() && _orig.size() <= 32) ? _orig.toHash().abridged() : std::string()); + tdebug << "mergeAtAux " << _orig << _k << sha3(_orig.data()) << ((_orig.isData() && _orig.size() <= 32) ? _orig.toHash() : std::string()); #endif RLP r = _orig; @@ -902,7 +902,7 @@ template std::string GenericTrieDB::deref(RLP const& _n) const template bytes GenericTrieDB::deleteAt(RLP const& _orig, NibbleSlice _k) { #if ETH_PARANOIA - tdebug << "deleteAt " << _orig << _k << sha3(_orig.data()).abridged(); + tdebug << "deleteAt " << _orig << _k << sha3(_orig.data()); #endif // The caller will make sure that the bytes are inserted properly. @@ -1009,7 +1009,7 @@ template bytes GenericTrieDB::deleteAt(RLP const& _orig, NibbleSl template bool GenericTrieDB::deleteAtAux(RLPStream& _out, RLP const& _orig, NibbleSlice _k) { #if ETH_PARANOIA - tdebug << "deleteAtAux " << _orig << _k << sha3(_orig.data()).abridged() << ((_orig.isData() && _orig.size() <= 32) ? _orig.toHash().abridged() : std::string()); + tdebug << "deleteAtAux " << _orig << _k << sha3(_orig.data()) << ((_orig.isData() && _orig.size() <= 32) ? _orig.toHash() : std::string()); #endif bytes b = _orig.isEmpty() ? bytes() : deleteAt(_orig.isList() ? _orig : RLP(node(_orig.toHash())), _k); diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index 750d80082..44064cde6 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -79,7 +79,7 @@ h256 EthashAux::seedHash(unsigned _number) for (; n <= epoch; ++n, ret = sha3(ret)) { get()->m_seedHashes[n] = ret; -// cdebug << "Epoch" << n << "is" << ret.abridged(); +// cdebug << "Epoch" << n << "is" << ret; } } return get()->m_seedHashes[epoch]; @@ -95,12 +95,12 @@ ethash_params EthashAux::params(h256 const& _seedHash) } catch (...) { -// cdebug << "Searching for seedHash " << _seedHash.abridged(); +// cdebug << "Searching for seedHash " << _seedHash; for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) {} if (epoch == 2048) { std::ostringstream error; - error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); + error << "apparent block number for " << _seedHash << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); throw std::invalid_argument(error.str()); } } diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 8bba5ff1b..46e4b9adf 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -247,7 +247,7 @@ void BlockChain::rebuild(std::string const& _path, std::function parent is" << bi.parentHash.abridged() << "; expected" << lastHash.abridged() << "#" << (d - 1); + cwarn << "DISJOINT CHAIN DETECTED; " << bi.hash() << "#" << d << " -> parent is" << bi.parentHash << "; expected" << lastHash << "#" << (d - 1); return; } lastHash = bi.hash(); @@ -280,16 +280,6 @@ bool contains(T const& _t, V const& _v) return false; } -inline string toString(h256s const& _bs) -{ - ostringstream out; - out << "[ "; - for (auto i: _bs) - out << i.abridged() << ", "; - out << "]"; - return out.str(); -} - LastHashes BlockChain::lastHashes(unsigned _n) const { Guard l(x_lastLastHashes); @@ -323,14 +313,14 @@ tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st } catch (dev::eth::UnknownParent) { - cwarn << "ODD: Import queue contains block with unknown parent." << boost::current_exception_diagnostic_information(); + cwarn << "ODD: Import queue contains block with unknown parent." << error << boost::current_exception_diagnostic_information(); // NOTE: don't reimport since the queue should guarantee everything in the right order. // Can't continue - chain bad. badBlocks.push_back(BlockInfo::headerHash(block)); } catch (Exception const& _e) { - cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!" << diagnostic_information(_e); + cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!" << error << diagnostic_information(_e); // NOTE: don't reimport since the queue should guarantee everything in the right order. // Can't continue - chain bad. badBlocks.push_back(BlockInfo::headerHash(block)); @@ -427,7 +417,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import BOOST_THROW_EXCEPTION(FutureTime()); } - clog(BlockChainChat) << "Attempting import of " << bi.hash().abridged() << "..."; + clog(BlockChainChat) << "Attempting import of " << bi.hash() << "..."; #if ETH_TIMED_IMPORTS preliminaryChecks = t.elapsed(); @@ -704,7 +694,7 @@ void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end) tuple BlockChain::treeRoute(h256 const& _from, h256 const& _to, bool _common, bool _pre, bool _post) const { -// cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); +// cdebug << "treeRoute" << _from << "..." << _to; if (!_from || !_to) return make_tuple(h256s(), h256(), 0); h256s ret; @@ -719,7 +709,7 @@ tuple BlockChain::treeRoute(h256 const& _from, h256 const ret.push_back(from); from = details(from).parent; fn--; -// cdebug << "from:" << fn << _from.abridged(); +// cdebug << "from:" << fn << _from; } h256 to = _to; while (fn < tn) @@ -728,7 +718,7 @@ tuple BlockChain::treeRoute(h256 const& _from, h256 const back.push_back(to); to = details(to).parent; tn--; -// cdebug << "to:" << tn << _to.abridged(); +// cdebug << "to:" << tn << _to; } for (;; from = details(from).parent, to = details(to).parent) { @@ -738,7 +728,7 @@ tuple BlockChain::treeRoute(h256 const& _from, h256 const back.push_back(to); fn--; tn--; -// cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged(); +// cdebug << "from:" << fn << _from << "; to:" << tn << _to; if (from == to) break; if (!from) @@ -1010,7 +1000,7 @@ bytes BlockChain::block(h256 const& _hash) const if (d.empty()) { - cwarn << "Couldn't find requested block:" << _hash.abridged(); + cwarn << "Couldn't find requested block:" << _hash; return bytes(); } diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index b056dc74f..63df3f9fb 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -36,7 +36,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo // Check if we already know this block. h256 h = BlockInfo::headerHash(_block); - cblockq << "Queuing block" << h.abridged() << "for import..."; + cblockq << "Queuing block" << h << "for import..."; UpgradableGuard l(m_lock); @@ -95,7 +95,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo else if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash)) { // We don't know the parent (yet) - queue it up for later. It'll get resent to us if we find out about its ancestry later on. - cblockq << "OK - queued as unknown parent:" << bi.parentHash.abridged(); + cblockq << "OK - queued as unknown parent:" << bi.parentHash; m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes()))); m_unknownSet.insert(h); diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index b9ddcdbaa..00d4ca021 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -291,7 +291,15 @@ static string filtersToString(T const& _fs) ret << "{"; unsigned i = 0; for (h256 const& f: _fs) - ret << (i++ ? ", " : "") << (f == PendingChangedFilter ? "pending" : f == ChainChangedFilter ? "chain" : f.abridged()); + { + ret << (i++ ? ", " : ""); + if (f == PendingChangedFilter) + ret << url << "pending"; + else if (f == ChainChangedFilter) + ret << url << "chain"; + else + ret << f; + } ret << "}"; return ret.str(); } @@ -475,7 +483,7 @@ void Client::onChainChanged(ImportRoute const& _ir) // insert transactions that we are declaring the dead part of the chain for (auto const& h: _ir.second) { - clog(ClientNote) << "Dead block:" << h.abridged(); + clog(ClientNote) << "Dead block:" << h; for (auto const& t: m_bc.transactions(h)) { clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None); @@ -486,10 +494,10 @@ void Client::onChainChanged(ImportRoute const& _ir) // remove transactions from m_tq nicely rather than relying on out of date nonce later on. for (auto const& h: _ir.first) { - clog(ClientChat) << "Live block:" << h.abridged(); + clog(ClientChat) << "Live block:" << h; for (auto const& th: m_bc.transactionHashes(h)) { - clog(ClientNote) << "Safely dropping transaction " << th.abridged(); + clog(ClientNote) << "Safely dropping transaction " << th; m_tq.drop(th); } } diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index ae8567e2a..eac946230 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -311,7 +311,7 @@ Assembly& Assembly::optimise(bool _enable) unsigned total = 0; for (unsigned count = 1; count > 0; total += count) { - copt << *this; + copt << toString(*this); count = 0; copt << "Performing control flow analysis..."; diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp index 5702fbce7..1c780c5e9 100644 --- a/libp2p/Network.cpp +++ b/libp2p/Network.cpp @@ -228,7 +228,7 @@ bi::tcp::endpoint Network::resolveHost(string const& _addr) bi::tcp::resolver r(s_resolverIoService); auto it = r.resolve({split[0], toString(port)}, ec); if (ec) - clog(NetWarn) << "Error resolving host address " << _addr << ":" << ec.message(); + clog(NetWarn) << "Error resolving host address..." << url << _addr << ":" << error << ec.message(); else ep = *it; } diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 35db7fbac..4f81e42b9 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -87,7 +87,7 @@ shared_ptr NodeTable::addNode(Node const& _node) // we handle when tcp endpoint is 0 below if (_node.endpoint.address.to_string() == "0.0.0.0") { - clog(NodeTableWarn) << "addNode Failed. Invalid UDP address 0.0.0.0 for" << _node.id.abridged(); + clog(NodeTableWarn) << "addNode Failed. Invalid UDP address" << url << "0.0.0.0" << "for" << _node.id; return move(shared_ptr()); } From 6da67f1581b100435724fac7edbc679a17e735da Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 21 Apr 2015 17:26:54 +0200 Subject: [PATCH 28/28] Fix style. Fix device id. --- eth/main.cpp | 11 ++++++----- libethcore/Ethash.cpp | 5 +++-- libethereum/BlockQueue.cpp | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index 9839ba4f2..2d79f4954 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -482,7 +482,7 @@ int main(int argc, char** argv) /// Mining options MinerType minerType = MinerType::CPU; unsigned openclPlatform = 0; - unsigned openclDevice = 0; + unsigned openclDevice = 0; unsigned miningThreads = UINT_MAX; /// File name for import/export. @@ -602,7 +602,7 @@ int main(int argc, char** argv) } else if (arg == "--opencl-platform" && i + 1 < argc) try { - openclPlatform= stol(argv[++i]); + openclPlatform = stol(argv[++i]); } catch (...) { @@ -920,14 +920,15 @@ int main(int argc, char** argv) - if (minerType == MinerType::CPU) { + if (minerType == MinerType::CPU) ProofOfWork::CPUMiner::setNumInstances(miningThreads); - } - else if (minerType == MinerType::GPU) { + else if (minerType == MinerType::GPU) + { ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform); ProofOfWork::GPUMiner::setDefaultDevice(openclDevice); ProofOfWork::GPUMiner::setNumInstances(miningThreads); } + // Two codepaths is necessary since named block require database, but numbered // blocks are superuseful to have when database is already open in another process. if (mode == OperationMode::DAGInit && !(initDAG == LatestBlock || initDAG == PendingBlock)) diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 91ba00710..c40ce2625 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -121,6 +121,8 @@ bool Ethash::verify(BlockInfo const& _header) return slow; } +unsigned Ethash::CPUMiner::s_numInstances = 1; + void Ethash::CPUMiner::workLoop() { auto tid = std::this_thread::get_id(); @@ -265,7 +267,6 @@ private: unsigned Ethash::GPUMiner::s_platformId = 0; unsigned Ethash::GPUMiner::s_deviceId = 0; unsigned Ethash::GPUMiner::s_numInstances = 1; -unsigned Ethash::CPUMiner::s_numInstances = 1; Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): Miner(_ci), @@ -311,7 +312,7 @@ void Ethash::GPUMiner::workLoop() auto p = EthashAux::params(m_minerSeed); auto cb = [&](void* d) { EthashAux::full(m_minerSeed, bytesRef((byte*)d, p.full_size)); }; unsigned device = instances() > 0 ? index() : s_deviceId; - m_miner->init(p, cb, 32, s_platformId, s_deviceId); + m_miner->init(p, cb, 32, s_platformId, device); } uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192); diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index f8a07bd99..44ddda637 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -30,7 +30,7 @@ using namespace dev; using namespace dev::eth; #ifdef _WIN32 -const char* BlockQueueChannel::name() { return EthOrange "[]->"; } +const char* BlockQueueChannel::name() { return EthOrange "[]>"; } #else const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; } #endif