diff options
-rw-r--r-- | AST.cpp | 15 | ||||
-rw-r--r-- | AST.h | 2 | ||||
-rw-r--r-- | ASTPrinter.cpp | 8 | ||||
-rw-r--r-- | ASTPrinter.h | 4 | ||||
-rw-r--r-- | CompilerStack.cpp | 100 | ||||
-rw-r--r-- | CompilerStack.h | 41 |
6 files changed, 155 insertions, 15 deletions
@@ -263,6 +263,21 @@ TypeError ASTNode::createTypeError(string const& _description) return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); } +vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const +{ + vector<FunctionDefinition const*> exportedFunctions; + for (ASTPointer<FunctionDefinition> 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<Statement> const& statement: m_statements) @@ -120,6 +120,8 @@ public: std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; } std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; } + /// Returns the functions that make up the calling interface in the intended order. + std::vector<FunctionDefinition const*> getInterfaceFunctions() const; private: std::vector<ASTPointer<StructDefinition>> m_definedStructs; std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; diff --git a/ASTPrinter.cpp b/ASTPrinter.cpp index eb9d92f0..987ad11c 100644 --- a/ASTPrinter.cpp +++ b/ASTPrinter.cpp @@ -30,8 +30,8 @@ namespace dev namespace solidity { -ASTPrinter::ASTPrinter(ASTPointer<ASTNode> 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 e87b2ba3..e0757fbc 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<ASTNode> 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<ASTNode> m_ast; + ASTNode* m_ast; std::ostream* m_ostream; }; diff --git a/CompilerStack.cpp b/CompilerStack.cpp index c991171a..d87c2791 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> _scanner, - bool _optimize) +void CompilerStack::setSource(string const& _sourceCode) { - if (!_scanner) - _scanner = make_shared<Scanner>(); - _scanner->reset(CharStream(_sourceCode)); + reset(); + m_scanner = make_shared<Scanner>(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<Compiler>(); + m_compiler->compileContract(*m_contractASTNode); + return m_bytecode = m_compiler->getAssembledBytecode(_optimize); +} - ASTPointer<ContractDefinition> contract = Parser().parse(_scanner); - NameAndTypeResolver().resolveNamesAndTypes(*contract); - return Compiler::compile(*contract, _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<FunctionDefinition const*> exportedFunctions = m_contractASTNode->getInterfaceFunctions(); + unsigned functionsCount = exportedFunctions.size(); + for (FunctionDefinition const* f: exportedFunctions) + { + auto streamVariables = [&](vector<ASTPointer<VariableDeclaration>> const& _vars) + { + unsigned varCount = _vars.size(); + for (ASTPointer<VariableDeclaration> 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 b003745d..2fb50589 100644 --- a/CompilerStack.h +++ b/CompilerStack.h @@ -22,6 +22,7 @@ #pragma once +#include <ostream> #include <string> #include <memory> #include <libdevcore/Common.h> @@ -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> _scanner = std::shared_ptr<Scanner>(), bool _optimize = false); + static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false); + +private: + std::shared_ptr<Scanner> m_scanner; + std::shared_ptr<ContractDefinition> m_contractASTNode; + bool m_parseSuccessful; + std::string m_interface; + std::shared_ptr<Compiler> m_compiler; + bytes m_bytecode; }; } |