mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			785 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			785 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
	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>
 | 
						|
 * @author Gav Wood <g@ethdev.com>
 | 
						|
 * @date 2014
 | 
						|
 * Solidity command line interface.
 | 
						|
 */
 | 
						|
#include "CommandLineInterface.h"
 | 
						|
 | 
						|
#include <string>
 | 
						|
#include <iostream>
 | 
						|
#include <fstream>
 | 
						|
 | 
						|
#include <boost/filesystem.hpp>
 | 
						|
#include <boost/algorithm/string.hpp>
 | 
						|
 | 
						|
#include "solidity/BuildInfo.h"
 | 
						|
#include <libdevcore/Common.h>
 | 
						|
#include <libdevcore/CommonData.h>
 | 
						|
#include <libdevcore/CommonIO.h>
 | 
						|
#include <libevmcore/Instruction.h>
 | 
						|
#include <libsolidity/interface/Version.h>
 | 
						|
#include <libsolidity/parsing/Scanner.h>
 | 
						|
#include <libsolidity/parsing/Parser.h>
 | 
						|
#include <libsolidity/ast/ASTPrinter.h>
 | 
						|
#include <libsolidity/ast/ASTJsonConverter.h>
 | 
						|
#include <libsolidity/analysis/NameAndTypeResolver.h>
 | 
						|
#include <libsolidity/interface/Exceptions.h>
 | 
						|
#include <libsolidity/interface/CompilerStack.h>
 | 
						|
#include <libsolidity/interface/SourceReferenceFormatter.h>
 | 
						|
#include <libsolidity/interface/GasEstimator.h>
 | 
						|
#include <libsolidity/formal/Why3Translator.h>
 | 
						|
 | 
						|
using namespace std;
 | 
						|
namespace po = boost::program_options;
 | 
						|
 | 
						|
