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