Skip to content

libevmasm: Add support to import evm assembly json. #12834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f042c6c
libevmasm: Add support to import evm assembly json.
aarlt Mar 22, 2022
de5f640
Add evm-assembly import/export tests.
aarlt Mar 28, 2022
263dc9b
Rename scripts/ASTImportTest.sh -> scripts/ImportExportTest.sh.
aarlt Mar 28, 2022
3aa916e
Update cmdlineTests.sh.
aarlt Mar 28, 2022
c5e6a5b
scripts/ImportExportTest.sh: refactorings.
aarlt Mar 28, 2022
3e2b62a
scripts/ImportExportTest.sh: add support for 'set -euo pipefail'.
aarlt Mar 29, 2022
d4c8c99
Refactorings.
aarlt Mar 31, 2022
8e55cac
Refactorings.
aarlt Apr 27, 2022
706e6aa
Assembly::loadFromAssemblyJSON(..) as static factory function.
aarlt May 9, 2022
793adf4
Assembly::OptimiserSettings: add translateSettings(..).
aarlt May 9, 2022
6c663ac
libevmasm/Assembly.cpp: minors.
aarlt May 9, 2022
f065760
[solc] CommandLineInterface.cpp: Remove optional parameter in solAsse…
aarlt Aug 16, 2022
65d09e7
[solc] CommandLineParser: introduction of any_of / none_of.
aarlt Aug 16, 2022
e57c137
Minors.
aarlt Aug 16, 2022
4f76877
Remove Assembly::OptimiserSettings::translateSettings(..).
aarlt Aug 16, 2022
5c9c88c
[libsolidity] interface/CompilerStack.h: Remove m_evmAssemblyJson.
aarlt Aug 17, 2022
555d774
[libsolutil] JSON: Add ofType, ofTypeIfExists & getOrDefault.
aarlt Aug 18, 2022
1451520
[libsolutil] JSON: Add get function.
aarlt Aug 18, 2022
1774de0
CommandLineParser: Remove any_of / none_of.
aarlt Aug 19, 2022
91e3b65
Minors.
aarlt Aug 19, 2022
0e9505c
[scripts] ImportExportTest.sh: shellcheck source=scripts/common.sh
aarlt Aug 19, 2022
bf5565d
scripts/ImportExportTest.sh: refactorings.
aarlt Aug 19, 2022
b56121f
solc/CommandLineInterface.cpp: fix style.
aarlt Sep 9, 2022
b174aba
scripts/ImportExportTest.sh: Fix "${OUTPUT[*]}".
aarlt Sep 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 155 additions & 1 deletion libevmasm/Assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
#include <liblangutil/CharStream.h>
#include <liblangutil/Exceptions.h>

#include <json/json.h>
#include <libsolutil/JSON.h>

#include <range/v3/algorithm/any_of.hpp>
#include <range/v3/view/enumerate.hpp>
Expand Down Expand Up @@ -74,6 +74,123 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
}
}

void Assembly::addAssemblyItemsFromJSON(Json::Value const& _code)
{
solAssert(m_items.empty(), "");
solAssert(_code.isArray(), "");
for (auto const& jsonItem: _code)
m_items.emplace_back(createAssemblyItemFromJSON(jsonItem));

for (auto current = m_items.begin(); current != m_items.end(); ++current)
{
// During the assembly json export a `JUMPDEST` is always generated after a `tag`.
// So we just ignore exactly these `JUMPDEST`'s.
auto const next = std::next(current);
if (
next != m_items.end() &&
current->type() == AssemblyItemType::Tag &&
next->type() == AssemblyItemType::Operation &&
next->instruction() == Instruction::JUMPDEST
)
m_items.erase(next);
}
}

