diff options
-rw-r--r-- | docs/index.rst | 1 | ||||
-rw-r--r-- | docs/metadata.rst | 144 | ||||
-rw-r--r-- | docs/miscellaneous.rst | 144 | ||||
-rw-r--r-- | libevmasm/Assembly.cpp | 32 | ||||
-rw-r--r-- | libevmasm/Assembly.h | 21 | ||||
-rw-r--r-- | liblll/Compiler.cpp | 5 | ||||
-rw-r--r-- | libsolidity/codegen/Compiler.h | 10 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.h | 11 | ||||
-rw-r--r-- | libsolidity/interface/AssemblyStack.cpp | 4 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 18 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.h | 10 | ||||
-rw-r--r-- | libsolidity/interface/StandardCompiler.cpp | 6 | ||||
-rw-r--r-- | solc/CommandLineInterface.cpp | 18 |
13 files changed, 220 insertions, 204 deletions
diff --git a/docs/index.rst b/docs/index.rst index 8c33fb9d..cb093bd6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -144,6 +144,7 @@ Contents solidity-in-depth.rst security-considerations.rst using-the-compiler.rst + metadata.rst abi-spec.rst style-guide.rst common-patterns.rst diff --git a/docs/metadata.rst b/docs/metadata.rst new file mode 100644 index 00000000..dbde87e8 --- /dev/null +++ b/docs/metadata.rst @@ -0,0 +1,144 @@ +################# +Contract Metadata +################# + +.. index:: metadata, contract verification + +The Solidity compiler automatically generates a JSON file, the +contract metadata, that contains information about the current contract. +It can be used to query the compiler version, the sources used, the ABI +and NatSpec documentation in order to more safely interact with the contract +and to verify its source code. + +The compiler appends a Swarm hash of the metadata file to the end of the +bytecode (for details, see below) of each contract, so that you can retrieve +the file in an authenticated way without having to resort to a centralized +data provider. + +Of course, you have to publish the metadata file to Swarm (or some other service) +so that others can access it. The file can be output by using ``solc --metadata`` +and the file will be called ``ContractName_meta.json``. +It will contain Swarm references to the source code, so you have to upload +all source files and the metadata file. + +The metadata file has the following format. The example below is presented in a +human-readable way. Properly formatted metadata should use quotes correctly, +reduce whitespace to a minimum and sort the keys of all objects to arrive at a +unique formatting. +Comments are of course also not permitted and used here only for explanatory purposes. + +.. code-block:: none + + { + // Required: The version of the metadata format + version: "1", + // Required: Source code language, basically selects a "sub-version" + // of the specification + language: "Solidity", + // Required: Details about the compiler, contents are specific + // to the language. + compiler: { + // Required for Solidity: Version of the compiler + version: "0.4.6+commit.2dabbdf0.Emscripten.clang", + // Optional: Hash of the compiler binary which produced this output + keccak256: "0x123..." + }, + // Required: Compilation source files/source units, keys are file names + sources: + { + "myFile.sol": { + // Required: keccak256 hash of the source file + "keccak256": "0x123...", + // Required (unless "content" is used, see below): Sorted URL(s) + // to the source file, protocol is more or less arbitrary, but a + // Swarm URL is recommended + "urls": [ "bzzr://56ab..." ] + }, + "mortal": { + // Required: keccak256 hash of the source file + "keccak256": "0x234...", + // Required (unless "url" is used): literal contents of the source file + "content": "contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }" + } + }, + // Required: Compiler settings + settings: + { + // Required for Solidity: Sorted list of remappings + remappings: [ ":g/dir" ], + // Optional: Optimizer settings (enabled defaults to false) + optimizer: { + enabled: true, + runs: 500 + }, + // Required for Solidity: File and name of the contract or library this + // metadata is created for. + compilationTarget: { + "myFile.sol": "MyContract" + }, + // Required for Solidity: Addresses for libraries used + libraries: { + "MyLib": "0x123123..." + } + }, + // Required: Generated information about the contract. + output: + { + // Required: ABI definition of the contract + abi: [ ... ], + // Required: NatSpec user documentation of the contract + userdoc: [ ... ], + // Required: NatSpec developer documentation of the contract + devdoc: [ ... ], + } + } + +.. note:: + Note the ABI definition above has no fixed order. It can change with compiler versions. + +.. note:: + Since the bytecode of the resulting contract contains the metadata hash, any change to + the metadata will result in a change of the bytecode. Furthermore, since the metadata + includes a hash of all the sources used, a single whitespace change in any of the source + codes will result in a different metadata, and subsequently a different bytecode. + +Encoding of the Metadata Hash in the Bytecode +============================================= + +Because we might support other ways to retrieve the metadata file in the future, +the mapping ``{"bzzr0": <Swarm hash>}`` is stored +`CBOR <https://tools.ietf.org/html/rfc7049>`_-encoded. Since the beginning of that +encoding is not easy to find, its length is added in a two-byte big-endian +encoding. The current version of the Solidity compiler thus adds the following +to the end of the deployed bytecode:: + + 0xa1 0x65 'b' 'z' 'z' 'r' '0' 0x58 0x20 <32 bytes swarm hash> 0x00 0x29 + +So in order to retrieve the data, the end of the deployed bytecode can be checked +to match that pattern and use the Swarm hash to retrieve the file. + +Usage for Automatic Interface Generation and NatSpec +==================================================== + +The metadata is used in the following way: A component that wants to interact +with a contract (e.g. Mist) retrieves the code of the contract, from that +the Swarm hash of a file which is then retrieved. +That file is JSON-decoded into a structure like above. + +The component can then use the ABI to automatically generate a rudimentary +user interface for the contract. + +Furthermore, Mist can use the userdoc to display a confirmation message to the user +whenever they interact with the contract. + +Usage for Source Code Verification +================================== + +In order to verify the compilation, sources can be retrieved from Swarm +via the link in the metadata file. +The compiler of the correct version (which is checked to be part of the "official" compilers) +is invoked on that input with the specified settings. The resulting +bytecode is compared to the data of the creation transaction or ``CREATE`` opcode data. +This automatically verifies the metadata since its hash is part of the bytecode. +Excess data corresponds to the constructor input data, which should be decoded +according to the interface and presented to the user. diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index e9b01340..6d6c25ac 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -221,150 +221,6 @@ This means the following source mappings represent the same information: ``1:2:1;:9;2::2;;`` -***************** -Contract Metadata -***************** - -The Solidity compiler automatically generates a JSON file, the -contract metadata, that contains information about the current contract. -It can be used to query the compiler version, the sources used, the ABI -and NatSpec documentation in order to more safely interact with the contract -and to verify its source code. - -The compiler appends a Swarm hash of the metadata file to the end of the -bytecode (for details, see below) of each contract, so that you can retrieve -the file in an authenticated way without having to resort to a centralized -data provider. - -Of course, you have to publish the metadata file to Swarm (or some other service) -so that others can access it. The file can be output by using ``solc --metadata`` -and the file will be called ``ContractName_meta.json``. -It will contain Swarm references to the source code, so you have to upload -all source files and the metadata file. - -The metadata file has the following format. The example below is presented in a -human-readable way. Properly formatted metadata should use quotes correctly, -reduce whitespace to a minimum and sort the keys of all objects to arrive at a -unique formatting. -Comments are of course also not permitted and used here only for explanatory purposes. - -.. code-block:: none - - { - // Required: The version of the metadata format - version: "1", - // Required: Source code language, basically selects a "sub-version" - // of the specification - language: "Solidity", - // Required: Details about the compiler, contents are specific - // to the language. - compiler: { - // Required for Solidity: Version of the compiler - version: "0.4.6+commit.2dabbdf0.Emscripten.clang", - // Optional: Hash of the compiler binary which produced this output - keccak256: "0x123..." - }, - // Required: Compilation source files/source units, keys are file names - sources: - { - "myFile.sol": { - // Required: keccak256 hash of the source file - "keccak256": "0x123...", - // Required (unless "content" is used, see below): Sorted URL(s) - // to the source file, protocol is more or less arbitrary, but a - // Swarm URL is recommended - "urls": [ "bzzr://56ab..." ] - }, - "mortal": { - // Required: keccak256 hash of the source file - "keccak256": "0x234...", - // Required (unless "url" is used): literal contents of the source file - "content": "contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }" - } - }, - // Required: Compiler settings - settings: - { - // Required for Solidity: Sorted list of remappings - remappings: [ ":g/dir" ], - // Optional: Optimizer settings (enabled defaults to false) - optimizer: { - enabled: true, - runs: 500 - }, - // Required for Solidity: File and name of the contract or library this - // metadata is created for. - compilationTarget: { - "myFile.sol": "MyContract" - }, - // Required for Solidity: Addresses for libraries used - libraries: { - "MyLib": "0x123123..." - } - }, - // Required: Generated information about the contract. - output: - { - // Required: ABI definition of the contract - abi: [ ... ], - // Required: NatSpec user documentation of the contract - userdoc: [ ... ], - // Required: NatSpec developer documentation of the contract - devdoc: [ ... ], - } - } - -.. note:: - Note the ABI definition above has no fixed order. It can change with compiler versions. - -.. note:: - Since the bytecode of the resulting contract contains the metadata hash, any change to - the metadata will result in a change of the bytecode. Furthermore, since the metadata - includes a hash of all the sources used, a single whitespace change in any of the source - codes will result in a different metadata, and subsequently a different bytecode. - -Encoding of the Metadata Hash in the Bytecode -============================================= - -Because we might support other ways to retrieve the metadata file in the future, -the mapping ``{"bzzr0": <Swarm hash>}`` is stored -`CBOR <https://tools.ietf.org/html/rfc7049>`_-encoded. Since the beginning of that -encoding is not easy to find, its length is added in a two-byte big-endian -encoding. The current version of the Solidity compiler thus adds the following -to the end of the deployed bytecode:: - - 0xa1 0x65 'b' 'z' 'z' 'r' '0' 0x58 0x20 <32 bytes swarm hash> 0x00 0x29 - -So in order to retrieve the data, the end of the deployed bytecode can be checked -to match that pattern and use the Swarm hash to retrieve the file. - -Usage for Automatic Interface Generation and NatSpec -==================================================== - -The metadata is used in the following way: A component that wants to interact -with a contract (e.g. Mist) retrieves the code of the contract, from that -the Swarm hash of a file which is then retrieved. -That file is JSON-decoded into a structure like above. - -The component can then use the ABI to automatically generate a rudimentary -user interface for the contract. - -Furthermore, Mist can use the userdoc to display a confirmation message to the user -whenever they interact with the contract. - -Usage for Source Code Verification -================================== - -In order to verify the compilation, sources can be retrieved from Swarm -via the link in the metadata file. -The compiler of the correct version (which is checked to be part of the "official" compilers) -is invoked on that input with the specified settings. The resulting -bytecode is compared to the data of the creation transaction or ``CREATE`` opcode data. -This automatically verifies the metadata since its hash is part of the bytecode. -Excess data corresponds to the constructor input data, which should be decoded -according to the interface and presented to the user. - - *************** Tips and Tricks *************** diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 8c1f9296..6b4bb52b 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -181,7 +181,7 @@ private: } -ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const +void Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const { Functionalizer f(_out, _prefix, _sourceCodes); @@ -199,18 +199,23 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con for (size_t i = 0; i < m_subs.size(); ++i) { _out << endl << _prefix << "sub_" << i << ": assembly {\n"; - m_subs[i]->streamAsm(_out, _prefix + " ", _sourceCodes); + m_subs[i]->assemblyStream(_out, _prefix + " ", _sourceCodes); _out << _prefix << "}" << endl; } } if (m_auxiliaryData.size() > 0) _out << endl << _prefix << "auxdata: 0x" << toHex(m_auxiliaryData) << endl; +} - return _out; +string Assembly::assemblyString(StringMap const& _sourceCodes) const +{ + ostringstream tmp; + assemblyStream(tmp, "", _sourceCodes); + return tmp.str(); } -Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string _value, string _jumpType) const +Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string _value, string _jumpType) { Json::Value value; value["name"] = _name; @@ -223,14 +228,14 @@ Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string return value; } -string toStringInHex(u256 _value) +string Assembly::toStringInHex(u256 _value) { std::stringstream hexStr; hexStr << hex << _value; return hexStr.str(); } -Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes) const +Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const { Json::Value root; @@ -301,29 +306,16 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes { std::stringstream hexStr; hexStr << hex << i; - data[hexStr.str()] = m_subs[i]->stream(_out, "", _sourceCodes, true); + data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceCodes); } } if (m_auxiliaryData.size() > 0) root[".auxdata"] = toHex(m_auxiliaryData); - _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) { assertThrow(m_deposit >= 0, AssemblyException, ""); diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 680cb1af..cbdd71bc 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -120,11 +120,19 @@ public: /// If @a _enable is not set, will perform some simple peephole optimizations. Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200); - Json::Value stream( + /// Create a text representation of the assembly. + std::string assemblyString( + StringMap const& _sourceCodes = StringMap() + ) const; + void assemblyStream( std::ostream& _out, std::string const& _prefix = "", - const StringMap &_sourceCodes = StringMap(), - bool _inJsonFormat = false + StringMap const& _sourceCodes = StringMap() + ) const; + + /// Create a JSON representation of the assembly. + Json::Value assemblyJSON( + StringMap const& _sourceCodes = StringMap() ) const; protected: @@ -136,9 +144,8 @@ protected: unsigned bytesRequired(unsigned subTagSize) const; private: - Json::Value streamAsmJson(std::ostream& _out, StringMap const& _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; + static Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()); + static std::string toStringInHex(u256 _value); protected: /// 0 is reserved for exception @@ -161,7 +168,7 @@ protected: inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a) { - _a.stream(_out); + _a.assemblyStream(_out); return _out; } diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index 4ec11ca9..b69675aa 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -72,14 +72,13 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::v { CompilerState cs; cs.populateStandard(); - stringstream ret; auto assembly = CodeFragment::compile(_src, cs).assembly(cs); if (_opt) assembly = assembly.optimise(true); - assembly.stream(ret); + string ret = assembly.assemblyString(); for (auto i: cs.treesToKill) killBigints(i); - return ret.str(); + return ret; } catch (Exception const& _e) { diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index c6ee93fb..06654486 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -60,10 +60,14 @@ public: /// @returns Only the runtime object (without constructor). eth::LinkerObject runtimeObject() const { return m_context.assembledRuntimeObject(m_runtimeSub); } /// @arg _sourceCodes is the map of input files to source code strings - /// @arg _inJsonFromat shows whether the out should be in Json format - Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const + std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const { - return m_context.streamAssembly(_stream, _sourceCodes, _inJsonFormat); + return m_context.assemblyString(_sourceCodes); + } + /// @arg _sourceCodes is the map of input files to source code strings + Json::Value assemblyJSON(StringMap const& _sourceCodes = StringMap()) const + { + return m_context.assemblyJSON(_sourceCodes); } /// @returns Assembly items of the normal compiler context eth::AssemblyItems const& assemblyItems() const { return m_context.assembly().items(); } diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 3994b010..5116585e 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -209,10 +209,15 @@ public: eth::Assembly& nonConstAssembly() { return *m_asm; } /// @arg _sourceCodes is the map of input files to source code strings - /// @arg _inJsonFormat shows whether the out should be in Json format - Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const + std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const { - return m_asm->stream(_stream, "", _sourceCodes, _inJsonFormat); + return m_asm->assemblyString(_sourceCodes); + } + + /// @arg _sourceCodes is the map of input files to source code strings + Json::Value assemblyJSON(StringMap const& _sourceCodes = StringMap()) const + { + return m_asm->assemblyJSON(_sourceCodes); } eth::LinkerObject const& assembledObject() const { return m_asm->assemble(); } diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 23524bb3..504ad92c 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -91,9 +91,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const eth::Assembly assembly; assembly::CodeGenerator::assemble(*m_parserResult, *m_analysisInfo, assembly); object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble()); - ostringstream tmp; - assembly.stream(tmp); - object.assembly = tmp.str(); + object.assembly = assembly.assemblyString(); return object; } case Machine::EVM15: diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 259694da..41bbf687 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -347,16 +347,24 @@ eth::LinkerObject const& CompilerStack::cloneObject(string const& _contractName) return contract(_contractName).cloneObject; } -Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const +/// FIXME: cache this string +string CompilerStack::assemblyString(string const& _contractName, StringMap _sourceCodes) const { Contract const& currentContract = contract(_contractName); if (currentContract.compiler) - return currentContract.compiler->streamAssembly(_outStream, _sourceCodes, _inJsonFormat); + return currentContract.compiler->assemblyString(_sourceCodes); + else + return string(); +} + +/// FIXME: cache the JSON +Json::Value CompilerStack::assemblyJSON(string const& _contractName, StringMap _sourceCodes) const +{ + Contract const& currentContract = contract(_contractName); + if (currentContract.compiler) + return currentContract.compiler->assemblyJSON(_sourceCodes); else - { - _outStream << "Contract not fully implemented" << endl; return Json::Value(); - } } vector<string> CompilerStack::sourceNames() const diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 2756e57d..f1bbae47 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -190,11 +190,15 @@ public: /// if the contract does not (yet) have bytecode. std::string const* runtimeSourceMapping(std::string const& _contractName = "") const; - /// Streams a verbose version of the assembly to @a _outStream. + /// @return a verbose text representation of the assembly. /// @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. - Json::Value streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const; + std::string assemblyString(std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; + + /// @returns a JSON representation of the assembly. + /// @arg _sourceCodes is the map of input files to source code strings + /// Prerequisite: Successful compilation. + Json::Value assemblyJSON(std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; /// @returns a JSON representing the contract ABI. /// Prerequisite: Successful call to parse or compile. diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index be823743..b4fbbef9 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -400,10 +400,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) // EVM Json::Value evmData(Json::objectValue); // @TODO: add ir - ostringstream tmp; - m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), false); - evmData["assembly"] = tmp.str(); - evmData["legacyAssembly"] = m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), true); + evmData["assembly"] = m_compilerStack.assemblyString(contractName, createSourceList(_input)); + evmData["legacyAssembly"] = m_compilerStack.assemblyJSON(contractName, createSourceList(_input)); evmData["methodIdentifiers"] = m_compilerStack.methodIdentifiers(contractName); evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 315f951e..e6d8776b 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -865,10 +865,7 @@ void CommandLineInterface::handleCombinedJSON() if (requests.count(g_strOpcodes)) contractData[g_strOpcodes] = solidity::disassemble(m_compiler->object(contractName).bytecode); if (requests.count(g_strAsm)) - { - ostringstream unused; - contractData[g_strAsm] = m_compiler->streamAssembly(unused, contractName, m_sourceCodes, true); - } + contractData[g_strAsm] = m_compiler->assemblyJSON(contractName, m_sourceCodes); if (requests.count(g_strSrcMap)) { auto map = m_compiler->sourceMapping(contractName); @@ -1150,16 +1147,19 @@ void CommandLineInterface::outputCompilationResults() // do we need EVM assembly? if (m_args.count(g_argAsm) || m_args.count(g_argAsmJson)) { + string ret; + if (m_args.count(g_argAsmJson)) + ret = dev::jsonPrettyPrint(m_compiler->assemblyJSON(contract, m_sourceCodes)); + else + ret = m_compiler->assemblyString(contract, m_sourceCodes); + if (m_args.count(g_argOutputDir)) { - stringstream data; - 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()); + createFile(m_compiler->filesystemFriendlyName(contract) + (m_args.count(g_argAsmJson) ? "_evm.json" : ".evm"), ret); } else { - cout << "EVM assembly:" << endl; - m_compiler->streamAssembly(cout, contract, m_sourceCodes, m_args.count(g_argAsmJson)); + cout << "EVM assembly:" << endl << ret << endl; } } |