aboutsummaryrefslogtreecommitdiffstats
path: root/solc/CommandLineInterface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'solc/CommandLineInterface.cpp')
-rw-r--r--solc/CommandLineInterface.cpp835
1 files changed, 533 insertions, 302 deletions
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index 6e59099a..0222ccb0 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -22,28 +22,9 @@
*/
#include "CommandLineInterface.h"
-#ifdef _WIN32 // windows
- #include <io.h>
- #define isatty _isatty
- #define fileno _fileno
-#else // unix
- #include <unistd.h>
-#endif
-#include <string>
-#include <iostream>
-#include <fstream>
-
-#include <boost/filesystem.hpp>
-#include <boost/filesystem/operations.hpp>
-#include <boost/algorithm/string.hpp>
-
#include "solidity/BuildInfo.h"
-#include <libdevcore/Common.h>
-#include <libdevcore/CommonData.h>
-#include <libdevcore/CommonIO.h>
-#include <libdevcore/JSON.h>
-#include <libevmasm/Instruction.h>
-#include <libevmasm/GasMeter.h>
+#include "license.h"
+
#include <libsolidity/interface/Version.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/parsing/Parser.h>
@@ -52,10 +33,33 @@
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/interface/CompilerStack.h>
+#include <libsolidity/interface/StandardCompiler.h>
#include <libsolidity/interface/SourceReferenceFormatter.h>
#include <libsolidity/interface/GasEstimator.h>
-#include <libsolidity/inlineasm/AsmParser.h>
-#include <libsolidity/formal/Why3Translator.h>
+#include <libsolidity/interface/AssemblyStack.h>
+
+#include <libevmasm/Instruction.h>
+#include <libevmasm/GasMeter.h>
+
+#include <libdevcore/Common.h>
+#include <libdevcore/CommonData.h>
+#include <libdevcore/CommonIO.h>
+#include <libdevcore/JSON.h>
+
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/algorithm/string.hpp>
+
+#ifdef _WIN32 // windows
+ #include <io.h>
+ #define isatty _isatty
+ #define fileno _fileno
+#else // unix
+ #include <unistd.h>
+#endif
+#include <string>
+#include <iostream>
+#include <fstream>
using namespace std;
namespace po = boost::program_options;
@@ -65,38 +69,112 @@ namespace dev
namespace solidity
{
-static string const g_argAbiStr = "abi";
-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_argMetadata = "metadata";
-static string const g_argAddStandard = "add-std";
-static string const g_stdinFileName = "<stdin>";
+static string const g_stdinFileNameStr = "<stdin>";
+static string const g_strAbi = "abi";
+static string const g_strAddStandard = "add-std";
+static string const g_strAllowPaths = "allow-paths";
+static string const g_strAsm = "asm";
+static string const g_strAsmJson = "asm-json";
+static string const g_strAssemble = "assemble";
+static string const g_strAst = "ast";
+static string const g_strAstJson = "ast-json";
+static string const g_strAstCompactJson = "ast-compact-json";
+static string const g_strBinary = "bin";
+static string const g_strBinaryRuntime = "bin-runtime";
+static string const g_strCloneBinary = "clone-bin";
+static string const g_strCombinedJson = "combined-json";
+static string const g_strCompactJSON = "compact-format";
+static string const g_strContracts = "contracts";
+static string const g_strEVM = "evm";
+static string const g_strEVM15 = "evm15";
+static string const g_streWasm = "ewasm";
+static string const g_strFormal = "formal";
+static string const g_strGas = "gas";
+static string const g_strHelp = "help";
+static string const g_strInputFile = "input-file";
+static string const g_strInterface = "interface";
+static string const g_strJulia = "julia";
+static string const g_strLicense = "license";
+static string const g_strLibraries = "libraries";
+static string const g_strLink = "link";
+static string const g_strMetadata = "metadata";
+static string const g_strMetadataLiteral = "metadata-literal";
+static string const g_strNatspecDev = "devdoc";
+static string const g_strNatspecUser = "userdoc";
+static string const g_strOpcodes = "opcodes";
+static string const g_strOptimize = "optimize";
+static string const g_strOptimizeRuns = "optimize-runs";
+static string const g_strOutputDir = "output-dir";
+static string const g_strOverwrite = "overwrite";
+static string const g_strSignatureHashes = "hashes";
+static string const g_strSources = "sources";
+static string const g_strSourceList = "sourceList";
+static string const g_strSrcMap = "srcmap";
+static string const g_strSrcMapRuntime = "srcmap-runtime";
+static string const g_strStandardJSON = "standard-json";
+static string const g_strVersion = "version";
+
+static string const g_argAbi = g_strAbi;
+static string const g_argAddStandard = g_strAddStandard;
+static string const g_argAllowPaths = g_strAllowPaths;
+static string const g_argAsm = g_strAsm;
+static string const g_argAsmJson = g_strAsmJson;
+static string const g_argAssemble = g_strAssemble;
+static string const g_argAst = g_strAst;
+static string const g_argAstCompactJson = g_strAstCompactJson;
+static string const g_argAstJson = g_strAstJson;
+static string const g_argBinary = g_strBinary;
+static string const g_argBinaryRuntime = g_strBinaryRuntime;
+static string const g_argCloneBinary = g_strCloneBinary;
+static string const g_argCombinedJson = g_strCombinedJson;
+static string const g_argCompactJSON = g_strCompactJSON;
+static string const g_argFormal = g_strFormal;
+static string const g_argGas = g_strGas;
+static string const g_argHelp = g_strHelp;
+static string const g_argInputFile = g_strInputFile;
+static string const g_argJulia = "julia";
+static string const g_argLibraries = g_strLibraries;
+static string const g_argLink = g_strLink;
+static string const g_argMachine = "machine";
+static string const g_argMetadata = g_strMetadata;
+static string const g_argMetadataLiteral = g_strMetadataLiteral;
+static string const g_argNatspecDev = g_strNatspecDev;
+static string const g_argNatspecUser = g_strNatspecUser;
+static string const g_argOpcodes = g_strOpcodes;
+static string const g_argOptimize = g_strOptimize;
+static string const g_argOptimizeRuns = g_strOptimizeRuns;
+static string const g_argOutputDir = g_strOutputDir;
+static string const g_argSignatureHashes = g_strSignatureHashes;
+static string const g_argStandardJSON = g_strStandardJSON;
+static string const g_argVersion = g_strVersion;
+static string const g_stdinFileName = g_stdinFileNameStr;
/// Possible arguments to for --combined-json
-static set<string> const g_combinedJsonArgs{
- "bin",
- "bin-runtime",
- "clone-bin",
- "srcmap",
- "srcmap-runtime",
- "opcodes",
- "abi",
- "interface",
- "metadata",
- "asm",
- "ast",
- "userdoc",
- "devdoc"
+static set<string> const g_combinedJsonArgs
+{
+ g_strAbi,
+ g_strAsm,
+ g_strAst,
+ g_strBinary,
+ g_strBinaryRuntime,
+ g_strCloneBinary,
+ g_strCompactJSON,
+ g_strInterface,
+ g_strMetadata,
+ g_strNatspecUser,
+ g_strNatspecDev,
+ g_strOpcodes,
+ g_strSignatureHashes,
+ g_strSrcMap,
+ g_strSrcMapRuntime
+};
+
+/// Possible arguments to for --machine
+static set<string> const g_machineArgs
+{
+ g_strEVM,
+ g_strEVM15,
+ g_streWasm
};
static void version()
@@ -110,26 +188,34 @@ static void version()
exit(0);
}
+static void license()
+{
+ cout << otherLicenses << endl;
+ // This is a static variable generated by cmake from LICENSE.txt
+ cout << licenseText << endl;
+ exit(0);
+}
+
static bool needsHumanTargetedStdout(po::variables_map const& _args)
{
if (_args.count(g_argGas))
return true;
- if (_args.count("output-dir"))
+ if (_args.count(g_argOutputDir))
return false;
for (string const& arg: {
- g_argAbiStr,
- g_argSignatureHashes,
- g_argMetadata,
- g_argNatspecUserStr,
+ g_argAbi,
+ g_argAsm,
+ g_argAsmJson,
g_argAstJson,
- g_argNatspecDevStr,
- g_argAsmStr,
- g_argAsmJsonStr,
- g_argOpcodesStr,
- g_argBinaryStr,
- g_argRuntimeBinaryStr,
- g_argCloneBinaryStr,
- string("formal")
+ g_argBinary,
+ g_argBinaryRuntime,
+ g_argCloneBinary,
+ g_argFormal,
+ g_argMetadata,
+ g_argNatspecUser,
+ g_argNatspecDev,
+ g_argOpcodes,
+ g_argSignatureHashes
})
if (_args.count(arg))
return true;
@@ -138,30 +224,30 @@ static bool needsHumanTargetedStdout(po::variables_map const& _args)
void CommandLineInterface::handleBinary(string const& _contract)
{
- if (m_args.count(g_argBinaryStr))
+ if (m_args.count(g_argBinary))
{
- if (m_args.count("output-dir"))
- createFile(_contract + ".bin", m_compiler->object(_contract).toHex());
+ if (m_args.count(g_argOutputDir))
+ createFile(m_compiler->filesystemFriendlyName(_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(g_argCloneBinary))
{
- if (m_args.count("output-dir"))
- createFile(_contract + ".clone_bin", m_compiler->cloneObject(_contract).toHex());
+ if (m_args.count(g_argOutputDir))
+ createFile(m_compiler->filesystemFriendlyName(_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(g_argBinaryRuntime))
{
- if (m_args.count("output-dir"))
- createFile(_contract + ".bin-runtime", m_compiler->runtimeObject(_contract).toHex());
+ if (m_args.count(g_argOutputDir))
+ createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", m_compiler->runtimeObject(_contract).toHex());
else
{
cout << "Binary of the runtime part: " << endl;
@@ -172,8 +258,8 @@ void CommandLineInterface::handleBinary(string const& _contract)
void CommandLineInterface::handleOpcode(string const& _contract)
{
- if (m_args.count("output-dir"))
- createFile(_contract + ".opcode", solidity::disassemble(m_compiler->object(_contract).bytecode));
+ if (m_args.count(g_argOutputDir))
+ createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", solidity::disassemble(m_compiler->object(_contract).bytecode));
else
{
cout << "Opcodes: " << endl;
@@ -184,9 +270,9 @@ void CommandLineInterface::handleOpcode(string const& _contract)
void CommandLineInterface::handleBytecode(string const& _contract)
{
- if (m_args.count(g_argOpcodesStr))
+ if (m_args.count(g_argOpcodes))
handleOpcode(_contract);
- if (m_args.count(g_argBinaryStr) || m_args.count(g_argCloneBinaryStr) || m_args.count(g_argRuntimeBinaryStr))
+ if (m_args.count(g_argBinary) || m_args.count(g_argCloneBinary) || m_args.count(g_argBinaryRuntime))
handleBinary(_contract);
}
@@ -195,12 +281,13 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract)
if (!m_args.count(g_argSignatureHashes))
return;
+ Json::Value methodIdentifiers = m_compiler->methodIdentifiers(_contract);
string out;
- for (auto const& it: m_compiler->contractDefinition(_contract).interfaceFunctions())
- out += toHex(it.first.ref()) + ": " + it.second->externalSignature() + "\n";
+ for (auto const& name: methodIdentifiers.getMemberNames())
+ out += methodIdentifiers[name].asString() + ": " + name + "\n";
- if (m_args.count("output-dir"))
- createFile(_contract + ".signatures", out);
+ if (m_args.count(g_argOutputDir))
+ createFile(m_compiler->filesystemFriendlyName(_contract) + ".signatures", out);
else
cout << "Function signatures: " << endl << out;
}
@@ -211,31 +298,38 @@ void CommandLineInterface::handleOnChainMetadata(string const& _contract)
return;
string data = m_compiler->onChainMetadata(_contract);
- if (m_args.count("output-dir"))
- createFile(_contract + "_meta.json", data);
+ if (m_args.count(g_argOutputDir))
+ createFile(m_compiler->filesystemFriendlyName(_contract) + "_meta.json", data);
else
cout << "Metadata: " << endl << data << endl;
}
-void CommandLineInterface::handleMeta(DocumentationType _type, string const& _contract)
+void CommandLineInterface::handleABI(string const& _contract)
+{
+ if (!m_args.count(g_argAbi))
+ return;
+
+ string data = dev::jsonCompactPrint(m_compiler->contractABI(_contract));
+ if (m_args.count(g_argOutputDir))
+ createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data);
+ else
+ cout << "Contract JSON ABI " << endl << data << endl;
+}
+
+void CommandLineInterface::handleNatspec(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::NatspecUser:
- argName = g_argNatspecUserStr;
+ argName = g_argNatspecUser;
suffix = ".docuser";
title = "User Documentation";
break;
case DocumentationType::NatspecDev:
- argName = g_argNatspecDevStr;
+ argName = g_argNatspecDev;
suffix = ".docdev";
title = "Developer Documentation";
break;
@@ -246,14 +340,10 @@ void CommandLineInterface::handleMeta(DocumentationType _type, string const& _co
if (m_args.count(argName))
{
- std::string output;
- if (_type == DocumentationType::ABIInterface)
- output = dev::jsonCompactPrint(m_compiler->metadata(_contract, _type));
- else
- output = dev::jsonPrettyPrint(m_compiler->metadata(_contract, _type));
+ std::string output = dev::jsonPrettyPrint(m_compiler->natspec(_contract, _type));
- if (m_args.count("output-dir"))
- createFile(_contract + suffix, output);
+ if (m_args.count(g_argOutputDir))
+ createFile(m_compiler->filesystemFriendlyName(_contract) + suffix, output);
else
{
cout << title << endl;
@@ -265,72 +355,51 @@ void CommandLineInterface::handleMeta(DocumentationType _type, string const& _co
void CommandLineInterface::handleGasEstimation(string const& _contract)
{
- using Gas = GasEstimator::GasConsumption;
- if (!m_compiler->assemblyItems(_contract) && !m_compiler->runtimeAssemblyItems(_contract))
- return;
+ Json::Value estimates = m_compiler->gasEstimates(_contract);
cout << "Gas estimation:" << endl;
- if (eth::AssemblyItems const* items = m_compiler->assemblyItems(_contract))
+
+ if (estimates["creation"].isObject())
{
- Gas gas = GasEstimator::functionalEstimation(*items);
- u256 bytecodeSize(m_compiler->runtimeObject(_contract).bytecode.size());
+ Json::Value creation = estimates["creation"];
cout << "construction:" << endl;
- cout << " " << gas << " + " << (bytecodeSize * eth::GasCosts::createDataGas) << " = ";
- gas += bytecodeSize * eth::GasCosts::createDataGas;
- cout << gas << endl;
+ cout << " " << creation["executionCost"].asString();
+ cout << " + " << creation["codeDepositCost"].asString();
+ cout << " = " << creation["totalCost"].asString() << endl;
}
- if (eth::AssemblyItems const* items = m_compiler->runtimeAssemblyItems(_contract))
+
+ if (estimates["external"].isObject())
{
- ContractDefinition const& contract = m_compiler->contractDefinition(_contract);
+ Json::Value externalFunctions = estimates["external"];
cout << "external:" << endl;
- for (auto it: contract.interfaceFunctions())
+ for (auto const& name: externalFunctions.getMemberNames())
{
- 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;
+ if (name.empty())
+ cout << " fallback:\t";
+ else
+ cout << " " << name << ":\t";
+ cout << externalFunctions[name].asString() << endl;
}
+ }
+
+ if (estimates["internal"].isObject())
+ {
+ Json::Value internalFunctions = estimates["internal"];
cout << "internal:" << endl;
- for (auto const& it: contract.definedFunctions())
+ for (auto const& name: internalFunctions.getMemberNames())
{
- 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 paramTypes = type.parameterTypes();
- for (auto it = paramTypes.begin(); it != paramTypes.end(); ++it)
- cout << (*it)->toString() << (it + 1 == paramTypes.end() ? "" : ",");
- cout << "):\t" << gas << endl;
+ cout << " " << name << ":\t";
+ cout << internalFunctions[name].asString() << 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;
-}
-
void CommandLineInterface::readInputFilesAndConfigureRemappings()
{
- vector<string> inputFiles;
bool addStdin = false;
- if (!m_args.count("input-file"))
+ if (!m_args.count(g_argInputFile))
addStdin = true;
else
- for (string path: m_args["input-file"].as<vector<string>>())
+ for (string path: m_args[g_argInputFile].as<vector<string>>())
{
auto eq = find(path.begin(), path.end(), '=');
if (eq != path.end())
@@ -371,14 +440,25 @@ void CommandLineInterface::readInputFilesAndConfigureRemappings()
bool CommandLineInterface::parseLibraryOption(string const& _input)
{
namespace fs = boost::filesystem;
- string data = fs::is_regular_file(_input) ? contentsString(_input) : _input;
+ string data = _input;
+ try
+ {
+ if (fs::is_regular_file(_input))
+ data = contentsString(_input);
+ }
+ catch (fs::filesystem_error const&)
+ {
+ // Thrown e.g. if path is too long.
+ }
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(':');
+ //search for last colon in string as our binaries output placeholders in the form of file:Name
+ //so we need to search for the second `:` in the string
+ auto colon = lib.rfind(':');
if (colon == string::npos)
{
cerr << "Colon separator missing in library address specifier \"" << lib << "\"" << endl;
@@ -388,6 +468,11 @@ bool CommandLineInterface::parseLibraryOption(string const& _input)
string addrString(lib.begin() + colon + 1, lib.end());
boost::trim(libName);
boost::trim(addrString);
+ if (!passesAddressChecksum(addrString, false))
+ {
+ cerr << "Invalid checksum on library address \"" << libName << "\": " << addrString << endl;
+ return false;
+ }
bytes binAddr = fromHex(addrString);
h160 address(binAddr, h160::AlignRight);
if (binAddr.size() > 20 || address == h160())
@@ -405,9 +490,17 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da
{
namespace fs = boost::filesystem;
// create directory if not existent
- fs::path p(m_args.at("output-dir").as<string>());
- fs::create_directories(p);
+ fs::path p(m_args.at(g_argOutputDir).as<string>());
+ // Do not try creating the directory if the first item is . or ..
+ if (p.filename() != "." && p.filename() != "..")
+ fs::create_directories(p);
string pathName = (p / _fileName).string();
+ if (fs::exists(pathName) && !m_args.count(g_strOverwrite))
+ {
+ cerr << "Refusing to overwrite existing file \"" << pathName << "\" (use --overwrite to force)." << endl;
+ m_error = true;
+ return;
+ }
ofstream outFile(pathName);
outFile << _data;
if (!outFile)
@@ -417,8 +510,12 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da
bool CommandLineInterface::parseArguments(int _argc, char** _argv)
{
// Declare the supported options.
- po::options_description desc(
- R"(solc, the Solidity commandline compiler.
+ po::options_description desc(R"(solc, the Solidity commandline compiler.
+
+This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you
+are welcome to redistribute it under certain conditions. See 'solc --license'
+for details.
+
Usage: solc [options] [input_file...]
Compiles the given Solidity input files (or the standard input if none given or
"-" is used as a file name) and outputs the components specified in the options
@@ -430,68 +527,92 @@ Example:
Allowed options)",
po::options_description::m_default_line_length,
- po::options_description::m_default_line_length - 23);
+ 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.")
+ (g_argHelp.c_str(), "Show help message and exit.")
+ (g_argVersion.c_str(), "Show version and exit.")
+ (g_strLicense.c_str(), "Show licensing information and exit.")
+ (g_argOptimize.c_str(), "Enable bytecode optimizer.")
(
- "optimize-runs",
+ g_argOptimizeRuns.c_str(),
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",
+ g_argLibraries.c_str(),
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",
+ (g_argOutputDir + ",o").c_str(),
po::value<string>()->value_name("path"),
"If given, creates one file per component and contract/file at the specified directory."
)
+ (g_strOverwrite.c_str(), "Overwrite existing files (used together with -o).")
(
- "combined-json",
+ g_argCombinedJson.c_str(),
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.")
(
- "assemble",
- "Switch to assembly mode, ignoring all options and assumes input is assembly."
+ g_argStandardJSON.c_str(),
+ "Switch to Standard JSON input / output mode, ignoring all options. "
+ "It reads from standard input and provides the result on the standard output."
)
(
- "link",
+ g_argAssemble.c_str(),
+ "Switch to assembly mode, ignoring all options except --machine and assumes input is assembly."
+ )
+ (
+ g_argJulia.c_str(),
+ "Switch to JULIA mode, ignoring all options except --machine and assumes input is JULIA."
+ )
+ (
+ g_argMachine.c_str(),
+ po::value<string>()->value_name(boost::join(g_machineArgs, ",")),
+ "Target machine in assembly or JULIA mode."
+ )
+ (
+ g_argLink.c_str(),
"Switch to linker mode, ignoring all options apart from --libraries "
"and modify binaries in place."
+ )
+ (g_argMetadataLiteral.c_str(), "Store referenced sources are literal data in the metadata output.")
+ (
+ g_argAllowPaths.c_str(),
+ po::value<string>()->value_name("path(s)"),
+ "Allow a given path for imports. A list of paths can be supplied by separating them with a comma."
);
po::options_description outputComponents("Output Components");
outputComponents.add_options()
- (g_argAstStr.c_str(), "AST of all source files.")
+ (g_argAst.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_argAstCompactJson.c_str(), "AST of all source files in a compact JSON format.")
+ (g_argAsm.c_str(), "EVM assembly of the contracts.")
+ (g_argAsmJson.c_str(), "EVM assembly of the contracts in JSON format.")
+ (g_argOpcodes.c_str(), "Opcodes of the contracts.")
+ (g_argBinary.c_str(), "Binary of the contracts in hex.")
+ (g_argBinaryRuntime.c_str(), "Binary of the runtime part of the contracts in hex.")
+ (g_argCloneBinary.c_str(), "Binary of the clone contracts in hex.")
+ (g_argAbi.c_str(), "ABI specification 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.")
+ (g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.")
+ (g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.")
(g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.")
- ("formal", "Translated source suitable for formal analysis.");
+ (g_argFormal.c_str(), "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");
+ allOptions.add_options()(g_argInputFile.c_str(), 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);
+ filesPositions.add(g_argInputFile.c_str(), -1);
// parse the compiler arguments
try
@@ -506,22 +627,28 @@ Allowed options)",
return false;
}
- if (m_args.count("help") || (isatty(fileno(stdin)) && _argc == 1))
+ if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1))
{
cout << desc;
return false;
}
- if (m_args.count("version"))
+ if (m_args.count(g_argVersion))
{
version();
return false;
}
- if (m_args.count("combined-json"))
+ if (m_args.count(g_strLicense))
+ {
+ license();
+ return false;
+ }
+
+ if (m_args.count(g_argCombinedJson))
{
vector<string> requests;
- for (string const& item: boost::split(requests, m_args["combined-json"].as<string>(), boost::is_any_of(",")))
+ for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as<string>(), boost::is_any_of(",")))
if (!g_combinedJsonArgs.count(item))
{
cerr << "Invalid option to --combined-json: " << item << endl;
@@ -535,74 +662,123 @@ Allowed options)",
bool CommandLineInterface::processInput()
{
+ ReadFile::Callback fileReader = [this](string const& _path)
+ {
+ try
+ {
+ auto path = boost::filesystem::path(_path);
+ auto canonicalPath = boost::filesystem::canonical(path);
+ bool isAllowed = false;
+ for (auto const& allowedDir: m_allowedDirectories)
+ {
+ // If dir is a prefix of boostPath, we are fine.
+ if (
+ std::distance(allowedDir.begin(), allowedDir.end()) <= std::distance(canonicalPath.begin(), canonicalPath.end()) &&
+ std::equal(allowedDir.begin(), allowedDir.end(), canonicalPath.begin())
+ )
+ {
+ isAllowed = true;
+ break;
+ }
+ }
+ if (!isAllowed)
+ return ReadFile::Result{false, "File outside of allowed directories."};
+ else if (!boost::filesystem::exists(path))
+ return ReadFile::Result{false, "File not found."};
+ else if (!boost::filesystem::is_regular_file(canonicalPath))
+ return ReadFile::Result{false, "Not a valid file."};
+ else
+ {
+ auto contents = dev::contentsString(canonicalPath.string());
+ m_sourceCodes[path.string()] = contents;
+ return ReadFile::Result{true, contents};
+ }
+ }
+ catch (Exception const& _exception)
+ {
+ return ReadFile::Result{false, "Exception in read callback: " + boost::diagnostic_information(_exception)};
+ }
+ catch (...)
+ {
+ return ReadFile::Result{false, "Unknown exception in read callback."};
+ }
+ };
+
+ if (m_args.count(g_argAllowPaths))
+ {
+ vector<string> paths;
+ for (string const& path: boost::split(paths, m_args[g_argAllowPaths].as<string>(), boost::is_any_of(",")))
+ m_allowedDirectories.push_back(boost::filesystem::path(path));
+ }
+
+ if (m_args.count(g_argStandardJSON))
+ {
+ string input;
+ while (!cin.eof())
+ {
+ string tmp;
+ getline(cin, tmp);
+ input.append(tmp + "\n");
+ }
+ StandardCompiler compiler(fileReader);
+ cout << compiler.compile(input) << endl;
+ return true;
+ }
+
readInputFilesAndConfigureRemappings();
- if (m_args.count("libraries"))
- for (string const& library: m_args["libraries"].as<vector<string>>())
+ if (m_args.count(g_argLibraries))
+ for (string const& library: m_args[g_argLibraries].as<vector<string>>())
if (!parseLibraryOption(library))
return false;
- if (m_args.count("assemble"))
+ if (m_args.count(g_argAssemble) || m_args.count(g_argJulia))
{
// switch to assembly mode
m_onlyAssemble = true;
- return assemble();
+ using Input = AssemblyStack::Language;
+ using Machine = AssemblyStack::Machine;
+ Input inputLanguage = m_args.count(g_argJulia) ? Input::JULIA : Input::Assembly;
+ Machine targetMachine = Machine::EVM;
+ if (m_args.count(g_argMachine))
+ {
+ string machine = m_args[g_argMachine].as<string>();
+ if (machine == g_strEVM)
+ targetMachine = Machine::EVM;
+ else if (machine == g_strEVM15)
+ targetMachine = Machine::EVM15;
+ else if (machine == g_streWasm)
+ targetMachine = Machine::eWasm;
+ else
+ {
+ cerr << "Invalid option for --machine: " << machine << endl;
+ return false;
+ }
+ }
+ return assemble(inputLanguage, targetMachine);
}
- if (m_args.count("link"))
+ if (m_args.count(g_argLink))
{
// switch to linker mode
m_onlyLink = true;
return link();
}
- CompilerStack::ReadFileCallback fileReader = [this](string const& _path)
- {
- auto path = boost::filesystem::path(_path);
- if (!boost::filesystem::exists(path))
- return CompilerStack::ReadFileResult{false, "File not found."};
- auto canonicalPath = boost::filesystem::canonical(path);
- bool isAllowed = false;
- for (auto const& allowedDir: m_allowedDirectories)
- {
- // If dir is a prefix of boostPath, we are fine.
- if (
- std::distance(allowedDir.begin(), allowedDir.end()) <= std::distance(canonicalPath.begin(), canonicalPath.end()) &&
- std::equal(allowedDir.begin(), allowedDir.end(), canonicalPath.begin())
- )
- {
- isAllowed = true;
- break;
- }
- }
- if (!isAllowed)
- return CompilerStack::ReadFileResult{false, "File outside of allowed directories."};
- else if (!boost::filesystem::is_regular_file(canonicalPath))
- return CompilerStack::ReadFileResult{false, "Not a valid file."};
- else
- {
- auto contents = dev::contentsString(canonicalPath.string());
- m_sourceCodes[path.string()] = contents;
- return CompilerStack::ReadFileResult{true, contents};
- }
- };
-
m_compiler.reset(new CompilerStack(fileReader));
auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); };
try
{
- if (m_args.count("input-file"))
- m_compiler->setRemappings(m_args["input-file"].as<vector<string>>());
+ if (m_args.count(g_argMetadataLiteral) > 0)
+ m_compiler->useMetadataLiteralSources(true);
+ if (m_args.count(g_argInputFile))
+ m_compiler->setRemappings(m_args[g_argInputFile].as<vector<string>>());
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 optimize = m_args.count(g_argOptimize) > 0;
+ unsigned runs = m_args[g_argOptimizeRuns].as<unsigned>();
bool successful = m_compiler->compile(optimize, runs, m_libraries);
- if (successful && m_args.count("formal"))
- if (!m_compiler->prepareFormalAnalysis())
- successful = false;
-
for (auto const& error: m_compiler->errors())
SourceReferenceFormatter::printExceptionInformation(
cerr,
@@ -656,73 +832,76 @@ bool CommandLineInterface::processInput()
void CommandLineInterface::handleCombinedJSON()
{
- if (!m_args.count("combined-json"))
+ if (!m_args.count(g_argCombinedJson))
return;
Json::Value output(Json::objectValue);
- output["version"] = ::dev::solidity::VersionString;
+ output[g_strVersion] = ::dev::solidity::VersionString;
set<string> requests;
- boost::split(requests, m_args["combined-json"].as<string>(), boost::is_any_of(","));
+ boost::split(requests, m_args[g_argCombinedJson].as<string>(), boost::is_any_of(","));
vector<string> contracts = m_compiler->contractNames();
if (!contracts.empty())
- output["contracts"] = Json::Value(Json::objectValue);
+ output[g_strContracts] = Json::Value(Json::objectValue);
for (string const& contractName: contracts)
{
Json::Value contractData(Json::objectValue);
- if (requests.count("abi"))
- contractData["abi"] = dev::jsonCompactPrint(m_compiler->interface(contractName));
+ if (requests.count(g_strAbi))
+ contractData[g_strAbi] = dev::jsonCompactPrint(m_compiler->contractABI(contractName));
if (requests.count("metadata"))
contractData["metadata"] = m_compiler->onChainMetadata(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"] = solidity::disassemble(m_compiler->object(contractName).bytecode);
- if (requests.count("asm"))
+ if (requests.count(g_strBinary))
+ contractData[g_strBinary] = m_compiler->object(contractName).toHex();
+ if (requests.count(g_strBinaryRuntime))
+ contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex();
+ if (requests.count(g_strCloneBinary))
+ contractData[g_strCloneBinary] = m_compiler->cloneObject(contractName).toHex();
+ if (requests.count(g_strOpcodes))
+ contractData[g_strOpcodes] = solidity::disassemble(m_compiler->object(contractName).bytecode);
+ if (requests.count(g_strAsm))
{
ostringstream unused;
- contractData["asm"] = m_compiler->streamAssembly(unused, contractName, m_sourceCodes, true);
+ contractData[g_strAsm] = m_compiler->streamAssembly(unused, contractName, m_sourceCodes, true);
}
- if (requests.count("srcmap"))
+ if (requests.count(g_strSrcMap))
{
auto map = m_compiler->sourceMapping(contractName);
- contractData["srcmap"] = map ? *map : "";
+ contractData[g_strSrcMap] = map ? *map : "";
}
- if (requests.count("srcmap-runtime"))
+ if (requests.count(g_strSrcMapRuntime))
{
auto map = m_compiler->runtimeSourceMapping(contractName);
- contractData["srcmap-runtime"] = map ? *map : "";
+ contractData[g_strSrcMapRuntime] = map ? *map : "";
}
- if (requests.count("devdoc"))
- contractData["devdoc"] = dev::jsonCompactPrint(m_compiler->metadata(contractName, DocumentationType::NatspecDev));
- if (requests.count("userdoc"))
- contractData["userdoc"] = dev::jsonCompactPrint(m_compiler->metadata(contractName, DocumentationType::NatspecUser));
- output["contracts"][contractName] = contractData;
+ if (requests.count(g_strSignatureHashes))
+ contractData[g_strSignatureHashes] = m_compiler->methodIdentifiers(contractName);
+ if (requests.count(g_strNatspecDev))
+ contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->natspec(contractName, DocumentationType::NatspecDev));
+ if (requests.count(g_strNatspecUser))
+ contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->natspec(contractName, DocumentationType::NatspecUser));
+ output[g_strContracts][contractName] = contractData;
}
- bool needsSourceList = requests.count("ast") || requests.count("srcmap") || requests.count("srcmap-runtime");
+ bool needsSourceList = requests.count(g_strAst) || requests.count(g_strSrcMap) || requests.count(g_strSrcMapRuntime);
if (needsSourceList)
{
// Indices into this array are used to abbreviate source names in source locations.
- output["sourceList"] = Json::Value(Json::arrayValue);
+ output[g_strSourceList] = Json::Value(Json::arrayValue);
for (auto const& source: m_compiler->sourceNames())
- output["sourceList"].append(source);
+ output[g_strSourceList].append(source);
}
- if (requests.count("ast"))
+ if (requests.count(g_strAst))
{
- output["sources"] = Json::Value(Json::objectValue);
+ bool legacyFormat = !requests.count(g_strCompactJSON);
+ output[g_strSources] = Json::Value(Json::objectValue);
for (auto const& sourceCode: m_sourceCodes)
{
- ASTJsonConverter converter(m_compiler->ast(sourceCode.first), m_compiler->sourceIndices());
- output["sources"][sourceCode.first] = Json::Value(Json::objectValue);
- output["sources"][sourceCode.first]["AST"] = converter.json();
+ ASTJsonConverter converter(legacyFormat, m_compiler->sourceIndices());
+ output[g_strSources][sourceCode.first] = Json::Value(Json::objectValue);
+ output[g_strSources][sourceCode.first]["AST"] = converter.toJson(m_compiler->ast(sourceCode.first));
}
}
cout << dev::jsonCompactPrint(output) << endl;
@@ -732,10 +911,12 @@ void CommandLineInterface::handleAst(string const& _argStr)
{
string title;
- if (_argStr == g_argAstStr)
+ if (_argStr == g_argAst)
title = "Syntax trees:";
else if (_argStr == g_argAstJson)
title = "JSON AST:";
+ else if (_argStr == g_argAstCompactJson)
+ title = "JSON AST (compact format):";
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal argStr for AST"));
@@ -752,21 +933,21 @@ void CommandLineInterface::handleAst(string const& _argStr)
asts
);
- if (m_args.count("output-dir"))
+ bool legacyFormat = !m_args.count(g_argAstCompactJson);
+ if (m_args.count(g_argOutputDir))
{
for (auto const& sourceCode: m_sourceCodes)
{
stringstream data;
string postfix = "";
- if (_argStr == g_argAstStr)
+ if (_argStr == g_argAst)
{
ASTPrinter printer(m_compiler->ast(sourceCode.first), sourceCode.second);
printer.print(data);
}
else
{
- ASTJsonConverter converter(m_compiler->ast(sourceCode.first));
- converter.print(data);
+ ASTJsonConverter(legacyFormat, m_compiler->sourceIndices()).print(data, m_compiler->ast(sourceCode.first));
postfix += "_json";
}
boost::filesystem::path path(sourceCode.first);
@@ -779,7 +960,7 @@ void CommandLineInterface::handleAst(string const& _argStr)
for (auto const& sourceCode: m_sourceCodes)
{
cout << endl << "======= " << sourceCode.first << " =======" << endl;
- if (_argStr == g_argAstStr)
+ if (_argStr == g_argAst)
{
ASTPrinter printer(
m_compiler->ast(sourceCode.first),
@@ -789,23 +970,22 @@ void CommandLineInterface::handleAst(string const& _argStr)
printer.print(cout);
}
else
- {
- ASTJsonConverter converter(m_compiler->ast(sourceCode.first));
- converter.print(cout);
- }
+ ASTJsonConverter(legacyFormat, m_compiler->sourceIndices()).print(cout, m_compiler->ast(sourceCode.first));
}
}
}
}
-void CommandLineInterface::actOnInput()
+bool CommandLineInterface::actOnInput()
{
- if (m_onlyAssemble)
- outputAssembly();
+ if (m_args.count(g_argStandardJSON) || m_onlyAssemble)
+ // Already done in "processInput" phase.
+ return true;
else if (m_onlyLink)
writeLinkedFiles();
else
outputCompilationResults();
+ return !m_error;
}
bool CommandLineInterface::link()
@@ -861,42 +1041,91 @@ void CommandLineInterface::writeLinkedFiles()
writeFile(src.first, src.second);
}
-bool CommandLineInterface::assemble()
+bool CommandLineInterface::assemble(
+ AssemblyStack::Language _language,
+ AssemblyStack::Machine _targetMachine
+)
{
- //@TODO later, we will use the convenience interface and should also remove the include above
bool successful = true;
- map<string, shared_ptr<Scanner>> scanners;
+ map<string, AssemblyStack> assemblyStacks;
for (auto const& src: m_sourceCodes)
{
- auto scanner = make_shared<Scanner>(CharStream(src.second), src.first);
- scanners[src.first] = scanner;
- if (!m_assemblyStacks[src.first].parse(scanner))
- successful = false;
- else
- //@TODO we should not just throw away the result here
- m_assemblyStacks[src.first].assemble();
+ auto& stack = assemblyStacks[src.first] = AssemblyStack(_language);
+ try
+ {
+ if (!stack.parseAndAnalyze(src.first, src.second))
+ successful = false;
+ }
+ catch (Exception const& _exception)
+ {
+ cerr << "Exception in assembler: " << boost::diagnostic_information(_exception) << endl;
+ return false;
+ }
+ catch (...)
+ {
+ cerr << "Unknown exception in assembler." << endl;
+ return false;
+ }
}
- for (auto const& stack: m_assemblyStacks)
- for (auto const& error: stack.second.errors())
+ for (auto const& sourceAndStack: assemblyStacks)
+ {
+ auto const& stack = sourceAndStack.second;
+ for (auto const& error: stack.errors())
SourceReferenceFormatter::printExceptionInformation(
cerr,
*error,
(error->type() == Error::Type::Warning) ? "Warning" : "Error",
- [&](string const& _source) -> Scanner const& { return *scanners.at(_source); }
+ [&](string const&) -> Scanner const& { return stack.scanner(); }
);
+ if (!Error::containsOnlyWarnings(stack.errors()))
+ successful = false;
+ }
- return successful;
-}
+ if (!successful)
+ return false;
-void CommandLineInterface::outputAssembly()
-{
for (auto const& src: m_sourceCodes)
{
- cout << endl << "======= " << src.first << " =======" << endl;
- eth::Assembly assembly = m_assemblyStacks[src.first].assemble();
- cout << assembly.assemble().toHex() << endl;
- assembly.stream(cout, "", m_sourceCodes);
+ string machine =
+ _targetMachine == AssemblyStack::Machine::EVM ? "EVM" :
+ _targetMachine == AssemblyStack::Machine::EVM15 ? "EVM 1.5" :
+ "eWasm";
+ cout << endl << "======= " << src.first << " (" << machine << ") =======" << endl;
+ AssemblyStack& stack = assemblyStacks[src.first];
+
+ cout << endl << "Pretty printed source:" << endl;
+ cout << stack.print() << endl;
+
+ MachineAssemblyObject object;
+ try
+ {
+ object = stack.assemble(_targetMachine);
+ }
+ catch (Exception const& _exception)
+ {
+ cerr << "Exception while assembling: " << boost::diagnostic_information(_exception) << endl;
+ return false;
+ }
+ catch (...)
+ {
+ cerr << "Unknown exception while assembling." << endl;
+ return false;
+ }
+
+ cout << endl << "Binary representation:" << endl;
+ if (object.bytecode)
+ cout << object.bytecode->toHex() << endl;
+ else
+ cerr << "No binary representation found." << endl;
+
+ cout << endl << "Text representation:" << endl;
+ if (!object.assembly.empty())
+ cout << object.assembly << endl;
+ else
+ cerr << "No text representation found." << endl;
}
+
+ return true;
}
void CommandLineInterface::outputCompilationResults()
@@ -904,8 +1133,9 @@ void CommandLineInterface::outputCompilationResults()
handleCombinedJSON();
// do we need AST output?
- handleAst(g_argAstStr);
+ handleAst(g_argAst);
handleAst(g_argAstJson);
+ handleAst(g_argAstCompactJson);
vector<string> contracts = m_compiler->contractNames();
for (string const& contract: contracts)
@@ -914,18 +1144,18 @@ void CommandLineInterface::outputCompilationResults()
cout << endl << "======= " << contract << " =======" << endl;
// do we need EVM assembly?
- if (m_args.count(g_argAsmStr) || m_args.count(g_argAsmJsonStr))
+ if (m_args.count(g_argAsm) || m_args.count(g_argAsmJson))
{
- if (m_args.count("output-dir"))
+ if (m_args.count(g_argOutputDir))
{
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());
+ m_compiler->streamAssembly(data, contract, m_sourceCodes, m_args.count(g_argAsmJson));
+ createFile(m_compiler->filesystemFriendlyName(contract) + (m_args.count(g_argAsmJson) ? "_evm.json" : ".evm"), data.str());
}
else
{
cout << "EVM assembly:" << endl;
- m_compiler->streamAssembly(cout, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr));
+ m_compiler->streamAssembly(cout, contract, m_sourceCodes, m_args.count(g_argAsmJson));
}
}
@@ -935,12 +1165,13 @@ void CommandLineInterface::outputCompilationResults()
handleBytecode(contract);
handleSignatureHashes(contract);
handleOnChainMetadata(contract);
- handleMeta(DocumentationType::ABIInterface, contract);
- handleMeta(DocumentationType::NatspecDev, contract);
- handleMeta(DocumentationType::NatspecUser, contract);
+ handleABI(contract);
+ handleNatspec(DocumentationType::NatspecDev, contract);
+ handleNatspec(DocumentationType::NatspecUser, contract);
} // end of contracts iteration
- handleFormal();
+ if (m_args.count(g_argFormal))
+ cerr << "Support for the Why3 output was removed." << endl;
}
}