AssemblyItem Assembly::createAssemblyItemFromJSON(Json::Value const& _json)
{
solAssert(ofType<std::string>(_json, "name"));
solAssert(ofType<int>(_json, "begin"));
solAssert(ofType<int>(_json, "end"));
solAssert(ofType<int>(_json, "source"));
solAssert(ofTypeIfExists<std::string>(_json, "value"));
solAssert(ofTypeIfExists<int>(_json, "modifierDepth"));
solAssert(ofTypeIfExists<std::string>(_json, "jumpType"));

std::string name = getOrDefault<std::string>(_json, "name", "");
solAssert(!name.empty());

SourceLocation location;
location.start = get<int>(_json, "begin");
location.end = get<int>(_json, "end");
int srcIndex = get<int>(_json, "source");
size_t modifierDepth = static_cast<size_t>(getOrDefault<int>(_json, "modifierDepth", 0));
std::string value = getOrDefault<std::string>(_json, "value", "");
std::string jumpType = getOrDefault<std::string>(_json, "jumpType", "");


auto updateUsedTags = [&](u256 const& data) {
m_usedTags = max(m_usedTags, static_cast<unsigned>(data) + 1);
return data;
};

auto immutableHash = [&](string const& _immutableName) -> h256 {
h256 hash(util::keccak256(_immutableName));
m_immutables[hash] = _immutableName;
return hash;
};

auto libraryHash = [&](string const& _libraryName) -> h256 {
h256 hash(util::keccak256(_libraryName));
m_libraries[hash] = _libraryName;
return hash;
};

if (srcIndex > -1 && srcIndex < static_cast<int>(sources().size()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we allow out-of-bounds source indices?

location.sourceName = sources()[static_cast<size_t>(srcIndex)];

AssemblyItem result(0);

if (c_instructions.count(name))
{
AssemblyItem item{c_instructions.at(name), location};
if (!jumpType.empty())
item.setJumpType(jumpType);
result = item;
}
else
{
if (name == "PUSH")
{
AssemblyItem item{AssemblyItemType::Push, u256("0x" + value)};
if (!jumpType.empty())
item.setJumpType(jumpType);
result = item;
}
else if (name == "PUSH [ErrorTag]")
result = {AssemblyItemType::PushTag, 0};
else if (name == "PUSH [tag]")
result = {AssemblyItemType::PushTag, updateUsedTags(u256(value))};
else if (name == "PUSH [$]")
result = {AssemblyItemType::PushSub, u256("0x" + value)};
else if (name == "PUSH #[$]")
result = {AssemblyItemType::PushSubSize, u256("0x" + value)};
else if (name == "PUSHSIZE")
result = {AssemblyItemType::PushProgramSize, 0};
else if (name == "PUSHLIB")
result = {AssemblyItemType::PushLibraryAddress, libraryHash(value)};
else if (name == "PUSHDEPLOYADDRESS")
result = {AssemblyItemType::PushDeployTimeAddress, 0};
else if (name == "PUSHIMMUTABLE")
result = {AssemblyItemType::PushImmutable, immutableHash(value)};
else if (name == "ASSIGNIMMUTABLE")
result = {AssemblyItemType::AssignImmutable, immutableHash(value)};
else if (name == "tag")
result = {AssemblyItemType::Tag, updateUsedTags(u256(value))};
else if (name == "PUSH data")
result = {AssemblyItemType::PushData, u256("0x" + value)};
else if (name == "VERBATIM")
{
AssemblyItem item(fromHex(value), 0, 0);
result = item;
}
else
assertThrow(false, InvalidOpcode, "");
}
result.setLocation(location);
result.m_modifierDepth = modifierDepth;
return result;
}

namespace
{

Expand Down Expand Up @@ -298,6 +415,43 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices,
return root;
}

std::shared_ptr<Assembly> Assembly::loadFromAssemblyJSON(Json::Value const& _json, std::vector<std::string> const& _sourceList /* = {} */, bool _isCreation /* = true */)
{
if (!_json[".code"].isArray())
return {};

std::shared_ptr<Assembly> result = std::make_shared<Assembly>(_isCreation, "");
vector<string> sourceList;
if (_sourceList.empty())
{
if (_json.isMember("sourceList"))
for (auto const& it: _json["sourceList"])
sourceList.emplace_back(it.asString());
}
else
sourceList = _sourceList;
result->setSources(sourceList);
result->addAssemblyItemsFromJSON(_json[".code"]);
if (_json[".auxdata"].isString())
result->m_auxiliaryData = fromHex(_json[".auxdata"].asString());
Json::Value const& data = _json[".data"];
for (Json::ValueConstIterator itr = data.begin(); itr != data.end(); itr++)
{
solAssert(itr.key().isString(), "");
std::string key = itr.key().asString();
Json::Value const& code = data[key];
if (code.isString())
result->m_data[h256(fromHex(key))] = fromHex(code.asString());
else
{
std::shared_ptr<Assembly> subassembly(Assembly::loadFromAssemblyJSON(code, sourceList, /* isCreation = */ false));
assertThrow(subassembly, AssemblyException, "");
result->m_subs.emplace_back(std::make_shared<Assembly>(*subassembly));
}
}
return result;
}

AssemblyItem Assembly::namedTag(string const& _name, size_t _params, size_t _returns, optional<uint64_t> _sourceID)
{
assertThrow(!_name.empty(), AssemblyException, "Empty named tag.");
Expand Down
31 changes: 31 additions & 0 deletions libevmasm/Assembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ class Assembly
bool _includeSourceList = true
) const;

/// Loads the JSON representation of assembly.
/// @param _json JSON object containing assembly
/// @param _loadSources true, if source list should be included, false otherwise.
/// @returns true on success, false otherwise
static std::shared_ptr<Assembly> loadFromAssemblyJSON(Json::Value const& _json, std::vector<std::string> const& _sourceList = {}, bool _isCreation = true);

/// Mark this assembly as invalid. Calling ``assemble`` on it will throw.
void markAsInvalid() { m_invalid = true; }

Expand All @@ -160,6 +166,22 @@ class Assembly

bool isCreation() const { return m_creation; }

/// Set the source name list.
void setSources(std::vector<std::shared_ptr<std::string const>> _sources)
{
m_sources = std::move(_sources);
}

/// Set the source name list from simple vector<string>.
void setSources(std::vector<std::string> const& _sources)
{
for (auto const& item: _sources)
m_sources.emplace_back(std::make_shared<std::string>(item));
}

/// @returns List of source names.
std::vector<std::shared_ptr<std::string const>> sources() const& { return m_sources; }

protected:
/// Does the same operations as @a optimise, but should only be applied to a sub and
/// returns the replaced tags. Also takes an argument containing the tags of this assembly
Expand All @@ -168,6 +190,14 @@ class Assembly

unsigned codeSize(unsigned subTagSize) const;

/// Add all assembly items from given JSON array.
void addAssemblyItemsFromJSON(Json::Value const& _code);

/// Creates an AssemblyItem from a given JSON representation.
/// @param _json JSON representation of an assembly item
/// @returns AssemblyItem of _json argument.
AssemblyItem createAssemblyItemFromJSON(Json::Value const& _json);

private:
bool m_invalid = false;

Expand Down Expand Up @@ -214,6 +244,7 @@ class Assembly
std::string m_name;

langutil::SourceLocation m_currentSourceLocation;
std::vector<std::shared_ptr<std::string const>> m_sources;

public:
size_t m_currentModifierDepth = 0;
Expand Down
12 changes: 12 additions & 0 deletions libevmasm/AssemblyItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,18 @@ string AssemblyItem::getJumpTypeAsString() const
}
}

void AssemblyItem::setJumpType(std::string const& _jumpType)
{
if (_jumpType == "[in]")
m_jumpType = JumpType::IntoFunction;
else if (_jumpType == "[out]")
m_jumpType = JumpType::OutOfFunction;
else if (_jumpType.empty())
m_jumpType = JumpType::Ordinary;
else
assertThrow(false, AssemblyException, "Invalid jump type.");
}

string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
{
string text;
Expand Down
1 change: 1 addition & 0 deletions libevmasm/AssemblyItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ class AssemblyItem
langutil::SourceLocation const& location() const { return m_location; }

void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; }
void setJumpType(std::string const& _jumpType);
JumpType getJumpType() const { return m_jumpType; }
std::string getJumpTypeAsString() const;

Expand Down
Loading