diff --git a/Changelog.md b/Changelog.md index e67cf0f07d91..9c9a9e8fd91b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ Compiler Features: * SMTChecker: Replace CVC4 as a possible BMC backend with cvc5. * Standard JSON Interface: Do not perform IR optimization when only unoptimized IR is requested. * Yul Optimizer: The optimizer now treats some previously unrecognized identical literals as identical. + * Commandline Interface: Allow the use of ``--asm-json`` output option in assembler mode to export EVM assembly of the contracts in JSON format. Bugfixes: diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 356a2818a4cd..f9b800c90884 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -479,8 +479,18 @@ Json Assembly::assemblyJSON(std::map const& _sourceIndice { root["sourceList"] = Json::array(); Json& jsonSourceList = root["sourceList"]; - for (auto const& [name, index]: _sourceIndices) - jsonSourceList[index] = name; + unsigned maxSourceIndex = 0; + for (auto const& [sourceName, sourceIndex]: _sourceIndices) + { + maxSourceIndex = std::max(sourceIndex, maxSourceIndex); + jsonSourceList[sourceIndex] = sourceName; + } + solAssert(maxSourceIndex + 1 >= _sourceIndices.size()); + solRequire( + _sourceIndices.size() == 0 || _sourceIndices.size() == maxSourceIndex + 1, + AssemblyImportException, + "The 'sourceList' array contains invalid 'null' item." + ); } if (!m_data.empty() || !m_subs.empty()) @@ -522,7 +532,14 @@ std::pair, std::vector> Assembly::fromJSO { solRequire(_json["sourceList"].is_array(), AssemblyImportException, "Optional member 'sourceList' is not an array."); for (Json const& sourceName: _json["sourceList"]) - solRequire(sourceName.is_string(), AssemblyImportException, "The 'sourceList' array contains an item that is not a string."); + { + solRequire(!sourceName.is_null(), AssemblyImportException, "The 'sourceList' array contains invalid 'null' item."); + solRequire( + sourceName.is_string(), + AssemblyImportException, + "The 'sourceList' array contains an item that is not a string." + ); + } } } else diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index cb0c8126820e..e4b9f9f7ae17 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -1694,7 +1694,7 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesExperimental)) output["contracts"][sourceName][contractName]["irOptimized"] = stack.print(); if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "evm.assembly", wildcardMatchesExperimental)) - output["contracts"][sourceName][contractName]["evm"]["assembly"] = object.assembly; + output["contracts"][sourceName][contractName]["evm"]["assembly"] = object.assembly->assemblyString(stack.debugInfoSelection()); return output; } diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 736c1bb8411e..989e7ce809de 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -160,3 +160,33 @@ std::vector Object::pathToSubObject(std::string_view _qualifiedName) con return path; } + +void Object::collectSourceIndices(std::map& _indices) const +{ + if (debugData && debugData->sourceNames.has_value()) + for (auto const& [sourceIndex, sourceName]: debugData->sourceNames.value()) + { + solAssert(_indices.count(*sourceName) == 0 || _indices[*sourceName] == sourceIndex); + _indices[*sourceName] = sourceIndex; + } + for (std::shared_ptr const& subNode: subObjects) + if (auto subObject = dynamic_cast(subNode.get())) + subObject->collectSourceIndices(_indices); +} + +bool Object::hasContiguousSourceIndices() const +{ + std::map sourceIndices; + collectSourceIndices(sourceIndices); + + unsigned maxSourceIndex = 0; + std::set indices; + for (auto const& [sources, sourceIndex]: sourceIndices) + { + maxSourceIndex = std::max(sourceIndex, maxSourceIndex); + indices.insert(sourceIndex); + } + + solAssert(maxSourceIndex + 1 >= indices.size()); + return indices.size() == 0 || indices.size() == maxSourceIndex + 1; +} diff --git a/libyul/Object.h b/libyul/Object.h index 4e891c26400a..c419a6d55e39 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -129,6 +129,14 @@ struct Object: public ObjectNode std::shared_ptr debugData; + /// Collects names of all Solidity source units present in the debug data + /// of the Yul object (including sub-objects) and their assigned indices. + /// @param _indices map that will be filled with source indices of the current Yul object & its sub-objects. + void collectSourceIndices(std::map& _indices) const; + + /// @returns true, if the range of source indices starts at zero and is contiguous, false otherwise. + bool hasContiguousSourceIndices() const; + /// @returns the name of the special metadata data object. static std::string metadataName() { return ".metadata"; } }; diff --git a/libyul/YulStack.cpp b/libyul/YulStack.cpp index 223d6c04a19b..b9fc6388c6bd 100644 --- a/libyul/YulStack.cpp +++ b/libyul/YulStack.cpp @@ -15,10 +15,6 @@ along with solidity. If not, see . */ // SPDX-License-Identifier: GPL-3.0 -/** - * Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 - */ - #include @@ -64,7 +60,6 @@ Dialect const& languageToDialect(YulStack::Language _language, EVMVersion _versi } - CharStream const& YulStack::charStream(std::string const& _sourceName) const { yulAssert(m_charStream, ""); @@ -274,7 +269,7 @@ YulStack::assembleWithDeployed(std::optional _deployName) { creationObject.bytecode = std::make_shared(creationAssembly->assemble()); yulAssert(creationObject.bytecode->immutableReferences.empty(), "Leftover immutables."); - creationObject.assembly = creationAssembly->assemblyString(m_debugInfoSelection); + creationObject.assembly = creationAssembly; creationObject.sourceMappings = std::make_unique( evmasm::AssemblyItem::computeSourceMapping( creationAssembly->items(), @@ -285,7 +280,7 @@ YulStack::assembleWithDeployed(std::optional _deployName) if (deployedAssembly) { deployedObject.bytecode = std::make_shared(deployedAssembly->assemble()); - deployedObject.assembly = deployedAssembly->assemblyString(m_debugInfoSelection); + deployedObject.assembly = deployedAssembly; deployedObject.sourceMappings = std::make_unique( evmasm::AssemblyItem::computeSourceMapping( deployedAssembly->items(), diff --git a/libyul/YulStack.h b/libyul/YulStack.h index c6d78a115c18..f547ef77027e 100644 --- a/libyul/YulStack.h +++ b/libyul/YulStack.h @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 /** - * Full assembly stack that can support EVM-assembly and Yul as input and EVM. + * Full assembly stack that can support Yul as input. */ #pragma once @@ -55,7 +55,7 @@ class AbstractAssembly; struct MachineAssemblyObject { std::shared_ptr bytecode; - std::string assembly; + std::shared_ptr assembly; std::unique_ptr sourceMappings; }; @@ -137,9 +137,12 @@ class YulStack: public langutil::CharStreamProvider langutil::CharStreamProvider const* _soliditySourceProvider = nullptr ) const; Json astJson() const; + /// Return the parsed and analyzed object. std::shared_ptr parserResult() const; + langutil::DebugInfoSelection debugInfoSelection() const { return m_debugInfoSelection; } + private: bool parse(std::string const& _sourceName, std::string const& _source); bool analyzeParsed(); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 5d0272dfa930..1351858405a1 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -177,7 +177,7 @@ void CommandLineInterface::handleEVMAssembly(std::string const& _contract) std::string assembly; if (m_options.compiler.outputs.asmJson) - assembly = util::jsonPrint(removeNullMembers(m_assemblyStack->assemblyJSON(_contract)), m_options.formatting.json); + assembly = util::jsonPrint(m_assemblyStack->assemblyJSON(_contract), m_options.formatting.json); else assembly = m_assemblyStack->assemblyString(_contract, m_fileReader.sourceUnits()); @@ -1197,6 +1197,17 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y successful = false; else stack.optimize(); + + if (successful && m_options.compiler.outputs.asmJson) + { + std::shared_ptr result = stack.parserResult(); + if (result && !result->hasContiguousSourceIndices()) + solThrow( + CommandLineExecutionError, + "Generating the assembly JSON output was not possible. " + "Source indices provided in the @use-src annotation in the Yul input do not start at 0 or are not contiguous." + ); + } } for (auto const& sourceAndStack: yulStacks) @@ -1256,11 +1267,22 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y if (m_options.compiler.outputs.asm_) { sout() << std::endl << "Text representation:" << std::endl; - if (!object.assembly.empty()) - sout() << object.assembly << std::endl; + std::string assemblyText{object.assembly->assemblyString(stack.debugInfoSelection())}; + if (!assemblyText.empty()) + sout() << assemblyText << std::endl; else report(Error::Severity::Info, "No text representation found."); } + if (m_options.compiler.outputs.asmJson) + { + sout() << std::endl << "EVM assembly:" << std::endl; + std::map sourceIndices; + stack.parserResult()->collectSourceIndices(sourceIndices); + sout() << util::jsonPrint( + object.assembly->assemblyJSON(sourceIndices), + m_options.formatting.json + ) << std::endl; + } } } diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 2697f7ebd09f..881c656dad9b 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -472,6 +472,7 @@ void CommandLineParser::parseOutputSelection() CompilerOutputs::componentName(&CompilerOutputs::binary), CompilerOutputs::componentName(&CompilerOutputs::irOptimized), CompilerOutputs::componentName(&CompilerOutputs::astCompactJson), + CompilerOutputs::componentName(&CompilerOutputs::asmJson), }; static std::set const evmAssemblyJsonImportModeOutputs = { CompilerOutputs::componentName(&CompilerOutputs::asm_), diff --git a/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/args b/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/args new file mode 100644 index 000000000000..c1bc80dabfcd --- /dev/null +++ b/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/args @@ -0,0 +1 @@ +--pretty-json --json-indent 4 --import-asm-json - diff --git a/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/err b/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/err new file mode 100644 index 000000000000..73f36f97191d --- /dev/null +++ b/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/err @@ -0,0 +1 @@ +Error: Assembly Import Error: The 'sourceList' array contains invalid 'null' item. diff --git a/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/exit b/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/stdin b/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/stdin new file mode 100644 index 000000000000..923376f40f91 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/stdin @@ -0,0 +1,17 @@ +{ + ".code": + [ + { + "begin": -1, + "end": -1, + "name": "STOP", + "source": -1 + } + ], + "sourceList": + [ + null, + null, + "L0.sol" + ] +} diff --git a/test/cmdlineTests/asm_json_yul_export_evm_asm_import/args b/test/cmdlineTests/asm_json_yul_export_evm_asm_import/args new file mode 100644 index 000000000000..15c962831667 --- /dev/null +++ b/test/cmdlineTests/asm_json_yul_export_evm_asm_import/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm --bin --asm-json --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/asm_json_yul_export_evm_asm_import/output b/test/cmdlineTests/asm_json_yul_export_evm_asm_import/output new file mode 100644 index 000000000000..9ebef414258d --- /dev/null +++ b/test/cmdlineTests/asm_json_yul_export_evm_asm_import/output @@ -0,0 +1,39 @@ +Opcodes: +PUSH1 0x5 PUSH0 SSTORE STOP +Binary: +60055f5500 +EVM assembly: +{ + ".code": [ + { + "begin": -1, + "end": -1, + "name": "PUSHSIZE", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "PUSH", + "source": -1, + "value": "0" + }, + { + "begin": -1, + "end": -1, + "name": "SSTORE", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "STOP", + "source": -1 + } + ], + "sourceList": [ + "L0.sol", + "L1.sol", + "L2.sol" + ] +} diff --git a/test/cmdlineTests/asm_json_yul_export_evm_asm_import/stdin b/test/cmdlineTests/asm_json_yul_export_evm_asm_import/stdin new file mode 100644 index 000000000000..840660f6bc2e --- /dev/null +++ b/test/cmdlineTests/asm_json_yul_export_evm_asm_import/stdin @@ -0,0 +1,36 @@ +{ + ".code": + [ + { + "begin": -1, + "end": -1, + "name": "PUSHSIZE", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "PUSH", + "source": -1, + "value": "0" + }, + { + "begin": -1, + "end": -1, + "name": "SSTORE", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "STOP", + "source": -1 + } + ], + "sourceList": + [ + "L0.sol", + "L1.sol", + "L2.sol" + ] +} diff --git a/test/cmdlineTests/strict_asm_asm_json_arbitrary_source_index/args b/test/cmdlineTests/strict_asm_asm_json_arbitrary_source_index/args new file mode 100644 index 000000000000..bf9bf8851a8c --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_arbitrary_source_index/args @@ -0,0 +1 @@ +--strict-assembly - --asm-json --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/strict_asm_asm_json_arbitrary_source_index/err b/test/cmdlineTests/strict_asm_asm_json_arbitrary_source_index/err new file mode 100644 index 000000000000..129c4a8cccab --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_arbitrary_source_index/err @@ -0,0 +1 @@ +Error: Generating the assembly JSON output was not possible. Source indices provided in the @use-src annotation in the Yul input do not start at 0 or are not contiguous. diff --git a/test/cmdlineTests/strict_asm_asm_json_arbitrary_source_index/exit b/test/cmdlineTests/strict_asm_asm_json_arbitrary_source_index/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_arbitrary_source_index/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/strict_asm_asm_json_arbitrary_source_index/stdin b/test/cmdlineTests/strict_asm_asm_json_arbitrary_source_index/stdin new file mode 100644 index 000000000000..5b36b4118ff7 --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_arbitrary_source_index/stdin @@ -0,0 +1,6 @@ +/// @use-src 2:"L0.sol" +object "L0" { + code { + sstore(0, datasize("L0")) + } +} diff --git a/test/cmdlineTests/strict_asm_asm_json_subobjects_with_debug/args b/test/cmdlineTests/strict_asm_asm_json_subobjects_with_debug/args new file mode 100644 index 000000000000..bf9bf8851a8c --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_subobjects_with_debug/args @@ -0,0 +1 @@ +--strict-assembly - --asm-json --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/strict_asm_asm_json_subobjects_with_debug/output b/test/cmdlineTests/strict_asm_asm_json_subobjects_with_debug/output new file mode 100644 index 000000000000..dfa7656834c8 --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_subobjects_with_debug/output @@ -0,0 +1,426 @@ + +======= (EVM) ======= + +EVM assembly: +{ + ".code": [ + { + "begin": 41, + "end": 137, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 41, + "end": 137, + "name": "DUP1", + "source": 0 + }, + { + "begin": 41, + "end": 137, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 41, + "end": 137, + "name": "PUSHSIZE", + "source": 0 + }, + { + "begin": 41, + "end": 137, + "name": "PUSH", + "source": 0, + "value": "1" + }, + { + "begin": 41, + "end": 137, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 41, + "end": 137, + "name": "PUSH [$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 41, + "end": 137, + "name": "PUSH", + "source": 0, + "value": "2" + }, + { + "begin": 41, + "end": 137, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 41, + "end": 137, + "name": "PUSH #[$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 41, + "end": 137, + "name": "PUSH", + "source": 0, + "value": "3" + }, + { + "begin": 41, + "end": 137, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 41, + "end": 137, + "name": "PUSH [$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000ffffffffffffffff" + }, + { + "begin": 41, + "end": 137, + "name": "PUSH", + "source": 0, + "value": "4" + }, + { + "begin": 41, + "end": 137, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 41, + "end": 137, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000ffffffffffffffff" + }, + { + "begin": 41, + "end": 137, + "name": "PUSH", + "source": 0, + "value": "5" + }, + { + "begin": 41, + "end": 137, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 41, + "end": 137, + "name": "PUSH [$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffe" + }, + { + "begin": 41, + "end": 137, + "name": "PUSH", + "source": 0, + "value": "6" + }, + { + "begin": 41, + "end": 137, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 41, + "end": 137, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffe" + }, + { + "begin": 41, + "end": 137, + "name": "PUSH", + "source": 0, + "value": "7" + }, + { + "begin": 41, + "end": 137, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 41, + "end": 137, + "name": "PUSH [$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffd" + }, + { + "begin": 41, + "end": 137, + "name": "PUSH", + "source": 0, + "value": "8" + }, + { + "begin": 41, + "end": 137, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 41, + "end": 137, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffd" + }, + { + "begin": 41, + "end": 137, + "name": "PUSH", + "source": 0, + "value": "9" + }, + { + "begin": 41, + "end": 137, + "name": "SSTORE", + "source": 0 + }, + { + "begin": -1, + "end": -1, + "name": "STOP", + "source": -1 + } + ], + ".data": { + "0": { + ".code": [ + { + "begin": 57, + "end": 158, + "name": "PUSH [$]", + "source": 1, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 57, + "end": 158, + "name": "PUSH", + "source": 1, + "value": "A" + }, + { + "begin": 57, + "end": 158, + "name": "SSTORE", + "source": 1 + }, + { + "begin": 57, + "end": 158, + "name": "PUSH #[$]", + "source": 1, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 57, + "end": 158, + "name": "PUSH", + "source": 1, + "value": "B" + }, + { + "begin": 57, + "end": 158, + "name": "SSTORE", + "source": 1 + }, + { + "begin": 57, + "end": 158, + "name": "PUSH [$]", + "source": 1, + "value": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "begin": 57, + "end": 158, + "name": "PUSH", + "source": 1, + "value": "C" + }, + { + "begin": 57, + "end": 158, + "name": "SSTORE", + "source": 1 + }, + { + "begin": 57, + "end": 158, + "name": "PUSH #[$]", + "source": 1, + "value": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "begin": 57, + "end": 158, + "name": "PUSH", + "source": 1, + "value": "D" + }, + { + "begin": 57, + "end": 158, + "name": "SSTORE", + "source": 1 + }, + { + "begin": 57, + "end": 158, + "name": "PUSH [$]", + "source": 1, + "value": "000000000000000000000000000000000000000000000000ffffffffffffffff" + }, + { + "begin": 57, + "end": 158, + "name": "PUSH", + "source": 1, + "value": "E" + }, + { + "begin": 57, + "end": 158, + "name": "SSTORE", + "source": 1 + }, + { + "begin": 57, + "end": 158, + "name": "PUSH #[$]", + "source": 1, + "value": "000000000000000000000000000000000000000000000000ffffffffffffffff" + }, + { + "begin": 57, + "end": 158, + "name": "PUSH", + "source": 1, + "value": "F" + }, + { + "begin": 57, + "end": 158, + "name": "SSTORE", + "source": 1 + }, + { + "begin": -1, + "end": -1, + "name": "STOP", + "source": -1 + } + ], + ".data": { + "0": { + ".code": [ + { + "begin": 41, + "end": 100, + "name": "PUSH [$]", + "source": 2, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 41, + "end": 100, + "name": "PUSH", + "source": 2, + "value": "10" + }, + { + "begin": 41, + "end": 100, + "name": "SSTORE", + "source": 2 + }, + { + "begin": 41, + "end": 100, + "name": "PUSH #[$]", + "source": 2, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 41, + "end": 100, + "name": "PUSH", + "source": 2, + "value": "11" + }, + { + "begin": 41, + "end": 100, + "name": "SSTORE", + "source": 2 + }, + { + "begin": -1, + "end": -1, + "name": "STOP", + "source": -1 + } + ], + ".data": { + "0": { + ".code": [ + { + "begin": -1, + "end": -1, + "name": "INVALID", + "source": -1 + } + ] + } + } + }, + "1": { + ".code": [ + { + "begin": -1, + "end": -1, + "name": "INVALID", + "source": -1 + } + ] + } + } + }, + "ACAF3289D7B601CBD114FB36C4D29C85BBFD5E133F14CB355C3FD8D99367964F": "48656c6c6f2c20576f726c6421" + }, + "sourceList": [ + "a.sol", + "b.sol", + "c.sol", + "d.sol", + "e.sol" + ] +} diff --git a/test/cmdlineTests/strict_asm_asm_json_subobjects_with_debug/stdin b/test/cmdlineTests/strict_asm_asm_json_subobjects_with_debug/stdin new file mode 100644 index 000000000000..01db34d6fd2a --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_subobjects_with_debug/stdin @@ -0,0 +1,51 @@ +/// @use-src 0:"a.sol" +object "A" { + code { + /// @src 0:41:137 "contract A {..." + sstore(0, dataoffset("A")) + sstore(1, datasize("A")) + sstore(2, dataoffset("B")) + sstore(3, datasize("B")) + sstore(4, dataoffset("B.C")) + sstore(5, datasize("B.C")) + sstore(6, dataoffset("B.E")) + sstore(7, datasize("B.E")) + sstore(8, dataoffset("B.C.D")) + sstore(9, datasize("B.C.D")) + } + + data "data1" "Hello, World!" + + /// @use-src 1:"b.sol" + object "B" { + code { + /// @src 1:57:158 "contract B {..." + sstore(10, dataoffset("C")) + sstore(11, datasize("C")) + sstore(12, dataoffset("E")) + sstore(13, datasize("E")) + sstore(14, dataoffset("C.D")) + sstore(15, datasize("C.D")) + } + /// @use-src 2:"c.sol" + object "C" { + code { + /// @src 2:41:100 "contract C {..." + sstore(16, dataoffset("D")) + sstore(17, datasize("D")) + } + /// @use-src 3:"d.sol" + object "D" { + code { + invalid() + } + } + } + /// @use-src 4:"e.sol" + object "E" { + code { + invalid() + } + } + } +} diff --git a/test/cmdlineTests/strict_asm_asm_json_with_debug_info_annotations/args b/test/cmdlineTests/strict_asm_asm_json_with_debug_info_annotations/args new file mode 100644 index 000000000000..bf9bf8851a8c --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_with_debug_info_annotations/args @@ -0,0 +1 @@ +--strict-assembly - --asm-json --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/strict_asm_asm_json_with_debug_info_annotations/output b/test/cmdlineTests/strict_asm_asm_json_with_debug_info_annotations/output new file mode 100644 index 000000000000..85642e709a4a --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_with_debug_info_annotations/output @@ -0,0 +1,384 @@ + +======= (EVM) ======= + +EVM assembly: +{ + ".code": [ + { + "begin": 0, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "80" + }, + { + "begin": 0, + "end": 125, + "name": "DUP1", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "40" + }, + { + "begin": 0, + "end": 125, + "name": "MSTORE", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "CALLVALUE", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH [tag]", + "source": 0, + "value": "1" + }, + { + "begin": 0, + "end": 125, + "name": "JUMPI", + "source": 0 + }, + { + "begin": 56, + "end": 57, + "name": "PUSH", + "source": 0, + "value": "2" + }, + { + "begin": 33, + "end": 34, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 0, + "end": 125, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 33, + "end": 34, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 33, + "end": 34, + "name": "PUSH", + "source": 0, + "value": "1" + }, + { + "begin": 0, + "end": 125, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH #[$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 0, + "end": 125, + "name": "SWAP1", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "DUP2", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH [$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 0, + "end": 125, + "name": "DUP3", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "CODECOPY", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "RETURN", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "tag", + "source": 0, + "value": "1" + }, + { + "begin": 0, + "end": 125, + "name": "JUMPDEST", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 0, + "end": 125, + "name": "DUP1", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "REVERT", + "source": 0 + } + ], + ".data": { + "0": { + ".auxdata": "1234abcd", + ".code": [ + { + "begin": 0, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "80" + }, + { + "begin": 0, + "end": 125, + "name": "DUP1", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "40" + }, + { + "begin": 0, + "end": 125, + "name": "MSTORE", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "4" + }, + { + "begin": 0, + "end": 125, + "name": "CALLDATASIZE", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "LT", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "ISZERO", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH [tag]", + "source": 0, + "value": "1" + }, + { + "begin": 0, + "end": 125, + "name": "JUMPI", + "source": 0 + }, + { + "begin": -1, + "end": -1, + "name": "tag", + "source": -1, + "value": "2" + }, + { + "begin": -1, + "end": -1, + "name": "JUMPDEST", + "source": -1 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 0, + "end": 125, + "name": "DUP1", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "REVERT", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "tag", + "source": 0, + "value": "1" + }, + { + "begin": 0, + "end": 125, + "name": "JUMPDEST", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 0, + "end": 125, + "name": "CALLDATALOAD", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "E0" + }, + { + "begin": 0, + "end": 125, + "name": "SHR", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "B4F40C61" + }, + { + "begin": 0, + "end": 125, + "name": "SUB", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH [tag]", + "source": 0, + "value": "2" + }, + { + "begin": 0, + "end": 125, + "name": "JUMPI", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "20" + }, + { + "begin": 0, + "end": 125, + "name": "SWAP1", + "source": 0 + }, + { + "begin": 40, + "end": 57, + "name": "PUSH", + "source": 0, + "value": "1" + }, + { + "begin": 0, + "end": 125, + "name": "SLOAD", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "DUP2", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "MSTORE", + "source": 0 + }, + { + "begin": 0, + "end": 125, + "name": "RETURN", + "source": 0 + } + ] + } + }, + "sourceList": [ + "state_var_initialization.sol" + ] +} diff --git a/test/cmdlineTests/strict_asm_asm_json_with_debug_info_annotations/stdin b/test/cmdlineTests/strict_asm_asm_json_with_debug_info_annotations/stdin new file mode 100644 index 000000000000..d96989ec8393 --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_with_debug_info_annotations/stdin @@ -0,0 +1,40 @@ +/// @use-src 0:"state_var_initialization.sol" +object "C_23" { + code { + { + /// @src 0:0:125 "contract C {..." + let _1 := memoryguard(0x80) + mstore(64, _1) + if callvalue() { revert(0, 0) } + sstore(/** @src 0:33:34 "1" */ 0x00, /** @src 0:56:57 "2" */ 0x02) + /// @src 0:0:125 "contract C {..." + sstore(/** @src 0:33:34 "1" */ 0x01, 0x00) + /// @src 0:0:125 "contract C {..." + let _2 := datasize("C_23_deployed") + codecopy(_1, dataoffset("C_23_deployed"), _2) + return(_1, _2) + } + } + /// @use-src 0:"state_var_initialization.sol" + object "C_23_deployed" { + code { + { + /// @src 0:0:125 "contract C {..." + let _1 := memoryguard(0x80) + mstore(64, _1) + if iszero(lt(calldatasize(), 4)) + { + let _2 := 0 + switch shr(224, calldataload(_2)) + case 0xb4f40c61 { + mstore(_1, sload(/** @src 0:40:57 "uint public k = 2" */ 1)) + /// @src 0:0:125 "contract C {..." + return(_1, 32) + } + } + revert(0, 0) + } + } + data ".metadata" hex"1234abcd" + } +} diff --git a/test/cmdlineTests/strict_asm_asm_json_without_debug_info_annotations/args b/test/cmdlineTests/strict_asm_asm_json_without_debug_info_annotations/args new file mode 100644 index 000000000000..bf9bf8851a8c --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_without_debug_info_annotations/args @@ -0,0 +1 @@ +--strict-assembly - --asm-json --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/strict_asm_asm_json_without_debug_info_annotations/output b/test/cmdlineTests/strict_asm_asm_json_without_debug_info_annotations/output new file mode 100644 index 000000000000..25fb482d484e --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_without_debug_info_annotations/output @@ -0,0 +1,384 @@ + +======= (EVM) ======= + +EVM assembly: +{ + ".code": [ + { + "begin": -1, + "end": -1, + "name": "PUSH", + "source": -1, + "value": "80" + }, + { + "begin": -1, + "end": -1, + "name": "DUP1", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "PUSH", + "source": -1, + "value": "40" + }, + { + "begin": -1, + "end": -1, + "name": "MSTORE", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "CALLVALUE", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "PUSH [tag]", + "source": -1, + "value": "1" + }, + { + "begin": -1, + "end": -1, + "name": "JUMPI", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "PUSH", + "source": -1, + "value": "2" + }, + { + "begin": -1, + "end": -1, + "name": "PUSH", + "source": -1, + "value": "0" + }, + { + "begin": -1, + "end": -1, + "name": "SSTORE", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "PUSH", + "source": -1, + "value": "0" + }, + { + "begin": -1, + "end": -1, + "name": "PUSH", + "source": -1, + "value": "1" + }, + { + "begin": -1, + "end": -1, + "name": "SSTORE", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "PUSH #[$]", + "source": -1, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": -1, + "end": -1, + "name": "SWAP1", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "DUP2", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "PUSH [$]", + "source": -1, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": -1, + "end": -1, + "name": "DUP3", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "CODECOPY", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "RETURN", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "tag", + "source": -1, + "value": "1" + }, + { + "begin": -1, + "end": -1, + "name": "JUMPDEST", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "PUSH", + "source": -1, + "value": "0" + }, + { + "begin": -1, + "end": -1, + "name": "DUP1", + "source": -1 + }, + { + "begin": -1, + "end": -1, + "name": "REVERT", + "source": -1 + } + ], + ".data": { + "0": { + ".auxdata": "1234abcd", + ".code": [ + { + "begin": 469, + "end": 486, + "name": "PUSH", + "source": -1, + "value": "80" + }, + { + "begin": 503, + "end": 517, + "name": "DUP1", + "source": -1 + }, + { + "begin": 510, + "end": 512, + "name": "PUSH", + "source": -1, + "value": "40" + }, + { + "begin": 503, + "end": 517, + "name": "MSTORE", + "source": -1 + }, + { + "begin": 563, + "end": 564, + "name": "PUSH", + "source": -1, + "value": "4" + }, + { + "begin": 547, + "end": 561, + "name": "CALLDATASIZE", + "source": -1 + }, + { + "begin": 544, + "end": 565, + "name": "LT", + "source": -1 + }, + { + "begin": 537, + "end": 566, + "name": "ISZERO", + "source": -1 + }, + { + "begin": 534, + "end": 832, + "name": "PUSH [tag]", + "source": -1, + "value": "1" + }, + { + "begin": 534, + "end": 832, + "name": "JUMPI", + "source": -1 + }, + { + "begin": 427, + "end": 885, + "name": "tag", + "source": -1, + "value": "2" + }, + { + "begin": 427, + "end": 885, + "name": "JUMPDEST", + "source": -1 + }, + { + "begin": 859, + "end": 860, + "name": "PUSH", + "source": -1, + "value": "0" + }, + { + "begin": 849, + "end": 861, + "name": "DUP1", + "source": -1 + }, + { + "begin": 849, + "end": 861, + "name": "REVERT", + "source": -1 + }, + { + "begin": 583, + "end": 832, + "name": "tag", + "source": -1, + "value": "1" + }, + { + "begin": 583, + "end": 832, + "name": "JUMPDEST", + "source": -1 + }, + { + "begin": 615, + "end": 616, + "name": "PUSH", + "source": -1, + "value": "0" + }, + { + "begin": 653, + "end": 669, + "name": "CALLDATALOAD", + "source": -1 + }, + { + "begin": 648, + "end": 651, + "name": "PUSH", + "source": -1, + "value": "E0" + }, + { + "begin": 644, + "end": 670, + "name": "SHR", + "source": -1 + }, + { + "begin": 696, + "end": 706, + "name": "PUSH", + "source": -1, + "value": "B4F40C61" + }, + { + "begin": 691, + "end": 814, + "name": "SUB", + "source": -1 + }, + { + "begin": 583, + "end": 832, + "name": "PUSH [tag]", + "source": -1, + "value": "2" + }, + { + "begin": 691, + "end": 814, + "name": "JUMPI", + "source": -1 + }, + { + "begin": 789, + "end": 791, + "name": "PUSH", + "source": -1, + "value": "20" + }, + { + "begin": 744, + "end": 752, + "name": "SWAP1", + "source": -1 + }, + { + "begin": 750, + "end": 751, + "name": "PUSH", + "source": -1, + "value": "1" + }, + { + "begin": 744, + "end": 752, + "name": "SLOAD", + "source": -1 + }, + { + "begin": 733, + "end": 753, + "name": "DUP2", + "source": -1 + }, + { + "begin": 733, + "end": 753, + "name": "MSTORE", + "source": -1 + }, + { + "begin": 778, + "end": 792, + "name": "RETURN", + "source": -1 + } + ] + } + }, + "sourceList": [ + "abc.sol" + ] +} diff --git a/test/cmdlineTests/strict_asm_asm_json_without_debug_info_annotations/stdin b/test/cmdlineTests/strict_asm_asm_json_without_debug_info_annotations/stdin new file mode 100644 index 000000000000..72c1b650c360 --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_without_debug_info_annotations/stdin @@ -0,0 +1,34 @@ +/// @use-src 0: "abc.sol" +object "C_23" { + code { + { + let _1 := memoryguard(0x80) + mstore(64, _1) + if callvalue() { revert(0, 0) } + sstore(0x00, 0x02) + sstore(0x01, 0x00) + let _2 := datasize("C_23_deployed") + codecopy(_1, dataoffset("C_23_deployed"), _2) + return(_1, _2) + } + } + object "C_23_deployed" { + code { + { + let _1 := memoryguard(0x80) + mstore(64, _1) + if iszero(lt(calldatasize(), 4)) + { + let _2 := 0 + switch shr(224, calldataload(_2)) + case 0xb4f40c61 { + mstore(_1, sload(1)) + return(_1, 32) + } + } + revert(0, 0) + } + } + data ".metadata" hex"1234abcd" + } +} diff --git a/test/cmdlineTests/strict_asm_asm_json_yul_verbatim/args b/test/cmdlineTests/strict_asm_asm_json_yul_verbatim/args new file mode 100644 index 000000000000..bf9bf8851a8c --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_yul_verbatim/args @@ -0,0 +1 @@ +--strict-assembly - --asm-json --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/strict_asm_asm_json_yul_verbatim/output b/test/cmdlineTests/strict_asm_asm_json_yul_verbatim/output new file mode 100644 index 000000000000..454f4a6d2d7b --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_yul_verbatim/output @@ -0,0 +1,18 @@ + +======= (EVM) ======= + +EVM assembly: +{ + ".code": [ + { + "begin": -1, + "end": -1, + "name": "VERBATIM", + "source": -1, + "value": "78797a" + } + ], + "sourceList": [ + "abc.sol" + ] +} diff --git a/test/cmdlineTests/strict_asm_asm_json_yul_verbatim/stdin b/test/cmdlineTests/strict_asm_asm_json_yul_verbatim/stdin new file mode 100644 index 000000000000..2dab36854801 --- /dev/null +++ b/test/cmdlineTests/strict_asm_asm_json_yul_verbatim/stdin @@ -0,0 +1,4 @@ +/// @use-src 0: "abc.sol" +{ + verbatim_0i_0o("xyz") +} diff --git a/test/cmdlineTests/strict_asm_output_selection_invalid/err b/test/cmdlineTests/strict_asm_output_selection_invalid/err index 81d6eff8ae72..90fd9470461f 100644 --- a/test/cmdlineTests/strict_asm_output_selection_invalid/err +++ b/test/cmdlineTests/strict_asm_output_selection_invalid/err @@ -1 +1 @@ -Error: The following outputs are not supported in assembler mode: --abi, --asm-json, --bin-runtime, --devdoc, --hashes, --ir, --metadata, --opcodes, --storage-layout, --userdoc. +Error: The following outputs are not supported in assembler mode: --abi, --bin-runtime, --devdoc, --hashes, --ir, --metadata, --opcodes, --storage-layout, --userdoc. diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index 65ff18a768de..5d6dd1b23dea 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -26,8 +26,9 @@ #include -#include +#include #include +#include #include #include @@ -83,7 +84,7 @@ TestCase::TestResult ObjectCompilerTest::run(std::ostream& _stream, std::string solAssert(obj.bytecode, ""); solAssert(obj.sourceMappings, ""); - m_obtainedResult = "Assembly:\n" + obj.assembly; + m_obtainedResult = "Assembly:\n" + obj.assembly->assemblyString(stack.debugInfoSelection()); if (obj.bytecode->bytecode.empty()) m_obtainedResult += "-- empty bytecode --\n"; else diff --git a/test/libyul/objectCompiler/sourceLocations.yul b/test/libyul/objectCompiler/sourceLocations.yul index 1c84773249d7..883e85b040e6 100644 --- a/test/libyul/objectCompiler/sourceLocations.yul +++ b/test/libyul/objectCompiler/sourceLocations.yul @@ -1,28 +1,28 @@ // something else /*-- another unrelated comment --*/ -/// @use-src 3: "abc.sol" , 2: "def.sol" +/// @use-src 1: "abc.sol" , 0: "def.sol" object "a" { code { - /// @src 3:0:2 + /// @src 1:0:2 datacopy(0, dataoffset("sub"), datasize("sub")) return(0, - /** @src 2:5:6 */ + /** @src 0:5:6 */ datasize("sub") ) } - /// @use-src 3: "abc.sol" , 2: "def.sol" + /// @use-src 1: "abc.sol" , 0: "def.sol" object "sub" { code { - /// @src 2:70:72 + /// @src 0:70:72 sstore(0, dataoffset("sub")) /** * @something else - * @src 3:2:5 + * @src 1:2:5 */ mstore( 0, datasize("data1") - /// @src 3:90:2 + /// @src 1:90:2 ) } data "data1" "Hello, World!" diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 9fe24bcc662f..2ba1d43c8746 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -310,6 +310,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) "dir1/file1.sol:L=0x1234567890123456789012345678901234567890," "dir2/file2.sol:L=0x1111122222333334444455555666667777788888", "--asm", + "--asm-json", "--bin", "--ir-optimized", "--ast-compact-json", @@ -350,6 +351,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) expectedOptions.formatting.coloredOutput = false; expectedOptions.formatting.withErrorIds = true; expectedOptions.compiler.outputs.asm_ = true; + expectedOptions.compiler.outputs.asmJson = true; expectedOptions.compiler.outputs.binary = true; expectedOptions.compiler.outputs.irOptimized = true; expectedOptions.compiler.outputs.astCompactJson = true;