mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge branch 'develop' into js_abi
This commit is contained in:
commit
48b89901c2
15
AST.cpp
15
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)
|
||||
|
2
AST.h
2
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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
38
Compiler.cpp
38
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();
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
||||
ASTPointer<ContractDefinition> contract = Parser().parse(_scanner);
|
||||
NameAndTypeResolver().resolveNamesAndTypes(*contract);
|
||||
return Compiler::compile(*contract, _optimize);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user