From d67e26398986057ed426be69701fd7148c94f04b Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 3 Dec 2014 07:47:08 +0100 Subject: [PATCH 01/10] Remove std::. --- libsolidity/Parser.cpp | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 0506bc3e3..2a76a05a8 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -26,12 +26,14 @@ #include #include +using namespace std; + namespace dev { namespace solidity { -ASTPointer Parser::parse(std::shared_ptr const& _scanner) +ASTPointer Parser::parse(shared_ptr const& _scanner) { m_scanner = _scanner; return parseContractDefinition(); @@ -55,7 +57,7 @@ public: { if (m_location.end < 0) markEndPosition(); - return std::make_shared(m_location, std::forward(_args)...); + return make_shared(m_location, forward(_args)...); } private: @@ -79,9 +81,9 @@ ASTPointer Parser::parseContractDefinition() expectToken(Token::CONTRACT); ASTPointer name = expectIdentifierToken(); expectToken(Token::LBRACE); - std::vector> structs; - std::vector> stateVariables; - std::vector> functions; + vector> structs; + vector> stateVariables; + vector> functions; bool visibilityIsPublic = true; while (true) { @@ -119,7 +121,7 @@ ASTPointer Parser::parseFunctionDefinition(bool _isPublic) ASTNodeFactory nodeFactory(*this); ASTPointer docstring; if (m_scanner->getCurrentCommentLiteral() != "") - docstring = std::make_shared(m_scanner->getCurrentCommentLiteral()); + docstring = make_shared(m_scanner->getCurrentCommentLiteral()); expectToken(Token::FUNCTION); ASTPointer name(expectIdentifierToken()); @@ -142,7 +144,7 @@ ASTPointer Parser::parseFunctionDefinition(bool _isPublic) // create an empty parameter list at a zero-length location ASTNodeFactory nodeFactory(*this); nodeFactory.setLocationEmpty(); - returnParameters = nodeFactory.createNode(std::vector>()); + returnParameters = nodeFactory.createNode(vector>()); } ASTPointer block = parseBlock(); nodeFactory.setEndPositionFromNode(block); @@ -156,7 +158,7 @@ ASTPointer Parser::parseStructDefinition() ASTNodeFactory nodeFactory(*this); expectToken(Token::STRUCT); ASTPointer name = expectIdentifierToken(); - std::vector> members; + vector> members; expectToken(Token::LBRACE); while (m_scanner->getCurrentToken() != Token::RBRACE) { @@ -228,7 +230,7 @@ ASTPointer Parser::parseMapping() ASTPointer Parser::parseParameterList(bool _allowEmpty) { ASTNodeFactory nodeFactory(*this); - std::vector> parameters; + vector> parameters; expectToken(Token::LPAREN); if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { @@ -249,7 +251,7 @@ ASTPointer Parser::parseBlock() { ASTNodeFactory nodeFactory(*this); expectToken(Token::LBRACE); - std::vector> statements; + vector> statements; while (m_scanner->getCurrentToken() != Token::RBRACE) statements.push_back(parseStatement()); nodeFactory.markEndPosition(); @@ -447,7 +449,7 @@ ASTPointer Parser::parseLeftHandSideExpression() case Token::LPAREN: { m_scanner->next(); - std::vector> arguments = parseFunctionCallArguments(); + vector> arguments = parseFunctionCallArguments(); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); expression = nodeFactory.createNode(expression, arguments); @@ -503,9 +505,9 @@ ASTPointer Parser::parsePrimaryExpression() return expression; } -std::vector> Parser::parseFunctionCallArguments() +vector> Parser::parseFunctionCallArguments() { - std::vector> arguments; + vector> arguments; if (m_scanner->getCurrentToken() != Token::RPAREN) { arguments.push_back(parseExpression()); @@ -521,7 +523,7 @@ std::vector> Parser::parseFunctionCallArguments() void Parser::expectToken(Token::Value _value) { if (m_scanner->getCurrentToken() != _value) - BOOST_THROW_EXCEPTION(createParserError(std::string("Expected token ") + std::string(Token::getName(_value)))); + BOOST_THROW_EXCEPTION(createParserError(string("Expected token ") + string(Token::getName(_value)))); m_scanner->next(); } @@ -543,12 +545,12 @@ ASTPointer Parser::expectIdentifierToken() ASTPointer Parser::getLiteralAndAdvance() { - ASTPointer identifier = std::make_shared(m_scanner->getCurrentLiteral()); + ASTPointer identifier = make_shared(m_scanner->getCurrentLiteral()); m_scanner->next(); return identifier; } -ParserError Parser::createParserError(std::string const& _description) const +ParserError Parser::createParserError(string const& _description) const { return ParserError() << errinfo_sourcePosition(getPosition()) << errinfo_comment(_description); } From 86219490674ca67b7c1b365be1b63a0a81079195 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 3 Dec 2014 07:46:55 +0100 Subject: [PATCH 02/10] Import directive. --- libsolidity/AST.cpp | 69 +++++++++++--------- libsolidity/AST.h | 34 ++++++++++ libsolidity/ASTForward.h | 2 + libsolidity/ASTPrinter.cpp | 9 ++- libsolidity/ASTPrinter.h | 3 +- libsolidity/ASTVisitor.h | 4 ++ libsolidity/CompilerStack.cpp | 87 +++++++++++++++----------- libsolidity/CompilerStack.h | 5 +- libsolidity/Parser.cpp | 43 ++++++++++--- libsolidity/Parser.h | 3 +- solc/main.cpp | 2 +- test/solidityCompiler.cpp | 26 +++++--- test/solidityExpressionCompiler.cpp | 47 +++++++------- test/solidityNameAndTypeResolution.cpp | 11 ++-- test/solidityParser.cpp | 55 +++++++++++++++- 15 files changed, 285 insertions(+), 115 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 4bd0b2c0e..697ffe8e3 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -33,6 +33,19 @@ namespace dev namespace solidity { +void SourceUnit::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + listAccept(m_nodes, _visitor); + _visitor.endVisit(*this); +} + +void ImportDirective::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + void ContractDefinition::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) @@ -57,34 +70,6 @@ void StructDefinition::checkValidityOfMembers() checkRecursion(); } -void StructDefinition::checkMemberTypes() -{ - for (ASTPointer const& member: getMembers()) - if (!member->getType()->canBeStored()) - BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct.")); -} - -void StructDefinition::checkRecursion() -{ - set definitionsSeen; - vector queue = {this}; - while (!queue.empty()) - { - StructDefinition const* def = queue.back(); - queue.pop_back(); - if (definitionsSeen.count(def)) - BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation()) - << errinfo_comment("Recursive struct definition.")); - definitionsSeen.insert(def); - for (ASTPointer const& member: def->getMembers()) - if (member->getType()->getCategory() == Type::Category::STRUCT) - { - UserDefinedTypeName const& typeName = dynamic_cast(*member->getTypeName()); - queue.push_back(&dynamic_cast(*typeName.getReferencedDeclaration())); - } - } -} - void ParameterList::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) @@ -312,6 +297,34 @@ vector ContractDefinition::getInterfaceFunctions() co return exportedFunctions; } +void StructDefinition::checkMemberTypes() +{ + for (ASTPointer const& member: getMembers()) + if (!member->getType()->canBeStored()) + BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct.")); +} + +void StructDefinition::checkRecursion() +{ + set definitionsSeen; + vector queue = {this}; + while (!queue.empty()) + { + StructDefinition const* def = queue.back(); + queue.pop_back(); + if (definitionsSeen.count(def)) + BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation()) + << errinfo_comment("Recursive struct definition.")); + definitionsSeen.insert(def); + for (ASTPointer const& member: def->getMembers()) + if (member->getType()->getCategory() == Type::Category::STRUCT) + { + UserDefinedTypeName const& typeName = dynamic_cast(*member->getTypeName()); + queue.push_back(&dynamic_cast(*typeName.getReferencedDeclaration())); + } + } +} + void FunctionDefinition::checkTypeRequirements() { for (ASTPointer const& var: getParameters() + getReturnParameters()) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 87bc3cd40..2b532d763 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -79,6 +79,40 @@ private: Location m_location; }; +/** + * Source unit containing import directives and contract definitions. + */ +class SourceUnit: public ASTNode +{ +public: + SourceUnit(Location const& _location, std::vector> const& _nodes): + ASTNode(_location), m_nodes(_nodes) {} + + virtual void accept(ASTVisitor& _visitor) override; + + std::vector> getNodes() const { return m_nodes; } + +private: + std::vector> m_nodes; +}; + +/** + * Import directive for referencing other files / source objects. + */ +class ImportDirective: public ASTNode +{ +public: + ImportDirective(Location const& _location, ASTPointer const& _url): + ASTNode(_location), m_url(_url) {} + + virtual void accept(ASTVisitor& _visitor) override; + + ASTString const& getURL() const { return *m_url; } + +private: + ASTPointer m_url; +}; + /** * Abstract AST class for a declaration (contract, function, struct, variable). */ diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index a369c8a79..8b4bac1ce 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -34,6 +34,8 @@ namespace solidity { class ASTNode; +class SourceUnit; +class ImportDirective; class Declaration; class ContractDefinition; class StructDefinition; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 987ad11cc..c62378fd3 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -43,6 +43,13 @@ void ASTPrinter::print(ostream& _stream) } +bool ASTPrinter::visit(ImportDirective& _node) +{ + writeLine("ImportDirective \"" + _node.getURL() + "\""); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(ContractDefinition& _node) { writeLine("ContractDefinition \"" + _node.getName() + "\""); @@ -270,7 +277,7 @@ bool ASTPrinter::visit(Literal& _node) return goDeeper(); } -void ASTPrinter::endVisit(ASTNode&) +void ASTPrinter::endVisit(ImportDirective&) { m_indentation--; } diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index e0757fbc4..1a18fc4a1 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -42,6 +42,7 @@ public: /// Output the string representation of the AST to _stream. void print(std::ostream& _stream); + bool visit(ImportDirective& _node) override; bool visit(ContractDefinition& _node) override; bool visit(StructDefinition& _node) override; bool visit(ParameterList& _node) override; @@ -73,7 +74,7 @@ public: bool visit(ElementaryTypeNameExpression& _node) override; bool visit(Literal& _node) override; - void endVisit(ASTNode& _node) override; + void endVisit(ImportDirective&) override; void endVisit(ContractDefinition&) override; void endVisit(StructDefinition&) override; void endVisit(ParameterList&) override; diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index 6e579f358..bf1ccc410 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/ASTVisitor.h @@ -42,6 +42,8 @@ class ASTVisitor { public: virtual bool visit(ASTNode&) { return true; } + virtual bool visit(SourceUnit&) { return true; } + virtual bool visit(ImportDirective&) { return true; } virtual bool visit(ContractDefinition&) { return true; } virtual bool visit(StructDefinition&) { return true; } virtual bool visit(ParameterList&) { return true; } @@ -74,6 +76,8 @@ public: virtual bool visit(Literal&) { return true; } virtual void endVisit(ASTNode&) { } + virtual void endVisit(SourceUnit&) { } + virtual void endVisit(ImportDirective&) { } virtual void endVisit(ContractDefinition&) { } virtual void endVisit(StructDefinition&) { } virtual void endVisit(ParameterList&) { } diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 6535e00d4..8f8c84fea 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -45,10 +45,14 @@ void CompilerStack::parse() { if (!m_scanner) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available.")); - m_contractASTNode = Parser().parse(m_scanner); + m_sourceUnitASTNode = Parser().parse(m_scanner); m_globalContext = make_shared(); - m_globalContext->setCurrentContract(*m_contractASTNode); - NameAndTypeResolver(m_globalContext->getDeclarations()).resolveNamesAndTypes(*m_contractASTNode); + for (ASTPointer const& node: m_sourceUnitASTNode->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + m_globalContext->setCurrentContract(*contract); + NameAndTypeResolver(m_globalContext->getDeclarations()).resolveNamesAndTypes(*contract); + } m_parseSuccessful = true; } @@ -62,10 +66,16 @@ bytes const& CompilerStack::compile(bool _optimize) { if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - m_bytecode.clear(); - m_compiler = make_shared(); - m_compiler->compileContract(*m_contractASTNode, m_globalContext->getMagicVariables()); - return m_bytecode = m_compiler->getAssembledBytecode(_optimize); + //@todo returns only the last contract for now + for (ASTPointer const& node: m_sourceUnitASTNode->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + m_bytecode.clear(); + m_compiler = make_shared(); + m_compiler->compileContract(*contract, m_globalContext->getMagicVariables()); + m_bytecode = m_compiler->getAssembledBytecode(_optimize); + } + return m_bytecode; } bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize) @@ -87,40 +97,45 @@ string const& CompilerStack::getInterface() BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); if (m_interface.empty()) { - stringstream interface; - interface << '['; - vector exportedFunctions = m_contractASTNode->getInterfaceFunctions(); - unsigned functionsCount = exportedFunctions.size(); - for (FunctionDefinition const* f: exportedFunctions) - { - auto streamVariables = [&](vector> const& _vars) + //@todo returns only the last contract for now + for (ASTPointer const& node: m_sourceUnitASTNode->getNodes()) + if (ContractDefinition const* contract = dynamic_cast(node.get())) { - unsigned varCount = _vars.size(); - for (ASTPointer const& var: _vars) + stringstream interface; + interface << '['; + vector exportedFunctions = contract->getInterfaceFunctions(); + unsigned functionsCount = exportedFunctions.size(); + for (FunctionDefinition const* f: exportedFunctions) { - interface << "{" - << "\"name\":" << escaped(var->getName(), false) << "," - << "\"type\":" << escaped(var->getType()->toString(), false) + auto streamVariables = [&](vector> const& _vars) + { + unsigned varCount = _vars.size(); + for (ASTPointer const& var: _vars) + { + interface << "{" + << "\"name\":" << escaped(var->getName(), false) << "," + << "\"type\":" << escaped(var->getType()->toString(), false) + << "}"; + if (--varCount > 0) + interface << ","; + } + }; + + interface << '{' + << "\"name\":" << escaped(f->getName(), false) << "," + << "\"inputs\":["; + streamVariables(f->getParameters()); + interface << "]," + << "\"outputs\":["; + streamVariables(f->getReturnParameters()); + interface << "]" << "}"; - if (--varCount > 0) + if (--functionsCount > 0) interface << ","; } - }; - - interface << '{' - << "\"name\":" << escaped(f->getName(), false) << "," - << "\"inputs\":["; - streamVariables(f->getParameters()); - interface << "]," - << "\"outputs\":["; - streamVariables(f->getReturnParameters()); - interface << "]" - << "}"; - if (--functionsCount > 0) - interface << ","; - } - interface << ']'; - m_interface = interface.str(); + interface << ']'; + m_interface = interface.str(); + } } return m_interface; } diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 6cae8660f..19f3cf99a 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -32,6 +32,7 @@ namespace solidity { // forward declarations class Scanner; +class SourceUnit; class ContractDefinition; class Compiler; class GlobalContext; @@ -65,7 +66,7 @@ public: /// Returns the previously used scanner, useful for counting lines during error reporting. Scanner const& getScanner() const { return *m_scanner; } - ContractDefinition& getAST() const { return *m_contractASTNode; } + SourceUnit& getAST() const { return *m_sourceUnitASTNode; } /// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for /// scanning the source code - this is useful for printing exception information. @@ -74,7 +75,7 @@ public: private: std::shared_ptr m_scanner; std::shared_ptr m_globalContext; - std::shared_ptr m_contractASTNode; + std::shared_ptr m_sourceUnitASTNode; bool m_parseSuccessful; std::string m_interface; std::shared_ptr m_compiler; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 2a76a05a8..c385dd8d1 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -20,6 +20,7 @@ * Solidity parser. */ +#include #include #include #include @@ -33,13 +34,6 @@ namespace dev namespace solidity { -ASTPointer Parser::parse(shared_ptr const& _scanner) -{ - m_scanner = _scanner; - return parseContractDefinition(); -} - - /// AST node factory that also tracks the begin and end position of an AST node /// while it is being parsed class Parser::ASTNodeFactory @@ -65,6 +59,28 @@ private: Location m_location; }; +ASTPointer Parser::parse(shared_ptr const& _scanner) +{ + m_scanner = _scanner; + ASTNodeFactory nodeFactory(*this); + vector> nodes; + while (_scanner->getCurrentToken() != Token::EOS) + { + switch (m_scanner->getCurrentToken()) + { + case Token::IMPORT: + nodes.push_back(parseImportDirective()); + break; + case Token::CONTRACT: + nodes.push_back(parseContractDefinition()); + break; + default: + BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition."))); + } + } + return nodeFactory.createNode(nodes); +} + int Parser::getPosition() const { return m_scanner->getCurrentLocation().start; @@ -75,6 +91,18 @@ int Parser::getEndPosition() const return m_scanner->getCurrentLocation().end; } +ASTPointer Parser::parseImportDirective() +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::IMPORT); + if (m_scanner->getCurrentToken() != Token::STRING_LITERAL) + BOOST_THROW_EXCEPTION(createParserError("Expected string literal (URL).")); + ASTPointer url = getLiteralAndAdvance(); + nodeFactory.markEndPosition(); + expectToken(Token::SEMICOLON); + return nodeFactory.createNode(url); +} + ASTPointer Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); @@ -112,7 +140,6 @@ ASTPointer Parser::parseContractDefinition() } nodeFactory.markEndPosition(); expectToken(Token::RBRACE); - expectToken(Token::EOS); return nodeFactory.createNode(name, structs, stateVariables, functions); } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 307a0d6a1..98a337b29 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -34,7 +34,7 @@ class Scanner; class Parser { public: - ASTPointer parse(std::shared_ptr const& _scanner); + ASTPointer parse(std::shared_ptr const& _scanner); private: class ASTNodeFactory; @@ -46,6 +46,7 @@ private: ///@{ ///@name Parsing functions for the AST nodes + ASTPointer parseImportDirective(); ASTPointer parseContractDefinition(); ASTPointer parseFunctionDefinition(bool _isPublic); ASTPointer parseStructDefinition(); diff --git a/solc/main.cpp b/solc/main.cpp index a7216e594..76d8ef6b7 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -127,7 +127,7 @@ int main(int argc, char** argv) return -1; } - cout << "Syntax tree for the contract:" << endl; + cout << "Syntax tree for the source unit:" << endl; ASTPrinter printer(compiler.getAST(), sourceCode); printer.print(cout); cout << "EVM assembly:" << endl; diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index 3e86919a6..883cecf28 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -46,16 +46,22 @@ namespace bytes compileContract(const string& _sourceCode) { Parser parser; - ASTPointer contract; - BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared(CharStream(_sourceCode)))); - NameAndTypeResolver resolver({}); - BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); - - Compiler compiler; - compiler.compileContract(*contract, {}); - // debug - //compiler.streamAssembly(cout); - return compiler.getAssembledBytecode(); + ASTPointer sourceUnit; + BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(CharStream(_sourceCode)))); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + NameAndTypeResolver resolver({}); + BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); + + Compiler compiler; + compiler.compileContract(*contract, {}); + // debug + //compiler.streamAssembly(cout); + return compiler.getAssembledBytecode(); + } + BOOST_FAIL("No contract found in source."); + return bytes(); } /// Checks that @a _compiledCode is present starting from offset @a _offset in @a _expectation. diff --git a/test/solidityExpressionCompiler.cpp b/test/solidityExpressionCompiler.cpp index 6ea66badb..b077e2620 100644 --- a/test/solidityExpressionCompiler.cpp +++ b/test/solidityExpressionCompiler.cpp @@ -86,27 +86,32 @@ bytes compileFirstExpression(const string& _sourceCode, vector> _ vector> _localVariables = {}) { Parser parser; - ASTPointer contract; - BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared(CharStream(_sourceCode)))); - NameAndTypeResolver resolver({}); - BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); - FirstExpressionExtractor extractor(*contract); - BOOST_REQUIRE(extractor.getExpression() != nullptr); - - CompilerContext context; - for (vector const& function: _functions) - context.addFunction(dynamic_cast(resolveDeclaration(function, resolver))); - for (vector const& variable: _localVariables) - context.addVariable(dynamic_cast(resolveDeclaration(variable, resolver))); - - ExpressionCompiler::compileExpression(context, *extractor.getExpression()); - - for (vector const& function: _functions) - context << context.getFunctionEntryLabel(dynamic_cast(resolveDeclaration(function, resolver))); - bytes instructions = context.getAssembledBytecode(); - // debug - // cout << eth::disassemble(instructions) << endl; - return instructions; + ASTPointer sourceUnit; + BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(CharStream(_sourceCode)))); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + NameAndTypeResolver resolver({}); + BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); + FirstExpressionExtractor extractor(*contract); + BOOST_REQUIRE(extractor.getExpression() != nullptr); + + CompilerContext context; + for (vector const& function: _functions) + context.addFunction(dynamic_cast(resolveDeclaration(function, resolver))); + for (vector const& variable: _localVariables) + context.addVariable(dynamic_cast(resolveDeclaration(variable, resolver))); + + ExpressionCompiler::compileExpression(context, *extractor.getExpression()); + + for (vector const& function: _functions) + context << context.getFunctionEntryLabel(dynamic_cast(resolveDeclaration(function, resolver))); + bytes instructions = context.getAssembledBytecode(); + // debug + // cout << eth::disassemble(instructions) << endl; + return instructions; + } + BOOST_FAIL("No contract found in source."); } } // end anonymous namespace diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 8804c519c..219fa2599 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -41,10 +41,13 @@ namespace void parseTextAndResolveNames(std::string const& _source) { Parser parser; - ASTPointer contract = parser.parse( - std::make_shared(CharStream(_source))); - NameAndTypeResolver resolver({}); - resolver.resolveNamesAndTypes(*contract); + ASTPointer sourceUnit = parser.parse(std::make_shared(CharStream(_source))); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + NameAndTypeResolver resolver({}); + resolver.resolveNamesAndTypes(*contract); + } } } diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 6a97a5d99..a9e2d531f 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -21,13 +21,15 @@ */ #include - +#include #include #include #include #include #include +using namespace std; + namespace dev { namespace solidity @@ -40,7 +42,12 @@ namespace ASTPointer parseText(std::string const& _source) { Parser parser; - return parser.parse(std::make_shared(CharStream(_source))); + ASTPointer sourceUnit = parser.parse(std::make_shared(CharStream(_source))); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ASTPointer contract = dynamic_pointer_cast(node)) + return contract; + BOOST_FAIL("No contract found in source."); + return ASTPointer(); } } @@ -380,6 +387,50 @@ BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(import_directive) +{ + char const* text = "import \"abc\";\n" + "contract test {\n" + " function fun() {\n" + " uint64(2);\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(multiple_contracts) +{ + char const* text = "contract test {\n" + " function fun() {\n" + " uint64(2);\n" + " }\n" + "}\n" + "contract test2 {\n" + " function fun() {\n" + " uint64(2);\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(multiple_contracts_and_imports) +{ + char const* text = "import \"abc\";\n" + "contract test {\n" + " function fun() {\n" + " uint64(2);\n" + " }\n" + "}\n" + "import \"def\";\n" + "contract test2 {\n" + " function fun() {\n" + " uint64(2);\n" + " }\n" + "}\n" + "import \"ghi\";\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + BOOST_AUTO_TEST_SUITE_END() } From 50b61e3dd390aa5bcabe8ad0d30dc00792443b90 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 3 Dec 2014 17:45:12 +0100 Subject: [PATCH 03/10] Multi-source and multi-contract compiler. --- libsolidity/BaseTypes.h | 7 +- libsolidity/CompilerStack.cpp | 219 +++++++++++++++++-------- libsolidity/CompilerStack.h | 61 +++++-- libsolidity/DeclarationContainer.cpp | 4 +- libsolidity/DeclarationContainer.h | 2 +- libsolidity/GlobalContext.cpp | 3 +- libsolidity/GlobalContext.h | 3 +- libsolidity/NameAndTypeResolver.cpp | 12 +- libsolidity/NameAndTypeResolver.h | 6 + libsolidity/Scanner.cpp | 17 +- libsolidity/Scanner.h | 11 +- test/solidityCompiler.cpp | 3 +- test/solidityExpressionCompiler.cpp | 3 +- test/solidityNameAndTypeResolution.cpp | 3 +- 14 files changed, 254 insertions(+), 100 deletions(-) diff --git a/libsolidity/BaseTypes.h b/libsolidity/BaseTypes.h index d1ffd7bbc..627aa7dfd 100644 --- a/libsolidity/BaseTypes.h +++ b/libsolidity/BaseTypes.h @@ -22,6 +22,8 @@ #pragma once +#include +#include #include namespace dev @@ -38,8 +40,9 @@ struct Location Location(int _start, int _end): start(_start), end(_end) { } Location(): start(-1), end(-1) { } - bool IsValid() const { return start >= 0 && end >= start; } + bool IsValid() const { return !!sourceName && start >= 0 && end >= start; } + std::shared_ptr sourceName; int start; int end; }; @@ -47,7 +50,7 @@ struct Location /// Stream output for Location (used e.g. in boost exceptions). inline std::ostream& operator<<(std::ostream& _out, Location const& _location) { - return _out << "[" << _location.start << "," << _location.end << ")"; + return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")"; } } diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 8f8c84fea..49ac3b64c 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -35,24 +35,43 @@ namespace dev namespace solidity { +void CompilerStack::addSource(string const& _name, string const& _content) +{ + if (m_sources.count(_name)) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source by given name already exists.")); + + reset(true); + m_sources[_name].scanner = make_shared(CharStream(_content), _name); +} + void CompilerStack::setSource(string const& _sourceCode) { reset(); - m_scanner = make_shared(CharStream(_sourceCode)); + addSource("", _sourceCode); } void CompilerStack::parse() { - if (!m_scanner) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available.")); - m_sourceUnitASTNode = Parser().parse(m_scanner); + for (auto& sourcePair: m_sources) + { + sourcePair.second.scanner->reset(); + sourcePair.second.ast = Parser().parse(sourcePair.second.scanner); + } + resolveImports(); + m_globalContext = make_shared(); - for (ASTPointer const& node: m_sourceUnitASTNode->getNodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - m_globalContext->setCurrentContract(*contract); - NameAndTypeResolver(m_globalContext->getDeclarations()).resolveNamesAndTypes(*contract); - } + NameAndTypeResolver resolver(m_globalContext->getDeclarations()); + for (Source const* source: m_sourceOrder) + resolver.registerDeclarations(*source->ast); + for (Source const* source: m_sourceOrder) + for (ASTPointer const& node: source->ast->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + m_globalContext->setCurrentContract(*contract); + resolver.updateDeclaration(*m_globalContext->getCurrentThis()); + resolver.resolveNamesAndTypes(*contract); + m_contracts[contract->getName()].contract = contract; + } m_parseSuccessful = true; } @@ -62,82 +81,91 @@ void CompilerStack::parse(string const& _sourceCode) parse(); } -bytes const& CompilerStack::compile(bool _optimize) +void CompilerStack::compile(bool _optimize) { if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - //@todo returns only the last contract for now - for (ASTPointer const& node: m_sourceUnitASTNode->getNodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - m_bytecode.clear(); - m_compiler = make_shared(); - m_compiler->compileContract(*contract, m_globalContext->getMagicVariables()); - m_bytecode = m_compiler->getAssembledBytecode(_optimize); - } - return m_bytecode; + for (Source const* source: m_sourceOrder) + for (ASTPointer const& node: source->ast->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + m_globalContext->setCurrentContract(*contract); + shared_ptr compiler = make_shared(); + compiler->compileContract(*contract, m_globalContext->getMagicVariables()); + Contract& compiledContract = m_contracts[contract->getName()]; + compiledContract.bytecode = compiler->getAssembledBytecode(_optimize); + compiledContract.compiler = move(compiler); + } } bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize) { parse(_sourceCode); - return compile(_optimize); + compile(_optimize); + return getBytecode(); } -void CompilerStack::streamAssembly(ostream& _outStream) +bytes const& CompilerStack::getBytecode(string const& _contractName) { - if (!m_compiler || m_bytecode.empty()) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); - m_compiler->streamAssembly(_outStream); + return getContract(_contractName).bytecode; } -string const& CompilerStack::getInterface() +void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) { - if (!m_parseSuccessful) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - if (m_interface.empty()) + getContract(_contractName).compiler->streamAssembly(_outStream); +} + +string const& CompilerStack::getInterface(std::string const& _contractName) +{ + Contract& contract = getContract(_contractName); + if (contract.interface.empty()) { - //@todo returns only the last contract for now - for (ASTPointer const& node: m_sourceUnitASTNode->getNodes()) - if (ContractDefinition const* contract = dynamic_cast(node.get())) + stringstream interface; + interface << '['; + vector exportedFunctions = contract.contract->getInterfaceFunctions(); + unsigned functionsCount = exportedFunctions.size(); + for (FunctionDefinition const* f: exportedFunctions) + { + auto streamVariables = [&](vector> const& _vars) { - stringstream interface; - interface << '['; - vector exportedFunctions = contract->getInterfaceFunctions(); - unsigned functionsCount = exportedFunctions.size(); - for (FunctionDefinition const* f: exportedFunctions) + unsigned varCount = _vars.size(); + for (ASTPointer const& var: _vars) { - auto streamVariables = [&](vector> const& _vars) - { - unsigned varCount = _vars.size(); - for (ASTPointer const& var: _vars) - { - interface << "{" - << "\"name\":" << escaped(var->getName(), false) << "," - << "\"type\":" << escaped(var->getType()->toString(), false) - << "}"; - if (--varCount > 0) - interface << ","; - } - }; - - interface << '{' - << "\"name\":" << escaped(f->getName(), false) << "," - << "\"inputs\":["; - streamVariables(f->getParameters()); - interface << "]," - << "\"outputs\":["; - streamVariables(f->getReturnParameters()); - interface << "]" + interface << "{" + << "\"name\":" << escaped(var->getName(), false) << "," + << "\"type\":" << escaped(var->getType()->toString(), false) << "}"; - if (--functionsCount > 0) + if (--varCount > 0) interface << ","; } - interface << ']'; - m_interface = interface.str(); - } + }; + + interface << '{' + << "\"name\":" << escaped(f->getName(), false) << "," + << "\"inputs\":["; + streamVariables(f->getParameters()); + interface << "]," + << "\"outputs\":["; + streamVariables(f->getReturnParameters()); + interface << "]" + << "}"; + if (--functionsCount > 0) + interface << ","; + } + interface << ']'; + contract.interface = interface.str(); } - return m_interface; + return contract.interface; +} + +Scanner const& CompilerStack::getScanner(string const& _sourceName) +{ + return *getSource(_sourceName).scanner; +} + +SourceUnit& CompilerStack::getAST(string const& _sourceName) +{ + return *getSource(_sourceName).ast; } bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) @@ -146,7 +174,68 @@ bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimiz return stack.compile(_sourceCode, _optimize); } +void CompilerStack::reset(bool _keepSources) +{ + m_parseSuccessful = false; + if (_keepSources) + for (auto sourcePair: m_sources) + sourcePair.second.reset(); + else + m_sources.clear(); + m_globalContext.reset(); + m_compiler.reset(); + m_sourceOrder.clear(); + m_contracts.clear(); +} + +void CompilerStack::resolveImports() +{ + // topological sorting (depth first search) of the import graph, cutting potential cycles + vector sourceOrder; + set sourcesSeen; + + function toposort = [&](Source const* _source) + { + if (sourcesSeen.count(_source)) + return; + sourcesSeen.insert(_source); + for (ASTPointer const& node: _source->ast->getNodes()) + if (ImportDirective const* import = dynamic_cast(node.get())) + { + string const& url = import->getURL(); + if (!m_sources.count(url)) + BOOST_THROW_EXCEPTION(ParserError() + << errinfo_sourceLocation(import->getLocation()) + << errinfo_comment("Source not found.")); + toposort(&m_sources[url]); + } + sourceOrder.push_back(_source); + }; + + for (auto const& sourcePair: m_sources) + toposort(&sourcePair.second); + swap(m_sourceOrder, sourceOrder); +} +CompilerStack::Contract& CompilerStack::getContract(string const& _contractName) +{ + if (m_contracts.empty()) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found.")); + if (_contractName.empty()) + return m_contracts.begin()->second; + auto it = m_contracts.find(_contractName); + if (it == m_contracts.end()) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found.")); + return it->second; +} + +CompilerStack::Source& CompilerStack::getSource(string const& _sourceName) +{ + auto it = m_sources.find(_sourceName); + if (it == m_sources.end()) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Given source file not found.")); + return it->second; +} } } diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 19f3cf99a..0520f2b1b 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace dev { @@ -33,53 +34,81 @@ namespace solidity { // forward declarations class Scanner; class SourceUnit; -class ContractDefinition; class Compiler; class GlobalContext; +class ContractDefinition; /** * Easy to use and self-contained Solidity compiler with as few header dependencies as possible. * It holds state and can be used to either step through the compilation stages (and abort e.g. * before compilation to bytecode) or run the whole compilation in one call. */ -class CompilerStack +class CompilerStack: boost::noncopyable { public: - CompilerStack() {} - void reset() { *this = CompilerStack(); } + CompilerStack(): m_parseSuccessful(false) {} + /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. + void addSource(std::string const& _name, std::string const& _content); void setSource(std::string const& _sourceCode); + /// Parses all source units that were added void parse(); + /// Sets the given source code as the only source unit and parses it. void parse(std::string const& _sourceCode); - /// Compiles the contract that was previously parsed. - bytes const& compile(bool _optimize = false); + /// Compiles the source units that were prevously added and parsed. + void compile(bool _optimize = false); /// Parses and compiles the given source code. + /// @returns the compiled bytecode bytes const& compile(std::string const& _sourceCode, bool _optimize = false); - bytes const& getBytecode() const { return m_bytecode; } + bytes const& getBytecode(std::string const& _contractName = ""); /// Streams a verbose version of the assembly to @a _outStream. /// Prerequisite: Successful compilation. - void streamAssembly(std::ostream& _outStream); + void streamAssembly(std::ostream& _outStream, std::string const& _contractName = ""); /// Returns a string representing the contract interface in JSON. /// Prerequisite: Successful call to parse or compile. - std::string const& getInterface(); + std::string const& getInterface(std::string const& _contractName = ""); /// Returns the previously used scanner, useful for counting lines during error reporting. - Scanner const& getScanner() const { return *m_scanner; } - SourceUnit& getAST() const { return *m_sourceUnitASTNode; } + Scanner const& getScanner(std::string const& _sourceName = ""); + SourceUnit& getAST(std::string const& _sourceName = ""); /// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for /// scanning the source code - this is useful for printing exception information. static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false); private: - std::shared_ptr m_scanner; - std::shared_ptr m_globalContext; - std::shared_ptr m_sourceUnitASTNode; + /** + * Information pertaining to one source unit, filled gradually during parsing and compilation. + */ + struct Source + { + std::shared_ptr scanner; + std::shared_ptr ast; + std::string interface; + void reset() { scanner.reset(); ast.reset(); interface.clear(); } + }; + + struct Contract + { + ContractDefinition const* contract; + std::string interface; + std::shared_ptr compiler; + bytes bytecode; + }; + + void reset(bool _keepSources = false); + void resolveImports(); + + Contract& getContract(std::string const& _contractName = ""); + Source& getSource(std::string const& _sourceName = ""); + bool m_parseSuccessful; - std::string m_interface; + std::map m_sources; + std::shared_ptr m_globalContext; std::shared_ptr m_compiler; - bytes m_bytecode; + std::vector m_sourceOrder; + std::map m_contracts; }; } diff --git a/libsolidity/DeclarationContainer.cpp b/libsolidity/DeclarationContainer.cpp index 6ea9c28c5..c0dea757a 100644 --- a/libsolidity/DeclarationContainer.cpp +++ b/libsolidity/DeclarationContainer.cpp @@ -28,9 +28,9 @@ namespace dev namespace solidity { -bool DeclarationContainer::registerDeclaration(Declaration& _declaration) +bool DeclarationContainer::registerDeclaration(Declaration& _declaration, bool _update) { - if (m_declarations.find(_declaration.getName()) != m_declarations.end()) + if (!_update && m_declarations.find(_declaration.getName()) != m_declarations.end()) return false; m_declarations[_declaration.getName()] = &_declaration; return true; diff --git a/libsolidity/DeclarationContainer.h b/libsolidity/DeclarationContainer.h index db6812890..e1b363e04 100644 --- a/libsolidity/DeclarationContainer.h +++ b/libsolidity/DeclarationContainer.h @@ -43,7 +43,7 @@ public: m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {} /// Registers the declaration in the scope unless its name is already declared. Returns true iff /// it was not yet declared. - bool registerDeclaration(Declaration& _declaration); + bool registerDeclaration(Declaration& _declaration, bool _update = false); Declaration* resolveName(ASTString const& _name, bool _recursive = false) const; Declaration* getEnclosingDeclaration() const { return m_enclosingDeclaration; } diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index d8b637076..45242bb1f 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -74,11 +74,10 @@ vector GlobalContext::getDeclarations() const declarations.reserve(m_magicVariables.size() + 1); for (ASTPointer const& variable: m_magicVariables) declarations.push_back(variable.get()); - declarations.push_back(getCurrentThis()); return declarations; } -MagicVariableDeclaration*GlobalContext::getCurrentThis() const +MagicVariableDeclaration* GlobalContext::getCurrentThis() const { if (!m_thisPointer[m_currentContract]) m_thisPointer[m_currentContract] = make_shared( diff --git a/libsolidity/GlobalContext.h b/libsolidity/GlobalContext.h index 0166734ca..fc3914d2a 100644 --- a/libsolidity/GlobalContext.h +++ b/libsolidity/GlobalContext.h @@ -47,12 +47,13 @@ class GlobalContext: private boost::noncopyable public: GlobalContext(); void setCurrentContract(ContractDefinition const& _contract); + MagicVariableDeclaration* getCurrentThis() const; std::vector getMagicVariables() const; + /// Returns a vector of all magic variables, excluding "this". std::vector getDeclarations() const; private: - MagicVariableDeclaration* getCurrentThis() const; std::vector> m_magicVariables; ContractDefinition const* m_currentContract; std::map> mutable m_thisPointer; diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index d473348b8..08deb66a0 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -38,9 +38,13 @@ NameAndTypeResolver::NameAndTypeResolver(std::vector const& _globa m_scopes[nullptr].registerDeclaration(*declaration); } +void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit) +{ + DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit); +} + void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) { - DeclarationRegistrationHelper registrar(m_scopes, _contract); m_currentScope = &m_scopes[&_contract]; for (ASTPointer const& structDef: _contract.getDefinedStructs()) ReferencesResolver resolver(*structDef, *this, nullptr); @@ -65,6 +69,12 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) m_currentScope = &m_scopes[nullptr]; } +void NameAndTypeResolver::updateDeclaration(Declaration& _declaration) +{ + m_scopes[nullptr].registerDeclaration(_declaration, true); + _declaration.setScope(nullptr); +} + Declaration* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const { auto iterator = m_scopes.find(_scope); diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 797eca605..816d8006f 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -42,7 +42,13 @@ class NameAndTypeResolver: private boost::noncopyable { public: explicit NameAndTypeResolver(std::vector const& _globals); + /// Registers all declarations found in the source unit. + void registerDeclarations(SourceUnit& _sourceUnit); + /// Resolves all names and types referenced from the given contract. void resolveNamesAndTypes(ContractDefinition& _contract); + /// Updates the given global declaration (used for "this"). Not to be used with declarations + /// that create their own scope. + void updateDeclaration(Declaration& _declaration); /// 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). diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 2f5f8d37a..e155f550d 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -143,17 +143,28 @@ private: }; // end of LiteralScope class -void Scanner::reset(CharStream const& _source) +void Scanner::reset(CharStream const& _source, std::string const& _sourceName) { m_source = _source; + + shared_ptr sourceName = make_shared(_sourceName); + m_currentToken.location.sourceName = sourceName; + m_nextToken.location.sourceName = sourceName; + m_skippedComment.location.sourceName = sourceName; + m_nextSkippedComment.location.sourceName = sourceName; + + reset(); +} + +void Scanner::reset() +{ + m_source.reset(); m_char = m_source.get(); skipWhitespace(); scanToken(); - next(); } - bool Scanner::scanHexByte(char& o_scannedByte) { char x = 0; diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 49ac3651c..deb7df502 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -79,6 +79,8 @@ public: char advanceAndGet(size_t _chars=1); char rollback(size_t _amount); + void reset() { m_pos = 0; } + ///@{ ///@name Error printing helper functions /// Functions that help pretty-printing parse errors @@ -99,11 +101,12 @@ class Scanner friend class LiteralScope; public: - Scanner() { reset(CharStream()); } - explicit Scanner(CharStream const& _source) { reset(_source); } + explicit Scanner(CharStream const& _source = CharStream(), std::string const& _sourceName = "") { reset(_source, _sourceName); } - /// Resets the scanner as if newly constructed with _input as input. - void reset(CharStream const& _source); + /// Resets the scanner as if newly constructed with _source and _sourceName as input. + void reset(CharStream const& _source, std::string const& _sourceName); + /// Resets scanner to the start of input. + void reset(); /// Returns the next token and advances input Token::Value next(); diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index 883cecf28..9862cba83 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -48,10 +48,11 @@ bytes compileContract(const string& _sourceCode) Parser parser; ASTPointer sourceUnit; BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(CharStream(_sourceCode)))); + NameAndTypeResolver resolver({}); + resolver.registerDeclarations(*sourceUnit); for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - NameAndTypeResolver resolver({}); BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); Compiler compiler; diff --git a/test/solidityExpressionCompiler.cpp b/test/solidityExpressionCompiler.cpp index b077e2620..cb22d5120 100644 --- a/test/solidityExpressionCompiler.cpp +++ b/test/solidityExpressionCompiler.cpp @@ -88,10 +88,11 @@ bytes compileFirstExpression(const string& _sourceCode, vector> _ Parser parser; ASTPointer sourceUnit; BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(CharStream(_sourceCode)))); + NameAndTypeResolver resolver({}); + resolver.registerDeclarations(*sourceUnit); for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - NameAndTypeResolver resolver({}); BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); FirstExpressionExtractor extractor(*contract); BOOST_REQUIRE(extractor.getExpression() != nullptr); diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 219fa2599..74af19b8f 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -42,10 +42,11 @@ void parseTextAndResolveNames(std::string const& _source) { Parser parser; ASTPointer sourceUnit = parser.parse(std::make_shared(CharStream(_source))); + NameAndTypeResolver resolver({}); + resolver.registerDeclarations(*sourceUnit); for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - NameAndTypeResolver resolver({}); resolver.resolveNamesAndTypes(*contract); } } From 04c8d8bdc08398089225c3e134df8ad533c887b8 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 3 Dec 2014 18:52:28 +0100 Subject: [PATCH 04/10] Improved external interface for multi-source and multi-contract compilation. --- alethzero/MainWin.cpp | 2 +- libsolidity/BaseTypes.h | 7 ++- libsolidity/CompilerStack.cpp | 12 +++++- libsolidity/CompilerStack.h | 3 ++ libsolidity/Exceptions.h | 1 - libsolidity/Parser.cpp | 11 ++++- libsolidity/Parser.h | 1 + libsolidity/Scanner.cpp | 10 +---- libsolidity/Scanner.h | 3 ++ libsolidity/SourceReferenceFormatter.cpp | 41 +++++++----------- libsolidity/SourceReferenceFormatter.h | 4 +- libweb3jsonrpc/WebThreeStubServer.cpp | 2 +- mix/ConstantCompilationModel.cpp | 2 +- solc/main.cpp | 55 +++++++++++++++--------- test/solidityExpressionCompiler.cpp | 1 + 15 files changed, 88 insertions(+), 67 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 4a32f66b2..4126fbf08 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1625,7 +1625,7 @@ void Main::on_data_textChanged() catch (dev::Exception const& exception) { ostringstream error; - solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler.getScanner()); + solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); solidity = "

