diff --git a/.circleci/config.yml b/.circleci/config.yml index 9165e7ad8c56..7ec500e2e823 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1123,7 +1123,7 @@ jobs: t_osx_cli: <<: *base_osx - parallelism: 7 # Should match number of tests in .circleci/cli.sh + parallelism: 8 # Should match number of tests in .circleci/parallel_cli_tests.py steps: - checkout - install_dependencies_osx @@ -1218,7 +1218,7 @@ jobs: t_ubu_cli: &t_ubu_cli <<: *base_ubuntu2204_small - parallelism: 7 # Should match number of tests in .circleci/cli.sh + parallelism: 8 # Should match number of tests in .circleci/parallel_cli_tests.py steps: - cmdline_tests @@ -1237,7 +1237,7 @@ jobs: t_ubu_asan_cli: # Runs slightly faster on medium but we only run it nightly so efficiency matters more. <<: *base_ubuntu2204 - parallelism: 7 # Should match number of tests in .circleci/cli.sh + parallelism: 8 # Should match number of tests in .circleci/parallel_cli_tests.py environment: <<: *base_ubuntu2204_env ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2 @@ -1286,7 +1286,7 @@ jobs: t_ubu_ubsan_clang_cli: <<: *base_ubuntu2204_clang - parallelism: 7 # Should match number of tests in .circleci/cli.sh + parallelism: 8 # Should match number of tests in .circleci/parallel_cli_tests.py steps: - cmdline_tests diff --git a/.circleci/parallel_cli_tests.py b/.circleci/parallel_cli_tests.py index ef789218063a..45cc83aec318 100755 --- a/.circleci/parallel_cli_tests.py +++ b/.circleci/parallel_cli_tests.py @@ -8,6 +8,7 @@ # TODO: We should switch to time-based splitting but that requires JUnit XML report support in cmdlineTests.sh. tests_to_run_in_parallel = [ '~ast_import_export', # ~7 min + '~evmasm_import_export', # ~5 min '~ast_export_with_stop_after_parsing', # ~4 min '~soljson_via_fuzzer', # ~3 min '~via_ir_equivalence', # ~1 min diff --git a/Changelog.md b/Changelog.md index 005e3aac5531..279fbec6348f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Language Features: Compiler Features: * Code Generator: Remove redundant overflow checks of certain ``for`` loops when the counter variable cannot overflow. * Commandline Interface: Add ``--no-import-callback`` option that prevents the compiler from loading source files not given explicitly on the CLI or in Standard JSON input. + * Commandline Interface: Add an experimental ``--import-asm-json`` option that can import EVM assembly in the format used by ``--asm-json``. * Commandline Interface: Use proper severity and coloring also for error messages produced outside of the compilation pipeline. * EVM: Deprecate support for "homestead", "tangerineWhistle", "spuriousDragon" and "byzantium" EVM versions. * Parser: Remove the experimental error recovery mode (``--error-recovery`` / ``settings.parserErrorRecovery``). diff --git a/libevmasm/AbstractAssemblyStack.h b/libevmasm/AbstractAssemblyStack.h new file mode 100644 index 000000000000..6be78cbc7cac --- /dev/null +++ b/libevmasm/AbstractAssemblyStack.h @@ -0,0 +1,54 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include +#include + +#include +#include + +namespace solidity::evmasm +{ + +class AbstractAssemblyStack +{ +public: + virtual ~AbstractAssemblyStack() {} + + virtual LinkerObject const& object(std::string const& _contractName) const = 0; + virtual LinkerObject const& runtimeObject(std::string const& _contractName) const = 0; + + virtual std::string const* sourceMapping(std::string const& _contractName) const = 0; + virtual std::string const* runtimeSourceMapping(std::string const& _contractName) const = 0; + + virtual Json::Value assemblyJSON(std::string const& _contractName) const = 0; + virtual std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const = 0; + + virtual std::string const filesystemFriendlyName(std::string const& _contractName) const = 0; + + virtual std::vector contractNames() const = 0; + virtual std::vector sourceNames() const = 0; + + virtual bool compilationSuccessful() const = 0; +}; + +} // namespace solidity::evmasm diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index f524aad66b8a..b6e4d0006c57 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -34,19 +34,27 @@ #include #include -#include +#include +#include + +#include #include +#include #include +#include #include #include +#include using namespace solidity; using namespace solidity::evmasm; using namespace solidity::langutil; using namespace solidity::util; +std::map> Assembly::s_sharedSourceNames; + AssemblyItem const& Assembly::append(AssemblyItem _i) { assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow."); @@ -73,6 +81,205 @@ unsigned Assembly::codeSize(unsigned subTagSize) const } } +void Assembly::importAssemblyItemsFromJSON(Json::Value const& _code, std::vector const& _sourceList) +{ + solAssert(m_items.empty()); + solRequire(_code.isArray(), AssemblyImportException, "Supplied JSON is not an array."); + for (auto jsonItemIter = std::begin(_code); jsonItemIter != std::end(_code); ++jsonItemIter) + { + AssemblyItem const& newItem = m_items.emplace_back(createAssemblyItemFromJSON(*jsonItemIter, _sourceList)); + if (newItem == Instruction::JUMPDEST) + solThrow(AssemblyImportException, "JUMPDEST instruction without a tag"); + else if (newItem.type() == AssemblyItemType::Tag) + { + ++jsonItemIter; + if (jsonItemIter != std::end(_code) && createAssemblyItemFromJSON(*jsonItemIter, _sourceList) != Instruction::JUMPDEST) + solThrow(AssemblyImportException, "JUMPDEST expected after tag."); + } + } +} + +AssemblyItem Assembly::createAssemblyItemFromJSON(Json::Value const& _json, std::vector const& _sourceList) +{ + solRequire(_json.isObject(), AssemblyImportException, "Supplied JSON is not an object."); + static std::set const validMembers{"name", "begin", "end", "source", "value", "modifierDepth", "jumpType"}; + for (std::string const& member: _json.getMemberNames()) + solRequire( + validMembers.count(member), + AssemblyImportException, + fmt::format( + "Unknown member '{}'. Valid members are: {}.", + member, + solidity::util::joinHumanReadable(validMembers, ", ") + ) + ); + solRequire(isOfType(_json["name"]), AssemblyImportException, "Member 'name' missing or not of type string."); + solRequire(isOfTypeIfExists(_json, "begin"), AssemblyImportException, "Optional member 'begin' not of type int."); + solRequire(isOfTypeIfExists(_json, "end"), AssemblyImportException, "Optional member 'end' not of type int."); + solRequire(isOfTypeIfExists(_json, "source"), AssemblyImportException, "Optional member 'source' not of type int."); + solRequire(isOfTypeIfExists(_json, "value"), AssemblyImportException, "Optional member 'value' not of type string."); + solRequire(isOfTypeIfExists(_json, "modifierDepth"), AssemblyImportException, "Optional member 'modifierDepth' not of type int."); + solRequire(isOfTypeIfExists(_json, "jumpType"), AssemblyImportException, "Optional member 'jumpType' not of type string."); + + std::string name = get(_json["name"]); + solRequire(!name.empty(), AssemblyImportException, "Member 'name' is empty."); + + SourceLocation location; + location.start = get(_json["begin"]); + location.end = get(_json["end"]); + int srcIndex = getOrDefault(_json["source"], -1); + size_t modifierDepth = static_cast(getOrDefault(_json["modifierDepth"], 0)); + std::string value = getOrDefault(_json["value"], ""); + std::string jumpType = getOrDefault(_json["jumpType"], ""); + + auto updateUsedTags = [&](u256 const& data) + { + m_usedTags = std::max(m_usedTags, static_cast(data) + 1); + return data; + }; + + auto storeImmutableHash = [&](std::string const& _immutableName) -> h256 + { + h256 hash(util::keccak256(_immutableName)); + solAssert(m_immutables.count(hash) == 0 || m_immutables[hash] == _immutableName); + m_immutables[hash] = _immutableName; + return hash; + }; + + auto storeLibraryHash = [&](std::string const& _libraryName) -> h256 + { + h256 hash(util::keccak256(_libraryName)); + solAssert(m_libraries.count(hash) == 0 || m_libraries[hash] == _libraryName); + m_libraries[hash] = _libraryName; + return hash; + }; + + auto requireValueDefinedForInstruction = [&](std::string const& _name, std::string const& _value) + { + solRequire( + !_value.empty(), + AssemblyImportException, + "Member 'value' is missing for instruction '" + _name + "', but the instruction needs a value." + ); + }; + + auto requireValueUndefinedForInstruction = [&](std::string const& _name, std::string const& _value) + { + solRequire( + _value.empty(), + AssemblyImportException, + "Member 'value' defined for instruction '" + _name + "', but the instruction does not need a value." + ); + }; + + solRequire(srcIndex >= -1 && srcIndex < static_cast(_sourceList.size()), AssemblyImportException, "Source index out of bounds."); + if (srcIndex != -1) + location.sourceName = sharedSourceName(_sourceList[static_cast(srcIndex)]); + + AssemblyItem result(0); + + if (c_instructions.count(name)) + { + AssemblyItem item{c_instructions.at(name), location}; + if (!jumpType.empty()) + { + if (item.instruction() == Instruction::JUMP || item.instruction() == Instruction::JUMPI) + { + std::optional parsedJumpType = AssemblyItem::parseJumpType(jumpType); + if (!parsedJumpType.has_value()) + solThrow(AssemblyImportException, "Invalid jump type."); + item.setJumpType(parsedJumpType.value()); + } + else + solThrow( + AssemblyImportException, + "Member 'jumpType' set on instruction different from JUMP or JUMPI (was set on instruction '" + name + "')" + ); + } + requireValueUndefinedForInstruction(name, value); + result = item; + } + else + { + solRequire( + jumpType.empty(), + AssemblyImportException, + "Member 'jumpType' set on instruction different from JUMP or JUMPI (was set on instruction '" + name + "')" + ); + if (name == "PUSH") + { + requireValueDefinedForInstruction(name, value); + result = {AssemblyItemType::Push, u256("0x" + value)}; + } + else if (name == "PUSH [ErrorTag]") + { + requireValueUndefinedForInstruction(name, value); + result = {AssemblyItemType::PushTag, 0}; + } + else if (name == "PUSH [tag]") + { + requireValueDefinedForInstruction(name, value); + result = {AssemblyItemType::PushTag, updateUsedTags(u256(value))}; + } + else if (name == "PUSH [$]") + { + requireValueDefinedForInstruction(name, value); + result = {AssemblyItemType::PushSub, u256("0x" + value)}; + } + else if (name == "PUSH #[$]") + { + requireValueDefinedForInstruction(name, value); + result = {AssemblyItemType::PushSubSize, u256("0x" + value)}; + } + else if (name == "PUSHSIZE") + { + requireValueUndefinedForInstruction(name, value); + result = {AssemblyItemType::PushProgramSize, 0}; + } + else if (name == "PUSHLIB") + { + requireValueDefinedForInstruction(name, value); + result = {AssemblyItemType::PushLibraryAddress, storeLibraryHash(value)}; + } + else if (name == "PUSHDEPLOYADDRESS") + { + requireValueUndefinedForInstruction(name, value); + result = {AssemblyItemType::PushDeployTimeAddress, 0}; + } + else if (name == "PUSHIMMUTABLE") + { + requireValueDefinedForInstruction(name, value); + result = {AssemblyItemType::PushImmutable, storeImmutableHash(value)}; + } + else if (name == "ASSIGNIMMUTABLE") + { + requireValueDefinedForInstruction(name, value); + result = {AssemblyItemType::AssignImmutable, storeImmutableHash(value)}; + } + else if (name == "tag") + { + requireValueDefinedForInstruction(name, value); + result = {AssemblyItemType::Tag, updateUsedTags(u256(value))}; + } + else if (name == "PUSH data") + { + requireValueDefinedForInstruction(name, value); + result = {AssemblyItemType::PushData, u256("0x" + value)}; + } + else if (name == "VERBATIM") + { + requireValueDefinedForInstruction(name, value); + AssemblyItem item(fromHex(value), 0, 0); + result = item; + } + else + solThrow(InvalidOpcode, "Invalid opcode: " + name); + } + result.setLocation(location); + result.m_modifierDepth = modifierDepth; + return result; +} + namespace { @@ -297,6 +504,154 @@ Json::Value Assembly::assemblyJSON(std::map const& _sourc return root; } +std::pair, std::vector> Assembly::fromJSON( + Json::Value const& _json, + std::vector const& _sourceList, + size_t _level +) +{ + solRequire(_json.isObject(), AssemblyImportException, "Supplied JSON is not an object."); + static std::set const validMembers{".code", ".data", ".auxdata", "sourceList"}; + for (std::string const& attribute: _json.getMemberNames()) + solRequire(validMembers.count(attribute), AssemblyImportException, "Unknown attribute '" + attribute + "'."); + + if (_level == 0) + { + if (_json.isMember("sourceList")) + { + solRequire(_json["sourceList"].isArray(), AssemblyImportException, "Optional member 'sourceList' is not an array."); + for (Json::Value const& sourceName: _json["sourceList"]) + solRequire(sourceName.isString(), AssemblyImportException, "The 'sourceList' array contains an item that is not a string."); + } + } + else + solRequire( + !_json.isMember("sourceList"), + AssemblyImportException, + "Member 'sourceList' may only be present in the root JSON object." + ); + + auto result = std::make_shared(EVMVersion{}, _level == 0 /* _creation */, "" /* _name */); + std::vector parsedSourceList; + if (_json.isMember("sourceList")) + { + solAssert(_level == 0); + solAssert(_sourceList.empty()); + for (Json::Value const& sourceName: _json["sourceList"]) + { + solRequire( + std::find(parsedSourceList.begin(), parsedSourceList.end(), sourceName.asString()) == parsedSourceList.end(), + AssemblyImportException, + "Items in 'sourceList' array are not unique." + ); + parsedSourceList.emplace_back(sourceName.asString()); + } + } + + solRequire(_json.isMember(".code"), AssemblyImportException, "Member '.code' is missing."); + solRequire(_json[".code"].isArray(), AssemblyImportException, "Member '.code' is not an array."); + for (Json::Value const& codeItem: _json[".code"]) + solRequire(codeItem.isObject(), AssemblyImportException, "The '.code' array contains an item that is not an object."); + + result->importAssemblyItemsFromJSON(_json[".code"], _level == 0 ? parsedSourceList : _sourceList); + + if (_json[".auxdata"]) + { + solRequire(_json[".auxdata"].isString(), AssemblyImportException, "Optional member '.auxdata' is not a string."); + result->m_auxiliaryData = fromHex(_json[".auxdata"].asString()); + solRequire(!result->m_auxiliaryData.empty(), AssemblyImportException, "Optional member '.auxdata' is not a valid hexadecimal string."); + } + + if (_json.isMember(".data")) + { + solRequire(_json[".data"].isObject(), AssemblyImportException, "Optional member '.data' is not an object."); + Json::Value const& data = _json[".data"]; + std::map> subAssemblies; + for (Json::ValueConstIterator dataIter = data.begin(); dataIter != data.end(); dataIter++) + { + solAssert(dataIter.key().isString()); + std::string dataItemID = dataIter.key().asString(); + Json::Value const& dataItem = data[dataItemID]; + if (dataItem.isString()) + { + solRequire( + dataItem.asString().empty() || !fromHex(dataItem.asString()).empty(), + AssemblyImportException, + "The value for key '" + dataItemID + "' inside '.data' is not a valid hexadecimal string." + ); + result->m_data[h256(fromHex(dataItemID))] = fromHex(dataItem.asString()); + } + else if (dataItem.isObject()) + { + size_t index{}; + try + { + // Using signed variant because stoul() still accepts negative numbers and + // just lets them wrap around. + int parsedDataItemID = std::stoi(dataItemID, nullptr, 16); + solRequire(parsedDataItemID >= 0, AssemblyImportException, "The key '" + dataItemID + "' inside '.data' is out of the supported integer range."); + index = static_cast(parsedDataItemID); + } + catch (std::invalid_argument const&) + { + solThrow(AssemblyImportException, "The key '" + dataItemID + "' inside '.data' is not an integer."); + } + catch (std::out_of_range const&) + { + solThrow(AssemblyImportException, "The key '" + dataItemID + "' inside '.data' is out of the supported integer range."); + } + + auto [subAssembly, emptySourceList] = Assembly::fromJSON(dataItem, _level == 0 ? parsedSourceList : _sourceList, _level + 1); + solAssert(subAssembly); + solAssert(emptySourceList.empty()); + solAssert(subAssemblies.count(index) == 0); + subAssemblies[index] = subAssembly; + } + else + solThrow(AssemblyImportException, "The value of key '" + dataItemID + "' inside '.data' is neither a hex string nor an object."); + } + + if (!subAssemblies.empty()) + solRequire( + ranges::max(subAssemblies | ranges::views::keys) == subAssemblies.size() - 1, + AssemblyImportException, + fmt::format( + "Invalid subassembly indices in '.data'. Not all numbers between 0 and {} are present.", + subAssemblies.size() - 1 + ) + ); + + result->m_subs = subAssemblies | ranges::views::values | ranges::to; + } + + if (_level == 0) + result->encodeAllPossibleSubPathsInAssemblyTree(); + + return std::make_pair(result, _level == 0 ? parsedSourceList : std::vector{}); +} + +void Assembly::encodeAllPossibleSubPathsInAssemblyTree(std::vector _pathFromRoot, std::vector _assembliesOnPath) +{ + _assembliesOnPath.push_back(this); + for (_pathFromRoot.push_back(0); _pathFromRoot.back() < m_subs.size(); ++_pathFromRoot.back()) + { + for (size_t distanceFromRoot = 0; distanceFromRoot < _assembliesOnPath.size(); ++distanceFromRoot) + _assembliesOnPath[distanceFromRoot]->encodeSubPath( + _pathFromRoot | ranges::views::drop_exactly(distanceFromRoot) | ranges::to + ); + + m_subs[_pathFromRoot.back()]->encodeAllPossibleSubPathsInAssemblyTree(_pathFromRoot, _assembliesOnPath); + } +} + +std::shared_ptr Assembly::sharedSourceName(std::string const& _name) const +{ + if (s_sharedSourceNames.find(_name) == s_sharedSourceNames.end()) + s_sharedSourceNames[_name] = std::make_shared(_name); + + return s_sharedSourceNames[_name]; +} + AssemblyItem Assembly::namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional _sourceID) { assertThrow(!_name.empty(), AssemblyException, "Empty named tag."); diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index bd37b3a7fd96..6e900282569b 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -150,10 +150,23 @@ class Assembly ) const; /// Create a JSON representation of the assembly. - Json::Value assemblyJSON( - std::map const& _sourceIndices = std::map(), - bool _includeSourceList = true - ) const; + Json::Value assemblyJSON(std::map const& _sourceIndices, bool _includeSourceList = true) const; + + /// Constructs an @a Assembly from the serialized JSON representation. + /// @param _json JSON object containing assembly in the format produced by assemblyJSON(). + /// @param _sourceList List of source files the assembly was built from. When the JSON represents + /// the root assembly, the function will read it from the 'sourceList' field and the parameter + /// must be empty. It is only used to pass the list down to recursive calls. + /// @param _level Nesting level of the current assembly in the assembly tree. The root is + /// at level 0 and the value increases down the tree. Necessary to distinguish between creation + /// and deployed objects. + /// @returns Created @a Assembly and the source list read from the 'sourceList' field of the root + /// assembly or an empty list (in recursive calls). + static std::pair, std::vector> fromJSON( + Json::Value const& _json, + std::vector const& _sourceList = {}, + size_t _level = 0 + ); /// Mark this assembly as invalid. Calling ``assemble`` on it will throw. void markAsInvalid() { m_invalid = true; } @@ -171,11 +184,27 @@ class Assembly unsigned codeSize(unsigned subTagSize) const; + /// Add all assembly items from given JSON array. This function imports the items by iterating through + /// the code array. This method only works on clean Assembly objects that don't have any items defined yet. + /// @param _json JSON array that contains assembly items (e.g. json['.code']) + /// @param _sourceList List of source names. + void importAssemblyItemsFromJSON(Json::Value const& _code, std::vector const& _sourceList); + + /// Creates an AssemblyItem from a given JSON representation. + /// @param _json JSON object that consists a single assembly item + /// @param _sourceList List of source names. + /// @returns AssemblyItem of _json argument. + AssemblyItem createAssemblyItemFromJSON(Json::Value const& _json, std::vector const& _sourceList); + private: bool m_invalid = false; Assembly const* subAssemblyById(size_t _subId) const; + void encodeAllPossibleSubPathsInAssemblyTree(std::vector _pathFromRoot = {}, std::vector _assembliesOnPath = {}); + + std::shared_ptr sharedSourceName(std::string const& _name) const; + protected: /// 0 is reserved for exception unsigned m_usedTags = 1; @@ -217,9 +246,11 @@ class Assembly /// Internal name of the assembly object, only used with the Yul backend /// currently std::string m_name; - langutil::SourceLocation m_currentSourceLocation; + // FIXME: This being static means that the strings won't be freed when they're no longer needed + static std::map> s_sharedSourceNames; + public: size_t m_currentModifierDepth = 0; }; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index 6c2b673c697d..2adaa023a13e 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -247,6 +247,18 @@ std::string AssemblyItem::getJumpTypeAsString() const } } +std::optional AssemblyItem::parseJumpType(std::string const& _jumpType) +{ + if (_jumpType == "[in]") + return JumpType::IntoFunction; + else if (_jumpType == "[out]") + return JumpType::OutOfFunction; + else if (_jumpType.empty()) + return JumpType::Ordinary; + + return std::nullopt; +} + std::string AssemblyItem::toAssemblyText(Assembly const& _assembly) const { std::string text; diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 077c1912d2a8..40f98dae875c 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -174,6 +174,7 @@ class AssemblyItem langutil::SourceLocation const& location() const { return m_location; } void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; } + static std::optional parseJumpType(std::string const& _jumpType); JumpType getJumpType() const { return m_jumpType; } std::string getJumpTypeAsString() const; diff --git a/libevmasm/CMakeLists.txt b/libevmasm/CMakeLists.txt index 6563b2c0fa13..c1918540e625 100644 --- a/libevmasm/CMakeLists.txt +++ b/libevmasm/CMakeLists.txt @@ -1,8 +1,11 @@ set(sources + AbstractAssemblyStack.h Assembly.cpp Assembly.h AssemblyItem.cpp AssemblyItem.h + EVMAssemblyStack.cpp + EVMAssemblyStack.h BlockDeduplicator.cpp BlockDeduplicator.h CommonSubexpressionEliminator.cpp diff --git a/libevmasm/EVMAssemblyStack.cpp b/libevmasm/EVMAssemblyStack.cpp new file mode 100644 index 000000000000..68f60808c9d5 --- /dev/null +++ b/libevmasm/EVMAssemblyStack.cpp @@ -0,0 +1,122 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include +#include + +#include +#include + +#include + +using namespace solidity::util; +using namespace solidity::langutil; +using namespace solidity::frontend; + +namespace solidity::evmasm +{ + +void EVMAssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source) +{ + solAssert(!m_evmAssembly); + m_name = _sourceName; + Json::Value assemblyJson; + solRequire(jsonParseStrict(_source, assemblyJson), AssemblyImportException, "Could not parse JSON file."); + std::tie(m_evmAssembly, m_sourceList) = evmasm::Assembly::fromJSON(assemblyJson); + solRequire(m_evmAssembly != nullptr, AssemblyImportException, "Could not create evm assembly object."); +} + +void EVMAssemblyStack::assemble() +{ + solAssert(m_evmAssembly); + solAssert(m_evmAssembly->isCreation()); + solAssert(!m_evmRuntimeAssembly); + + m_object = m_evmAssembly->assemble(); + m_sourceMapping = AssemblyItem::computeSourceMapping(m_evmAssembly->items(), sourceIndices()); + if (m_evmAssembly->numSubs() > 0) + { + m_evmRuntimeAssembly = std::make_shared(m_evmAssembly->sub(0)); + solAssert(m_evmRuntimeAssembly && !m_evmRuntimeAssembly->isCreation()); + m_runtimeSourceMapping = AssemblyItem::computeSourceMapping(m_evmRuntimeAssembly->items(), sourceIndices()); + m_runtimeObject = m_evmRuntimeAssembly->assemble(); + } +} + +LinkerObject const& EVMAssemblyStack::object(std::string const& _contractName) const +{ + solAssert(_contractName == m_name); + return m_object; +} + +LinkerObject const& EVMAssemblyStack::runtimeObject(std::string const& _contractName) const +{ + solAssert(_contractName == m_name); + return m_runtimeObject; +} + +std::map EVMAssemblyStack::sourceIndices() const +{ + solAssert(m_evmAssembly); + return m_sourceList + | ranges::views::enumerate + | ranges::views::transform([](auto const& _source) { return std::make_pair(_source.second, _source.first); }) + | ranges::to>; +} + +std::string const* EVMAssemblyStack::sourceMapping(std::string const& _contractName) const +{ + solAssert(_contractName == m_name); + return &m_sourceMapping; +} + +std::string const* EVMAssemblyStack::runtimeSourceMapping(std::string const& _contractName) const +{ + solAssert(_contractName == m_name); + return &m_runtimeSourceMapping; +} + +Json::Value EVMAssemblyStack::assemblyJSON(std::string const& _contractName) const +{ + solAssert(_contractName == m_name); + solAssert(m_evmAssembly); + return m_evmAssembly->assemblyJSON(sourceIndices()); +} + +std::string EVMAssemblyStack::assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const +{ + solAssert(_contractName == m_name); + solAssert(m_evmAssembly); + return m_evmAssembly->assemblyString(m_debugInfoSelection, _sourceCodes); +} + +std::string const EVMAssemblyStack::filesystemFriendlyName(std::string const& _contractName) const +{ + solAssert(_contractName == m_name); + return m_name; +} + +std::vector EVMAssemblyStack::sourceNames() const +{ + return m_sourceList; +} + +} // namespace solidity::evmasm diff --git a/libevmasm/EVMAssemblyStack.h b/libevmasm/EVMAssemblyStack.h new file mode 100644 index 000000000000..cf02cec52f2c --- /dev/null +++ b/libevmasm/EVMAssemblyStack.h @@ -0,0 +1,85 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include +#include + +#include + +#include +#include + +namespace solidity::evmasm +{ + +class EVMAssemblyStack: public AbstractAssemblyStack +{ +public: + explicit EVMAssemblyStack(langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) {} + + /// Runs parsing and analysis steps. + /// Multiple calls overwrite the previous state. + /// @throws AssemblyImportException, if JSON could not be validated. + void parseAndAnalyze(std::string const& _sourceName, std::string const& _source); + + void assemble(); + + std::string const& name() const { return m_name; } + + virtual LinkerObject const& object(std::string const& _contractName) const override; + virtual LinkerObject const& runtimeObject(std::string const& _contractName) const override; + + std::shared_ptr const& evmAssembly() const { return m_evmAssembly; } + std::shared_ptr const& evmRuntimeAssembly() const { return m_evmRuntimeAssembly; } + + virtual std::string const* sourceMapping(std::string const& _contractName) const override; + virtual std::string const* runtimeSourceMapping(std::string const& _contractName) const override; + + virtual Json::Value assemblyJSON(std::string const& _contractName) const override; + virtual std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const override; + + virtual std::string const filesystemFriendlyName(std::string const& _contractName) const override; + + virtual std::vector contractNames() const override { return {m_name}; } + virtual std::vector sourceNames() const override; + std::map sourceIndices() const; + + virtual bool compilationSuccessful() const override { return m_evmAssembly != nullptr; } + + void selectDebugInfo(langutil::DebugInfoSelection _debugInfoSelection) + { + m_debugInfoSelection = _debugInfoSelection; + } + +private: + langutil::EVMVersion m_evmVersion; + std::string m_name; + std::shared_ptr m_evmAssembly; + std::shared_ptr m_evmRuntimeAssembly; + evmasm::LinkerObject m_object; ///< Deployment object (includes the runtime sub-object). + evmasm::LinkerObject m_runtimeObject; ///< Runtime object. + std::vector m_sourceList; + langutil::DebugInfoSelection m_debugInfoSelection = langutil::DebugInfoSelection::Default(); + std::string m_sourceMapping; + std::string m_runtimeSourceMapping; +}; + +} // namespace solidity::evmasm diff --git a/libevmasm/Exceptions.h b/libevmasm/Exceptions.h index 02757757602e..ae6d1bf9c617 100644 --- a/libevmasm/Exceptions.h +++ b/libevmasm/Exceptions.h @@ -28,6 +28,7 @@ namespace solidity::evmasm { struct AssemblyException: virtual util::Exception {}; +struct AssemblyImportException: virtual AssemblyException {}; struct OptimizerException: virtual AssemblyException {}; struct StackTooDeepException: virtual OptimizerException {}; struct ItemNotAvailableException: virtual OptimizerException {}; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 3afe11fa82ef..14b335016395 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -74,7 +74,6 @@ #include #include -#include #include #include diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 06c78b686099..929e7e54f49a 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -41,6 +41,7 @@ #include #include +#include #include #include @@ -91,7 +92,7 @@ class Analysis; * It holds state and can be used to either step through the compilation stages (and abort e.g. * before compilation to bytecode) or run the whole compilation in one call. */ -class CompilerStack: public langutil::CharStreamProvider +class CompilerStack: public langutil::CharStreamProvider, public evmasm::AbstractAssemblyStack { public: /// Noncopyable. @@ -123,7 +124,7 @@ class CompilerStack: public langutil::CharStreamProvider /// Regular compilation from Solidity source files. Solidity, /// Compilation from an imported Solidity AST. - SolidityAST + SolidityAST, }; /// Creates a new compiler stack. @@ -139,6 +140,8 @@ class CompilerStack: public langutil::CharStreamProvider /// @returns the current state. State state() const { return m_stackState; } + virtual bool compilationSuccessful() const override { return m_stackState >= CompilationSuccessful; } + /// Resets the compiler to an empty state. Unless @a _keepSettings is set to true, /// all settings are reset as well. void reset(bool _keepSettings = false); @@ -234,7 +237,7 @@ class CompilerStack: public langutil::CharStreamProvider bool compile(State _stopAfter = State::CompilationSuccessful); /// @returns the list of sources (paths) used - std::vector sourceNames() const; + virtual std::vector sourceNames() const override; /// @returns a mapping assigning each source name its index inside the vector returned /// by sourceNames(). @@ -255,13 +258,13 @@ class CompilerStack: public langutil::CharStreamProvider std::vector const& unhandledSMTLib2Queries() const { return m_unhandledSMTLib2Queries; } /// @returns a list of the contract names in the sources. - std::vector contractNames() const; + virtual std::vector contractNames() const override; /// @returns the name of the last contract. If _sourceName is defined the last contract of that source will be returned. std::string const lastContractName(std::optional const& _sourceName = std::nullopt) const; /// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use - std::string const filesystemFriendlyName(std::string const& _contractName) const; + virtual std::string const filesystemFriendlyName(std::string const& _contractName) const override; /// @returns the IR representation of a contract. std::string const& yulIR(std::string const& _contractName) const; @@ -276,10 +279,10 @@ class CompilerStack: public langutil::CharStreamProvider Json::Value const& yulIROptimizedAst(std::string const& _contractName) const; /// @returns the assembled object for a contract. - evmasm::LinkerObject const& object(std::string const& _contractName) const; + virtual evmasm::LinkerObject const& object(std::string const& _contractName) const override; /// @returns the runtime object for the contract. - evmasm::LinkerObject const& runtimeObject(std::string const& _contractName) const; + virtual evmasm::LinkerObject const& runtimeObject(std::string const& _contractName) const override; /// @returns normal contract assembly items evmasm::AssemblyItems const* assemblyItems(std::string const& _contractName) const; @@ -293,21 +296,21 @@ class CompilerStack: public langutil::CharStreamProvider /// @returns the string that provides a mapping between bytecode and sourcecode or a nullptr /// if the contract does not (yet) have bytecode. - std::string const* sourceMapping(std::string const& _contractName) const; + virtual std::string const* sourceMapping(std::string const& _contractName) const override; /// @returns the string that provides a mapping between runtime bytecode and sourcecode. /// if the contract does not (yet) have bytecode. - std::string const* runtimeSourceMapping(std::string const& _contractName) const; + virtual std::string const* runtimeSourceMapping(std::string const& _contractName) const override; /// @return a verbose text representation of the assembly. /// @arg _sourceCodes is the map of input files to source code strings /// Prerequisite: Successful compilation. - std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes = StringMap()) const; + virtual std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes = StringMap()) const override; /// @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) const; + virtual Json::Value assemblyJSON(std::string const& _contractName) const override; /// @returns a JSON representing the contract ABI. /// Prerequisite: Successful call to parse or compile. diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index c245261951de..5c3f04d8e0ff 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -23,6 +23,13 @@ # ast import/export tests: # - first exporting a .sol file to JSON, then loading it into the compiler # and exporting it again. The second JSON should be identical to the first. +# +# evm-assembly import/export tests: +# - first a .sol file will be compiled and the EVM Assembly will be exported +# to JSON format using --asm-json command-line option. +# The EVM Assembly JSON output is then imported with --import-asm-json +# and compiled again. The binary generated initially and after the import +# should be identical. set -euo pipefail @@ -30,6 +37,11 @@ READLINK=readlink if [[ "$OSTYPE" == "darwin"* ]]; then READLINK=greadlink fi +EXPR="expr" +if [[ "$OSTYPE" == "darwin"* ]]; then + EXPR="gexpr" +fi + REPO_ROOT=$(${READLINK} -f "$(dirname "$0")"/..) SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-${REPO_ROOT}/build} SOLC="${SOLIDITY_BUILD_DIR}/solc/solc" @@ -37,10 +49,12 @@ SPLITSOURCES="${REPO_ROOT}/scripts/splitSources.py" # shellcheck source=scripts/common.sh source "${REPO_ROOT}/scripts/common.sh" +# shellcheck source=scripts/common_cmdline.sh +source "${REPO_ROOT}/scripts/common_cmdline.sh" function print_usage { - echo "Usage: ${0} ast [--exit-on-error|--help]." + echo "Usage: ${0} ast|evm-assembly [--exit-on-error|--help]." } function print_used_commands @@ -48,8 +62,9 @@ function print_used_commands local test_directory="$1" local export_command="$2" local import_command="$3" - printError "You can find the files used for this test here: ${test_directory}" - printError "Used commands for test:" + echo + printError "You can find the files used for this test in ${test_directory}" + printError "Commands used in the test:" printError "# export" echo "$ ${export_command}" >&2 printError "# import" @@ -81,6 +96,7 @@ for PARAM in "$@" do case "$PARAM" in ast) check_import_test_type_unset ; IMPORT_TEST_TYPE="ast" ;; + evm-assembly) check_import_test_type_unset ; IMPORT_TEST_TYPE="evm-assembly" ;; --help) print_usage ; exit 0 ;; --exit-on-error) EXIT_ON_ERROR=1 ;; *) fail "Unknown option '$PARAM'. Aborting. $(print_usage)" ;; @@ -89,6 +105,7 @@ done SYNTAXTESTS_DIR="${REPO_ROOT}/test/libsolidity/syntaxTests" ASTJSONTESTS_DIR="${REPO_ROOT}/test/libsolidity/ASTJSON" +SEMANTICTESTS_DIR="${REPO_ROOT}/test/libsolidity/semanticTests" FAILED=0 UNCOMPILABLE=0 @@ -153,6 +170,92 @@ function test_ast_import_export_equivalence TESTED=$((TESTED + 1)) } +function run_solc +{ + local parameters=( "${@}" ) + + if ! "${SOLC}" "${parameters[@]}" > /dev/null 2> solc_stderr + then + printError "ERROR: ${parameters[*]}" + printError "${PWD}" + # FIXME: EXIT_ON_ERROR seems to be ignored here and in some other places. + # We just exit unconditionally instead. + fail "$(cat solc_stderr)" + fi + rm solc_stderr +} + +function run_solc_store_stdout +{ + local output_file=$1 + local parameters=( "${@:2}" ) + + if ! "${SOLC}" "${parameters[@]}" > "${output_file}" 2> "${output_file}.error" + then + printError "ERROR: ${parameters[*]}" + printError "${PWD}" + fail "$(cat "${output_file}.error")" + fi + rm "${output_file}.error" +} + +function test_evmjson_import_export_equivalence +{ + local sol_file="$1" + local input_files=( "${@:2}" ) + + # Generate bytecode and EVM assembly JSON through normal complication + mkdir -p export/ + local export_options=(--bin --asm-json "${input_files[@]}" --output-dir export/) + run_solc "${export_options[@]}" + + # NOTE: If there is no bytecode, the compiler produces a JSON file that contains just 'null'. + # This is not accepted by assembly import though so we must skip such contracts. + echo -n null > null + find export/ -name '*.json' -exec cmp --quiet --bytes=4 {} null \; -delete + + find export/ -name '*.bin' -size 0 -delete + + for asm_json_file in export/*.json + do + mv "${asm_json_file}" "${asm_json_file/_evm/}" + done + + # Import EVM assembly JSON + mkdir -p import/ + for asm_json_file in export/*.json + do + local bin_file_from_asm_import + bin_file_from_asm_import="import/$(basename "${asm_json_file}" .json).bin" + + local import_options=(--bin --import-asm-json "${asm_json_file}") + run_solc_store_stdout "${bin_file_from_asm_import}" "${import_options[@]}" + + stripCLIDecorations < "$bin_file_from_asm_import" > tmpfile + mv tmpfile "$bin_file_from_asm_import" + done + + # Compare bytecode from compilation with bytecode from import + for bin_file in export/*.bin + do + local bin_file_from_asm_import=${bin_file/export/import} + if ! diff --strip-trailing-cr --ignore-all-space "${bin_file}" "${bin_file_from_asm_import}" > diff_error + then + printError "ERROR: Bytecode from compilation (${bin_file}) differs from bytecode from EVM asm import (${bin_file_from_asm_import}):" + printError " $(cat diff_error)" + if (( EXIT_ON_ERROR == 1 )) + then + # NOTE: The import_options we print here refers to the wrong file (the last one + # processed by the previous loop) - but it's still a good starting point for debugging ;) + print_used_commands "${PWD}" "${SOLC} ${export_options[*]}" "${SOLC} ${import_options[*]}" + return 1 + fi + FAILED=$((FAILED + 1)) + fi + done + TESTED=$((TESTED + 1)) +} + # function tests whether exporting and importing again is equivalent. # Results are recorded by incrementing the FAILED or UNCOMPILABLE global variable. # Also, in case of a mismatch a diff is printed @@ -168,6 +271,7 @@ function test_import_export_equivalence { case "$IMPORT_TEST_TYPE" in ast) compile_test="--ast-compact-json" ;; + evm-assembly) compile_test="--bin" ;; *) assertFail "Unknown import test type '${IMPORT_TEST_TYPE}'. Aborting." ;; esac @@ -181,6 +285,7 @@ function test_import_export_equivalence { then case "$IMPORT_TEST_TYPE" in ast) test_ast_import_export_equivalence "${sol_file}" "${input_files[@]}" ;; + evm-assembly) test_evmjson_import_export_equivalence "${sol_file}" "${input_files[@]}" ;; *) assertFail "Unknown import test type '${IMPORT_TEST_TYPE}'. Aborting." ;; esac else @@ -191,18 +296,30 @@ function test_import_export_equivalence { # and print some details about the corresponding solc invocation. if (( solc_return_code == 2 )) then - fail "\n\nERROR: Uncaught Exception while executing '$SOLC ${compile_test} ${input_files[*]}':\n${output}\n" + # For the evm-assembly import/export tests, this script uses only the old code generator. + # Some semantic tests can only be compiled with --via-ir (some need to be additionally + # compiled with --optimize). The tests that are meant to be compiled with --via-ir are + # throwing an UnimplementedFeatureError exception, e.g.: + # "Copying of type struct C.S memory[] memory to storage not yet supported", + # "Copying nested calldata dynamic arrays to storage is not implemented in the old code generator". + # We will just ignore these kind of exceptions for now. However, any other exception + # will be treated as a fatal error and the script execution will be terminated with an error. + if [[ "${output}" != *"UnimplementedFeatureError"* ]] + then + fail "\n\nERROR: Uncaught exception while executing '${SOLC} ${compile_test} ${input_files[*]}':\n${output}\n" + fi fi fi } -WORKINGDIR=$PWD - command_available "$SOLC" --version command_available jq --version +command_available "$EXPR" --version +command_available "$READLINK" --version case "$IMPORT_TEST_TYPE" in ast) TEST_DIRS=("${SYNTAXTESTS_DIR}" "${ASTJSONTESTS_DIR}") ;; + evm-assembly) TEST_DIRS=("${SEMANTICTESTS_DIR}") ;; *) assertFail "Import test type not defined. $(print_usage)" ;; esac @@ -214,12 +331,17 @@ IMPORT_TEST_FILES=$(find "${TEST_DIRS[@]}" -name "*.sol" -and -not -name "boost_ NSOURCES="$(echo "${IMPORT_TEST_FILES}" | wc -l)" echo "Looking at ${NSOURCES} .sol files..." +COUNTER=0 +TEST_DIR=$(mktemp -d -t "import-export-test-XXXXXX") +pushd "$TEST_DIR" > /dev/null + for solfile in $IMPORT_TEST_FILES do echo -n "ยท" - # create a temporary sub-directory - FILETMP=$(mktemp -d) - cd "$FILETMP" + + TEST_SUBDIR="$(printf "%05d" "$COUNTER")-$(basename "$solfile")" + mkdir "$TEST_SUBDIR" + pushd "$TEST_SUBDIR" > /dev/null set +e OUTPUT=$("$SPLITSOURCES" "$solfile") @@ -244,19 +366,16 @@ do # All other return codes will be treated as critical errors. The script will exit. printError "\n\nGot unexpected return code ${SPLITSOURCES_RC} from '${SPLITSOURCES} ${solfile}'. Aborting." printError "\n\n${OUTPUT}\n\n" - - cd "$WORKINGDIR" - # Delete temporary files - rm -rf "$FILETMP" - exit 1 fi - cd "$WORKINGDIR" - # Delete temporary files - rm -rf "$FILETMP" + popd > /dev/null + ((++COUNTER)) done +popd > /dev/null +rm -r "$TEST_DIR" + echo if (( FAILED == 0 )) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 1dc2a59f068f..7181bfd4cc7e 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -166,42 +166,86 @@ static bool coloredOutput(CommandLineOptions const& _options) (_options.formatting.coloredOutput.has_value() && _options.formatting.coloredOutput.value()); } +void CommandLineInterface::handleEVMAssembly(std::string const& _contract) +{ + solAssert(m_assemblyStack); + solAssert( + CompilerInputModes.count(m_options.input.mode) == 1 || + m_options.input.mode == frontend::InputMode::EVMAssemblerJSON + ); + + if (!m_options.compiler.outputs.asm_ && !m_options.compiler.outputs.asmJson) + return; + + std::string assembly; + if (m_options.compiler.outputs.asmJson) + assembly = util::jsonPrint(removeNullMembers(m_assemblyStack->assemblyJSON(_contract)), m_options.formatting.json); + else + assembly = m_assemblyStack->assemblyString(_contract, m_fileReader.sourceUnits()); + + if (!m_options.output.dir.empty()) + createFile( + m_compiler->filesystemFriendlyName(_contract) + + (m_options.compiler.outputs.asmJson ? "_evm.json" : ".evm"), + assembly + ); + else + sout() << "EVM assembly:" << std::endl << assembly << std::endl; +} + void CommandLineInterface::handleBinary(std::string const& _contract) { - solAssert(CompilerInputModes.count(m_options.input.mode) == 1); + solAssert(m_assemblyStack); + solAssert( + CompilerInputModes.count(m_options.input.mode) == 1 || + m_options.input.mode == frontend::InputMode::EVMAssemblerJSON + ); + + std::string binary; + std::string binaryRuntime; + if (m_options.compiler.outputs.binary) + binary = objectWithLinkRefsHex(m_assemblyStack->object(_contract)); + if (m_options.compiler.outputs.binaryRuntime) + binaryRuntime = objectWithLinkRefsHex(m_assemblyStack->runtimeObject(_contract)); if (m_options.compiler.outputs.binary) { if (!m_options.output.dir.empty()) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", objectWithLinkRefsHex(m_compiler->object(_contract))); + createFile(m_assemblyStack->filesystemFriendlyName(_contract) + ".bin", binary); else { sout() << "Binary:" << std::endl; - sout() << objectWithLinkRefsHex(m_compiler->object(_contract)) << std::endl; + sout() << binary << std::endl; } } if (m_options.compiler.outputs.binaryRuntime) { if (!m_options.output.dir.empty()) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", objectWithLinkRefsHex(m_compiler->runtimeObject(_contract))); + createFile(m_assemblyStack->filesystemFriendlyName(_contract) + ".bin-runtime", binaryRuntime); else { sout() << "Binary of the runtime part:" << std::endl; - sout() << objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)) << std::endl; + sout() << binaryRuntime << std::endl; } } } void CommandLineInterface::handleOpcode(std::string const& _contract) { - solAssert(CompilerInputModes.count(m_options.input.mode) == 1); + solAssert(m_assemblyStack); + solAssert( + CompilerInputModes.count(m_options.input.mode) == 1 || + m_options.input.mode == frontend::InputMode::EVMAssemblerJSON + ); + + std::string opcodes{evmasm::disassemble(m_assemblyStack->object(_contract).bytecode, m_options.output.evmVersion)}; if (!m_options.output.dir.empty()) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode, m_options.output.evmVersion)); + createFile(m_assemblyStack->filesystemFriendlyName(_contract) + ".opcode", opcodes); else { sout() << "Opcodes:" << std::endl; - sout() << std::uppercase << evmasm::disassemble(m_compiler->object(_contract).bytecode, m_options.output.evmVersion); + sout() << std::uppercase << opcodes; sout() << std::endl; } } @@ -293,7 +337,10 @@ void CommandLineInterface::handleIROptimizedAst(std::string const& _contractName void CommandLineInterface::handleBytecode(std::string const& _contract) { - solAssert(CompilerInputModes.count(m_options.input.mode) == 1); + solAssert( + CompilerInputModes.count(m_options.input.mode) == 1 || + m_options.input.mode == frontend::InputMode::EVMAssemblerJSON + ); if (m_options.compiler.outputs.opcodes) handleOpcode(_contract); @@ -722,6 +769,12 @@ void CommandLineInterface::processInput() compile(); outputCompilationResults(); break; + case InputMode::EVMAssemblerJSON: + assembleFromEVMAssemblyJSON(); + handleCombinedJSON(); + handleBytecode(m_assemblyStack->contractNames().front()); + handleEVMAssembly(m_assemblyStack->contractNames().front()); + break; } } @@ -738,11 +791,41 @@ void CommandLineInterface::printLicense() sout() << licenseText << std::endl; } +void CommandLineInterface::assembleFromEVMAssemblyJSON() +{ + solAssert(m_options.input.mode == InputMode::EVMAssemblerJSON); + solAssert(!m_assemblyStack); + solAssert(!m_evmAssemblyStack && !m_compiler); + + solAssert(m_fileReader.sourceUnits().size() == 1); + auto&& [sourceUnitName, source] = *m_fileReader.sourceUnits().begin(); + + auto evmAssemblyStack = std::make_unique(m_options.output.evmVersion); + try + { + evmAssemblyStack->parseAndAnalyze(sourceUnitName, source); + } + catch (evmasm::AssemblyImportException const& _exception) + { + solThrow(CommandLineExecutionError, "Assembly Import Error: "s + _exception.what()); + } + + if (m_options.output.debugInfoSelection.has_value()) + evmAssemblyStack->selectDebugInfo(m_options.output.debugInfoSelection.value()); + evmAssemblyStack->assemble(); + + m_evmAssemblyStack = std::move(evmAssemblyStack); + m_assemblyStack = m_evmAssemblyStack.get(); +} + void CommandLineInterface::compile() { solAssert(CompilerInputModes.count(m_options.input.mode) == 1); + solAssert(!m_assemblyStack); + solAssert(!m_evmAssemblyStack && !m_compiler); m_compiler = std::make_unique(m_universalCallback.callback()); + m_assemblyStack = m_compiler.get(); SourceReferenceFormatter formatter(serr(false), *m_compiler, coloredOutput(m_options), m_options.formatting.withErrorIds); @@ -852,7 +935,11 @@ void CommandLineInterface::compile() void CommandLineInterface::handleCombinedJSON() { - solAssert(CompilerInputModes.count(m_options.input.mode) == 1); + solAssert(m_assemblyStack); + solAssert( + CompilerInputModes.count(m_options.input.mode) == 1 || + m_options.input.mode == frontend::InputMode::EVMAssemblerJSON + ); if (!m_options.compiler.combinedJsonRequests.has_value()) return; @@ -860,59 +947,65 @@ void CommandLineInterface::handleCombinedJSON() Json::Value output(Json::objectValue); output[g_strVersion] = frontend::VersionString; - std::vector contracts = m_compiler->contractNames(); - - // NOTE: The state checks here are more strict that in Standard JSON. There we allow - // requesting certain outputs even if compilation fails as long as analysis went ok. - bool compilationSuccess = m_compiler->state() >= CompilerStack::State::CompilationSuccessful; + std::vector contracts = m_assemblyStack->contractNames(); if (!contracts.empty()) output[g_strContracts] = Json::Value(Json::objectValue); for (std::string const& contractName: contracts) { Json::Value& contractData = output[g_strContracts][contractName] = Json::objectValue; - if (m_options.compiler.combinedJsonRequests->abi && compilationSuccess) - contractData[g_strAbi] = m_compiler->contractABI(contractName); - if (m_options.compiler.combinedJsonRequests->metadata && compilationSuccess) - contractData["metadata"] = m_compiler->metadata(contractName); - if (m_options.compiler.combinedJsonRequests->binary && compilationSuccess) - contractData[g_strBinary] = m_compiler->object(contractName).toHex(); - if (m_options.compiler.combinedJsonRequests->binaryRuntime && compilationSuccess) - contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex(); - if (m_options.compiler.combinedJsonRequests->opcodes && compilationSuccess) - contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode, m_options.output.evmVersion); - if (m_options.compiler.combinedJsonRequests->asm_ && compilationSuccess) - contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); - if (m_options.compiler.combinedJsonRequests->storageLayout && compilationSuccess) - contractData[g_strStorageLayout] = m_compiler->storageLayout(contractName); - if (m_options.compiler.combinedJsonRequests->generatedSources && compilationSuccess) - contractData[g_strGeneratedSources] = m_compiler->generatedSources(contractName, false); - if (m_options.compiler.combinedJsonRequests->generatedSourcesRuntime && compilationSuccess) - contractData[g_strGeneratedSourcesRuntime] = m_compiler->generatedSources(contractName, true); - if (m_options.compiler.combinedJsonRequests->srcMap && compilationSuccess) + + // NOTE: The state checks here are more strict that in Standard JSON. There we allow + // requesting certain outputs even if compilation fails as long as analysis went ok. + if (m_compiler && m_compiler->compilationSuccessful()) { - auto map = m_compiler->sourceMapping(contractName); - contractData[g_strSrcMap] = map ? *map : ""; + if (m_options.compiler.combinedJsonRequests->abi) + contractData[g_strAbi] = m_compiler->contractABI(contractName); + if (m_options.compiler.combinedJsonRequests->metadata) + contractData["metadata"] = m_compiler->metadata(contractName); + if (m_options.compiler.combinedJsonRequests->storageLayout) + contractData[g_strStorageLayout] = m_compiler->storageLayout(contractName); + if (m_options.compiler.combinedJsonRequests->generatedSources) + contractData[g_strGeneratedSources] = m_compiler->generatedSources(contractName, false); + if (m_options.compiler.combinedJsonRequests->generatedSourcesRuntime) + contractData[g_strGeneratedSourcesRuntime] = m_compiler->generatedSources(contractName, true); + if (m_options.compiler.combinedJsonRequests->signatureHashes) + contractData[g_strSignatureHashes] = m_compiler->interfaceSymbols(contractName)["methods"]; + if (m_options.compiler.combinedJsonRequests->natspecDev) + contractData[g_strNatspecDev] = m_compiler->natspecDev(contractName); + if (m_options.compiler.combinedJsonRequests->natspecUser) + contractData[g_strNatspecUser] = m_compiler->natspecUser(contractName); } - if (m_options.compiler.combinedJsonRequests->srcMapRuntime && compilationSuccess) + + if (m_assemblyStack->compilationSuccessful()) { - auto map = m_compiler->runtimeSourceMapping(contractName); - contractData[g_strSrcMapRuntime] = map ? *map : ""; + if (m_options.compiler.combinedJsonRequests->binary) + contractData[g_strBinary] = m_assemblyStack->object(contractName).toHex(); + if (m_options.compiler.combinedJsonRequests->binaryRuntime) + contractData[g_strBinaryRuntime] = m_assemblyStack->runtimeObject(contractName).toHex(); + if (m_options.compiler.combinedJsonRequests->opcodes) + contractData[g_strOpcodes] = evmasm::disassemble(m_assemblyStack->object(contractName).bytecode, m_options.output.evmVersion); + if (m_options.compiler.combinedJsonRequests->asm_) + contractData[g_strAsm] = m_assemblyStack->assemblyJSON(contractName); + if (m_options.compiler.combinedJsonRequests->srcMap) + { + auto map = m_assemblyStack->sourceMapping(contractName); + contractData[g_strSrcMap] = map ? *map : ""; + } + if (m_options.compiler.combinedJsonRequests->srcMapRuntime) + { + auto map = m_assemblyStack->runtimeSourceMapping(contractName); + contractData[g_strSrcMapRuntime] = map ? *map : ""; + } + if (m_options.compiler.combinedJsonRequests->funDebug) + contractData[g_strFunDebug] = StandardCompiler::formatFunctionDebugData( + m_assemblyStack->object(contractName).functionDebugData + ); + if (m_options.compiler.combinedJsonRequests->funDebugRuntime) + contractData[g_strFunDebugRuntime] = StandardCompiler::formatFunctionDebugData( + m_assemblyStack->runtimeObject(contractName).functionDebugData + ); } - if (m_options.compiler.combinedJsonRequests->funDebug && compilationSuccess) - contractData[g_strFunDebug] = StandardCompiler::formatFunctionDebugData( - m_compiler->object(contractName).functionDebugData - ); - if (m_options.compiler.combinedJsonRequests->funDebugRuntime && compilationSuccess) - contractData[g_strFunDebugRuntime] = StandardCompiler::formatFunctionDebugData( - m_compiler->runtimeObject(contractName).functionDebugData - ); - if (m_options.compiler.combinedJsonRequests->signatureHashes && compilationSuccess) - contractData[g_strSignatureHashes] = m_compiler->interfaceSymbols(contractName)["methods"]; - if (m_options.compiler.combinedJsonRequests->natspecDev && compilationSuccess) - contractData[g_strNatspecDev] = m_compiler->natspecDev(contractName); - if (m_options.compiler.combinedJsonRequests->natspecUser && compilationSuccess) - contractData[g_strNatspecUser] = m_compiler->natspecUser(contractName); } bool needsSourceList = @@ -924,12 +1017,13 @@ void CommandLineInterface::handleCombinedJSON() // Indices into this array are used to abbreviate source names in source locations. output[g_strSourceList] = Json::Value(Json::arrayValue); - for (auto const& source: m_compiler->sourceNames()) + for (auto const& source: m_assemblyStack->sourceNames()) output[g_strSourceList].append(source); } if (m_options.compiler.combinedJsonRequests->ast) { + solAssert(m_compiler); output[g_strSources] = Json::Value(Json::objectValue); for (auto const& sourceCode: m_fileReader.sourceUnits()) { @@ -1201,20 +1295,7 @@ void CommandLineInterface::outputCompilationResults() if (needsHumanTargetedStdout(m_options)) sout() << std::endl << "======= " << contract << " =======" << std::endl; - // do we need EVM assembly? - if (m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asmJson) - { - std::string ret; - if (m_options.compiler.outputs.asmJson) - ret = util::jsonPrint(removeNullMembers(m_compiler->assemblyJSON(contract)), m_options.formatting.json); - else - ret = m_compiler->assemblyString(contract, m_fileReader.sourceUnits()); - - if (!m_options.output.dir.empty()) - createFile(m_compiler->filesystemFriendlyName(contract) + (m_options.compiler.outputs.asmJson ? "_evm.json" : ".evm"), ret); - else - sout() << "EVM assembly:" << std::endl << ret << std::endl; - } + handleEVMAssembly(contract); if (m_options.compiler.estimateGas) handleGasEstimation(contract); diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index f08eea662a27..906b34d62b53 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -24,6 +24,8 @@ #include +#include +#include #include #include #include @@ -84,6 +86,7 @@ class CommandLineInterface void printVersion(); void printLicense(); void compile(); + void assembleFromEVMAssemblyJSON(); void serveLSP(); void link(); void writeLinkedFiles(); @@ -98,6 +101,7 @@ class CommandLineInterface void handleCombinedJSON(); void handleAst(); + void handleEVMAssembly(std::string const& _contract); void handleBinary(std::string const& _contract); void handleOpcode(std::string const& _contract); void handleIR(std::string const& _contract); @@ -147,6 +151,8 @@ class CommandLineInterface UniversalCallback m_universalCallback{&m_fileReader, m_solverCommand}; std::optional m_standardJsonInput; std::unique_ptr m_compiler; + std::unique_ptr m_evmAssemblyStack; + evmasm::AbstractAssemblyStack* m_assemblyStack = nullptr; CommandLineOptions m_options; }; diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index c21659a9bc35..0c7484fd4017 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -30,6 +30,8 @@ #include #include +#include + using namespace solidity::langutil; namespace po = boost::program_options; @@ -50,6 +52,7 @@ static std::string const g_strExperimentalViaIR = "experimental-via-ir"; static std::string const g_strGas = "gas"; static std::string const g_strHelp = "help"; static std::string const g_strImportAst = "import-ast"; +static std::string const g_strImportEvmAssemblerJson = "import-asm-json"; static std::string const g_strInputFile = "input-file"; static std::string const g_strYul = "yul"; static std::string const g_strYulDialect = "yul-dialect"; @@ -141,6 +144,7 @@ static std::map const g_inputModeName = { {InputMode::StandardJson, "standard JSON"}, {InputMode::Linker, "linker"}, {InputMode::LanguageServer, "language server (LSP)"}, + {InputMode::EVMAssemblerJSON, "EVM assembler (JSON format)"}, }; void CommandLineParser::checkMutuallyExclusive(std::vector const& _optionNames) @@ -468,7 +472,13 @@ void CommandLineParser::parseOutputSelection() CompilerOutputs::componentName(&CompilerOutputs::irOptimized), CompilerOutputs::componentName(&CompilerOutputs::astCompactJson), }; - + static std::set const evmAssemblyJsonImportModeOutputs = { + CompilerOutputs::componentName(&CompilerOutputs::asm_), + CompilerOutputs::componentName(&CompilerOutputs::binary), + CompilerOutputs::componentName(&CompilerOutputs::binaryRuntime), + CompilerOutputs::componentName(&CompilerOutputs::opcodes), + CompilerOutputs::componentName(&CompilerOutputs::asmJson), + }; switch (_mode) { case InputMode::Help: @@ -479,6 +489,8 @@ void CommandLineParser::parseOutputSelection() case InputMode::Compiler: case InputMode::CompilerWithASTImport: return util::contains(compilerModeOutputs, _outputName); + case InputMode::EVMAssemblerJSON: + return util::contains(evmAssemblyJsonImportModeOutputs, _outputName); case InputMode::Assembler: return util::contains(assemblerModeOutputs, _outputName); case InputMode::StandardJson: @@ -664,6 +676,10 @@ General Information)").c_str(), "Supported Inputs is the output of the --" + g_strStandardJSON + " or the one produced by " "--" + g_strCombinedJson + " " + CombinedJsonRequests::componentName(&CombinedJsonRequests::ast)).c_str() ) + ( + g_strImportEvmAssemblerJson.c_str(), + "Import EVM assembly from JSON. Assumes input is in the format used by --asm-json." + ) ( g_strLSP.c_str(), "Switch to language server mode (\"LSP\"). Allows the compiler to be used as an analysis backend " @@ -940,6 +956,7 @@ void CommandLineParser::processArgs() g_strYul, g_strImportAst, g_strLSP, + g_strImportEvmAssemblerJson, }); if (m_args.count(g_strHelp) > 0) @@ -958,6 +975,8 @@ void CommandLineParser::processArgs() m_options.input.mode = InputMode::Linker; else if (m_args.count(g_strImportAst) > 0) m_options.input.mode = InputMode::CompilerWithASTImport; + else if (m_args.count(g_strImportEvmAssemblerJson) > 0) + m_options.input.mode = InputMode::EVMAssemblerJSON; else m_options.input.mode = InputMode::Compiler; @@ -1017,9 +1036,39 @@ void CommandLineParser::processArgs() if (option != CompilerOutputs::componentName(&CompilerOutputs::astCompactJson)) checkMutuallyExclusive({g_strStopAfter, option}); + if (m_options.input.mode == InputMode::EVMAssemblerJSON) + { + static std::set const supportedByEvmAsmJsonImport{ + g_strImportEvmAssemblerJson, + CompilerOutputs::componentName(&CompilerOutputs::asm_), + CompilerOutputs::componentName(&CompilerOutputs::binary), + CompilerOutputs::componentName(&CompilerOutputs::binaryRuntime), + CompilerOutputs::componentName(&CompilerOutputs::asmJson), + CompilerOutputs::componentName(&CompilerOutputs::opcodes), + g_strCombinedJson, + g_strInputFile, + g_strJsonIndent, + g_strPrettyJson, + "srcmap", + "srcmap-runtime", + }; + + for (auto const& [optionName, optionValue]: m_args) + if (!optionValue.defaulted() && !supportedByEvmAsmJsonImport.count(optionName)) + solThrow( + CommandLineValidationError, + fmt::format( + "Option --{} is not supported with --{}.", + optionName, + g_strImportEvmAssemblerJson + ) + ); + } + if ( m_options.input.mode != InputMode::Compiler && m_options.input.mode != InputMode::CompilerWithASTImport && + m_options.input.mode != InputMode::EVMAssemblerJSON && m_options.input.mode != InputMode::Assembler ) { @@ -1377,7 +1426,11 @@ void CommandLineParser::processArgs() m_args.count(g_strModelCheckerTimeout); m_options.output.viaIR = (m_args.count(g_strExperimentalViaIR) > 0 || m_args.count(g_strViaIR) > 0); - solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport); + solAssert( + m_options.input.mode == InputMode::Compiler || + m_options.input.mode == InputMode::CompilerWithASTImport || + m_options.input.mode == InputMode::EVMAssemblerJSON + ); } void CommandLineParser::parseCombinedJsonOption() @@ -1393,6 +1446,34 @@ void CommandLineParser::parseCombinedJsonOption() m_options.compiler.combinedJsonRequests = CombinedJsonRequests{}; for (auto&& [componentName, component]: CombinedJsonRequests::componentMap()) m_options.compiler.combinedJsonRequests.value().*component = (requests.count(componentName) > 0); + + if (m_options.input.mode == InputMode::EVMAssemblerJSON && m_options.compiler.combinedJsonRequests.has_value()) + { + static bool CombinedJsonRequests::* invalidOptions[]{ + &CombinedJsonRequests::abi, + &CombinedJsonRequests::ast, + &CombinedJsonRequests::funDebug, + &CombinedJsonRequests::funDebugRuntime, + &CombinedJsonRequests::generatedSources, + &CombinedJsonRequests::generatedSourcesRuntime, + &CombinedJsonRequests::metadata, + &CombinedJsonRequests::natspecDev, + &CombinedJsonRequests::natspecUser, + &CombinedJsonRequests::signatureHashes, + &CombinedJsonRequests::storageLayout + }; + + for (auto const invalidOption: invalidOptions) + if (m_options.compiler.combinedJsonRequests.value().*invalidOption) + solThrow( + CommandLineValidationError, + fmt::format( + "The --{} {} output is not available in EVM assembly import mode.", + g_strCombinedJson, + CombinedJsonRequests::componentName(invalidOption) + ) + ); + } } size_t CommandLineParser::countEnabledOptions(std::vector const& _optionNames) const diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index cad84871e087..b95973562e51 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -57,6 +57,7 @@ enum class InputMode Linker, Assembler, LanguageServer, + EVMAssemblerJSON }; struct CompilerOutputs diff --git a/test/cmdlineTests/asm_json_import_all_valid_flags/args b/test/cmdlineTests/asm_json_import_all_valid_flags/args new file mode 100644 index 000000000000..e6010eac3b05 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_all_valid_flags/args @@ -0,0 +1 @@ +--pretty-json --json-indent 4 --combined-json bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime --asm --bin --bin-runtime --asm-json --import-asm-json - diff --git a/test/cmdlineTests/asm_json_import_all_valid_flags/output b/test/cmdlineTests/asm_json_import_all_valid_flags/output new file mode 100644 index 000000000000..7b8aa0297982 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_all_valid_flags/output @@ -0,0 +1,91 @@ +{ + "contracts": + { + "": + { + "asm": + { + ".code": + [ + { + "begin": 0, + "end": 0, + "name": "PUSH", + "source": -1, + "value": "0" + } + ], + ".data": + { + "0": + { + ".code": + [ + { + "begin": 0, + "end": 0, + "name": "PUSH", + "source": -1, + "value": "1" + } + ] + } + }, + "sourceList": + [ + "contract.sol", + "#utility.yul" + ] + }, + "bin": "5ffe", + "bin-runtime": "6001", + "opcodes": "PUSH0 INVALID ", + "srcmap": "0:0::-:0", + "srcmap-runtime": "0:0::-:0" + } + }, + "sourceList": + [ + "contract.sol", + "#utility.yul" + ], + "version": "" +} +Binary: +5ffe +Binary of the runtime part: +6001 +EVM assembly: +{ + ".code": + [ + { + "begin": 0, + "end": 0, + "name": "PUSH", + "source": -1, + "value": "0" + } + ], + ".data": + { + "0": + { + ".code": + [ + { + "begin": 0, + "end": 0, + "name": "PUSH", + "source": -1, + "value": "1" + } + ] + } + }, + "sourceList": + [ + "contract.sol", + "#utility.yul" + ] +} diff --git a/test/cmdlineTests/asm_json_import_all_valid_flags/stdin b/test/cmdlineTests/asm_json_import_all_valid_flags/stdin new file mode 100644 index 000000000000..1a707b786744 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_all_valid_flags/stdin @@ -0,0 +1,22 @@ +{ + ".code": [ + { + "name": "PUSH", + "value": "0" + } + ], + ".data": { + "0": { + ".code": [ + { + "name": "PUSH", + "value": "1" + } + ] + } + }, + "sourceList": [ + "contract.sol", + "#utility.yul" + ] +} diff --git a/test/cmdlineTests/asm_json_import_difficulty_prevrandao/args b/test/cmdlineTests/asm_json_import_difficulty_prevrandao/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_difficulty_prevrandao/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_difficulty_prevrandao/output b/test/cmdlineTests/asm_json_import_difficulty_prevrandao/output new file mode 100644 index 000000000000..3d19f83d668f --- /dev/null +++ b/test/cmdlineTests/asm_json_import_difficulty_prevrandao/output @@ -0,0 +1,6 @@ +Opcodes: +PREVRANDAO PREVRANDAO +EVM assembly: + /* */ + prevrandao + prevrandao diff --git a/test/cmdlineTests/asm_json_import_difficulty_prevrandao/stdin b/test/cmdlineTests/asm_json_import_difficulty_prevrandao/stdin new file mode 100644 index 000000000000..f6cc546647c3 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_difficulty_prevrandao/stdin @@ -0,0 +1,6 @@ +{ + ".code": [ + {"name": "DIFFICULTY"}, + {"name": "PREVRANDAO"} + ] +} diff --git a/test/cmdlineTests/asm_json_import_hex_subassembly_indices/args b/test/cmdlineTests/asm_json_import_hex_subassembly_indices/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_hex_subassembly_indices/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_hex_subassembly_indices/output b/test/cmdlineTests/asm_json_import_hex_subassembly_indices/output new file mode 100644 index 000000000000..873465aa1f3f --- /dev/null +++ b/test/cmdlineTests/asm_json_import_hex_subassembly_indices/output @@ -0,0 +1,57 @@ +Opcodes: +PUSH0 INVALID +EVM assembly: + /* */ + 0x00 +stop + +sub_0: assembly { +} + +sub_1: assembly { +} + +sub_2: assembly { +} + +sub_3: assembly { +} + +sub_4: assembly { +} + +sub_5: assembly { +} + +sub_6: assembly { +} + +sub_7: assembly { +} + +sub_8: assembly { +} + +sub_9: assembly { +} + +sub_10: assembly { +} + +sub_11: assembly { +} + +sub_12: assembly { +} + +sub_13: assembly { +} + +sub_14: assembly { +} + +sub_15: assembly { +} + +sub_16: assembly { +} diff --git a/test/cmdlineTests/asm_json_import_hex_subassembly_indices/stdin b/test/cmdlineTests/asm_json_import_hex_subassembly_indices/stdin new file mode 100644 index 000000000000..70c89a2347b9 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_hex_subassembly_indices/stdin @@ -0,0 +1,27 @@ +{ + ".code": [ + { + "name": "PUSH", + "value": "0" + } + ], + ".data": { + "0": {".code": []}, + "1": {".code": []}, + "2": {".code": []}, + "3": {".code": []}, + "4": {".code": []}, + "5": {".code": []}, + "6": {".code": []}, + "7": {".code": []}, + "8": {".code": []}, + "9": {".code": []}, + "a": {".code": []}, + "b": {".code": []}, + "c": {".code": []}, + "d": {".code": []}, + "e": {".code": []}, + "f": {".code": []}, + "10": {".code": []} + } +} diff --git a/test/cmdlineTests/asm_json_import_invalid_data_not_hex/args b/test/cmdlineTests/asm_json_import_invalid_data_not_hex/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_data_not_hex/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_invalid_data_not_hex/err b/test/cmdlineTests/asm_json_import_invalid_data_not_hex/err new file mode 100644 index 000000000000..8e3458bd6b60 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_data_not_hex/err @@ -0,0 +1 @@ +Error: Assembly Import Error: The value for key '0' inside '.data' is not a valid hexadecimal string. diff --git a/test/cmdlineTests/asm_json_import_invalid_data_not_hex/exit b/test/cmdlineTests/asm_json_import_invalid_data_not_hex/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_data_not_hex/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/asm_json_import_invalid_data_not_hex/stdin b/test/cmdlineTests/asm_json_import_invalid_data_not_hex/stdin new file mode 100644 index 000000000000..6c1673694318 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_data_not_hex/stdin @@ -0,0 +1,11 @@ +{ + ".code": [ + { + "name": "PUSH", + "value": "0" + } + ], + ".data": { + "0": "no-hex-string" + } +} diff --git a/test/cmdlineTests/asm_json_import_invalid_data_not_object/args b/test/cmdlineTests/asm_json_import_invalid_data_not_object/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_data_not_object/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_invalid_data_not_object/err b/test/cmdlineTests/asm_json_import_invalid_data_not_object/err new file mode 100644 index 000000000000..ddcfeca7d45b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_data_not_object/err @@ -0,0 +1 @@ +Error: Assembly Import Error: The value of key '0' inside '.data' is neither a hex string nor an object. diff --git a/test/cmdlineTests/asm_json_import_invalid_data_not_object/exit b/test/cmdlineTests/asm_json_import_invalid_data_not_object/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_data_not_object/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/asm_json_import_invalid_data_not_object/stdin b/test/cmdlineTests/asm_json_import_invalid_data_not_object/stdin new file mode 100644 index 000000000000..29cc24aada13 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_data_not_object/stdin @@ -0,0 +1,11 @@ +{ + ".code": [ + { + "name": "PUSH", + "value": "0" + } + ], + ".data": { + "0": [0, 1, 2, 3] + } +} diff --git a/test/cmdlineTests/asm_json_import_invalid_jumptype_instruction/args b/test/cmdlineTests/asm_json_import_invalid_jumptype_instruction/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_jumptype_instruction/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_invalid_jumptype_instruction/err b/test/cmdlineTests/asm_json_import_invalid_jumptype_instruction/err new file mode 100644 index 000000000000..e553029e60a0 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_jumptype_instruction/err @@ -0,0 +1 @@ +Error: Assembly Import Error: Member 'jumpType' set on instruction different from JUMP or JUMPI (was set on instruction 'PUSH') diff --git a/test/cmdlineTests/asm_json_import_invalid_jumptype_instruction/exit b/test/cmdlineTests/asm_json_import_invalid_jumptype_instruction/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_jumptype_instruction/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/asm_json_import_invalid_jumptype_instruction/stdin b/test/cmdlineTests/asm_json_import_invalid_jumptype_instruction/stdin new file mode 100644 index 000000000000..2df5445d6336 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_jumptype_instruction/stdin @@ -0,0 +1,9 @@ +{ + ".code": [ + { + "name": "PUSH", + "value": "0", + "jumpType": "[in]" + } + ] +} diff --git a/test/cmdlineTests/asm_json_import_invalid_value/args b/test/cmdlineTests/asm_json_import_invalid_value/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_value/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_invalid_value/err b/test/cmdlineTests/asm_json_import_invalid_value/err new file mode 100644 index 000000000000..67723f82d228 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_value/err @@ -0,0 +1 @@ +Error: Assembly Import Error: Member 'value' defined for instruction 'DIFFICULTY', but the instruction does not need a value. diff --git a/test/cmdlineTests/asm_json_import_invalid_value/exit b/test/cmdlineTests/asm_json_import_invalid_value/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_value/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/asm_json_import_invalid_value/stdin b/test/cmdlineTests/asm_json_import_invalid_value/stdin new file mode 100644 index 000000000000..6252ff695cbd --- /dev/null +++ b/test/cmdlineTests/asm_json_import_invalid_value/stdin @@ -0,0 +1,8 @@ +{ + ".code": [ + { + "name": "DIFFICULTY", + "value": "0" + } + ] +} diff --git a/test/cmdlineTests/asm_json_import_missing_subobject_indices/args b/test/cmdlineTests/asm_json_import_missing_subobject_indices/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_missing_subobject_indices/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_missing_subobject_indices/err b/test/cmdlineTests/asm_json_import_missing_subobject_indices/err new file mode 100644 index 000000000000..1decd6dda6ce --- /dev/null +++ b/test/cmdlineTests/asm_json_import_missing_subobject_indices/err @@ -0,0 +1 @@ +Error: Assembly Import Error: Invalid subassembly indices in '.data'. Not all numbers between 0 and 8 are present. diff --git a/test/cmdlineTests/asm_json_import_missing_subobject_indices/exit b/test/cmdlineTests/asm_json_import_missing_subobject_indices/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_missing_subobject_indices/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/asm_json_import_missing_subobject_indices/stdin b/test/cmdlineTests/asm_json_import_missing_subobject_indices/stdin new file mode 100644 index 000000000000..6832823ebdda --- /dev/null +++ b/test/cmdlineTests/asm_json_import_missing_subobject_indices/stdin @@ -0,0 +1,19 @@ +{ + ".code": [ + { + "name": "PUSH", + "value": "0" + } + ], + ".data": { + "2": {".code": []}, + "0": {".code": []}, + "4": {".code": []}, + "5": {".code": []}, + "7": {".code": []}, + "6": {".code": []}, + "9": {".code": []}, + "1": {".code": []}, + "8": {".code": []} + } +} diff --git a/test/cmdlineTests/asm_json_import_no_value/args b/test/cmdlineTests/asm_json_import_no_value/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_no_value/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_no_value/err b/test/cmdlineTests/asm_json_import_no_value/err new file mode 100644 index 000000000000..02428d99a874 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_no_value/err @@ -0,0 +1 @@ +Error: Assembly Import Error: Member 'value' is missing for instruction 'PUSH', but the instruction needs a value. diff --git a/test/cmdlineTests/asm_json_import_no_value/exit b/test/cmdlineTests/asm_json_import_no_value/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_no_value/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/asm_json_import_no_value/stdin b/test/cmdlineTests/asm_json_import_no_value/stdin new file mode 100644 index 000000000000..99ab122c90e4 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_no_value/stdin @@ -0,0 +1,5 @@ +{ + ".code": [ + {"name": "PUSH"} + ] +} diff --git a/test/cmdlineTests/asm_json_import_non_unique_sources/args b/test/cmdlineTests/asm_json_import_non_unique_sources/args new file mode 100644 index 000000000000..d27cc7bb214e --- /dev/null +++ b/test/cmdlineTests/asm_json_import_non_unique_sources/args @@ -0,0 +1 @@ +--asm-json --import-asm-json - diff --git a/test/cmdlineTests/asm_json_import_non_unique_sources/err b/test/cmdlineTests/asm_json_import_non_unique_sources/err new file mode 100644 index 000000000000..78eadfc0b532 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_non_unique_sources/err @@ -0,0 +1 @@ +Error: Assembly Import Error: Items in 'sourceList' array are not unique. diff --git a/test/cmdlineTests/asm_json_import_non_unique_sources/exit b/test/cmdlineTests/asm_json_import_non_unique_sources/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_non_unique_sources/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/asm_json_import_non_unique_sources/stdin b/test/cmdlineTests/asm_json_import_non_unique_sources/stdin new file mode 100644 index 000000000000..431d5a6468c6 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_non_unique_sources/stdin @@ -0,0 +1,23 @@ +{ + ".code": [ + { + "name": "PUSH", + "value": "0" + } + ], + ".data": { + "0": { + ".code": [ + { + "name": "PUSH", + "value": "1" + } + ] + } + }, + "sourceList": [ + "contract.sol", + "contract.sol", + "#utility.yul" + ] +} diff --git a/test/cmdlineTests/asm_json_import_optimize_not_supported/args b/test/cmdlineTests/asm_json_import_optimize_not_supported/args new file mode 100644 index 000000000000..f649e9740882 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_optimize_not_supported/args @@ -0,0 +1 @@ +--optimize --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_optimize_not_supported/err b/test/cmdlineTests/asm_json_import_optimize_not_supported/err new file mode 100644 index 000000000000..8f42dca78694 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_optimize_not_supported/err @@ -0,0 +1 @@ +Error: Option --optimize is not supported with --import-asm-json. diff --git a/test/cmdlineTests/asm_json_import_optimize_not_supported/exit b/test/cmdlineTests/asm_json_import_optimize_not_supported/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_optimize_not_supported/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/asm_json_import_optimize_not_supported/stdin b/test/cmdlineTests/asm_json_import_optimize_not_supported/stdin new file mode 100644 index 000000000000..f6cc546647c3 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_optimize_not_supported/stdin @@ -0,0 +1,6 @@ +{ + ".code": [ + {"name": "DIFFICULTY"}, + {"name": "PREVRANDAO"} + ] +} diff --git a/test/cmdlineTests/asm_json_import_out_of_range_data_index/args b/test/cmdlineTests/asm_json_import_out_of_range_data_index/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_out_of_range_data_index/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_out_of_range_data_index/err b/test/cmdlineTests/asm_json_import_out_of_range_data_index/err new file mode 100644 index 000000000000..08c766c3a82f --- /dev/null +++ b/test/cmdlineTests/asm_json_import_out_of_range_data_index/err @@ -0,0 +1 @@ +Error: Assembly Import Error: The key '18446744073709551615' inside '.data' is out of the supported integer range. diff --git a/test/cmdlineTests/asm_json_import_out_of_range_data_index/exit b/test/cmdlineTests/asm_json_import_out_of_range_data_index/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_out_of_range_data_index/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/asm_json_import_out_of_range_data_index/stdin b/test/cmdlineTests/asm_json_import_out_of_range_data_index/stdin new file mode 100644 index 000000000000..3e8f88970e8b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_out_of_range_data_index/stdin @@ -0,0 +1,13 @@ +{ + ".code": [ + { + "name": "PUSH", + "value": "0" + } + ], + ".data": { + "18446744073709551615": { + ".code": [] + } + } +} diff --git a/test/cmdlineTests/asm_json_import_random_order_data_index/args b/test/cmdlineTests/asm_json_import_random_order_data_index/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_random_order_data_index/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_random_order_data_index/output b/test/cmdlineTests/asm_json_import_random_order_data_index/output new file mode 100644 index 000000000000..b23848e5ddcb --- /dev/null +++ b/test/cmdlineTests/asm_json_import_random_order_data_index/output @@ -0,0 +1,35 @@ +Opcodes: +PUSH0 INVALID +EVM assembly: + /* */ + 0x00 +stop + +sub_0: assembly { +} + +sub_1: assembly { + stop + + sub_0: assembly { + } + + sub_1: assembly { + } + + sub_2: assembly { + } +} + +sub_2: assembly { + stop + + sub_0: assembly { + } + + sub_1: assembly { + } +} + +sub_3: assembly { +} diff --git a/test/cmdlineTests/asm_json_import_random_order_data_index/stdin b/test/cmdlineTests/asm_json_import_random_order_data_index/stdin new file mode 100644 index 000000000000..c695de5cd441 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_random_order_data_index/stdin @@ -0,0 +1,27 @@ +{ + ".code": [ + { + "name": "PUSH", + "value": "0" + } + ], + ".data": { + "2": { + ".code": [], + ".data": { + "1": {".code": []}, + "0": {".code": []} + } + }, + "0": {".code": []}, + "1": { + ".code": [], + ".data": { + "1": {".code": []}, + "2": {".code": []}, + "0": {".code": []} + } + }, + "3": {".code": []} + } +} diff --git a/test/cmdlineTests/asm_json_import_unrecognized_field/args b/test/cmdlineTests/asm_json_import_unrecognized_field/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_unrecognized_field/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_unrecognized_field/err b/test/cmdlineTests/asm_json_import_unrecognized_field/err new file mode 100644 index 000000000000..35b7fc9f6faf --- /dev/null +++ b/test/cmdlineTests/asm_json_import_unrecognized_field/err @@ -0,0 +1 @@ +Error: Assembly Import Error: Unknown member '_name'. Valid members are: begin, end, jumpType, modifierDepth, name, source, value. diff --git a/test/cmdlineTests/asm_json_import_unrecognized_field/exit b/test/cmdlineTests/asm_json_import_unrecognized_field/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_unrecognized_field/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/asm_json_import_unrecognized_field/stdin b/test/cmdlineTests/asm_json_import_unrecognized_field/stdin new file mode 100644 index 000000000000..c1dc7cd84d60 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_unrecognized_field/stdin @@ -0,0 +1,5 @@ +{ + ".code": [ + {"_name": "DIFFICULTY"} + ] +} diff --git a/test/cmdlineTests/asm_json_import_untagged_jumpdest/args b/test/cmdlineTests/asm_json_import_untagged_jumpdest/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_untagged_jumpdest/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_untagged_jumpdest/err b/test/cmdlineTests/asm_json_import_untagged_jumpdest/err new file mode 100644 index 000000000000..f31c32fef8d0 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_untagged_jumpdest/err @@ -0,0 +1 @@ +Error: Assembly Import Error: JUMPDEST instruction without a tag diff --git a/test/cmdlineTests/asm_json_import_untagged_jumpdest/exit b/test/cmdlineTests/asm_json_import_untagged_jumpdest/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/asm_json_import_untagged_jumpdest/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/asm_json_import_untagged_jumpdest/stdin b/test/cmdlineTests/asm_json_import_untagged_jumpdest/stdin new file mode 100644 index 000000000000..beaa19be5543 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_untagged_jumpdest/stdin @@ -0,0 +1,10 @@ +{ + ".code": [ + { + "name": "tag", + "value": "0x00" + }, + {"name": "JUMPDEST"}, + {"name": "JUMPDEST"} + ] +} diff --git a/test/cmdlineTests/asm_json_import_verbatim/args b/test/cmdlineTests/asm_json_import_verbatim/args new file mode 100644 index 000000000000..2b4b3a1534b7 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_verbatim/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/asm_json_import_verbatim/output b/test/cmdlineTests/asm_json_import_verbatim/output new file mode 100644 index 000000000000..50b8a4ce948f --- /dev/null +++ b/test/cmdlineTests/asm_json_import_verbatim/output @@ -0,0 +1,58 @@ +Opcodes: +PUSH0 CALLDATALOAD PUSH1 0x14 DUP1 DUP3 SSTORE PUSH1 0x20 CALLDATALOAD ISZERO PUSH1 0x1C JUMPI DUP1 DUP3 SSTORE PUSH0 DUP1 PUSH0 DUP1 PUSH0 DUP1 STATICCALL POP PUSH25 0x797A5B80825550500000000000000000000000000000000000 +EVM assembly: + /* "":28:29 */ + 0x00 + /* "":15:30 [... */ + calldataload + /* "":44:46 n" */ + 0x14 + /* "":61:62 */ + dup1 + /* "":58:59 */ + dup3 + /* "":51:63 ... */ + sstore + /* "":84:86 */ + 0x20 + /* "":71:87 29,... */ + calldataload + /* "":68:188 ": 29,... */ + iszero + tag_1 + jumpi + /* "":108:109 */ + dup1 + /* "":105:106 */ + dup3 + /* "":98:110 SH",... */ + sstore + /* "":149:150 " */ + 0x00 + /* "":146:147 " */ + dup1 + /* "":143:144 l */ + 0x00 + /* "":140:141 " */ + dup1 + /* "":137:138 */ + 0x00 + /* "":134:135 */ + dup1 + /* "":123:151 : 0,... */ + staticcall + /* "":119:152 rce": 0,... */ + pop + /* "":161:182 },... */ + verbatimbytecode_78797a + /* "":68:188 ": 29,... */ +tag_1: + /* "":203:204 */ + dup1 + /* "":200:201 */ + dup3 + /* "":193:205 : 15,... */ + sstore + /* "":0:207 {... */ + pop + pop diff --git a/test/cmdlineTests/asm_json_import_verbatim/stdin b/test/cmdlineTests/asm_json_import_verbatim/stdin new file mode 100644 index 000000000000..df4b0ce08268 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_verbatim/stdin @@ -0,0 +1,196 @@ +{ + ".code": [ + { + "begin": 28, + "end": 29, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 15, + "end": 30, + "name": "CALLDATALOAD", + "source": 0 + }, + { + "begin": 44, + "end": 46, + "name": "PUSH", + "source": 0, + "value": "14" + }, + { + "begin": 61, + "end": 62, + "name": "DUP1", + "source": 0 + }, + { + "begin": 58, + "end": 59, + "name": "DUP3", + "source": 0 + }, + { + "begin": 51, + "end": 63, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 84, + "end": 86, + "name": "PUSH", + "source": 0, + "value": "20" + }, + { + "begin": 71, + "end": 87, + "name": "CALLDATALOAD", + "source": 0 + }, + { + "begin": 68, + "end": 188, + "name": "ISZERO", + "source": 0 + }, + { + "begin": 68, + "end": 188, + "name": "PUSH [tag]", + "source": 0, + "value": "1" + }, + { + "begin": 68, + "end": 188, + "name": "JUMPI", + "source": 0 + }, + { + "begin": 108, + "end": 109, + "name": "DUP1", + "source": 0 + }, + { + "begin": 105, + "end": 106, + "name": "DUP3", + "source": 0 + }, + { + "begin": 98, + "end": 110, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 149, + "end": 150, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 146, + "end": 147, + "name": "DUP1", + "source": 0 + }, + { + "begin": 143, + "end": 144, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 140, + "end": 141, + "name": "DUP1", + "source": 0 + }, + { + "begin": 137, + "end": 138, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 134, + "end": 135, + "name": "DUP1", + "source": 0 + }, + { + "begin": 123, + "end": 151, + "name": "STATICCALL", + "source": 0 + }, + { + "begin": 119, + "end": 152, + "name": "POP", + "source": 0 + }, + { + "begin": 161, + "end": 182, + "name": "VERBATIM", + "source": 0, + "value": "78797a" + }, + { + "begin": 68, + "end": 188, + "name": "tag", + "source": 0, + "value": "1" + }, + { + "begin": 68, + "end": 188, + "name": "JUMPDEST", + "source": 0 + }, + { + "begin": 203, + "end": 204, + "name": "DUP1", + "source": 0 + }, + { + "begin": 200, + "end": 201, + "name": "DUP3", + "source": 0 + }, + { + "begin": 193, + "end": 205, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 0, + "end": 207, + "name": "POP", + "source": 0 + }, + { + "begin": 0, + "end": 207, + "name": "POP", + "source": 0 + } + ], + "sourceList": [ + "" + ] +} diff --git a/test/cmdlineTests/asm_json_import_yul_more_subobjects/args b/test/cmdlineTests/asm_json_import_yul_more_subobjects/args new file mode 100644 index 000000000000..58dfc8cc7348 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_yul_more_subobjects/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes diff --git a/test/cmdlineTests/asm_json_import_yul_more_subobjects/output b/test/cmdlineTests/asm_json_import_yul_more_subobjects/output new file mode 100644 index 000000000000..81aa5b938705 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_yul_more_subobjects/output @@ -0,0 +1,2 @@ +Opcodes: +PUSH1 0x1E PUSH0 SSTORE PUSH1 0x1A PUSH1 0x1 SSTORE PUSH1 0x15 PUSH1 0x2 SSTORE PUSH1 0x1 PUSH1 0x3 SSTORE PUSH1 0x1 PUSH1 0x4 SSTORE PUSH1 0x1 PUSH1 0x5 SSTORE INVALID diff --git a/test/cmdlineTests/asm_json_import_yul_more_subobjects/stdin b/test/cmdlineTests/asm_json_import_yul_more_subobjects/stdin new file mode 100644 index 000000000000..1820bcf52273 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_yul_more_subobjects/stdin @@ -0,0 +1,348 @@ +{ + ".code": [ + { + "begin": 37, + "end": 51, + "name": "PUSHSIZE", + "source": 0 + }, + { + "begin": 34, + "end": 35, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 27, + "end": 52, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 67, + "end": 81, + "name": "PUSH #[$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 64, + "end": 65, + "name": "PUSH", + "source": 0, + "value": "1" + }, + { + "begin": 57, + "end": 82, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 97, + "end": 114, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000ffffffffffffffff" + }, + { + "begin": 94, + "end": 95, + "name": "PUSH", + "source": 0, + "value": "2" + }, + { + "begin": 87, + "end": 115, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 130, + "end": 152, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffe" + }, + { + "begin": 127, + "end": 128, + "name": "PUSH", + "source": 0, + "value": "3" + }, + { + "begin": 120, + "end": 153, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 168, + "end": 190, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffd" + }, + { + "begin": 165, + "end": 166, + "name": "PUSH", + "source": 0, + "value": "4" + }, + { + "begin": 158, + "end": 191, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 206, + "end": 228, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffc" + }, + { + "begin": 203, + "end": 204, + "name": "PUSH", + "source": 0, + "value": "5" + }, + { + "begin": 196, + "end": 229, + "name": "SSTORE", + "source": 0 + } + ], + ".data": { + "0": { + ".code": [ + { + "begin": 278, + "end": 292, + "name": "PUSHSIZE", + "source": 0 + }, + { + "begin": 275, + "end": 276, + "name": "PUSH", + "source": 0, + "value": "6" + }, + { + "begin": 268, + "end": 293, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 310, + "end": 324, + "name": "PUSH #[$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 307, + "end": 308, + "name": "PUSH", + "source": 0, + "value": "7" + }, + { + "begin": 300, + "end": 325, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 342, + "end": 361, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000ffffffffffffffff" + }, + { + "begin": 339, + "end": 340, + "name": "PUSH", + "source": 0, + "value": "8" + }, + { + "begin": 332, + "end": 362, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 379, + "end": 398, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffe" + }, + { + "begin": 376, + "end": 377, + "name": "PUSH", + "source": 0, + "value": "9" + }, + { + "begin": 369, + "end": 399, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 417, + "end": 436, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffd" + }, + { + "begin": 413, + "end": 415, + "name": "PUSH", + "source": 0, + "value": "A" + }, + { + "begin": 406, + "end": 437, + "name": "SSTORE", + "source": 0 + } + ], + ".data": { + "0": { + ".code": [ + { + "begin": 494, + "end": 508, + "name": "PUSHSIZE", + "source": 0 + }, + { + "begin": 490, + "end": 492, + "name": "PUSH", + "source": 0, + "value": "B" + }, + { + "begin": 483, + "end": 509, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 529, + "end": 545, + "name": "PUSH #[$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 525, + "end": 527, + "name": "PUSH", + "source": 0, + "value": "C" + }, + { + "begin": 518, + "end": 546, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 566, + "end": 582, + "name": "PUSH #[$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "begin": 562, + "end": 564, + "name": "PUSH", + "source": 0, + "value": "D" + }, + { + "begin": 555, + "end": 583, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 603, + "end": 619, + "name": "PUSH #[$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000002" + }, + { + "begin": 599, + "end": 601, + "name": "PUSH", + "source": 0, + "value": "E" + }, + { + "begin": 592, + "end": 620, + "name": "SSTORE", + "source": 0 + } + ], + ".data": { + "0": { + ".code": [ + { + "begin": 676, + "end": 685, + "name": "INVALID", + "source": 0 + } + ] + }, + "1": { + ".code": [ + { + "begin": 751, + "end": 760, + "name": "INVALID", + "source": 0 + } + ] + }, + "2": { + ".code": [ + { + "begin": 826, + "end": 835, + "name": "INVALID", + "source": 0 + } + ] + } + } + } + } + } + }, + "sourceList": [ + "" + ] +} diff --git a/test/cmdlineTests/asm_json_import_yul_subobjects/args b/test/cmdlineTests/asm_json_import_yul_subobjects/args new file mode 100644 index 000000000000..58dfc8cc7348 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_yul_subobjects/args @@ -0,0 +1 @@ +--import-asm-json - --opcodes diff --git a/test/cmdlineTests/asm_json_import_yul_subobjects/output b/test/cmdlineTests/asm_json_import_yul_subobjects/output new file mode 100644 index 000000000000..6aa2d0f64d90 --- /dev/null +++ b/test/cmdlineTests/asm_json_import_yul_subobjects/output @@ -0,0 +1,2 @@ +Opcodes: +PUSH0 DUP1 SSTORE PUSH1 0x6A PUSH1 0x1 SSTORE PUSH1 0x31 PUSH1 0x2 SSTORE PUSH1 0x2C PUSH1 0x3 SSTORE PUSH1 0x5E PUSH1 0x4 SSTORE PUSH1 0xC PUSH1 0x5 SSTORE PUSH1 0x5D PUSH1 0x6 SSTORE PUSH1 0x1 PUSH1 0x7 SSTORE PUSH1 0x5D PUSH1 0x8 SSTORE PUSH1 0x1 PUSH1 0x9 SSTORE INVALID PUSH1 0x1F PUSH1 0xA SSTORE PUSH1 0xC PUSH1 0xB SSTORE PUSH1 0x2B PUSH1 0xC SSTORE PUSH1 0x1 PUSH1 0xD SSTORE PUSH1 0x2B PUSH1 0xE SSTORE PUSH1 0x1 PUSH1 0xF SSTORE INVALID PUSH1 0xB PUSH1 0x10 SSTORE PUSH1 0x1 PUSH1 0x11 SSTORE INVALID INVALID INVALID INVALID PUSH1 0xB PUSH1 0x10 SSTORE PUSH1 0x1 PUSH1 0x11 SSTORE INVALID INVALID diff --git a/test/cmdlineTests/asm_json_import_yul_subobjects/stdin b/test/cmdlineTests/asm_json_import_yul_subobjects/stdin new file mode 100644 index 000000000000..0c8cdcf41f4a --- /dev/null +++ b/test/cmdlineTests/asm_json_import_yul_subobjects/stdin @@ -0,0 +1,400 @@ +{ + ".code": [ + { + "begin": 36, + "end": 51, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 33, + "end": 34, + "name": "DUP1", + "source": 0 + }, + { + "begin": 26, + "end": 52, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 67, + "end": 80, + "name": "PUSHSIZE", + "source": 0 + }, + { + "begin": 64, + "end": 65, + "name": "PUSH", + "source": 0, + "value": "1" + }, + { + "begin": 57, + "end": 81, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 96, + "end": 111, + "name": "PUSH [$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 93, + "end": 94, + "name": "PUSH", + "source": 0, + "value": "2" + }, + { + "begin": 86, + "end": 112, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 127, + "end": 140, + "name": "PUSH #[$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 124, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "3" + }, + { + "begin": 117, + "end": 141, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 156, + "end": 173, + "name": "PUSH [$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000ffffffffffffffff" + }, + { + "begin": 153, + "end": 154, + "name": "PUSH", + "source": 0, + "value": "4" + }, + { + "begin": 146, + "end": 174, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 189, + "end": 204, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000ffffffffffffffff" + }, + { + "begin": 186, + "end": 187, + "name": "PUSH", + "source": 0, + "value": "5" + }, + { + "begin": 179, + "end": 205, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 220, + "end": 237, + "name": "PUSH [$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffe" + }, + { + "begin": 217, + "end": 218, + "name": "PUSH", + "source": 0, + "value": "6" + }, + { + "begin": 210, + "end": 238, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 253, + "end": 268, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffe" + }, + { + "begin": 250, + "end": 251, + "name": "PUSH", + "source": 0, + "value": "7" + }, + { + "begin": 243, + "end": 269, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 284, + "end": 303, + "name": "PUSH [$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffd" + }, + { + "begin": 281, + "end": 282, + "name": "PUSH", + "source": 0, + "value": "8" + }, + { + "begin": 274, + "end": 304, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 319, + "end": 336, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000fffffffffffffffd" + }, + { + "begin": 316, + "end": 317, + "name": "PUSH", + "source": 0, + "value": "9" + }, + { + "begin": 309, + "end": 337, + "name": "SSTORE", + "source": 0 + } + ], + ".data": { + "0": { + ".code": [ + { + "begin": 418, + "end": 433, + "name": "PUSH [$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 414, + "end": 416, + "name": "PUSH", + "source": 0, + "value": "A" + }, + { + "begin": 407, + "end": 434, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 452, + "end": 465, + "name": "PUSH #[$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 448, + "end": 450, + "name": "PUSH", + "source": 0, + "value": "B" + }, + { + "begin": 441, + "end": 466, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 484, + "end": 499, + "name": "PUSH [$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "begin": 480, + "end": 482, + "name": "PUSH", + "source": 0, + "value": "C" + }, + { + "begin": 473, + "end": 500, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 518, + "end": 531, + "name": "PUSH #[$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "begin": 514, + "end": 516, + "name": "PUSH", + "source": 0, + "value": "D" + }, + { + "begin": 507, + "end": 532, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 550, + "end": 567, + "name": "PUSH [$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000ffffffffffffffff" + }, + { + "begin": 546, + "end": 548, + "name": "PUSH", + "source": 0, + "value": "E" + }, + { + "begin": 539, + "end": 568, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 586, + "end": 601, + "name": "PUSH #[$]", + "source": 0, + "value": "000000000000000000000000000000000000000000000000ffffffffffffffff" + }, + { + "begin": 582, + "end": 584, + "name": "PUSH", + "source": 0, + "value": "F" + }, + { + "begin": 575, + "end": 602, + "name": "SSTORE", + "source": 0 + } + ], + ".data": { + "0": { + ".code": [ + { + "begin": 658, + "end": 673, + "name": "PUSH [$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 654, + "end": 656, + "name": "PUSH", + "source": 0, + "value": "10" + }, + { + "begin": 647, + "end": 674, + "name": "SSTORE", + "source": 0 + }, + { + "begin": 694, + "end": 707, + "name": "PUSH #[$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 690, + "end": 692, + "name": "PUSH", + "source": 0, + "value": "11" + }, + { + "begin": 683, + "end": 708, + "name": "SSTORE", + "source": 0 + } + ], + ".data": { + "0": { + ".code": [ + { + "begin": 761, + "end": 770, + "name": "INVALID", + "source": 0 + } + ] + } + } + }, + "1": { + ".code": [ + { + "begin": 833, + "end": 842, + "name": "INVALID", + "source": 0 + } + ] + } + } + }, + "ACAF3289D7B601CBD114FB36C4D29C85BBFD5E133F14CB355C3FD8D99367964F": "48656c6c6f2c20576f726c6421" + }, + "sourceList": [ + "" + ] +} diff --git a/test/cmdlineTests/~evmasm_import_export/test.sh b/test/cmdlineTests/~evmasm_import_export/test.sh new file mode 100755 index 000000000000..f1ef07af8dcb --- /dev/null +++ b/test/cmdlineTests/~evmasm_import_export/test.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +# shellcheck source=scripts/common.sh +source "${REPO_ROOT}/scripts/common.sh" + +SOLTMPDIR=$(mktemp -d -t "cmdline-test-evmasm-import-export-XXXXXX") +cd "$SOLTMPDIR" +if ! "$REPO_ROOT/scripts/ASTImportTest.sh" evm-assembly +then + rm -r "$SOLTMPDIR" + fail +fi +rm -r "$SOLTMPDIR" diff --git a/test/libsolidity/semanticTests/expressions/module_from_ternary_expression.sol b/test/libsolidity/semanticTests/expressions/module_from_ternary_expression.sol index 7aa8a5caa886..937d06c1877c 100644 --- a/test/libsolidity/semanticTests/expressions/module_from_ternary_expression.sol +++ b/test/libsolidity/semanticTests/expressions/module_from_ternary_expression.sol @@ -1,5 +1,5 @@ ==== Source: A ==== -contract C { +contract D { } ==== Source: B ==== import "A" as M; @@ -7,7 +7,7 @@ import "A" as M; contract C { function f() public pure returns (bool) { bool flag; - ((flag = true) ? M : M).C; + ((flag = true) ? M : M).D; return flag; } } diff --git a/test/libsolidity/semanticTests/various/many_subassemblies.sol b/test/libsolidity/semanticTests/various/many_subassemblies.sol new file mode 100644 index 000000000000..26c3a1bbff7b --- /dev/null +++ b/test/libsolidity/semanticTests/various/many_subassemblies.sol @@ -0,0 +1,35 @@ +contract C0 {} +contract C1 {} +contract C2 {} +contract C3 {} +contract C4 {} +contract C5 {} +contract C6 {} +contract C7 {} +contract C8 {} +contract C9 {} +contract C10 {} + +contract D { + function run() public { + // This is primarily meant to test assembly import via --import-asm-json. + // The exported JSON will fail the reimport unless the subassembly indices are parsed + // correctly - as hex numbers. + new C0(); + new C1(); + new C2(); + new C3(); + new C4(); + new C5(); + new C6(); + new C7(); + new C8(); + new C9(); + new C10(); + } +} +// ---- +// run() -> +// gas irOptimized: 381615 +// gas legacy: 392719 +// gas legacyOptimized: 392719 diff --git a/test/libyul/objectCompiler/manySubObjects.yul b/test/libyul/objectCompiler/manySubObjects.yul new file mode 100644 index 000000000000..2f4b1638765f --- /dev/null +++ b/test/libyul/objectCompiler/manySubObjects.yul @@ -0,0 +1,475 @@ +object "root" { + code { + sstore(0, datasize("root")) + sstore(1, datasize("0")) + sstore(2, datasize("1")) + sstore(3, datasize("2")) + sstore(4, datasize("3")) + sstore(5, datasize("4")) + sstore(6, datasize("5")) + sstore(7, datasize("6")) + sstore(8, datasize("7")) + sstore(9, datasize("8")) + sstore(10, datasize("9")) + sstore(11, datasize("a")) + sstore(12, datasize("b")) + sstore(13, datasize("c")) + sstore(14, datasize("d")) + sstore(15, datasize("e")) + sstore(16, datasize("f")) + sstore(17, datasize("10")) + } + + object "0" { + code { + sstore(100, 0) + sstore(200, datasize("sub0")) + } + object "sub0" { + code { + sstore(300, 0) + } + } + } + + object "1" { + code { + sstore(100, 1) + } + } + + object "2" { + code { + sstore(101, 2) + } + } + + object "3" { + code { + sstore(102, 3) + } + } + + object "4" { + code { + sstore(103, 4) + } + } + + object "5" { + code { + sstore(104, 5) + } + } + + object "6" { + code { + sstore(105, 6) + } + } + + object "7" { + code { + sstore(106, 7) + } + } + + object "8" { + code { + sstore(107, 8) + } + } + + object "9" { + code { + sstore(108, 9) + } + } + + object "a" { + code { + sstore(109, 10) + } + } + + object "b" { + code { + sstore(110, 11) + } + } + + object "c" { + code { + sstore(111, 12) + } + } + + object "d" { + code { + sstore(112, 13) + } + } + + object "e" { + code { + sstore(113, 14) + } + } + + object "f" { + code { + sstore(114, 15) + } + } + + object "10" { + code { + sstore(115, 16) + sstore(201, datasize("sub10")) + } + object "sub10" { + code { + sstore(300, 16) + } + } + } +} +// ---- +// Assembly: +// /* "source":45:61 */ +// bytecodeSize +// /* "source":42:43 */ +// 0x00 +// /* "source":35:62 */ +// sstore +// /* "source":81:94 */ +// dataSize(sub_0) +// /* "source":78:79 */ +// 0x01 +// /* "source":71:95 */ +// sstore +// /* "source":114:127 */ +// dataSize(sub_1) +// /* "source":111:112 */ +// 0x02 +// /* "source":104:128 */ +// sstore +// /* "source":147:160 */ +// dataSize(sub_2) +// /* "source":144:145 */ +// 0x03 +// /* "source":137:161 */ +// sstore +// /* "source":180:193 */ +// dataSize(sub_3) +// /* "source":177:178 */ +// 0x04 +// /* "source":170:194 */ +// sstore +// /* "source":213:226 */ +// dataSize(sub_4) +// /* "source":210:211 */ +// 0x05 +// /* "source":203:227 */ +// sstore +// /* "source":246:259 */ +// dataSize(sub_5) +// /* "source":243:244 */ +// 0x06 +// /* "source":236:260 */ +// sstore +// /* "source":279:292 */ +// dataSize(sub_6) +// /* "source":276:277 */ +// 0x07 +// /* "source":269:293 */ +// sstore +// /* "source":312:325 */ +// dataSize(sub_7) +// /* "source":309:310 */ +// 0x08 +// /* "source":302:326 */ +// sstore +// /* "source":345:358 */ +// dataSize(sub_8) +// /* "source":342:343 */ +// 0x09 +// /* "source":335:359 */ +// sstore +// /* "source":379:392 */ +// dataSize(sub_9) +// /* "source":375:377 */ +// 0x0a +// /* "source":368:393 */ +// sstore +// /* "source":413:426 */ +// dataSize(sub_10) +// /* "source":409:411 */ +// 0x0b +// /* "source":402:427 */ +// sstore +// /* "source":447:460 */ +// dataSize(sub_11) +// /* "source":443:445 */ +// 0x0c +// /* "source":436:461 */ +// sstore +// /* "source":481:494 */ +// dataSize(sub_12) +// /* "source":477:479 */ +// 0x0d +// /* "source":470:495 */ +// sstore +// /* "source":515:528 */ +// dataSize(sub_13) +// /* "source":511:513 */ +// 0x0e +// /* "source":504:529 */ +// sstore +// /* "source":549:562 */ +// dataSize(sub_14) +// /* "source":545:547 */ +// 0x0f +// /* "source":538:563 */ +// sstore +// /* "source":583:596 */ +// dataSize(sub_15) +// /* "source":579:581 */ +// 0x10 +// /* "source":572:597 */ +// sstore +// /* "source":617:631 */ +// dataSize(sub_16) +// /* "source":613:615 */ +// 0x11 +// /* "source":606:632 */ +// sstore +// /* "source":25:638 */ +// stop +// stop +// +// sub_0: assembly { +// /* "source":696:697 */ +// 0x00 +// /* "source":691:694 */ +// 0x64 +// /* "source":684:698 */ +// sstore +// /* "source":723:739 */ +// dataSize(sub_0) +// /* "source":718:721 */ +// 0xc8 +// /* "source":711:740 */ +// sstore +// /* "source":670:750 */ +// stop +// stop +// +// sub_0: assembly { +// /* "source":822:823 */ +// 0x00 +// /* "source":817:820 */ +// 0x012c +// /* "source":810:824 */ +// sstore +// /* "source":792:838 */ +// stop +// } +// } +// +// sub_1: assembly { +// /* "source":912:913 */ +// 0x01 +// /* "source":907:910 */ +// 0x64 +// /* "source":900:914 */ +// sstore +// /* "source":886:924 */ +// stop +// } +// +// sub_2: assembly { +// /* "source":988:989 */ +// 0x02 +// /* "source":983:986 */ +// 0x65 +// /* "source":976:990 */ +// sstore +// /* "source":962:1000 */ +// stop +// } +// +// sub_3: assembly { +// /* "source":1064:1065 */ +// 0x03 +// /* "source":1059:1062 */ +// 0x66 +// /* "source":1052:1066 */ +// sstore +// /* "source":1038:1076 */ +// stop +// } +// +// sub_4: assembly { +// /* "source":1140:1141 */ +// 0x04 +// /* "source":1135:1138 */ +// 0x67 +// /* "source":1128:1142 */ +// sstore +// /* "source":1114:1152 */ +// stop +// } +// +// sub_5: assembly { +// /* "source":1216:1217 */ +// 0x05 +// /* "source":1211:1214 */ +// 0x68 +// /* "source":1204:1218 */ +// sstore +// /* "source":1190:1228 */ +// stop +// } +// +// sub_6: assembly { +// /* "source":1292:1293 */ +// 0x06 +// /* "source":1287:1290 */ +// 0x69 +// /* "source":1280:1294 */ +// sstore +// /* "source":1266:1304 */ +// stop +// } +// +// sub_7: assembly { +// /* "source":1368:1369 */ +// 0x07 +// /* "source":1363:1366 */ +// 0x6a +// /* "source":1356:1370 */ +// sstore +// /* "source":1342:1380 */ +// stop +// } +// +// sub_8: assembly { +// /* "source":1444:1445 */ +// 0x08 +// /* "source":1439:1442 */ +// 0x6b +// /* "source":1432:1446 */ +// sstore +// /* "source":1418:1456 */ +// stop +// } +// +// sub_9: assembly { +// /* "source":1520:1521 */ +// 0x09 +// /* "source":1515:1518 */ +// 0x6c +// /* "source":1508:1522 */ +// sstore +// /* "source":1494:1532 */ +// stop +// } +// +// sub_10: assembly { +// /* "source":1596:1598 */ +// 0x0a +// /* "source":1591:1594 */ +// 0x6d +// /* "source":1584:1599 */ +// sstore +// /* "source":1570:1609 */ +// stop +// } +// +// sub_11: assembly { +// /* "source":1673:1675 */ +// 0x0b +// /* "source":1668:1671 */ +// 0x6e +// /* "source":1661:1676 */ +// sstore +// /* "source":1647:1686 */ +// stop +// } +// +// sub_12: assembly { +// /* "source":1750:1752 */ +// 0x0c +// /* "source":1745:1748 */ +// 0x6f +// /* "source":1738:1753 */ +// sstore +// /* "source":1724:1763 */ +// stop +// } +// +// sub_13: assembly { +// /* "source":1827:1829 */ +// 0x0d +// /* "source":1822:1825 */ +// 0x70 +// /* "source":1815:1830 */ +// sstore +// /* "source":1801:1840 */ +// stop +// } +// +// sub_14: assembly { +// /* "source":1904:1906 */ +// 0x0e +// /* "source":1899:1902 */ +// 0x71 +// /* "source":1892:1907 */ +// sstore +// /* "source":1878:1917 */ +// stop +// } +// +// sub_15: assembly { +// /* "source":1981:1983 */ +// 0x0f +// /* "source":1976:1979 */ +// 0x72 +// /* "source":1969:1984 */ +// sstore +// /* "source":1955:1994 */ +// stop +// } +// +// sub_16: assembly { +// /* "source":2059:2061 */ +// 0x10 +// /* "source":2054:2057 */ +// 0x73 +// /* "source":2047:2062 */ +// sstore +// /* "source":2087:2104 */ +// dataSize(sub_0) +// /* "source":2082:2085 */ +// 0xc9 +// /* "source":2075:2105 */ +// sstore +// /* "source":2033:2115 */ +// stop +// stop +// +// sub_0: assembly { +// /* "source":2188:2190 */ +// 0x10 +// /* "source":2183:2186 */ +// 0x012c +// /* "source":2176:2191 */ +// sstore +// /* "source":2158:2205 */ +// stop +// } +// } +// Bytecode: 61005c5f55600b600155600660025560066003556006600455600660055560066006556006600755600660085560066009556006600a556006600b556006600c556006600d556006600e556006600f556006601055600c60115500fe +// Opcodes: PUSH2 0x5C PUSH0 SSTORE PUSH1 0xB PUSH1 0x1 SSTORE PUSH1 0x6 PUSH1 0x2 SSTORE PUSH1 0x6 PUSH1 0x3 SSTORE PUSH1 0x6 PUSH1 0x4 SSTORE PUSH1 0x6 PUSH1 0x5 SSTORE PUSH1 0x6 PUSH1 0x6 SSTORE PUSH1 0x6 PUSH1 0x7 SSTORE PUSH1 0x6 PUSH1 0x8 SSTORE PUSH1 0x6 PUSH1 0x9 SSTORE PUSH1 0x6 PUSH1 0xA SSTORE PUSH1 0x6 PUSH1 0xB SSTORE PUSH1 0x6 PUSH1 0xC SSTORE PUSH1 0x6 PUSH1 0xD SSTORE PUSH1 0x6 PUSH1 0xE SSTORE PUSH1 0x6 PUSH1 0xF SSTORE PUSH1 0x6 PUSH1 0x10 SSTORE PUSH1 0xC PUSH1 0x11 SSTORE STOP INVALID +// SourceMappings: 45:16:0:-:0;42:1;35:27;81:13;78:1;71:24;114:13;111:1;104:24;147:13;144:1;137:24;180:13;177:1;170:24;213:13;210:1;203:24;246:13;243:1;236:24;279:13;276:1;269:24;312:13;309:1;302:24;345:13;342:1;335:24;379:13;375:2;368:25;413:13;409:2;402:25;447:13;443:2;436:25;481:13;477:2;470:25;515:13;511:2;504:25;549:13;545:2;538:25;583:13;579:2;572:25;617:14;613:2;606:26;25:613 diff --git a/test/solc/CommandLineInterface.cpp b/test/solc/CommandLineInterface.cpp index 6a976df42ac8..df57572a5349 100644 --- a/test/solc/CommandLineInterface.cpp +++ b/test/solc/CommandLineInterface.cpp @@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE(version) BOOST_AUTO_TEST_CASE(multiple_input_modes) { - array inputModeOptions = { + array inputModeOptions = { "--help", "--license", "--version", @@ -159,10 +159,11 @@ BOOST_AUTO_TEST_CASE(multiple_input_modes) "--strict-assembly", "--yul", "--import-ast", + "--import-asm-json", }; string expectedMessage = "The following options are mutually exclusive: " - "--help, --license, --version, --standard-json, --link, --assemble, --strict-assembly, --yul, --import-ast, --lsp. " + "--help, --license, --version, --standard-json, --link, --assemble, --strict-assembly, --yul, --import-ast, --lsp, --import-asm-json. " "Select at most one."; for (string const& mode1: inputModeOptions)