From 8c6bf21c01d3ae3c22301bbe0b039aa18e1f58a6 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 11 Nov 2014 17:41:48 +0100 Subject: [PATCH] Provide interface for calls in JSON and some other formatting changes. --- AST.cpp | 15 +++++++ AST.h | 2 + ASTPrinter.cpp | 8 ++-- ASTPrinter.h | 4 +- CompilerStack.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++---- CompilerStack.h | 41 ++++++++++++++++++- 6 files changed, 156 insertions(+), 16 deletions(-) diff --git a/AST.cpp b/AST.cpp index 565560adc..70af8f98e 100644 --- a/AST.cpp +++ b/AST.cpp @@ -263,6 +263,21 @@ TypeError ASTNode::createTypeError(string const& _description) return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); } +vector ContractDefinition::getInterfaceFunctions() const +{ + vector exportedFunctions; + for (ASTPointer const& f: m_definedFunctions) + if (f->isPublic() && f->getName() != getName()) + exportedFunctions.push_back(f.get()); + auto compareNames = [](FunctionDefinition const* _a, FunctionDefinition const* _b) + { + return _a->getName().compare(_b->getName()) < 0; + }; + + sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames); + return exportedFunctions; +} + void Block::checkTypeRequirements() { for (shared_ptr const& statement: m_statements) diff --git a/AST.h b/AST.h index 19328e5f7..7b266f132 100644 --- a/AST.h +++ b/AST.h @@ -120,6 +120,8 @@ public: std::vector> const& getStateVariables() const { return m_stateVariables; } std::vector> const& getDefinedFunctions() const { return m_definedFunctions; } + /// Returns the functions that make up the calling interface in the intended order. + std::vector getInterfaceFunctions() const; private: std::vector> m_definedStructs; std::vector> m_stateVariables; diff --git a/ASTPrinter.cpp b/ASTPrinter.cpp index eb9d92f08..987ad11cc 100644 --- a/ASTPrinter.cpp +++ b/ASTPrinter.cpp @@ -30,8 +30,8 @@ namespace dev namespace solidity { -ASTPrinter::ASTPrinter(ASTPointer const& _ast, string const& _source): - m_indentation(0), m_source(_source), m_ast(_ast) +ASTPrinter::ASTPrinter(ASTNode& _ast, string const& _source): + m_indentation(0), m_source(_source), m_ast(&_ast) { } @@ -430,8 +430,8 @@ void ASTPrinter::printSourcePart(ASTNode const& _node) if (!m_source.empty()) { Location const& location(_node.getLocation()); - *m_ostream << getIndentation() << " Source: |" - << m_source.substr(location.start, location.end - location.start) << "|" << endl; + *m_ostream << getIndentation() << " Source: " + << escaped(m_source.substr(location.start, location.end - location.start), false) << endl; } } diff --git a/ASTPrinter.h b/ASTPrinter.h index e87b2ba3b..e0757fbc4 100644 --- a/ASTPrinter.h +++ b/ASTPrinter.h @@ -38,7 +38,7 @@ class ASTPrinter: public ASTVisitor public: /// Create a printer for the given abstract syntax tree. If the source is specified, /// the corresponding parts of the source are printed with each node. - ASTPrinter(ASTPointer const& _ast, std::string const& _source = std::string()); + ASTPrinter(ASTNode& _ast, std::string const& _source = std::string()); /// Output the string representation of the AST to _stream. void print(std::ostream& _stream); @@ -114,7 +114,7 @@ private: int m_indentation; std::string m_source; - ASTPointer m_ast; + ASTNode* m_ast; std::ostream* m_ostream; }; diff --git a/CompilerStack.cpp b/CompilerStack.cpp index c991171a5..d87c27916 100644 --- a/CompilerStack.cpp +++ b/CompilerStack.cpp @@ -34,17 +34,101 @@ namespace dev namespace solidity { -bytes CompilerStack::compile(std::string const& _sourceCode, shared_ptr _scanner, - bool _optimize) +void CompilerStack::setSource(string const& _sourceCode) { - if (!_scanner) - _scanner = make_shared(); - _scanner->reset(CharStream(_sourceCode)); - - ASTPointer contract = Parser().parse(_scanner); - NameAndTypeResolver().resolveNamesAndTypes(*contract); - return Compiler::compile(*contract, _optimize); + reset(); + m_scanner = make_shared(CharStream(_sourceCode)); } +void CompilerStack::parse() +{ + if (!m_scanner) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available.")); + m_contractASTNode = Parser().parse(m_scanner); + NameAndTypeResolver().resolveNamesAndTypes(*m_contractASTNode); + m_parseSuccessful = true; +} + +void CompilerStack::parse(string const& _sourceCode) +{ + setSource(_sourceCode); + parse(); +} + +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); + return m_bytecode = m_compiler->getAssembledBytecode(_optimize); +} + +bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize) +{ + parse(_sourceCode); + return compile(_optimize); +} + +void CompilerStack::streamAssembly(ostream& _outStream) +{ + if (!m_compiler || m_bytecode.empty()) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); + m_compiler->streamAssembly(_outStream); +} + +string const& CompilerStack::getInterface() +{ + if (!m_parseSuccessful) + 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) + { + 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 (--functionsCount > 0) + interface << ","; + } + interface << ']'; + m_interface = interface.str(); + } + return m_interface; +} + +bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) +{ + CompilerStack stack; + return stack.compile(_sourceCode, _optimize); +} + + + } } diff --git a/CompilerStack.h b/CompilerStack.h index b003745d2..2fb505897 100644 --- a/CompilerStack.h +++ b/CompilerStack.h @@ -22,6 +22,7 @@ #pragma once +#include #include #include #include @@ -30,13 +31,51 @@ namespace dev { namespace solidity { class Scanner; // forward +class ContractDefinition; // forward +class Compiler; // forward +/** + * 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 { public: + CompilerStack() {} + void reset() { *this = CompilerStack(); } + void setSource(std::string const& _sourceCode); + void parse(); + void parse(std::string const& _sourceCode); + /// Compiles the contract that was previously parsed. + bytes const& compile(bool _optimize = false); + /// Parses and compiles the given source code. + bytes const& compile(std::string const& _sourceCode, bool _optimize = false); + + bytes const& getBytecode() const { return m_bytecode; } + /// Streams a verbose version of the assembly to @a _outStream. + /// Prerequisite: Successful compilation. + void streamAssembly(std::ostream& _outStream); + + /// Returns a string representing the contract interface in JSON. + /// Prerequisite: Successful call to parse or compile. + std::string const& getInterface(); + + /// 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; } + /// 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 compile(std::string const& _sourceCode, std::shared_ptr _scanner = std::shared_ptr(), bool _optimize = false); + static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false); + +private: + std::shared_ptr m_scanner; + std::shared_ptr m_contractASTNode; + bool m_parseSuccessful; + std::string m_interface; + std::shared_ptr m_compiler; + bytes m_bytecode; }; }