Skip to content

Commit f23e0e9

Browse files
committed
[libevmasm] Add support to import evm assembly json.
1 parent e9a7742 commit f23e0e9

File tree

4 files changed

+199
-1
lines changed

4 files changed

+199
-1
lines changed

libevmasm/Assembly.cpp

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
#include <liblangutil/CharStream.h>
3535
#include <liblangutil/Exceptions.h>
3636

37-
#include <json/json.h>
37+
#include <libsolutil/JSON.h>
3838

3939
#include <range/v3/algorithm/any_of.hpp>
4040
#include <range/v3/view/enumerate.hpp>
@@ -74,6 +74,123 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
7474
}
7575
}
7676

77+
void Assembly::addAssemblyItemsFromJSON(Json::Value const& _code)
78+
{
79+
solAssert(m_items.empty(), "");
80+
solAssert(_code.isArray(), "");
81+
for (auto const& jsonItem: _code)
82+
m_items.emplace_back(createAssemblyItemFromJSON(jsonItem));
83+
84+
for (auto current = m_items.begin(); current != m_items.end(); ++current)
85+
{
86+
// During the assembly json export a `JUMPDEST` is always generated after a `tag`.
87+
// So we just ignore exactly these `JUMPDEST`'s.
88+
auto const next = std::next(current);
89+
if (
90+
next != m_items.end() &&
91+
current->type() == AssemblyItemType::Tag &&
92+
next->type() == AssemblyItemType::Operation &&
93+
next->instruction() == Instruction::JUMPDEST
94+
)
95+
m_items.erase(next);
96+
}
97+
}
98+
99+
AssemblyItem Assembly::createAssemblyItemFromJSON(Json::Value const& _json)
100+
{
101+
solAssert(isOfType<std::string>(_json["name"]));
102+
solAssert(isOfType<int>(_json["begin"]));
103+
solAssert(isOfType<int>(_json["end"]));
104+
solAssert(isOfType<int>(_json["source"]));
105+
solAssert(isOfTypeIfExists<std::string>(_json, "value"));
106+
solAssert(isOfTypeIfExists<int>(_json, "modifierDepth"));
107+
solAssert(isOfTypeIfExists<std::string>(_json, "jumpType"));
108+
109+
std::string name = getOrDefault<std::string>(_json["name"], "");
110+
solAssert(!name.empty());
111+
112+
SourceLocation location;
113+
location.start = get<int>(_json["begin"]);
114+
location.end = get<int>(_json["end"]);
115+
int srcIndex = get<int>(_json["source"]);
116+
size_t modifierDepth = static_cast<size_t>(getOrDefault<int>(_json["modifierDepth"], 0));
117+
std::string value = getOrDefault<std::string>(_json["value"], "");
118+
std::string jumpType = getOrDefault<std::string>(_json["jumpType"], "");
119+
120+
121+
auto updateUsedTags = [&](u256 const& data) {
122+
m_usedTags = max(m_usedTags, static_cast<unsigned>(data) + 1);
123+
return data;
124+
};
125+
126+
auto immutableHash = [&](string const& _immutableName) -> h256 {
127+
h256 hash(util::keccak256(_immutableName));
128+
m_immutables[hash] = _immutableName;
129+
return hash;
130+
};
131+
132+
auto libraryHash = [&](string const& _libraryName) -> h256 {
133+
h256 hash(util::keccak256(_libraryName));
134+
m_libraries[hash] = _libraryName;
135+
return hash;
136+
};
137+
138+
if (srcIndex > -1 && srcIndex < static_cast<int>(sources().size()))
139+
location.sourceName = sources()[static_cast<size_t>(srcIndex)];
140+
141+
AssemblyItem result(0);
142+
143+
if (c_instructions.count(name))
144+
{
145+
AssemblyItem item{c_instructions.at(name), location};
146+
if (!jumpType.empty())
147+
item.setJumpType(jumpType);
148+
result = item;
149+
}
150+
else
151+
{
152+
if (name == "PUSH")
153+
{
154+
AssemblyItem item{AssemblyItemType::Push, u256("0x" + value)};
155+
if (!jumpType.empty())
156+
item.setJumpType(jumpType);
157+
result = item;
158+
}
159+
else if (name == "PUSH [ErrorTag]")
160+
result = {AssemblyItemType::PushTag, 0};
161+
else if (name == "PUSH [tag]")
162+
result = {AssemblyItemType::PushTag, updateUsedTags(u256(value))};
163+
else if (name == "PUSH [$]")
164+
result = {AssemblyItemType::PushSub, u256("0x" + value)};
165+
else if (name == "PUSH #[$]")
166+
result = {AssemblyItemType::PushSubSize, u256("0x" + value)};
167+
else if (name == "PUSHSIZE")
168+
result = {AssemblyItemType::PushProgramSize, 0};
169+
else if (name == "PUSHLIB")
170+
result = {AssemblyItemType::PushLibraryAddress, libraryHash(value)};
171+
else if (name == "PUSHDEPLOYADDRESS")
172+
result = {AssemblyItemType::PushDeployTimeAddress, 0};
173+
else if (name == "PUSHIMMUTABLE")
174+
result = {AssemblyItemType::PushImmutable, immutableHash(value)};
175+
else if (name == "ASSIGNIMMUTABLE")
176+
result = {AssemblyItemType::AssignImmutable, immutableHash(value)};
177+
else if (name == "tag")
178+
result = {AssemblyItemType::Tag, updateUsedTags(u256(value))};
179+
else if (name == "PUSH data")
180+
result = {AssemblyItemType::PushData, u256("0x" + value)};
181+
else if (name == "VERBATIM")
182+
{
183+
AssemblyItem item(fromHex(value), 0, 0);
184+
result = item;
185+
}
186+
else
187+
assertThrow(false, InvalidOpcode, "");
188+
}
189+
result.setLocation(location);
190+
result.m_modifierDepth = modifierDepth;
191+
return result;
192+
}
193+
77194
namespace
78195
{
79196

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

418+
std::shared_ptr<Assembly> Assembly::loadFromAssemblyJSON(Json::Value const& _json, std::vector<std::string> const& _sourceList /* = {} */, bool _isCreation /* = true */)
419+
{
420+
if (!_json[".code"].isArray())
421+
return {};
422+
423+
std::shared_ptr<Assembly> result = std::make_shared<Assembly>(_isCreation, "");
424+
vector<string> sourceList;
425+
if (_sourceList.empty())
426+
{
427+
if (_json.isMember("sourceList"))
428+
for (auto const& it: _json["sourceList"])
429+
sourceList.emplace_back(it.asString());
430+
}
431+
else
432+
sourceList = _sourceList;
433+
result->setSources(sourceList);
434+
result->addAssemblyItemsFromJSON(_json[".code"]);
435+
if (_json[".auxdata"].isString())
436+
result->m_auxiliaryData = fromHex(_json[".auxdata"].asString());
437+
Json::Value const& data = _json[".data"];
438+
for (Json::ValueConstIterator itr = data.begin(); itr != data.end(); itr++)
439+
{
440+
solAssert(itr.key().isString(), "");
441+
std::string key = itr.key().asString();
442+
Json::Value const& code = data[key];
443+
if (code.isString())
444+
result->m_data[h256(fromHex(key))] = fromHex(code.asString());
445+
else
446+
{
447+
std::shared_ptr<Assembly> subassembly(Assembly::loadFromAssemblyJSON(code, sourceList, /* isCreation = */ false));
448+
assertThrow(subassembly, AssemblyException, "");
449+
result->m_subs.emplace_back(std::make_shared<Assembly>(*subassembly));
450+
}
451+
}
452+
return result;
453+
}
454+
301455
AssemblyItem Assembly::namedTag(string const& _name, size_t _params, size_t _returns, optional<uint64_t> _sourceID)
302456
{
303457
assertThrow(!_name.empty(), AssemblyException, "Empty named tag.");

libevmasm/Assembly.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@ class Assembly
154154
bool _includeSourceList = true
155155
) const;
156156

157+
/// Loads the JSON representation of assembly.
158+
/// @param _json JSON object containing assembly
159+
/// @param _loadSources true, if source list should be included, false otherwise.
160+
/// @returns true on success, false otherwise
161+
static std::shared_ptr<Assembly> loadFromAssemblyJSON(Json::Value const& _json, std::vector<std::string> const& _sourceList = {}, bool _isCreation = true);
162+
157163
/// Mark this assembly as invalid. Calling ``assemble`` on it will throw.
158164
void markAsInvalid() { m_invalid = true; }
159165

@@ -162,6 +168,22 @@ class Assembly
162168

163169
bool isCreation() const { return m_creation; }
164170

171+
/// Set the source name list.
172+
void setSources(std::vector<std::shared_ptr<std::string const>> _sources)
173+
{
174+
m_sources = std::move(_sources);
175+
}
176+
177+
/// Set the source name list from simple vector<string>.
178+
void setSources(std::vector<std::string> const& _sources)
179+
{
180+
for (auto const& item: _sources)
181+
m_sources.emplace_back(std::make_shared<std::string>(item));
182+
}
183+
184+
/// @returns List of source names.
185+
std::vector<std::shared_ptr<std::string const>> sources() const& { return m_sources; }
186+
165187
protected:
166188
/// Does the same operations as @a optimise, but should only be applied to a sub and
167189
/// returns the replaced tags. Also takes an argument containing the tags of this assembly
@@ -170,6 +192,14 @@ class Assembly
170192

171193
unsigned codeSize(unsigned subTagSize) const;
172194

195+
/// Add all assembly items from given JSON array.
196+
void addAssemblyItemsFromJSON(Json::Value const& _code);
197+
198+
/// Creates an AssemblyItem from a given JSON representation.
199+
/// @param _json JSON representation of an assembly item
200+
/// @returns AssemblyItem of _json argument.
201+
AssemblyItem createAssemblyItemFromJSON(Json::Value const& _json);
202+
173203
private:
174204
bool m_invalid = false;
175205

@@ -216,6 +246,7 @@ class Assembly
216246
std::string m_name;
217247

218248
langutil::SourceLocation m_currentSourceLocation;
249+
std::vector<std::shared_ptr<std::string const>> m_sources;
219250

220251
public:
221252
size_t m_currentModifierDepth = 0;

libevmasm/AssemblyItem.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,18 @@ string AssemblyItem::getJumpTypeAsString() const
243243
}
244244
}
245245

246+
void AssemblyItem::setJumpType(std::string const& _jumpType)
247+
{
248+
if (_jumpType == "[in]")
249+
m_jumpType = JumpType::IntoFunction;
250+
else if (_jumpType == "[out]")
251+
m_jumpType = JumpType::OutOfFunction;
252+
else if (_jumpType.empty())
253+
m_jumpType = JumpType::Ordinary;
254+
else
255+
assertThrow(false, AssemblyException, "Invalid jump type.");
256+
}
257+
246258
string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
247259
{
248260
string text;

libevmasm/AssemblyItem.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ class AssemblyItem
173173
langutil::SourceLocation const& location() const { return m_location; }
174174

175175
void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; }
176+
void setJumpType(std::string const& _jumpType);
176177
JumpType getJumpType() const { return m_jumpType; }
177178
std::string getJumpTypeAsString() const;
178179

0 commit comments

Comments
 (0)