Solidity

" + QString::fromStdString(error.str()).toHtmlEscaped() + "
"; } catch (...) diff --git a/libsolidity/BaseTypes.h b/libsolidity/BaseTypes.h index 627aa7dfd..a8fd77c86 100644 --- a/libsolidity/BaseTypes.h +++ b/libsolidity/BaseTypes.h @@ -37,14 +37,13 @@ namespace solidity */ struct Location { - Location(int _start, int _end): start(_start), end(_end) { } + Location(int _start, int _end, std::shared_ptr _sourceName): + start(_start), end(_end), sourceName(_sourceName) { } Location(): start(-1), end(-1) { } - bool IsValid() const { return !!sourceName && start >= 0 && end >= start; } - - std::shared_ptr sourceName; int start; int end; + std::shared_ptr sourceName; }; /// Stream output for Location (used e.g. in boost exceptions). diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 49ac3b64c..198ded090 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -81,10 +81,20 @@ void CompilerStack::parse(string const& _sourceCode) parse(); } -void CompilerStack::compile(bool _optimize) +vector CompilerStack::getContractNames() { if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + vector contractNames; + for (auto const& contract: m_contracts) + contractNames.push_back(contract.first); + return contractNames; +} + +void CompilerStack::compile(bool _optimize) +{ + if (!m_parseSuccessful) + parse(); for (Source const* source: m_sourceOrder) for (ASTPointer const& node: source->ast->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 0520f2b1b..a5b8ec41e 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -54,6 +54,9 @@ public: void parse(); /// Sets the given source code as the only source unit and parses it. void parse(std::string const& _sourceCode); + /// Returns a list of the contract names in the sources. + std::vector getContractNames(); + /// Compiles the source units that were prevously added and parsed. void compile(bool _optimize = false); /// Parses and compiles the given source code. diff --git a/libsolidity/Exceptions.h b/libsolidity/Exceptions.h index 1903c1dc2..ffd8a72d4 100644 --- a/libsolidity/Exceptions.h +++ b/libsolidity/Exceptions.h @@ -37,7 +37,6 @@ struct DeclarationError: virtual Exception {}; struct CompilerError: virtual Exception {}; struct InternalCompilerError: virtual Exception {}; -typedef boost::error_info errinfo_sourcePosition; typedef boost::error_info errinfo_sourceLocation; } diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index c385dd8d1..ddab489b6 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -39,7 +39,8 @@ namespace solidity class Parser::ASTNodeFactory { public: - ASTNodeFactory(Parser const& _parser): m_parser(_parser), m_location(_parser.getPosition(), -1) {} + ASTNodeFactory(Parser const& _parser): + m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {} void markEndPosition() { m_location.end = m_parser.getEndPosition(); } void setLocationEmpty() { m_location.end = m_location.start; } @@ -81,6 +82,11 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) return nodeFactory.createNode(nodes); } +std::shared_ptr const& Parser::getSourceName() const +{ + return m_scanner->getSourceName(); +} + int Parser::getPosition() const { return m_scanner->getCurrentLocation().start; @@ -579,7 +585,8 @@ ASTPointer Parser::getLiteralAndAdvance() ParserError Parser::createParserError(string const& _description) const { - return ParserError() << errinfo_sourcePosition(getPosition()) << errinfo_comment(_description); + return ParserError() << errinfo_sourceLocation(Location(getPosition(), getPosition(), getSourceName())) + << errinfo_comment(_description); } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 98a337b29..52a374e03 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -35,6 +35,7 @@ class Parser { public: ASTPointer parse(std::shared_ptr const& _scanner); + std::shared_ptr const& getSourceName() const; private: class ASTNodeFactory; diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index e155f550d..08bf744d4 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -143,16 +143,10 @@ private: }; // end of LiteralScope class -void Scanner::reset(CharStream const& _source, std::string const& _sourceName) +void Scanner::reset(CharStream const& _source, string const& _sourceName) { m_source = _source; - - shared_ptr sourceName = make_shared(_sourceName); - m_currentToken.location.sourceName = sourceName; - m_nextToken.location.sourceName = sourceName; - m_skippedComment.location.sourceName = sourceName; - m_nextSkippedComment.location.sourceName = sourceName; - + m_sourceName = make_shared(_sourceName); reset(); } diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index deb7df502..18b1f5d3a 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -142,6 +142,8 @@ public: std::string const& peekLiteral() const { return m_nextToken.literal; } ///@} + std::shared_ptr const& getSourceName() const { return m_sourceName; } + ///@{ ///@name Error printing helper functions /// Functions that help pretty-printing parse errors @@ -206,6 +208,7 @@ private: TokenDesc m_nextToken; // desc for next token (one token look-ahead) CharStream m_source; + std::shared_ptr m_sourceName; /// one character look-ahead, equals 0 at end of input char m_char; diff --git a/libsolidity/SourceReferenceFormatter.cpp b/libsolidity/SourceReferenceFormatter.cpp index d3f2152a6..1a7d12a95 100644 --- a/libsolidity/SourceReferenceFormatter.cpp +++ b/libsolidity/SourceReferenceFormatter.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -38,7 +39,6 @@ void SourceReferenceFormatter::printSourceLocation(ostream& _stream, int startLine; int startColumn; tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start); - _stream << "starting at line " << (startLine + 1) << ", column " << (startColumn + 1) << "\n"; int endLine; int endColumn; tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end); @@ -58,37 +58,28 @@ void SourceReferenceFormatter::printSourceLocation(ostream& _stream, << "Spanning multiple lines.\n"; } -void SourceReferenceFormatter::printSourcePosition(ostream& _stream, - int _position, - const Scanner& _scanner) -{ - int line; - int column; - tie(line, column) = _scanner.translatePositionToLineColumn(_position); - _stream << "at line " << (line + 1) << ", column " << (column + 1) << endl - << _scanner.getLineAtPosition(_position) << endl - << string(column, ' ') << "^" << endl; -} - void SourceReferenceFormatter::printExceptionInformation(ostream& _stream, Exception const& _exception, string const& _name, - Scanner const& _scanner) + CompilerStack& _compiler) { - _stream << _name; - if (string const* description = boost::get_error_info(_exception)) - _stream << ": " << *description; + Location const* location = boost::get_error_info(_exception); + Scanner const* scanner; - if (int const* position = boost::get_error_info(_exception)) + if (location) { - _stream << " "; - printSourcePosition(_stream, *position, _scanner); - } - if (Location const* location = boost::get_error_info(_exception)) - { - _stream << " "; - printSourceLocation(_stream, *location, _scanner); + scanner = &_compiler.getScanner(*location->sourceName); + int startLine; + int startColumn; + tie(startLine, startColumn) = scanner->translatePositionToLineColumn(location->start); + _stream << *location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": "; } + _stream << _name; + if (string const* description = boost::get_error_info(_exception)) + _stream << ": " << *description << endl; + + if (location) + printSourceLocation(_stream, *location, *scanner); } } diff --git a/libsolidity/SourceReferenceFormatter.h b/libsolidity/SourceReferenceFormatter.h index 4736066fd..9b5567045 100644 --- a/libsolidity/SourceReferenceFormatter.h +++ b/libsolidity/SourceReferenceFormatter.h @@ -34,14 +34,14 @@ namespace solidity { class Scanner; // forward +class CompilerStack; // forward struct SourceReferenceFormatter { public: static void printSourceLocation(std::ostream& _stream, Location const& _location, Scanner const& _scanner); - static void printSourcePosition(std::ostream& _stream, int _position, Scanner const& _scanner); static void printExceptionInformation(std::ostream& _stream, Exception const& _exception, - std::string const& _name, Scanner const& _scanner); + std::string const& _name, CompilerStack& _compiler); }; } diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index 7607b7a95..84a0ae42a 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -510,7 +510,7 @@ std::string WebThreeStubServer::eth_solidity(std::string const& _code) catch (dev::Exception const& exception) { ostringstream error; - solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler.getScanner()); + solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); cwarn << "Solidity compilation error: " << error.str(); } catch (...) diff --git a/mix/ConstantCompilationModel.cpp b/mix/ConstantCompilationModel.cpp index e06734f59..ea12a267c 100644 --- a/mix/ConstantCompilationModel.cpp +++ b/mix/ConstantCompilationModel.cpp @@ -46,7 +46,7 @@ CompilerResult ConstantCompilationModel::compile(QString _code) catch (dev::Exception const& _exception) { ostringstream error; - solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler.getScanner()); + solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler); res.success = false; res.comment = QString::fromStdString(error.str()).toHtmlEscaped(); res.hexCode = ""; diff --git a/solc/main.cpp b/solc/main.cpp index 76d8ef6b7..3284be74a 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -59,7 +59,7 @@ void version() int main(int argc, char** argv) { - string infile; + vector infiles; bool optimize = false; for (int i = 1; i < argc; ++i) { @@ -71,49 +71,52 @@ int main(int argc, char** argv) else if (arg == "-V" || arg == "--version") version(); else - infile = argv[i]; + infiles.push_back(argv[i]); } - string sourceCode; - if (infile.empty()) + map sourceCodes; + if (infiles.empty()) { string s; while (!cin.eof()) { getline(cin, s); - sourceCode.append(s); + sourceCodes[""].append(s); } } else - sourceCode = asString(dev::contents(infile)); + for (string const& infile: infiles) + sourceCodes[infile] = asString(dev::contents(infile)); CompilerStack compiler; try { - compiler.compile(sourceCode, optimize); + for (auto const& sourceCode: sourceCodes) + compiler.addSource(sourceCode.first, sourceCode.second); + compiler.compile(optimize); } catch (ParserError const& exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", compiler.getScanner()); + SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", compiler); return -1; } catch (DeclarationError const& exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", compiler.getScanner()); + SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", compiler); return -1; } catch (TypeError const& exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", compiler.getScanner()); + SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", compiler); return -1; } catch (CompilerError const& exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", compiler.getScanner()); + SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", compiler); return -1; } catch (InternalCompilerError const& exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", compiler.getScanner()); + SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", compiler); return -1; } catch (Exception const& exception) @@ -127,15 +130,25 @@ int main(int argc, char** argv) return -1; } - cout << "Syntax tree for the source unit:" << endl; - ASTPrinter printer(compiler.getAST(), sourceCode); - printer.print(cout); - cout << "EVM assembly:" << endl; - compiler.streamAssembly(cout); - cout << "Opcodes:" << endl; - cout << eth::disassemble(compiler.getBytecode()) << endl; - cout << "Binary: " << toHex(compiler.getBytecode()) << endl; - cout << "Interface specification: " << compiler.getInterface() << endl; + cout << "Syntax trees:" << endl << endl; + for (auto const& sourceCode: sourceCodes) + { + cout << endl << "======= " << sourceCode.first << " =======" << endl; + ASTPrinter printer(compiler.getAST(sourceCode.first), sourceCode.second); + printer.print(cout); + } + vector contracts = compiler.getContractNames(); + cout << endl << "Contracts:" << endl; + for (string const& contract: contracts) + { + cout << endl << "======= " << contract << " =======" << endl + << "EVM assembly:" << endl; + compiler.streamAssembly(cout, contract); + cout << "Opcodes:" << endl + << eth::disassemble(compiler.getBytecode(contract)) << endl + << "Binary: " << toHex(compiler.getBytecode(contract)) << endl + << "Interface specification: " << compiler.getInterface(contract) << endl; + } return 0; } diff --git a/test/solidityExpressionCompiler.cpp b/test/solidityExpressionCompiler.cpp index cb22d5120..486b46ebb 100644 --- a/test/solidityExpressionCompiler.cpp +++ b/test/solidityExpressionCompiler.cpp @@ -113,6 +113,7 @@ bytes compileFirstExpression(const string& _sourceCode, vector> _ return instructions; } BOOST_FAIL("No contract found in source."); + return bytes(); } } // end anonymous namespace From dcce76392b0c3023729d43ae231c53e99a98ae2d Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 5 Dec 2014 15:35:05 +0100 Subject: [PATCH 05/10] Renamed url to identifier and added some comments. --- libsolidity/AST.h | 10 ++++++---- libsolidity/ASTPrinter.cpp | 2 +- libsolidity/CompilerStack.cpp | 6 +++--- libsolidity/CompilerStack.h | 2 +- libsolidity/NameAndTypeResolver.cpp | 1 + 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index d2fb15a45..10616022b 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -98,19 +98,21 @@ private: /** * Import directive for referencing other files / source objects. + * Example: import "abc.sol" + * Source objects are identified by a string which can be a file name but does not have to be. */ class ImportDirective: public ASTNode { public: - ImportDirective(Location const& _location, ASTPointer const& _url): - ASTNode(_location), m_url(_url) {} + ImportDirective(Location const& _location, ASTPointer const& _identifier): + ASTNode(_location), m_identifier(_identifier) {} virtual void accept(ASTVisitor& _visitor) override; - ASTString const& getURL() const { return *m_url; } + ASTString const& getIdentifier() const { return *m_identifier; } private: - ASTPointer m_url; + ASTPointer m_identifier; }; /** diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index c62378fd3..3d09fd9af 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -45,7 +45,7 @@ void ASTPrinter::print(ostream& _stream) bool ASTPrinter::visit(ImportDirective& _node) { - writeLine("ImportDirective \"" + _node.getURL() + "\""); + writeLine("ImportDirective \"" + _node.getIdentifier() + "\""); printSourcePart(_node); return goDeeper(); } diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 621723848..20b0b3e57 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -201,12 +201,12 @@ void CompilerStack::resolveImports() for (ASTPointer const& node: _source->ast->getNodes()) if (ImportDirective const* import = dynamic_cast(node.get())) { - string const& url = import->getURL(); - if (!m_sources.count(url)) + string const& id = import->getIdentifier(); + if (!m_sources.count(id)) BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(import->getLocation()) << errinfo_comment("Source not found.")); - toposort(&m_sources[url]); + toposort(&m_sources[id]); } sourceOrder.push_back(_source); }; diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 34178841a..bedb4cc38 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -66,7 +66,7 @@ public: /// Returns a list of the contract names in the sources. std::vector getContractNames(); - /// Compiles the source units that were prevously added and parsed. + /// Compiles the source units that were previously added and parsed. void compile(bool _optimize = false); /// Parses and compiles the given source code. /// @returns the compiled bytecode diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 08deb66a0..3715df6ad 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -40,6 +40,7 @@ NameAndTypeResolver::NameAndTypeResolver(std::vector const& _globa void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit) { + // The helper registers all declarations in m_scopes as a side-effect of its construction. DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit); } From ddb5111d3e959f4643fbc91751ec329e43b5db17 Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 7 Dec 2014 09:46:21 +0100 Subject: [PATCH 06/10] Comments and braces. --- libsolidity/GlobalContext.h | 3 ++- test/solidityNameAndTypeResolution.cpp | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/libsolidity/GlobalContext.h b/libsolidity/GlobalContext.h index fc3914d2a..ddbd049c2 100644 --- a/libsolidity/GlobalContext.h +++ b/libsolidity/GlobalContext.h @@ -49,8 +49,9 @@ public: void setCurrentContract(ContractDefinition const& _contract); MagicVariableDeclaration* getCurrentThis() const; + /// @returns all magic variables. std::vector getMagicVariables() const; - /// Returns a vector of all magic variables, excluding "this". + /// @returns a vector of all implicit global declarations excluding "this". std::vector getDeclarations() const; private: diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 74af19b8f..783972296 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -46,9 +46,7 @@ void parseTextAndResolveNames(std::string const& _source) resolver.registerDeclarations(*sourceUnit); for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) - { resolver.resolveNamesAndTypes(*contract); - } } } From 1e0684c1b43a2bc5a4cbe9b5678cf9b980128bcd Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 8 Dec 2014 09:47:47 +0000 Subject: [PATCH 07/10] Avoid overwriting messages with same expiry. --- libwhisper/Message.cpp | 1 - libwhisper/WhisperHost.cpp | 2 +- libwhisper/WhisperHost.h | 2 +- libwhisper/WhisperPeer.cpp | 2 +- libwhisper/WhisperPeer.h | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/libwhisper/Message.cpp b/libwhisper/Message.cpp index 8e3e80a12..07620a7cf 100644 --- a/libwhisper/Message.cpp +++ b/libwhisper/Message.cpp @@ -91,7 +91,6 @@ Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigne Envelope::Envelope(RLP const& _m) { - cdebug << _m; m_expiry = _m[0].toInt(); m_ttl = _m[1].toInt(); m_topic = _m[2].toVector>(); diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 769b39057..589f8f047 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -68,7 +68,7 @@ void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p) return; UpgradeGuard ll(l); m_messages[h] = _m; - m_expiryQueue[_m.expiry()] = h; + m_expiryQueue.insert(make_pair(_m.expiry(), h)); } // if (_p) diff --git a/libwhisper/WhisperHost.h b/libwhisper/WhisperHost.h index b38964f8e..1a4ec2a71 100644 --- a/libwhisper/WhisperHost.h +++ b/libwhisper/WhisperHost.h @@ -78,7 +78,7 @@ private: mutable dev::SharedMutex x_messages; std::map m_messages; - std::map m_expiryQueue; + std::multimap m_expiryQueue; mutable dev::Mutex m_filterLock; std::map m_filters; diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp index 359d31c4e..1dbd6e16e 100644 --- a/libwhisper/WhisperPeer.cpp +++ b/libwhisper/WhisperPeer.cpp @@ -106,5 +106,5 @@ void WhisperPeer::sendMessages() void WhisperPeer::noteNewMessage(h256 _h, Message const& _m) { Guard l(x_unseen); - m_unseen[rating(_m)] = _h; + m_unseen.insert(make_pair(rating(_m), _h)); } diff --git a/libwhisper/WhisperPeer.h b/libwhisper/WhisperPeer.h index faac2d870..d21f33725 100644 --- a/libwhisper/WhisperPeer.h +++ b/libwhisper/WhisperPeer.h @@ -67,7 +67,7 @@ private: void noteNewMessage(h256 _h, Message const& _m); mutable dev::Mutex x_unseen; - std::map m_unseen; ///< Rated according to what they want. + std::multimap m_unseen; ///< Rated according to what they want. std::chrono::system_clock::time_point m_timer = std::chrono::system_clock::now(); }; From 2102ab2870546f546296db2258bf7cc5916ec2e8 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 8 Dec 2014 10:23:24 +0000 Subject: [PATCH 08/10] Evade networking crash. --- libp2p/Host.cpp | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 15521cb28..93a6ce672 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -447,26 +447,31 @@ void Host::connect(std::shared_ptr const& _n) _n->failedAttempts++; m_ready -= _n->index; bi::tcp::socket* s = new bi::tcp::socket(m_ioService); - s->async_connect(_n->address, [=](boost::system::error_code const& ec) - { - if (ec) - { - clog(NetConnect) << "Connection refused to node" << _n->id.abridged() << "@" << _n->address << "(" << ec.message() << ")"; - _n->lastDisconnect = TCPError; - _n->lastAttempted = std::chrono::system_clock::now(); - m_ready += _n->index; - } - else + + auto n = node(_n->id); + if (n) + s->async_connect(_n->address, [=](boost::system::error_code const& ec) { - clog(NetConnect) << "Connected to" << _n->id.abridged() << "@" << _n->address; - _n->lastConnected = std::chrono::system_clock::now(); - auto p = make_shared(this, std::move(*s), node(_n->id), true); // true because we don't care about ids matched for now. Once we have permenant IDs this will matter a lot more and we can institute a safer mechanism. - p->start(); - } - delete s; - Guard l(x_pendingNodeConns); - m_pendingNodeConns.erase(nptr); - }); + if (ec) + { + clog(NetConnect) << "Connection refused to node" << _n->id.abridged() << "@" << _n->address << "(" << ec.message() << ")"; + _n->lastDisconnect = TCPError; + _n->lastAttempted = std::chrono::system_clock::now(); + m_ready += _n->index; + } + else + { + clog(NetConnect) << "Connected to" << _n->id.abridged() << "@" << _n->address; + _n->lastConnected = std::chrono::system_clock::now(); + auto p = make_shared(this, std::move(*s), n, true); // true because we don't care about ids matched for now. Once we have permenant IDs this will matter a lot more and we can institute a safer mechanism. + p->start(); + } + delete s; + Guard l(x_pendingNodeConns); + m_pendingNodeConns.erase(nptr); + }); + else + clog(NetWarn) << "Trying to connect to node not in node table."; } bool Host::havePeer(NodeId _id) const From 702acc142d018aab3adaeb11bf967679a9d3d1b2 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 8 Dec 2014 12:19:23 +0100 Subject: [PATCH 09/10] Compile fix. --- libsolidity/CompilerStack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index d0b1df181..41ae56036 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -128,7 +128,7 @@ void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractN string const& CompilerStack::getInterface(std::string const& _contractName) { - return getJsonDocumentation(_contractName, ABI_INTERFACE); + return getJsonDocumentation(_contractName, DocumentationType::ABI_INTERFACE); } std::string const& CompilerStack::getJsonDocumentation(std::string const& _contractName, DocumentationType _type) From e8c39062f821598ce9d42b00dddd61403f7fab8e Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 4 Dec 2014 19:38:24 +0100 Subject: [PATCH 10/10] Calling functions of other contracts. --- libsolidity/ExpressionCompiler.cpp | 139 ++++++++++++++++++----------- libsolidity/ExpressionCompiler.h | 7 +- libsolidity/Types.cpp | 33 ++++++- libsolidity/Types.h | 9 +- test/solidityEndToEndTest.cpp | 135 +++++++++++++++++++++++++++- 5 files changed, 259 insertions(+), 64 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index c3c7116e4..352b0e6d8 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -185,7 +185,9 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) if (asserts(arguments.size() == function.getParameterTypes().size())) BOOST_THROW_EXCEPTION(InternalCompilerError()); - if (function.getLocation() == Location::INTERNAL) + switch (function.getLocation()) + { + case Location::INTERNAL: { // Calling convention: Caller pushes return address and arguments // Callee removes them and pushes return values @@ -208,61 +210,90 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) // all others for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i) m_context << eth::Instruction::POP; + break; } - else if (function.getLocation() == Location::EXTERNAL) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("External function calls not implemented yet.")); - else + case Location::EXTERNAL: { - switch (function.getLocation()) - { - case Location::SEND: - m_context << u256(0) << u256(0) << u256(0) << u256(0); - arguments.front()->accept(*this); - //@todo might not be necessary - appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); - _functionCall.getExpression().accept(*this); - m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB - << eth::Instruction::CALL - << eth::Instruction::POP; - break; - case Location::SUICIDE: - arguments.front()->accept(*this); - //@todo might not be necessary - appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); - m_context << eth::Instruction::SUICIDE; - break; - case Location::SHA3: - arguments.front()->accept(*this); - appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); - // @todo move this once we actually use memory - m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3; - break; - case Location::ECRECOVER: - case Location::SHA256: - case Location::RIPEMD160: + unsigned dataOffset = 1; // reserve one byte for the function index + for (unsigned i = 0; i < arguments.size(); ++i) { - static const map contractAddresses{{Location::ECRECOVER, 1}, - {Location::SHA256, 2}, - {Location::RIPEMD160, 3}}; - u256 contractAddress = contractAddresses.find(function.getLocation())->second; - // @todo later, combine this code with external function call - for (unsigned i = 0; i < arguments.size(); ++i) - { - arguments[i]->accept(*this); - appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true); - // @todo move this once we actually use memory - m_context << u256(i * 32) << eth::Instruction::MSTORE; - } - m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0) - << contractAddress << u256(500) //@todo determine actual gas requirement - << eth::Instruction::CALL - << eth::Instruction::POP - << u256(0) << eth::Instruction::MLOAD; - break; + arguments[i]->accept(*this); + Type const& type = *function.getParameterTypes()[i]; + appendTypeConversion(*arguments[i]->getType(), type); + unsigned const numBytes = type.getCalldataEncodedSize(); + if (numBytes == 0 || numBytes > 32) + BOOST_THROW_EXCEPTION(CompilerError() + << errinfo_sourceLocation(arguments[i]->getLocation()) + << errinfo_comment("Type " + type.toString() + " not yet supported.")); + if (numBytes != 32) + m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; + m_context << u256(dataOffset) << eth::Instruction::MSTORE; + dataOffset += numBytes; } - default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function not yet implemented.")); + //@todo only return the first return value for now + unsigned retSize = function.getReturnParameterTypes().empty() ? 0 + : function.getReturnParameterTypes().front()->getCalldataEncodedSize(); + // CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top) + m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0); + _functionCall.getExpression().accept(*this); // pushes addr and function index + m_context << u256(0) << eth::Instruction::MSTORE8 + << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB + << eth::Instruction::CALL + << eth::Instruction::POP; // @todo do not ignore failure indicator + if (retSize == 32) + m_context << u256(0) << eth::Instruction::MLOAD; + else if (retSize > 0) + m_context << (u256(1) << ((32 - retSize) * 8)) + << u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV; + break; + } + case Location::SEND: + m_context << u256(0) << u256(0) << u256(0) << u256(0); + arguments.front()->accept(*this); + //@todo might not be necessary + appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); + _functionCall.getExpression().accept(*this); + m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB + << eth::Instruction::CALL + << eth::Instruction::POP; + break; + case Location::SUICIDE: + arguments.front()->accept(*this); + //@todo might not be necessary + appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); + m_context << eth::Instruction::SUICIDE; + break; + case Location::SHA3: + arguments.front()->accept(*this); + appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); + // @todo move this once we actually use memory + m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3; + break; + case Location::ECRECOVER: + case Location::SHA256: + case Location::RIPEMD160: + { + static const map contractAddresses{{Location::ECRECOVER, 1}, + {Location::SHA256, 2}, + {Location::RIPEMD160, 3}}; + u256 contractAddress = contractAddresses.find(function.getLocation())->second; + // @todo later, combine this code with external function call + for (unsigned i = 0; i < arguments.size(); ++i) + { + arguments[i]->accept(*this); + appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true); + // @todo move this once we actually use memory + m_context << u256(i * 32) << eth::Instruction::MSTORE; } + m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0) + << contractAddress << u256(500) //@todo determine actual gas requirement + << eth::Instruction::CALL + << eth::Instruction::POP + << u256(0) << eth::Instruction::MLOAD; + break; + } + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type.")); } } return false; @@ -289,9 +320,11 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); break; case Type::Category::CONTRACT: - // call function - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Contract variables not yet implemented.")); + { + ContractType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); + m_context << type.getFunctionIndex(member); break; + } case Type::Category::MAGIC: // we can ignore the kind of magic and only look at the name of the member if (member == "coinbase") diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 83d7cdc6c..fbecbdc8e 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -31,9 +31,10 @@ class AssemblyItem; // forward } namespace solidity { -class CompilerContext; // forward -class Type; // forward -class IntegerType; // forward +// forward declarations +class CompilerContext; +class Type; +class IntegerType; /** * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 7e07b1162..3829016f5 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -140,7 +140,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return _convertTo.getCategory() == getCategory(); + return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::CONTRACT; } bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const @@ -247,6 +247,31 @@ string ContractType::toString() const return "contract " + m_contract.getName(); } +MemberList const& ContractType::getMembers() const +{ + // We need to lazy-initialize it because of recursive references. + if (!m_members) + { + map> members; + for (FunctionDefinition const* function: m_contract.getInterfaceFunctions()) + members[function->getName()] = make_shared(*function, false); + m_members.reset(new MemberList(members)); + } + return *m_members; +} + +unsigned ContractType::getFunctionIndex(string const& _functionName) const +{ + unsigned index = 0; + for (FunctionDefinition const* function: m_contract.getInterfaceFunctions()) + { + if (function->getName() == _functionName) + return index; + ++index; + } + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index of non-existing contract function requested.")); +} + bool StructType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -302,7 +327,7 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); } -FunctionType::FunctionType(FunctionDefinition const& _function) +FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal) { TypePointers params; TypePointers retParams; @@ -314,7 +339,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function) retParams.push_back(var->getType()); swap(params, m_parameterTypes); swap(retParams, m_returnParameterTypes); - m_location = Location::INTERNAL; + m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL; } bool FunctionType::operator==(Type const& _other) const @@ -323,6 +348,8 @@ bool FunctionType::operator==(Type const& _other) const return false; FunctionType const& other = dynamic_cast(_other); + if (m_location != other.m_location) + return false; if (m_parameterTypes.size() != other.m_parameterTypes.size() || m_returnParameterTypes.size() != other.m_returnParameterTypes.size()) return false; diff --git a/libsolidity/Types.h b/libsolidity/Types.h index b655f9e0d..8e2f4803b 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -214,10 +214,17 @@ public: virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool operator==(Type const& _other) const override; virtual u256 getStorageSize() const override; + virtual bool isValueType() const override { return true; } virtual std::string toString() const override; + virtual MemberList const& getMembers() const override; + + unsigned getFunctionIndex(std::string const& _functionName) const; + private: ContractDefinition const& m_contract; + /// List of member types, will be lazy-initialized because of recursive references. + mutable std::unique_ptr m_members; }; /** @@ -263,7 +270,7 @@ public: enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 }; virtual Category getCategory() const override { return Category::FUNCTION; } - explicit FunctionType(FunctionDefinition const& _function); + explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes, Location _location = Location::INTERNAL): m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 8b25d4031..9e02438e8 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -47,9 +47,11 @@ class ExecutionFramework public: ExecutionFramework() { g_logVerbosity = 0; } - bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0) + bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0, string const& _contractName = "") { - bytes code = dev::solidity::CompilerStack::staticCompile(_sourceCode); + dev::solidity::CompilerStack compiler; + compiler.compile(_sourceCode); + bytes code = compiler.getBytecode(_contractName); sendMessage(code, true, _value); BOOST_REQUIRE(!m_output.empty()); return m_output; @@ -115,6 +117,7 @@ private: void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0) { + m_state.addBalance(m_sender, _value); // just in case eth::Executive executive(m_state); eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) : eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); @@ -127,7 +130,7 @@ private: catch (...) {} if (_isCreation) { - BOOST_REQUIRE(!executive.create(Address(), _value, m_gasPrice, m_gas, &_data, Address())); + BOOST_REQUIRE(!executive.create(m_sender, _value, m_gasPrice, m_gas, &_data, m_sender)); m_contractAddress = executive.newAddress(); BOOST_REQUIRE(m_contractAddress); BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); @@ -135,14 +138,16 @@ private: else { BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); - BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), _value, m_gasPrice, &_data, m_gas, Address())); + BOOST_REQUIRE(!executive.call(m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas, m_sender)); } BOOST_REQUIRE(executive.go()); + m_state.noteSending(m_sender); executive.finalize(); m_output = executive.out().toVector(); } protected: + Address m_sender; Address m_contractAddress; eth::State m_state; u256 const m_gasPrice = 100 * eth::szabo; @@ -898,6 +903,128 @@ BOOST_AUTO_TEST_CASE(ecrecover) BOOST_CHECK(callContractFunction(0, h, v, r, s) == toBigEndian(addr)); } +BOOST_AUTO_TEST_CASE(inter_contract_calls) +{ + char const* sourceCode = R"( + contract Helper { + function multiply(uint a, uint b) returns (uint c) { + return a * b; + } + } + contract Main { + Helper h; + function callHelper(uint a, uint b) returns (uint c) { + return h.multiply(a, b); + } + function getHelper() returns (address haddress) { + return address(h); + } + function setHelper(address haddress) { + h = Helper(haddress); + } + })"; + compileAndRun(sourceCode, 0, "Helper"); + u160 const helperAddress = m_contractAddress; + compileAndRun(sourceCode, 0, "Main"); + BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes()); + BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress)); + u256 a(3456789); + u256 b("0x282837623374623234aa74"); + BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b)); +} + +BOOST_AUTO_TEST_CASE(inter_contract_calls_with_complex_parameters) +{ + char const* sourceCode = R"( + contract Helper { + function sel(uint a, bool select, uint b) returns (uint c) { + if (select) return a; else return b; + } + } + contract Main { + Helper h; + function callHelper(uint a, bool select, uint b) returns (uint c) { + return h.sel(a, select, b) * 3; + } + function getHelper() returns (address haddress) { + return address(h); + } + function setHelper(address haddress) { + h = Helper(haddress); + } + })"; + compileAndRun(sourceCode, 0, "Helper"); + u160 const helperAddress = m_contractAddress; + compileAndRun(sourceCode, 0, "Main"); + BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes()); + BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress)); + u256 a(3456789); + u256 b("0x282837623374623234aa74"); + BOOST_REQUIRE(callContractFunction(0, a, true, b) == toBigEndian(a * 3)); + BOOST_REQUIRE(callContractFunction(0, a, false, b) == toBigEndian(b * 3)); +} + +BOOST_AUTO_TEST_CASE(inter_contract_calls_accessing_this) +{ + char const* sourceCode = R"( + contract Helper { + function getAddress() returns (address addr) { + return address(this); + } + } + contract Main { + Helper h; + function callHelper() returns (address addr) { + return h.getAddress(); + } + function getHelper() returns (address addr) { + return address(h); + } + function setHelper(address addr) { + h = Helper(addr); + } + })"; + compileAndRun(sourceCode, 0, "Helper"); + u160 const helperAddress = m_contractAddress; + compileAndRun(sourceCode, 0, "Main"); + BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes()); + BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress)); + BOOST_REQUIRE(callContractFunction(0) == toBigEndian(helperAddress)); +} + +BOOST_AUTO_TEST_CASE(calls_to_this) +{ + char const* sourceCode = R"( + contract Helper { + function invoke(uint a, uint b) returns (uint c) { + return this.multiply(a, b, 10); + } + function multiply(uint a, uint b, uint8 c) returns (uint ret) { + return a * b + c; + } + } + contract Main { + Helper h; + function callHelper(uint a, uint b) returns (uint ret) { + return h.invoke(a, b); + } + function getHelper() returns (address addr) { + return address(h); + } + function setHelper(address addr) { + h = Helper(addr); + } + })"; + compileAndRun(sourceCode, 0, "Helper"); + u160 const helperAddress = m_contractAddress; + compileAndRun(sourceCode, 0, "Main"); + BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes()); + BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress)); + u256 a(3456789); + u256 b("0x282837623374623234aa74"); + BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 10)); +} + BOOST_AUTO_TEST_SUITE_END() }