aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Kotewicz <marek.kotewicz@gmail.com>2014-11-14 21:08:44 +0800
committerMarek Kotewicz <marek.kotewicz@gmail.com>2014-11-14 21:08:44 +0800
commit48b89901c29052e0a663efa01081193bb332157d (patch)
tree48fbabe870b6f6b5017970ccf7cfac687b92a041
parentc3e80e69f9adc2beb5a485404914a4e0d9eef29b (diff)
parentc4a65cf6888f6b15fa7740b6db5d9dae8f18b7ba (diff)
downloaddexon-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.cpp15
-rw-r--r--AST.h2
-rw-r--r--ASTPrinter.cpp8
-rw-r--r--ASTPrinter.h4
-rw-r--r--Compiler.cpp40
-rw-r--r--CompilerStack.cpp100
-rw-r--r--CompilerStack.h41
7 files changed, 174 insertions, 36 deletions
diff --git a/AST.cpp b/AST.cpp
index 565560ad..70af8f98 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<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)
diff --git a/AST.h b/AST.h
index 19328e5f..7b266f13 100644
--- a/AST.h
+++ b/AST.h
@@ -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;
};
}