diff options
author | Marek Kotewicz <marek.kotewicz@gmail.com> | 2014-11-14 21:08:44 +0800 |
---|---|---|
committer | Marek Kotewicz <marek.kotewicz@gmail.com> | 2014-11-14 21:08:44 +0800 |
commit | 48b89901c29052e0a663efa01081193bb332157d (patch) | |
tree | 48fbabe870b6f6b5017970ccf7cfac687b92a041 | |
parent | c3e80e69f9adc2beb5a485404914a4e0d9eef29b (diff) | |
parent | c4a65cf6888f6b15fa7740b6db5d9dae8f18b7ba (diff) | |
download | dexon-solidity-48b89901c29052e0a663efa01081193bb332157d.tar dexon-solidity-48b89901c29052e0a663efa01081193bb332157d.tar.gz dexon-solidity-48b89901c29052e0a663efa01081193bb332157d.tar.bz2 dexon-solidity-48b89901c29052e0a663efa01081193bb332157d.tar.lz dexon-solidity-48b89901c29052e0a663efa01081193bb332157d.tar.xz dexon-solidity-48b89901c29052e0a663efa01081193bb332157d.tar.zst dexon-solidity-48b89901c29052e0a663efa01081193bb332157d.zip |
Merge branch 'develop' into js_abi
-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-- | Compiler.cpp | 40 | ||||
-rw-r--r-- | CompilerStack.cpp | 100 | ||||
-rw-r--r-- | CompilerStack.h | 41 |
7 files changed, 174 insertions, 36 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/Compiler.cpp b/Compiler.cpp index ed2b1f45..da28ba8a 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -81,36 +81,34 @@ void Compiler::appendFunctionSelector(vector<ASTPointer<FunctionDefinition>> con if (publicFunctions.size() > 255) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract.")); - //@todo check for calldatasize? - // retrieve the first byte of the call data - m_context << u256(0) << eth::Instruction::CALLDATALOAD << u256(0) << eth::Instruction::BYTE; - // check that it is not too large - m_context << eth::Instruction::DUP1 << u256(publicFunctions.size() - 1) << eth::Instruction::LT; - eth::AssemblyItem returnTag = m_context.appendConditionalJump(); - - // otherwise, jump inside jump table (each entry of the table has size 4) - m_context << u256(4) << eth::Instruction::MUL; - eth::AssemblyItem jumpTableStart = m_context.pushNewTag(); - m_context << eth::Instruction::ADD << eth::Instruction::JUMP; - - // jump table, tell the optimizer not to remove the JUMPDESTs - m_context << eth::AssemblyItem(eth::NoOptimizeBegin) << jumpTableStart; + // retrieve the first byte of the call data, which determines the called function + // @todo This code had a jump table in a previous version which was more efficient but also + // error prone (due to the optimizer and variable length tag addresses) + m_context << u256(1) << u256(0) // some constants + << eth::dupInstruction(1) << eth::Instruction::CALLDATALOAD + << eth::dupInstruction(2) << eth::Instruction::BYTE + << eth::dupInstruction(2); + + // stack here: 1 0 <funid> 0, stack top will be counted up until it matches funid for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions) - m_context.appendJumpTo(f.second.second) << eth::Instruction::JUMPDEST; - m_context << eth::AssemblyItem(eth::NoOptimizeEnd); - - m_context << returnTag << eth::Instruction::STOP; + { + eth::AssemblyItem const& callDataUnpackerEntry = f.second.second; + m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ; + m_context.appendConditionalJumpTo(callDataUnpackerEntry); + m_context << eth::dupInstruction(4) << eth::Instruction::ADD; + //@todo avoid the last ADD (or remove it in the optimizer) + } + m_context << eth::Instruction::STOP; // function not found for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions) { FunctionDefinition const& function = *f.second.first; - m_context << f.second.second; - + eth::AssemblyItem const& callDataUnpackerEntry = f.second.second; + m_context << callDataUnpackerEntry; eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(function); m_context.appendJumpTo(m_context.getFunctionEntryLabel(function)); m_context << returnTag; - appendReturnValuePacker(function); } } 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; }; } |