namespace dev
 | 
						|
{
 | 
						|
namespace solidity
 | 
						|
{
 | 
						|
 | 
						|
static string const g_argAbiStr = "abi";
 | 
						|
static string const g_argSolInterfaceStr = "interface";
 | 
						|
static string const g_argSignatureHashes = "hashes";
 | 
						|
static string const g_argGas = "gas";
 | 
						|
static string const g_argAsmStr = "asm";
 | 
						|
static string const g_argAsmJsonStr = "asm-json";
 | 
						|
static string const g_argAstStr = "ast";
 | 
						|
static string const g_argAstJson = "ast-json";
 | 
						|
static string const g_argBinaryStr = "bin";
 | 
						|
static string const g_argRuntimeBinaryStr = "bin-runtime";
 | 
						|
static string const g_argCloneBinaryStr = "clone-bin";
 | 
						|
static string const g_argOpcodesStr = "opcodes";
 | 
						|
static string const g_argNatspecDevStr = "devdoc";
 | 
						|
static string const g_argNatspecUserStr = "userdoc";
 | 
						|
static string const g_argAddStandard = "add-std";
 | 
						|
static string const g_stdinFileName = "<stdin>";
 | 
						|
 | 
						|
/// Possible arguments to for --combined-json
 | 
						|
static set<string> const g_combinedJsonArgs{
 | 
						|
	"bin",
 | 
						|
	"bin-runtime",
 | 
						|
	"clone-bin",
 | 
						|
	"opcodes",
 | 
						|
	"abi",
 | 
						|
	"interface",
 | 
						|
	"asm",
 | 
						|
	"ast",
 | 
						|
	"userdoc",
 | 
						|
	"devdoc"
 | 
						|
};
 | 
						|
 | 
						|
static void version()
 | 
						|
{
 | 
						|
	cout <<
 | 
						|
		"solc, the solidity compiler commandline interface" <<
 | 
						|
		endl <<
 | 
						|
		"Version: " <<
 | 
						|
		dev::solidity::VersionString <<
 | 
						|
		endl;
 | 
						|
	exit(0);
 | 
						|
}
 | 
						|
 | 
						|
static bool needsHumanTargetedStdout(po::variables_map const& _args)
 | 
						|
{
 | 
						|
	if (_args.count(g_argGas))
 | 
						|
		return true;
 | 
						|
	if (_args.count("output-dir"))
 | 
						|
		return false;
 | 
						|
	for (string const& arg: {
 | 
						|
		g_argAbiStr,
 | 
						|
		g_argSolInterfaceStr,
 | 
						|
		g_argSignatureHashes,
 | 
						|
		g_argNatspecUserStr,
 | 
						|
		g_argAstJson,
 | 
						|
		g_argNatspecDevStr,
 | 
						|
		g_argAsmStr,
 | 
						|
		g_argAsmJsonStr,
 | 
						|
		g_argOpcodesStr,
 | 
						|
		g_argBinaryStr,
 | 
						|
		g_argRuntimeBinaryStr,
 | 
						|
		g_argCloneBinaryStr,
 | 
						|
		string("formal")
 | 
						|
	})
 | 
						|
		if (_args.count(arg))
 | 
						|
			return true;
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
void CommandLineInterface::handleBinary(string const& _contract)
 | 
						|
{
 | 
						|
	if (m_args.count(g_argBinaryStr))
 | 
						|
	{
 | 
						|
		if (m_args.count("output-dir"))
 | 
						|
			createFile(_contract + ".bin", m_compiler->object(_contract).toHex());
 | 
						|
		else
 | 
						|
		{
 | 
						|
			cout << "Binary: " << endl;
 | 
						|
			cout << m_compiler->object(_contract).toHex() << endl;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (m_args.count(g_argCloneBinaryStr))
 | 
						|
	{
 | 
						|
		if (m_args.count("output-dir"))
 | 
						|
			createFile(_contract + ".clone_bin", m_compiler->cloneObject(_contract).toHex());
 | 
						|
		else
 | 
						|
		{
 | 
						|
			cout << "Clone Binary: " << endl;
 | 
						|
			cout << m_compiler->cloneObject(_contract).toHex() << endl;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (m_args.count(g_argRuntimeBinaryStr))
 | 
						|
	{
 | 
						|
		if (m_args.count("output-dir"))
 | 
						|
			createFile(_contract + ".bin-runtime", m_compiler->runtimeObject(_contract).toHex());
 | 
						|
		else
 | 
						|
		{
 | 
						|
			cout << "Binary of the runtime part: " << endl;
 | 
						|
			cout << m_compiler->runtimeObject(_contract).toHex() << endl;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void CommandLineInterface::handleOpcode(string const& _contract)
 | 
						|
{
 | 
						|
	if (m_args.count("output-dir"))
 | 
						|
		createFile(_contract + ".opcode", eth::disassemble(m_compiler->object(_contract).bytecode));
 | 
						|
	else
 | 
						|
	{
 | 
						|
		cout << "Opcodes: " << endl;
 | 
						|
		cout << eth::disassemble(m_compiler->object(_contract).bytecode);
 | 
						|
		cout << endl;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void CommandLineInterface::handleBytecode(string const& _contract)
 | 
						|
{
 | 
						|
	if (m_args.count(g_argOpcodesStr))
 | 
						|
		handleOpcode(_contract);
 | 
						|
	if (m_args.count(g_argBinaryStr) || m_args.count(g_argCloneBinaryStr) || m_args.count(g_argRuntimeBinaryStr))
 | 
						|
		handleBinary(_contract);
 | 
						|
}
 | 
						|
 | 
						|
void CommandLineInterface::handleSignatureHashes(string const& _contract)
 | 
						|
{
 | 
						|
	if (!m_args.count(g_argSignatureHashes))
 | 
						|
		return;
 | 
						|
 | 
						|
	string out;
 | 
						|
	for (auto const& it: m_compiler->contractDefinition(_contract).interfaceFunctions())
 | 
						|
		out += toHex(it.first.ref()) + ": " + it.second->externalSignature() + "\n";
 | 
						|
 | 
						|
	if (m_args.count("output-dir"))
 | 
						|
		createFile(_contract + ".signatures", out);
 | 
						|
	else
 | 
						|
		cout << "Function signatures: " << endl << out;
 | 
						|
}
 | 
						|
 | 
						|
void CommandLineInterface::handleMeta(DocumentationType _type, string const& _contract)
 | 
						|
{
 | 
						|
	std::string argName;
 | 
						|
	std::string suffix;
 | 
						|
	std::string title;
 | 
						|
	switch(_type)
 | 
						|
	{
 | 
						|
	case DocumentationType::ABIInterface:
 | 
						|
		argName = g_argAbiStr;
 | 
						|
		suffix = ".abi";
 | 
						|
		title = "Contract JSON ABI";
 | 
						|
		break;
 | 
						|
	case DocumentationType::ABISolidityInterface:
 | 
						|
		argName = g_argSolInterfaceStr;
 | 
						|
		suffix = "_interface.sol";
 | 
						|
		title = "Contract Solidity ABI";
 | 
						|
		break;
 | 
						|
	case DocumentationType::NatspecUser:
 | 
						|
		argName = g_argNatspecUserStr;
 | 
						|
		suffix = ".docuser";
 | 
						|
		title = "User Documentation";
 | 
						|
		break;
 | 
						|
	case DocumentationType::NatspecDev:
 | 
						|
		argName = g_argNatspecDevStr;
 | 
						|
		suffix = ".docdev";
 | 
						|
		title = "Developer Documentation";
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		// should never happen
 | 
						|
		BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation _type"));
 | 
						|
	}
 | 
						|
 | 
						|
	if (m_args.count(argName))
 | 
						|
	{
 | 
						|
		if (m_args.count("output-dir"))
 | 
						|
			createFile(_contract + suffix, m_compiler->metadata(_contract, _type));
 | 
						|
		else
 | 
						|
		{
 | 
						|
			cout << title << endl;
 | 
						|
			cout << m_compiler->metadata(_contract, _type) << endl;
 | 
						|
		}
 | 
						|
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void CommandLineInterface::handleGasEstimation(string const& _contract)
 | 
						|
{
 | 
						|
	eth::EVMSchedule schedule;	// TODO: make it relevant to the SealEngine/EnvInfo.
 | 
						|
	using Gas = GasEstimator::GasConsumption;
 | 
						|
	if (!m_compiler->assemblyItems(_contract) && !m_compiler->runtimeAssemblyItems(_contract))
 | 
						|
		return;
 | 
						|
	cout << "Gas estimation:" << endl;
 | 
						|
	if (eth::AssemblyItems const* items = m_compiler->assemblyItems(_contract))
 | 
						|
	{
 | 
						|
		Gas gas = GasEstimator::functionalEstimation(*items);
 | 
						|
		u256 bytecodeSize(m_compiler->runtimeObject(_contract).bytecode.size());
 | 
						|
		cout << "construction:" << endl;
 | 
						|
		cout << "   " << gas << " + " << (bytecodeSize * schedule.createDataGas) << " = ";
 | 
						|
		gas += bytecodeSize * schedule.createDataGas;
 | 
						|
		cout << gas << endl;
 | 
						|
	}
 | 
						|
	if (eth::AssemblyItems const* items = m_compiler->runtimeAssemblyItems(_contract))
 | 
						|
	{
 | 
						|
		ContractDefinition const& contract = m_compiler->contractDefinition(_contract);
 | 
						|
		cout << "external:" << endl;
 | 
						|
		for (auto it: contract.interfaceFunctions())
 | 
						|
		{
 | 
						|
			string sig = it.second->externalSignature();
 | 
						|
			GasEstimator::GasConsumption gas = GasEstimator::functionalEstimation(*items, sig);
 | 
						|
			cout << "   " << sig << ":\t" << gas << endl;
 | 
						|
		}
 | 
						|
		if (contract.fallbackFunction())
 | 
						|
		{
 | 
						|
			GasEstimator::GasConsumption gas = GasEstimator::functionalEstimation(*items, "INVALID");
 | 
						|
			cout << "   fallback:\t" << gas << endl;
 | 
						|
		}
 | 
						|
		cout << "internal:" << endl;
 | 
						|
		for (auto const& it: contract.definedFunctions())
 | 
						|
		{
 | 
						|
			if (it->isPartOfExternalInterface() || it->isConstructor())
 | 
						|
				continue;
 | 
						|
			size_t entry = m_compiler->functionEntryPoint(_contract, *it);
 | 
						|
			GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
 | 
						|
			if (entry > 0)
 | 
						|
				gas = GasEstimator::functionalEstimation(*items, entry, *it);
 | 
						|
			FunctionType type(*it);
 | 
						|
			cout << "   " << it->name() << "(";
 | 
						|
			auto end = type.parameterTypes().end();
 | 
						|
			for (auto it = type.parameterTypes().begin(); it != end; ++it)
 | 
						|
				cout << (*it)->toString() << (it + 1 == end ? "" : ",");
 | 
						|
			cout << "):\t" << gas << endl;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void CommandLineInterface::handleFormal()
 | 
						|
{
 | 
						|
	if (!m_args.count("formal"))
 | 
						|
		return;
 | 
						|
 | 
						|
	if (m_args.count("output-dir"))
 | 
						|
		createFile("solidity.mlw", m_compiler->formalTranslation());
 | 
						|
	else
 | 
						|
		cout << "Formal version:" << endl << m_compiler->formalTranslation() << endl;
 | 
						|
}
 | 
						|
 | 
						|
bool CommandLineInterface::parseLibraryOption(string const& _input)
 | 
						|
{
 | 
						|
	namespace fs = boost::filesystem;
 | 
						|
	string data = fs::is_regular_file(_input) ? contentsString(_input) : _input;
 | 
						|
 | 
						|
	vector<string> libraries;
 | 
						|
	boost::split(libraries, data, boost::is_space() || boost::is_any_of(","), boost::token_compress_on);
 | 
						|
	for (string const& lib: libraries)
 | 
						|
		if (!lib.empty())
 | 
						|
		{
 | 
						|
			auto colon = lib.find(':');
 | 
						|
			if (colon == string::npos)
 | 
						|
			{
 | 
						|
				cerr << "Colon separator missing in library address specifier \"" << lib << "\"" << endl;
 | 
						|
				return false;
 | 
						|
			}
 | 
						|
			string libName(lib.begin(), lib.begin() + colon);
 | 
						|
			string addrString(lib.begin() + colon + 1, lib.end());
 | 
						|
			boost::trim(libName);
 | 
						|
			boost::trim(addrString);
 | 
						|
			bytes binAddr = fromHex(addrString);
 | 
						|
			h160 address(binAddr, h160::AlignRight);
 | 
						|
			if (binAddr.size() > 20 || address == h160())
 | 
						|
			{
 | 
						|
				cerr << "Invalid address for library \"" << libName << "\": " << addrString << endl;
 | 
						|
				return false;
 | 
						|
			}
 | 
						|
			m_libraries[libName] = address;
 | 
						|
		}
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
void CommandLineInterface::createFile(string const& _fileName, string const& _data)
 | 
						|
{
 | 
						|
	namespace fs = boost::filesystem;
 | 
						|
	// create directory if not existent
 | 
						|
	fs::path p(m_args.at("output-dir").as<string>());
 | 
						|
	fs::create_directories(p);
 | 
						|
	string pathName = (p / _fileName).string();
 | 
						|
	ofstream outFile(pathName);
 | 
						|
	outFile << _data;
 | 
						|
	if (!outFile)
 | 
						|
		BOOST_THROW_EXCEPTION(FileError() << errinfo_comment("Could not write to file: " + pathName));
 | 
						|
}
 | 
						|
 | 
						|
bool CommandLineInterface::parseArguments(int _argc, char** _argv)
 | 
						|
{
 | 
						|
	// Declare the supported options.
 | 
						|
	po::options_description desc(
 | 
						|
		R"(solc, the Solidity commandline compiler.
 | 
						|
Usage: solc [options] [input_file...]
 | 
						|
Compiles the given Solidity input files (or the standard input if none given) and
 | 
						|
outputs the components specified in the options at standard output or in files in
 | 
						|
the output directory, if specified.
 | 
						|
Example: solc --bin -o /tmp/solcoutput contract.sol
 | 
						|
 | 
						|
Allowed options)",
 | 
						|
		po::options_description::m_default_line_length,
 | 
						|
		po::options_description::m_default_line_length - 23);
 | 
						|
	desc.add_options()
 | 
						|
		("help", "Show help message and exit.")
 | 
						|
		("version", "Show version and exit.")
 | 
						|
		("optimize", "Enable bytecode optimizer.")
 | 
						|
		(
 | 
						|
			"optimize-runs",
 | 
						|
			po::value<unsigned>()->value_name("n")->default_value(200),
 | 
						|
			"Estimated number of contract runs for optimizer tuning."
 | 
						|
		)
 | 
						|
		(g_argAddStandard.c_str(), "Add standard contracts.")
 | 
						|
		(
 | 
						|
			"libraries",
 | 
						|
			po::value<vector<string>>()->value_name("libs"),
 | 
						|
			"Direct string or file containing library addresses. Syntax: "
 | 
						|
			"<libraryName>: <address> [, or whitespace] ...\n"
 | 
						|
			"Address is interpreted as a hex string optionally prefixed by 0x."
 | 
						|
		)
 | 
						|
		(
 | 
						|
			"output-dir,o",
 | 
						|
			po::value<string>()->value_name("path"),
 | 
						|
			"If given, creates one file per component and contract/file at the specified directory."
 | 
						|
		)
 | 
						|
		(
 | 
						|
			"combined-json",
 | 
						|
			po::value<string>()->value_name(boost::join(g_combinedJsonArgs, ",")),
 | 
						|
			"Output a single json document containing the specified information."
 | 
						|
		)
 | 
						|
		(g_argGas.c_str(), "Print an estimate of the maximal gas usage for each function.")
 | 
						|
		(
 | 
						|
			"link",
 | 
						|
			"Switch to linker mode, ignoring all options apart from --libraries "
 | 
						|
			"and modify binaries in place."
 | 
						|
		);
 | 
						|
	po::options_description outputComponents("Output Components");
 | 
						|
	outputComponents.add_options()
 | 
						|
		(g_argAstStr.c_str(), "AST of all source files.")
 | 
						|
		(g_argAstJson.c_str(), "AST of all source files in JSON format.")
 | 
						|
		(g_argAsmStr.c_str(), "EVM assembly of the contracts.")
 | 
						|
		(g_argAsmJsonStr.c_str(), "EVM assembly of the contracts in JSON format.")
 | 
						|
		(g_argOpcodesStr.c_str(), "Opcodes of the contracts.")
 | 
						|
		(g_argBinaryStr.c_str(), "Binary of the contracts in hex.")
 | 
						|
		(g_argRuntimeBinaryStr.c_str(), "Binary of the runtime part of the contracts in hex.")
 | 
						|
		(g_argCloneBinaryStr.c_str(), "Binary of the clone contracts in hex.")
 | 
						|
		(g_argAbiStr.c_str(), "ABI specification of the contracts.")
 | 
						|
		(g_argSolInterfaceStr.c_str(), "Solidity interface of the contracts.")
 | 
						|
		(g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.")
 | 
						|
		(g_argNatspecUserStr.c_str(), "Natspec user documentation of all contracts.")
 | 
						|
		(g_argNatspecDevStr.c_str(), "Natspec developer documentation of all contracts.")
 | 
						|
		("formal", "Translated source suitable for formal analysis.");
 | 
						|
	desc.add(outputComponents);
 | 
						|
 | 
						|
	po::options_description allOptions = desc;
 | 
						|
	allOptions.add_options()("input-file", po::value<vector<string>>(), "input file");
 | 
						|
 | 
						|
	// All positional options should be interpreted as input files
 | 
						|
	po::positional_options_description filesPositions;
 | 
						|
	filesPositions.add("input-file", -1);
 | 
						|
 | 
						|
	// parse the compiler arguments
 | 
						|
	try
 | 
						|
	{
 | 
						|
		po::command_line_parser cmdLineParser(_argc, _argv);
 | 
						|
		cmdLineParser.options(allOptions).positional(filesPositions).allow_unregistered();
 | 
						|
		po::store(cmdLineParser.run(), m_args);
 | 
						|
	}
 | 
						|
	catch (po::error const& _exception)
 | 
						|
	{
 | 
						|
		cerr << _exception.what() << endl;
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (m_args.count("help"))
 | 
						|
	{
 | 
						|
		cout << desc;
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (m_args.count("version"))
 | 
						|
	{
 | 
						|
		version();
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (m_args.count("combined-json"))
 | 
						|
	{
 | 
						|
		vector<string> requests;
 | 
						|
		for (string const& item: boost::split(requests, m_args["combined-json"].as<string>(), boost::is_any_of(",")))
 | 
						|
			if (!g_combinedJsonArgs.count(item))
 | 
						|
			{
 | 
						|
				cerr << "Invalid option to --combined-json: " << item << endl;
 | 
						|
				return false;
 | 
						|
			}
 | 
						|
	}
 | 
						|
	po::notify(m_args);
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
bool CommandLineInterface::processInput()
 | 
						|
{
 | 
						|
	if (!m_args.count("input-file"))
 | 
						|
	{
 | 
						|
		string s;
 | 
						|
		while (!cin.eof())
 | 
						|
		{
 | 
						|
			getline(cin, s);
 | 
						|
			m_sourceCodes[g_stdinFileName].append(s + '\n');
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else
 | 
						|
		for (string const& infile: m_args["input-file"].as<vector<string>>())
 | 
						|
		{
 | 
						|
			auto path = boost::filesystem::path(infile);
 | 
						|
			if (!boost::filesystem::exists(path))
 | 
						|
			{
 | 
						|
				cerr << "Skipping non existant input file \"" << infile << "\"" << endl;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			if (!boost::filesystem::is_regular_file(path))
 | 
						|
			{
 | 
						|
				cerr << "\"" << infile << "\" is not a valid file. Skipping" << endl;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			m_sourceCodes[infile] = dev::contentsString(infile);
 | 
						|
		}
 | 
						|
 | 
						|
	if (m_args.count("libraries"))
 | 
						|
		for (string const& library: m_args["libraries"].as<vector<string>>())
 | 
						|
			if (!parseLibraryOption(library))
 | 
						|
				return false;
 | 
						|
 | 
						|
	if (m_args.count("link"))
 | 
						|
	{
 | 
						|
		// switch to linker mode
 | 
						|
		m_onlyLink = true;
 | 
						|
		return link();
 | 
						|
	}
 | 
						|
 | 
						|
	m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0));
 | 
						|
	try
 | 
						|
	{
 | 
						|
		for (auto const& sourceCode: m_sourceCodes)
 | 
						|
			m_compiler->addSource(sourceCode.first, sourceCode.second);
 | 
						|
		// TODO: Perhaps we should not compile unless requested
 | 
						|
		bool optimize = m_args.count("optimize") > 0;
 | 
						|
		unsigned runs = m_args["optimize-runs"].as<unsigned>();
 | 
						|
		bool successful = m_compiler->compile(optimize, runs);
 | 
						|
		if (successful)
 | 
						|
			m_compiler->link(m_libraries);
 | 
						|
 | 
						|
		if (successful && m_args.count("formal"))
 | 
						|
			if (!m_compiler->prepareFormalAnalysis())
 | 
						|
				successful = false;
 | 
						|
 | 
						|
		for (auto const& error: m_compiler->errors())
 | 
						|
			SourceReferenceFormatter::printExceptionInformation(
 | 
						|
				cerr,
 | 
						|
				*error,
 | 
						|
				(error->type() == Error::Type::Warning) ? "Warning" : "Error", *m_compiler
 | 
						|
			);
 | 
						|
 | 
						|
		if (!successful)
 | 
						|
			return false;
 | 
						|
	}
 | 
						|
	catch (CompilerError const& _exception)
 | 
						|
	{
 | 
						|
		SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Compiler error", *m_compiler);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	catch (InternalCompilerError const& _exception)
 | 
						|
	{
 | 
						|
		cerr << "Internal compiler error during compilation:" << endl
 | 
						|
			 << boost::diagnostic_information(_exception);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	catch (Error const& _error)
 | 
						|
	{
 | 
						|
		if (_error.type() == Error::Type::DocstringParsingError)
 | 
						|
			cerr << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_error) << endl;
 | 
						|
		else
 | 
						|
			SourceReferenceFormatter::printExceptionInformation(cerr, _error, _error.typeName(), *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::handleCombinedJSON()
 | 
						|
{
 | 
						|
	if (!m_args.count("combined-json"))
 | 
						|
		return;
 | 
						|
 | 
						|
	Json::Value output(Json::objectValue);
 | 
						|
 | 
						|
	output["version"] = ::dev::solidity::VersionString;
 | 
						|
	set<string> requests;
 | 
						|
	boost::split(requests, m_args["combined-json"].as<string>(), boost::is_any_of(","));
 | 
						|
	vector<string> contracts = m_compiler->contractNames();
 | 
						|
 | 
						|
	if (!contracts.empty())
 | 
						|
		output["contracts"] = Json::Value(Json::objectValue);
 | 
						|
	for (string const& contractName: contracts)
 | 
						|
	{
 | 
						|
		Json::Value contractData(Json::objectValue);
 | 
						|
		if (requests.count("interface"))
 | 
						|
			contractData["interface"] = m_compiler->solidityInterface(contractName);
 | 
						|
		if (requests.count("abi"))
 | 
						|
			contractData["abi"] = m_compiler->interface(contractName);
 | 
						|
		if (requests.count("bin"))
 | 
						|
			contractData["bin"] = m_compiler->object(contractName).toHex();
 | 
						|
		if (requests.count("bin-runtime"))
 | 
						|
			contractData["bin-runtime"] = m_compiler->runtimeObject(contractName).toHex();
 | 
						|
		if (requests.count("clone-bin"))
 | 
						|
			contractData["clone-bin"] = m_compiler->cloneObject(contractName).toHex();
 | 
						|
		if (requests.count("opcodes"))
 | 
						|
			contractData["opcodes"] = eth::disassemble(m_compiler->object(contractName).bytecode);
 | 
						|
		if (requests.count("asm"))
 | 
						|
		{
 | 
						|
			ostringstream unused;
 | 
						|
			contractData["asm"] = m_compiler->streamAssembly(unused, contractName, m_sourceCodes, true);
 | 
						|
		}
 | 
						|
		if (requests.count("devdoc"))
 | 
						|
			contractData["devdoc"] = m_compiler->metadata(contractName, DocumentationType::NatspecDev);
 | 
						|
		if (requests.count("userdoc"))
 | 
						|
			contractData["userdoc"] = m_compiler->metadata(contractName, DocumentationType::NatspecUser);
 | 
						|
		output["contracts"][contractName] = contractData;
 | 
						|
	}
 | 
						|
 | 
						|
	if (requests.count("ast"))
 | 
						|
	{
 | 
						|
		output["sources"] = Json::Value(Json::objectValue);
 | 
						|
		for (auto const& sourceCode: m_sourceCodes)
 | 
						|
		{
 | 
						|
			ASTJsonConverter converter(m_compiler->ast(sourceCode.first));
 | 
						|
			output["sources"][sourceCode.first] = Json::Value(Json::objectValue);
 | 
						|
			output["sources"][sourceCode.first]["AST"] = converter.json();
 | 
						|
		}
 | 
						|
	}
 | 
						|
	cout << Json::FastWriter().write(output) << endl;
 | 
						|
}
 | 
						|
 | 
						|
void CommandLineInterface::handleAst(string const& _argStr)
 | 
						|
{
 | 
						|
	string title;
 | 
						|
 | 
						|
	if (_argStr == g_argAstStr)
 | 
						|
		title = "Syntax trees:";
 | 
						|
	else if (_argStr == g_argAstJson)
 | 
						|
		title = "JSON AST:";
 | 
						|
	else
 | 
						|
		BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal argStr for AST"));
 | 
						|
 | 
						|
	// do we need AST output?
 | 
						|
	if (m_args.count(_argStr))
 | 
						|
	{
 | 
						|
		vector<ASTNode const*> asts;
 | 
						|
		for (auto const& sourceCode: m_sourceCodes)
 | 
						|
			asts.push_back(&m_compiler->ast(sourceCode.first));
 | 
						|
		map<ASTNode const*, eth::GasMeter::GasConsumption> gasCosts;
 | 
						|
		if (m_compiler->runtimeAssemblyItems())
 | 
						|
			gasCosts = GasEstimator::breakToStatementLevel(
 | 
						|
				GasEstimator::structuralEstimation(*m_compiler->runtimeAssemblyItems(), asts),
 | 
						|
				asts
 | 
						|
			);
 | 
						|
 | 
						|
		if (m_args.count("output-dir"))
 | 
						|
		{
 | 
						|
			for (auto const& sourceCode: m_sourceCodes)
 | 
						|
			{
 | 
						|
				stringstream data;
 | 
						|
				string postfix = "";
 | 
						|
				if (_argStr == g_argAstStr)
 | 
						|
				{
 | 
						|
					ASTPrinter printer(m_compiler->ast(sourceCode.first), sourceCode.second);
 | 
						|
					printer.print(data);
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					ASTJsonConverter converter(m_compiler->ast(sourceCode.first));
 | 
						|
					converter.print(data);
 | 
						|
					postfix += "_json";
 | 
						|
				}
 | 
						|
				boost::filesystem::path path(sourceCode.first);
 | 
						|
				createFile(path.filename().string() + postfix + ".ast", data.str());
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			cout << title << endl << endl;
 | 
						|
			for (auto const& sourceCode: m_sourceCodes)
 | 
						|
			{
 | 
						|
				cout << endl << "======= " << sourceCode.first << " =======" << endl;
 | 
						|
				if (_argStr == g_argAstStr)
 | 
						|
				{
 | 
						|
					ASTPrinter printer(
 | 
						|
						m_compiler->ast(sourceCode.first),
 | 
						|
						sourceCode.second,
 | 
						|
						gasCosts
 | 
						|
					);
 | 
						|
					printer.print(cout);
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					ASTJsonConverter converter(m_compiler->ast(sourceCode.first));
 | 
						|
					converter.print(cout);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void CommandLineInterface::actOnInput()
 | 
						|
{
 | 
						|
	if (m_onlyLink)
 | 
						|
		writeLinkedFiles();
 | 
						|
	else
 | 
						|
		outputCompilationResults();
 | 
						|
}
 | 
						|
 | 
						|
bool CommandLineInterface::link()
 | 
						|
{
 | 
						|
	for (auto& src: m_sourceCodes)
 | 
						|
	{
 | 
						|
		auto end = src.second.end();
 | 
						|
		for (auto it = src.second.begin(); it != end;)
 | 
						|
		{
 | 
						|
			while (it != end && *it != '_') ++it;
 | 
						|
			auto insertStart = it;
 | 
						|
			while (it != end && *it == '_') ++it;
 | 
						|
			auto nameStart = it;
 | 
						|
			while (it != end && *it != '_') ++it;
 | 
						|
			auto nameEnd = it;
 | 
						|
			while (it != end && *it == '_') ++it;
 | 
						|
			auto insertEnd = it;
 | 
						|
 | 
						|
			if (insertStart == end)
 | 
						|
				break;
 | 
						|
 | 
						|
			if (insertEnd - insertStart != 40)
 | 
						|
			{
 | 
						|
				cerr << "Error in binary object file " << src.first << " at position " << (insertStart - src.second.begin()) << endl;
 | 
						|
				return false;
 | 
						|
			}
 | 
						|
 | 
						|
			string name(nameStart, nameEnd);
 | 
						|
			if (m_libraries.count(name))
 | 
						|
			{
 | 
						|
				string hexStr(toHex(m_libraries.at(name).asBytes()));
 | 
						|
				copy(hexStr.begin(), hexStr.end(), insertStart);
 | 
						|
			}
 | 
						|
			else
 | 
						|
				cerr << "Reference \"" << name << "\" in file \"" << src.first << "\" still unresolved." << endl;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
void CommandLineInterface::writeLinkedFiles()
 | 
						|
{
 | 
						|
	for (auto const& src: m_sourceCodes)
 | 
						|
		if (src.first == g_stdinFileName)
 | 
						|
			cout << src.second << endl;
 | 
						|
		else
 | 
						|
			writeFile(src.first, src.second);
 | 
						|
}
 | 
						|
 | 
						|
void CommandLineInterface::outputCompilationResults()
 | 
						|
{
 | 
						|
	handleCombinedJSON();
 | 
						|
 | 
						|
	// do we need AST output?
 | 
						|
	handleAst(g_argAstStr);
 | 
						|
	handleAst(g_argAstJson);
 | 
						|
 | 
						|
	vector<string> contracts = m_compiler->contractNames();
 | 
						|
	for (string const& contract: contracts)
 | 
						|
	{
 | 
						|
		if (needsHumanTargetedStdout(m_args))
 | 
						|
			cout << endl << "======= " << contract << " =======" << endl;
 | 
						|
 | 
						|
		// do we need EVM assembly?
 | 
						|
		if (m_args.count(g_argAsmStr) || m_args.count(g_argAsmJsonStr))
 | 
						|
		{
 | 
						|
			if (m_args.count("output-dir"))
 | 
						|
			{
 | 
						|
				stringstream data;
 | 
						|
				m_compiler->streamAssembly(data, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr));
 | 
						|
				createFile(contract + (m_args.count(g_argAsmJsonStr) ? "_evm.json" : ".evm"), data.str());
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				cout << "EVM assembly:" << endl;
 | 
						|
				m_compiler->streamAssembly(cout, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr));
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (m_args.count(g_argGas))
 | 
						|
			handleGasEstimation(contract);
 | 
						|
 | 
						|
		handleBytecode(contract);
 | 
						|
		handleSignatureHashes(contract);
 | 
						|
		handleMeta(DocumentationType::ABIInterface, contract);
 | 
						|
		handleMeta(DocumentationType::ABISolidityInterface, contract);
 | 
						|
		handleMeta(DocumentationType::NatspecDev, contract);
 | 
						|
		handleMeta(DocumentationType::NatspecUser, contract);
 | 
						|
	} // end of contracts iteration
 | 
						|
 | 
						|
	handleFormal();
 | 
						|
}
 | 
						|
 | 
						|
}
 | 
						|
}
 |