ethdev
10 years ago
15 changed files with 726 additions and 186 deletions
@ -0,0 +1,71 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Routines used by both the compiler and the expression compiler. |
|||
*/ |
|||
|
|||
#include <libsolidity/CompilerUtils.h> |
|||
#include <libsolidity/AST.h> |
|||
#include <libevmcore/Instruction.h> |
|||
|
|||
using namespace std; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) |
|||
{ |
|||
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable)); |
|||
unsigned const size = _variable.getType()->getSizeOnStack(); |
|||
// move variable starting from its top end in the stack
|
|||
if (stackPosition - size + 1 > 16) |
|||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation()) |
|||
<< errinfo_comment("Stack too deep.")); |
|||
for (unsigned i = 0; i < size; ++i) |
|||
m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP; |
|||
} |
|||
|
|||
void CompilerUtils::copyToStackTop(unsigned _stackDepth, Type const& _type) |
|||
{ |
|||
if (_stackDepth > 16) |
|||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Stack too deep.")); |
|||
unsigned const size = _type.getSizeOnStack(); |
|||
for (unsigned i = 0; i < size; ++i) |
|||
m_context << eth::dupInstruction(_stackDepth); |
|||
} |
|||
|
|||
void CompilerUtils::popStackElement(Type const& _type) |
|||
{ |
|||
unsigned const size = _type.getSizeOnStack(); |
|||
for (unsigned i = 0; i < size; ++i) |
|||
m_context << eth::Instruction::POP; |
|||
} |
|||
|
|||
unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _variableTypes) |
|||
{ |
|||
unsigned size = 0; |
|||
for (shared_ptr<Type const> const& type: _variableTypes) |
|||
size += type->getSizeOnStack(); |
|||
return size; |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,63 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Routines used by both the compiler and the expression compiler. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <libsolidity/CompilerContext.h> |
|||
#include <libsolidity/ASTForward.h> |
|||
|
|||
namespace dev { |
|||
namespace solidity { |
|||
|
|||
class Type; // forward
|
|||
|
|||
class CompilerUtils |
|||
{ |
|||
public: |
|||
CompilerUtils(CompilerContext& _context): m_context(_context) {} |
|||
|
|||
/// Moves the value that is at the top of the stack to a stack variable.
|
|||
void moveToStackVariable(VariableDeclaration const& _variable); |
|||
/// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack.
|
|||
void copyToStackTop(unsigned _stackDepth, Type const& _type); |
|||
/// Removes the current value from the top of the stack.
|
|||
void popStackElement(Type const& _type); |
|||
|
|||
template <class T> |
|||
static unsigned getSizeOnStack(std::vector<T> const& _variables); |
|||
static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes); |
|||
|
|||
private: |
|||
CompilerContext& m_context; |
|||
}; |
|||
|
|||
template <class T> |
|||
unsigned CompilerUtils::getSizeOnStack(std::vector<T> const& _variables) |
|||
{ |
|||
unsigned size = 0; |
|||
for (T const& variable: _variables) |
|||
size += variable->getType()->getSizeOnStack(); |
|||
return size; |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,368 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Lefteris <lefteris@ethdev.com> |
|||
* @date 2014 |
|||
* Solidity command line interface. |
|||
*/ |
|||
#include "CommandLineInterface.h" |
|||
|
|||
#include <string> |
|||
#include <iostream> |
|||
#include <fstream> |
|||
|
|||
#include <boost/filesystem.hpp> |
|||
|
|||
#include "BuildInfo.h" |
|||
#include <libdevcore/Common.h> |
|||
#include <libdevcore/CommonData.h> |
|||
#include <libdevcore/CommonIO.h> |
|||
#include <libevmcore/Instruction.h> |
|||
#include <libsolidity/Scanner.h> |
|||
#include <libsolidity/Parser.h> |
|||
#include <libsolidity/ASTPrinter.h> |
|||
#include <libsolidity/NameAndTypeResolver.h> |
|||
#include <libsolidity/Exceptions.h> |
|||
#include <libsolidity/CompilerStack.h> |
|||
#include <libsolidity/SourceReferenceFormatter.h> |
|||
|
|||
using namespace std; |
|||
namespace po = boost::program_options; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
static void version() |
|||
{ |
|||
cout << "solc, the solidity complier commandline interface " << dev::Version << endl |
|||
<< " by Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com>, (c) 2014." << endl |
|||
<< "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; |
|||
exit(0); |
|||
} |
|||
|
|||
static inline bool argToStdout(po::variables_map const& _args, const char* _name) |
|||
{ |
|||
return _args.count(_name) && _args[_name].as<OutputType>() != OutputType::FILE; |
|||
} |
|||
|
|||
static bool needStdout(po::variables_map const& _args) |
|||
{ |
|||
return argToStdout(_args, "abi") || argToStdout(_args, "natspec-user") || argToStdout(_args, "natspec-dev") || |
|||
argToStdout(_args, "asm") || argToStdout(_args, "opcodes") || argToStdout(_args, "binary"); |
|||
} |
|||
|
|||
static inline bool outputToFile(OutputType type) |
|||
{ |
|||
return type == OutputType::FILE || type == OutputType::BOTH; |
|||
} |
|||
|
|||
static inline bool outputToStdout(OutputType type) |
|||
{ |
|||
return type == OutputType::STDOUT || type == OutputType::BOTH; |
|||
} |
|||
|
|||
static std::istream& operator>>(std::istream& _in, OutputType& io_output) |
|||
{ |
|||
std::string token; |
|||
_in >> token; |
|||
if (token == "stdout") |
|||
io_output = OutputType::STDOUT; |
|||
else if (token == "file") |
|||
io_output = OutputType::FILE; |
|||
else if (token == "both") |
|||
io_output = OutputType::BOTH; |
|||
else |
|||
throw boost::program_options::invalid_option_value(token); |
|||
return _in; |
|||
} |
|||
|
|||
void CommandLineInterface::handleBinary(string const& _contract) |
|||
{ |
|||
auto choice = m_args["binary"].as<OutputType>(); |
|||
if (outputToStdout(choice)) |
|||
{ |
|||
cout << "Binary: " << endl; |
|||
cout << toHex(m_compiler.getBytecode(_contract)) << endl; |
|||
} |
|||
|
|||
if (outputToFile(choice)) |
|||
{ |
|||
ofstream outFile(_contract + ".binary"); |
|||
outFile << toHex(m_compiler.getBytecode(_contract)); |
|||
outFile.close(); |
|||
} |
|||
} |
|||
|
|||
void CommandLineInterface::handleOpcode(string const& _contract) |
|||
{ |
|||
// TODO: Figure out why the wrong operator << (from boost) is used here
|
|||
auto choice = m_args["opcode"].as<OutputType>(); |
|||
if (outputToStdout(choice)) |
|||
{ |
|||
cout << "Opcodes: " << endl; |
|||
dev::operator<<(cout, m_compiler.getBytecode(_contract)); |
|||
cout << endl; |
|||
} |
|||
|
|||
if (outputToFile(choice)) |
|||
{ |
|||
ofstream outFile(_contract + ".opcode"); |
|||
dev::operator<<(outFile, m_compiler.getBytecode(_contract)); |
|||
outFile.close(); |
|||
} |
|||
} |
|||
|
|||
void CommandLineInterface::handleBytecode(string const& _contract) |
|||
{ |
|||
if (m_args.count("opcodes")) |
|||
handleOpcode(_contract); |
|||
if (m_args.count("binary")) |
|||
handleBinary(_contract); |
|||
} |
|||
|
|||
void CommandLineInterface::handleJson(DocumentationType _type, |
|||
string const& _contract) |
|||
{ |
|||
std::string argName; |
|||
std::string suffix; |
|||
std::string title; |
|||
switch(_type) |
|||
{ |
|||
case DocumentationType::ABI_INTERFACE: |
|||
argName = "abi"; |
|||
suffix = ".abi"; |
|||
title = "Contract ABI"; |
|||
break; |
|||
case DocumentationType::NATSPEC_USER: |
|||
argName = "natspec-user"; |
|||
suffix = ".docuser"; |
|||
title = "User Documentation"; |
|||
break; |
|||
case DocumentationType::NATSPEC_DEV: |
|||
argName = "natspec-dev"; |
|||
suffix = ".docdev"; |
|||
title = "Developer Documentation"; |
|||
break; |
|||
default: |
|||
// should never happen
|
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation _type")); |
|||
} |
|||
|
|||
if (m_args.count(argName)) |
|||
{ |
|||
auto choice = m_args[argName].as<OutputType>(); |
|||
if (outputToStdout(choice)) |
|||
{ |
|||
cout << title << endl; |
|||
cout << m_compiler.getJsonDocumentation(_contract, _type); |
|||
} |
|||
|
|||
if (outputToFile(choice)) |
|||
{ |
|||
ofstream outFile(_contract + suffix); |
|||
outFile << m_compiler.getJsonDocumentation(_contract, _type); |
|||
outFile.close(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
bool CommandLineInterface::parseArguments(int argc, char** argv) |
|||
{ |
|||
#define OUTPUT_TYPE_STR "Legal values:\n" \ |
|||
"\tstdout: Print it to standard output\n" \ |
|||
"\tfile: Print it to a file with same name\n" \ |
|||
"\tboth: Print both to a file and the stdout\n" |
|||
// Declare the supported options.
|
|||
po::options_description desc("Allowed options"); |
|||
desc.add_options() |
|||
("help", "Show help message and exit") |
|||
("version", "Show version and exit") |
|||
("optimize", po::value<bool>()->default_value(false), "Optimize bytecode for size") |
|||
("input-file", po::value<vector<string>>(), "input file") |
|||
("ast", po::value<OutputType>(), |
|||
"Request to output the AST of the contract. " OUTPUT_TYPE_STR) |
|||
("asm", po::value<OutputType>(), |
|||
"Request to output the EVM assembly of the contract. " OUTPUT_TYPE_STR) |
|||
("opcodes", po::value<OutputType>(), |
|||
"Request to output the Opcodes of the contract. " OUTPUT_TYPE_STR) |
|||
("binary", po::value<OutputType>(), |
|||
"Request to output the contract in binary (hexadecimal). " OUTPUT_TYPE_STR) |
|||
("abi", po::value<OutputType>(), |
|||
"Request to output the contract's ABI interface. " OUTPUT_TYPE_STR) |
|||
("natspec-user", po::value<OutputType>(), |
|||
"Request to output the contract's Natspec user documentation. " OUTPUT_TYPE_STR) |
|||
("natspec-dev", po::value<OutputType>(), |
|||
"Request to output the contract's Natspec developer documentation. " OUTPUT_TYPE_STR); |
|||
#undef OUTPUT_TYPE_STR |
|||
|
|||
// All positional options should be interpreted as input files
|
|||
po::positional_options_description p; |
|||
p.add("input-file", -1); |
|||
|
|||
// parse the compiler arguments
|
|||
try |
|||
{ |
|||
po::store(po::command_line_parser(argc, argv).options(desc).positional(p).allow_unregistered().run(), m_args); |
|||
} |
|||
catch (po::error const& exception) |
|||
{ |
|||
cout << exception.what() << endl; |
|||
return false; |
|||
} |
|||
po::notify(m_args); |
|||
|
|||
if (m_args.count("help")) |
|||
{ |
|||
cout << desc; |
|||
return false; |
|||
} |
|||
|
|||
if (m_args.count("version")) |
|||
{ |
|||
version(); |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
bool CommandLineInterface::processInput() |
|||
{ |
|||
if (!m_args.count("input-file")) |
|||
{ |
|||
string s; |
|||
while (!cin.eof()) |
|||
{ |
|||
getline(cin, s); |
|||
m_sourceCodes["<stdin>"].append(s); |
|||
} |
|||
} |
|||
else |
|||
for (string const& infile: m_args["input-file"].as<vector<string>>()) |
|||
m_sourceCodes[infile] = asString(dev::contents(infile)); |
|||
|
|||
try |
|||
{ |
|||
for (auto const& sourceCode: m_sourceCodes) |
|||
m_compiler.addSource(sourceCode.first, sourceCode.second); |
|||
// TODO: Perhaps we should not compile unless requested
|
|||
m_compiler.compile(m_args["optimize"].as<bool>()); |
|||
} |
|||
catch (ParserError const& exception) |
|||
{ |
|||
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", m_compiler); |
|||
return false; |
|||
} |
|||
catch (DeclarationError const& exception) |
|||
{ |
|||
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", m_compiler); |
|||
return false; |
|||
} |
|||
catch (TypeError const& exception) |
|||
{ |
|||
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", m_compiler); |
|||
return false; |
|||
} |
|||
catch (CompilerError const& exception) |
|||
{ |
|||
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", m_compiler); |
|||
return false; |
|||
} |
|||
catch (InternalCompilerError const& exception) |
|||
{ |
|||
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", m_compiler); |
|||
return false; |
|||
} |
|||
catch (Exception const& exception) |
|||
{ |
|||
cerr << "Exception during compilation: " << boost::diagnostic_information(exception) << endl; |
|||
return false; |
|||
} |
|||
catch (...) |
|||
{ |
|||
cerr << "Unknown exception during compilation." << endl; |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void CommandLineInterface::actOnInput() |
|||
{ |
|||
// do we need AST output?
|
|||
if (m_args.count("ast")) |
|||
{ |
|||
auto choice = m_args["ast"].as<OutputType>(); |
|||
if (outputToStdout(choice)) |
|||
{ |
|||
cout << "Syntax trees:" << endl << endl; |
|||
for (auto const& sourceCode: m_sourceCodes) |
|||
{ |
|||
cout << endl << "======= " << sourceCode.first << " =======" << endl; |
|||
ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second); |
|||
printer.print(cout); |
|||
} |
|||
} |
|||
|
|||
if (outputToFile(choice)) |
|||
{ |
|||
for (auto const& sourceCode: m_sourceCodes) |
|||
{ |
|||
boost::filesystem::path p(sourceCode.first); |
|||
ofstream outFile(p.stem().string() + ".ast"); |
|||
ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second); |
|||
printer.print(outFile); |
|||
outFile.close(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
vector<string> contracts = m_compiler.getContractNames(); |
|||
for (string const& contract: contracts) |
|||
{ |
|||
if (needStdout(m_args)) |
|||
cout << endl << "======= " << contract << " =======" << endl; |
|||
|
|||
// do we need EVM assembly?
|
|||
if (m_args.count("asm")) |
|||
{ |
|||
auto choice = m_args["asm"].as<OutputType>(); |
|||
if (outputToStdout(choice)) |
|||
{ |
|||
cout << "EVM assembly:" << endl; |
|||
m_compiler.streamAssembly(cout, contract); |
|||
} |
|||
|
|||
if (outputToFile(choice)) |
|||
{ |
|||
ofstream outFile(contract + ".evm"); |
|||
m_compiler.streamAssembly(outFile, contract); |
|||
outFile.close(); |
|||
} |
|||
} |
|||
|
|||
handleBytecode(contract); |
|||
handleJson(DocumentationType::ABI_INTERFACE, contract); |
|||
handleJson(DocumentationType::NATSPEC_DEV, contract); |
|||
handleJson(DocumentationType::NATSPEC_USER, contract); |
|||
} // end of contracts iteration
|
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,71 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Lefteris <lefteris@ethdev.com> |
|||
* @date 2014 |
|||
* Solidity command line interface. |
|||
*/ |
|||
#pragma once |
|||
|
|||
#include <boost/program_options.hpp> |
|||
|
|||
#include <libsolidity/CompilerStack.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
//forward declaration
|
|||
enum class DocumentationType: uint8_t; |
|||
|
|||
enum class OutputType: uint8_t |
|||
{ |
|||
STDOUT, |
|||
FILE, |
|||
BOTH |
|||
}; |
|||
|
|||
class CommandLineInterface |
|||
{ |
|||
public: |
|||
CommandLineInterface() {} |
|||
|
|||
/// Parse command line arguments and return false if we should not continue
|
|||
bool parseArguments(int argc, char** argv); |
|||
/// Parse the files and create source code objects
|
|||
bool processInput(); |
|||
/// Perform actions on the input depending on provided compiler arguments
|
|||
void actOnInput(); |
|||
|
|||
private: |
|||
void handleBinary(std::string const& _contract); |
|||
void handleOpcode(std::string const& _contract); |
|||
void handleBytecode(std::string const& _contract); |
|||
void handleJson(DocumentationType _type, |
|||
std::string const& _contract); |
|||
|
|||
/// Compiler arguments variable map
|
|||
boost::program_options::variables_map m_args; |
|||
/// map of input files to source code strings
|
|||
std::map<std::string, std::string> m_sourceCodes; |
|||
/// Solidity compiler stack
|
|||
dev::solidity::CompilerStack m_compiler; |
|||
}; |
|||
|
|||
} |
|||
} |
Loading…
Reference in new issue