diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index bf4ea2145..ae8567e2a 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -24,7 +24,7 @@ #include #include #include - +#include using namespace std; using namespace dev; using namespace dev::eth; @@ -65,6 +65,13 @@ void Assembly::append(Assembly const& _a, int _deposit) } } +string Assembly::out() const +{ + stringstream ret; + stream(ret); + return ret.str(); +} + unsigned Assembly::bytesRequired() const { for (unsigned br = 1;; ++br) @@ -101,7 +108,7 @@ string Assembly::getLocationFromSources(StringMap const& _sourceCodes, SourceLoc return move(cut); } -ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const +ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const { _out << _prefix << ".code:" << endl; for (AssemblyItem const& i: m_items) @@ -157,6 +164,115 @@ ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const& return _out; } +Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string _value, string _jumpType) const +{ + Json::Value value; + value["name"] = _name; + value["begin"] = _begin; + value["end"] = _end; + if (!_value.empty()) + value["value"] = _value; + if (!_jumpType.empty()) + value["jumpType"] = _jumpType; + return value; +} + +string toStringInHex(u256 _value) +{ + std::stringstream hexStr; + hexStr << hex << _value; + return hexStr.str(); +} + +Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes) const +{ + Json::Value root; + + Json::Value collection(Json::arrayValue); + for (AssemblyItem const& i: m_items) + { + switch (i.type()) + { + case Operation: + collection.append( + createJsonValue(instructionInfo(i.instruction()).name, i.getLocation().start, i.getLocation().end, i.getJumpTypeAsString())); + break; + case Push: + collection.append( + createJsonValue("PUSH", i.getLocation().start, i.getLocation().end, toStringInHex(i.data()), i.getJumpTypeAsString())); + break; + case PushString: + collection.append( + createJsonValue("PUSH tag", i.getLocation().start, i.getLocation().end, m_strings.at((h256)i.data()))); + break; + case PushTag: + collection.append( + createJsonValue("PUSH [tag]", i.getLocation().start, i.getLocation().end, toStringInHex(i.data()))); + break; + case PushSub: + collection.append( + createJsonValue("PUSH [$]", i.getLocation().start, i.getLocation().end, dev::toString(h256(i.data())))); + break; + case PushSubSize: + collection.append( + createJsonValue("PUSH #[$]", i.getLocation().start, i.getLocation().end, dev::toString(h256(i.data())))); + break; + case PushProgramSize: + collection.append( + createJsonValue("PUSHSIZE", i.getLocation().start, i.getLocation().end)); + break; + case Tag: + { + collection.append( + createJsonValue("tag", i.getLocation().start, i.getLocation().end, string(i.data()))); + collection.append( + createJsonValue("JUMDEST", i.getLocation().start, i.getLocation().end)); + } + break; + case PushData: + { + Json::Value pushData; + pushData["name"] = "PUSH hex"; + collection.append(createJsonValue("PUSH hex", i.getLocation().start, i.getLocation().end, toStringInHex(i.data()))); + } + break; + default: + BOOST_THROW_EXCEPTION(InvalidOpcode()); + } + } + + root[".code"] = collection; + + if (!m_data.empty() || !m_subs.empty()) + { + Json::Value data; + for (auto const& i: m_data) + if (u256(i.first) >= m_subs.size()) + data[toStringInHex((u256)i.first)] = toHex(i.second); + + for (size_t i = 0; i < m_subs.size(); ++i) + { + std::stringstream hexStr; + hexStr << hex << i; + data[hexStr.str()] = m_subs[i].stream(_out, "", _sourceCodes, true); + } + root[".data"] = data; + _out << root; + } + return root; +} + +Json::Value Assembly::stream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes, bool _inJsonFormat) const +{ + if (_inJsonFormat) + return streamAsmJson(_out, _sourceCodes); + else + { + streamAsm(_out, _prefix, _sourceCodes); + return Json::Value(); + } +} + AssemblyItem const& Assembly::append(AssemblyItem const& _i) { m_deposit += _i.deposit(); diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h index 2744af900..4ac873682 100644 --- a/libevmcore/Assembly.h +++ b/libevmcore/Assembly.h @@ -29,7 +29,12 @@ #include #include #include "Exceptions.h" +#include +namespace Json +{ +class Value; +} namespace dev { namespace eth @@ -76,7 +81,7 @@ public: void popTo(int _deposit) { while (m_deposit > _deposit) append(Instruction::POP); } void injectStart(AssemblyItem const& _i); - std::string out() const { std::stringstream ret; stream(ret); return ret.str(); } + std::string out() const; int deposit() const { return m_deposit; } void adjustDeposit(int _adjustment) { m_deposit += _adjustment; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); } void setDeposit(int _deposit) { m_deposit = _deposit; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); } @@ -86,13 +91,24 @@ public: bytes assemble() const; Assembly& optimise(bool _enable); - std::ostream& stream(std::ostream& _out, std::string const& _prefix = "", const StringMap &_sourceCodes = StringMap()) const; + Json::Value stream( + std::ostream& _out, + std::string const& _prefix = "", + const StringMap &_sourceCodes = StringMap(), + bool _inJsonFormat = false + ) const; protected: std::string getLocationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const; void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); } unsigned bytesRequired() const; +private: + Json::Value streamAsmJson(std::ostream& _out, const StringMap &_sourceCodes) const; + std::ostream& streamAsm(std::ostream& _out, std::string const& _prefix, StringMap const& _sourceCodes) const; + Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()) const; + +protected: unsigned m_usedTags = 0; AssemblyItems m_items; mutable std::map m_data; diff --git a/libevmcore/CMakeLists.txt b/libevmcore/CMakeLists.txt index 6a834936b..83a4e115c 100644 --- a/libevmcore/CMakeLists.txt +++ b/libevmcore/CMakeLists.txt @@ -11,6 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") aux_source_directory(. SRC_LIST) +include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ..) include_directories(${Boost_INCLUDE_DIRS}) diff --git a/liblll/CMakeLists.txt b/liblll/CMakeLists.txt index e60522a7c..b865d4afe 100644 --- a/liblll/CMakeLists.txt +++ b/liblll/CMakeLists.txt @@ -11,6 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") aux_source_directory(. SRC_LIST) +include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ..) include_directories(${Boost_INCLUDE_DIRS}) diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 4b1e1b4d6..d476ec684 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -42,9 +42,10 @@ public: bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);} /// @arg _sourceCodes is the map of input files to source code strings - void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const + /// @arg _inJsonFromat shows whether the out should be in Json format + void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const { - m_context.streamAssembly(_stream, _sourceCodes); + m_context.streamAssembly(_stream, _sourceCodes, _inJsonFormat); } /// @returns Assembly items of the normal compiler context eth::AssemblyItems const& getAssemblyItems() const { return m_context.getAssembly().getItems(); } diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index e752d59b8..9c2156bfa 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -122,7 +122,11 @@ public: eth::Assembly const& getAssembly() const { return m_asm; } /// @arg _sourceCodes is the map of input files to source code strings - void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.stream(_stream, "", _sourceCodes); } + /// @arg _inJsonFormat shows whether the out should be in Json format + void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const + { + m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat); + } bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); } diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index c35d9324c..d6274e2c7 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -184,11 +184,11 @@ dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const return dev::sha3(getRuntimeBytecode(_contractName)); } -void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes) const +void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const { Contract const& contract = getContract(_contractName); if (contract.compiler) - getContract(_contractName).compiler->streamAssembly(_outStream, _sourceCodes); + contract.compiler->streamAssembly(_outStream, _sourceCodes, _inJsonFormat); else _outStream << "Contract not fully implemented" << endl; } diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 19c1ba4e1..2e7c217d5 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -102,8 +102,9 @@ public: /// Streams a verbose version of the assembly to @a _outStream. /// @arg _sourceCodes is the map of input files to source code strings + /// @arg _inJsonFromat shows whether the out should be in Json format /// Prerequisite: Successful compilation. - void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; + void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const; /// Returns a string representing the contract interface in JSON. /// Prerequisite: Successful call to parse or compile. diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 6ed90cdea..182015709 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -55,6 +55,7 @@ namespace solidity static string const g_argAbiStr = "json-abi"; static string const g_argSolAbiStr = "sol-abi"; 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 = "binary"; @@ -80,10 +81,15 @@ static bool needStdout(po::variables_map const& _args) { return - argToStdout(_args, g_argAbiStr) || argToStdout(_args, g_argSolAbiStr) || - argToStdout(_args, g_argNatspecUserStr) || argToStdout(_args, g_argAstJson) || - argToStdout(_args, g_argNatspecDevStr) || argToStdout(_args, g_argAsmStr) || - argToStdout(_args, g_argOpcodesStr) || argToStdout(_args, g_argBinaryStr); + argToStdout(_args, g_argAbiStr) || + argToStdout(_args, g_argSolAbiStr) || + argToStdout(_args, g_argNatspecUserStr) || + argToStdout(_args, g_argAstJson) || + argToStdout(_args, g_argNatspecDevStr) || + argToStdout(_args, g_argAsmStr) || + argToStdout(_args, g_argAsmJsonStr) || + argToStdout(_args, g_argOpcodesStr) || + argToStdout(_args, g_argBinaryStr); } static inline bool outputToFile(OutputType type) @@ -215,23 +221,25 @@ bool CommandLineInterface::parseArguments(int argc, char** argv) ("add-std", po::value()->default_value(false), "Add standard contracts") ("input-file", po::value>(), "input file") (g_argAstStr.c_str(), po::value()->value_name("stdout|file|both"), - "Request to output the AST of the contract.") + "Request to output the AST of the contract.") (g_argAstJson.c_str(), po::value()->value_name("stdout|file|both"), - "Request to output the AST of the contract in JSON format.") + "Request to output the AST of the contract in JSON format.") (g_argAsmStr.c_str(), po::value()->value_name("stdout|file|both"), - "Request to output the EVM assembly of the contract.") + "Request to output the EVM assembly of the contract.") + (g_argAsmJsonStr.c_str(), po::value()->value_name("stdout|file|both"), + "Request to output the EVM assembly of the contract in JSON format.") (g_argOpcodesStr.c_str(), po::value()->value_name("stdout|file|both"), - "Request to output the Opcodes of the contract.") + "Request to output the Opcodes of the contract.") (g_argBinaryStr.c_str(), po::value()->value_name("stdout|file|both"), - "Request to output the contract in binary (hexadecimal).") + "Request to output the contract in binary (hexadecimal).") (g_argAbiStr.c_str(), po::value()->value_name("stdout|file|both"), - "Request to output the contract's JSON ABI interface.") + "Request to output the contract's JSON ABI interface.") (g_argSolAbiStr.c_str(), po::value()->value_name("stdout|file|both"), - "Request to output the contract's Solidity ABI interface.") + "Request to output the contract's Solidity ABI interface.") (g_argNatspecUserStr.c_str(), po::value()->value_name("stdout|file|both"), - "Request to output the contract's Natspec user documentation.") + "Request to output the contract's Natspec user documentation.") (g_argNatspecDevStr.c_str(), po::value()->value_name("stdout|file|both"), - "Request to output the contract's Natspec developer documentation."); + "Request to output the contract's Natspec developer documentation."); // All positional options should be interpreted as input files po::positional_options_description p; @@ -411,19 +419,19 @@ void CommandLineInterface::actOnInput() cout << endl << "======= " << contract << " =======" << endl; // do we need EVM assembly? - if (m_args.count(g_argAsmStr)) + if (m_args.count(g_argAsmStr) || m_args.count(g_argAsmJsonStr)) { - auto choice = m_args[g_argAsmStr].as(); + auto choice = m_args.count(g_argAsmStr) ? m_args[g_argAsmStr].as() : m_args[g_argAsmJsonStr].as(); if (outputToStdout(choice)) { cout << "EVM assembly:" << endl; - m_compiler->streamAssembly(cout, contract, m_sourceCodes); + m_compiler->streamAssembly(cout, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr)); } if (outputToFile(choice)) { - ofstream outFile(contract + ".evm"); - m_compiler->streamAssembly(outFile, contract, m_sourceCodes); + ofstream outFile(contract + (m_args.count(g_argAsmJsonStr) ? "_evm.json" : ".evm")); + m_compiler->streamAssembly(outFile, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr)); outFile.close(); } }