Skip to content

Commit d534134

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

File tree

4 files changed

+207
-8
lines changed

4 files changed

+207
-8
lines changed

libevmasm/Assembly.cpp

Lines changed: 159 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@
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>
4141

4242
#include <fstream>
4343
#include <limits>
44+
#include <iterator>
4445

4546
using namespace std;
4647
using namespace solidity;
@@ -74,6 +75,123 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
7475
}
7576
}
7677

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

@@ -244,7 +362,7 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices,
244362
jsonItem["end"] = item.location().end;
245363
if (item.m_modifierDepth != 0)
246364
jsonItem["modifierDepth"] = static_cast<int>(item.m_modifierDepth);
247-
std::string jumpType = item.getJumpTypeAsString();
365+
string jumpType = item.getJumpTypeAsString();
248366
if (!jumpType.empty())
249367
jsonItem["jumpType"] = jumpType;
250368
if (name == "PUSHLIB")
@@ -286,7 +404,7 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices,
286404

287405
for (size_t i = 0; i < m_subs.size(); ++i)
288406
{
289-
std::stringstream hexStr;
407+
stringstream hexStr;
290408
hexStr << hex << i;
291409
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices, /*_includeSourceList = */false);
292410
}
@@ -298,6 +416,43 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices,
298416
return root;
299417
}
300418

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

342497
map<u256, u256> const& Assembly::optimiseInternal(
343498
OptimiserSettings const& _settings,
344-
std::set<size_t> _tagsReferencedFromOutside
499+
set<size_t> _tagsReferencedFromOutside
345500
)
346501
{
347502
if (m_tagReplacements)

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: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ namespace
4242

4343
string toStringInHex(u256 _value)
4444
{
45-
std::stringstream hexStr;
46-
hexStr << std::uppercase << hex << _value;
45+
stringstream hexStr;
46+
hexStr << uppercase << hex << _value;
4747
return hexStr.str();
4848
}
4949

@@ -158,7 +158,7 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision)
158158
return 2;
159159
}
160160
case VerbatimBytecode:
161-
return std::get<2>(*m_verbatimBytecode).size();
161+
return get<2>(*m_verbatimBytecode).size();
162162
default:
163163
break;
164164
}
@@ -243,6 +243,18 @@ string AssemblyItem::getJumpTypeAsString() const
243243
}
244244
}
245245

246+
void AssemblyItem::setJumpType(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;
@@ -406,7 +418,7 @@ size_t AssemblyItem::opcodeCount() const noexcept
406418
}
407419
}
408420

409-
std::string AssemblyItem::computeSourceMapping(
421+
string AssemblyItem::computeSourceMapping(
410422
AssemblyItems const& _items,
411423
map<string, unsigned> const& _sourceIndicesMap
412424
)

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)