Skip to content

Commit e4acc25

Browse files
committed
libevmasm: refactor asm-json export & add support for source list.
1 parent 43f29c0 commit e4acc25

File tree

7 files changed

+206
-164
lines changed

7 files changed

+206
-164
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Compiler Features:
88

99

1010
Bugfixes:
11+
* Assembly: Fix assembly json export to store jump types of operations in `jumpType` field (instead of `value`).
1112

1213

1314

libevmasm/Assembly.cpp

Lines changed: 46 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -222,141 +222,80 @@ string Assembly::assemblyString(
222222
return tmp.str();
223223
}
224224

225-
Json::Value Assembly::createJsonValue(string _name, int _source, int _begin, int _end, string _value, string _jumpType)
226-
{
227-
Json::Value value{Json::objectValue};
228-
value["name"] = _name;
229-
value["source"] = _source;
230-
value["begin"] = _begin;
231-
value["end"] = _end;
232-
if (!_value.empty())
233-
value["value"] = _value;
234-
if (!_jumpType.empty())
235-
value["jumpType"] = _jumpType;
236-
return value;
237-
}
238-
239-
string Assembly::toStringInHex(u256 _value)
240-
{
241-
std::stringstream hexStr;
242-
hexStr << std::uppercase << hex << _value;
243-
return hexStr.str();
244-
}
245-
246-
Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices) const
225+
Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices, bool _includeSourceList) const
247226
{
248227
Json::Value root;
249228
root[".code"] = Json::arrayValue;
250-
251-
Json::Value& collection = root[".code"];
252-
for (AssemblyItem const& i: m_items)
229+
Json::Value& code = root[".code"];
230+
for (AssemblyItem const& item: m_items)
253231
{
254232
int sourceIndex = -1;
255-
if (i.location().sourceName)
233+
if (item.location().sourceName)
256234
{
257-
auto iter = _sourceIndices.find(*i.location().sourceName);
235+
auto iter = _sourceIndices.find(*item.location().sourceName);
258236
if (iter != _sourceIndices.end())
259237
sourceIndex = static_cast<int>(iter->second);
260238
}
261239

262-
switch (i.type())
240+
auto [name, data] = item.nameAndData();
241+
Json::Value jsonItem;
242+
jsonItem["name"] = name;
243+
jsonItem["begin"] = item.location().start;
244+
jsonItem["end"] = item.location().end;
245+
if (item.m_modifierDepth != 0)
246+
jsonItem["modifierDepth"] = static_cast<int>(item.m_modifierDepth);
247+
std::string jumpType = item.getJumpTypeAsString();
248+
if (!jumpType.empty())
249+
jsonItem["jumpType"] = jumpType;
250+
if (name == "PUSHLIB")
251+
data = m_libraries.at(h256(data));
252+
else if (name == "PUSHIMMUTABLE" || name == "ASSIGNIMMUTABLE")
253+
data = m_immutables.at(h256(data));
254+
if (!data.empty())
255+
jsonItem["value"] = data;
256+
jsonItem["source"] = sourceIndex;
257+
code.append(move(jsonItem));
258+
259+
if (item.type() == AssemblyItemType::Tag)
263260
{
264-
case Operation:
265-
collection.append(
266-
createJsonValue(
267-
instructionInfo(i.instruction()).name,
268-
sourceIndex,
269-
i.location().start,
270-
i.location().end,
271-
i.getJumpTypeAsString())
272-
);
273-
break;
274-
case Push:
275-
collection.append(
276-
createJsonValue("PUSH", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data()), i.getJumpTypeAsString()));
277-
break;
278-
case PushTag:
279-
if (i.data() == 0)
280-
collection.append(
281-
createJsonValue("PUSH [ErrorTag]", sourceIndex, i.location().start, i.location().end, ""));
282-
else
283-
collection.append(
284-
createJsonValue("PUSH [tag]", sourceIndex, i.location().start, i.location().end, toString(i.data())));
285-
break;
286-
case PushSub:
287-
collection.append(
288-
createJsonValue("PUSH [$]", sourceIndex, i.location().start, i.location().end, toString(h256(i.data()))));
289-
break;
290-
case PushSubSize:
291-
collection.append(
292-
createJsonValue("PUSH #[$]", sourceIndex, i.location().start, i.location().end, toString(h256(i.data()))));
293-
break;
294-
case PushProgramSize:
295-
collection.append(
296-
createJsonValue("PUSHSIZE", sourceIndex, i.location().start, i.location().end));
297-
break;
298-
case PushLibraryAddress:
299-
collection.append(
300-
createJsonValue("PUSHLIB", sourceIndex, i.location().start, i.location().end, m_libraries.at(h256(i.data())))
301-
);
302-
break;
303-
case PushDeployTimeAddress:
304-
collection.append(
305-
createJsonValue("PUSHDEPLOYADDRESS", sourceIndex, i.location().start, i.location().end)
306-
);
307-
break;
308-
case PushImmutable:
309-
collection.append(createJsonValue(
310-
"PUSHIMMUTABLE",
311-
sourceIndex,
312-
i.location().start,
313-
i.location().end,
314-
m_immutables.at(h256(i.data()))
315-
));
316-
break;
317-
case AssignImmutable:
318-
collection.append(createJsonValue(
319-
"ASSIGNIMMUTABLE",
320-
sourceIndex,
321-
i.location().start,
322-
i.location().end,
323-
m_immutables.at(h256(i.data()))
324-
));
325-
break;
326-
case Tag:
327-
collection.append(
328-
createJsonValue("tag", sourceIndex, i.location().start, i.location().end, toString(i.data())));
329-
collection.append(
330-
createJsonValue("JUMPDEST", sourceIndex, i.location().start, i.location().end));
331-
break;
332-
case PushData:
333-
collection.append(createJsonValue("PUSH data", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data())));
334-
break;
335-
case VerbatimBytecode:
336-
collection.append(createJsonValue("VERBATIM", sourceIndex, i.location().start, i.location().end, util::toHex(i.verbatimData())));
337-
break;
338-
default:
339-
assertThrow(false, InvalidOpcode, "");
261+
Json::Value jumpdest;
262+
jumpdest["name"] = "JUMPDEST";
263+
jumpdest["begin"] = item.location().start;
264+
jumpdest["end"] = item.location().end;
265+
jumpdest["source"] = sourceIndex;
266+
if (item.m_modifierDepth != 0)
267+
jumpdest["modifierDepth"] = static_cast<int>(item.m_modifierDepth);
268+
code.append(move(jumpdest));
340269
}
341270
}
271+
if (_includeSourceList)
272+
{
273+
root["sourceList"] = Json::arrayValue;
274+
Json::Value& jsonSourceList = root["sourceList"];
275+
vector<string> sourceNames(_sourceIndices.size());
276+
for (auto const& [name, index]: _sourceIndices)
277+
sourceNames[index] = name;
278+
for (auto const& item: sourceNames)
279+
jsonSourceList.append(item);
280+
}
342281

343282
if (!m_data.empty() || !m_subs.empty())
344283
{
345284
root[".data"] = Json::objectValue;
346285
Json::Value& data = root[".data"];
347286
for (auto const& i: m_data)
348287
if (u256(i.first) >= m_subs.size())
349-
data[toStringInHex((u256)i.first)] = util::toHex(i.second);
288+
data[util::toHex(toBigEndian((u256)i.first), util::HexPrefix::DontAdd, util::HexCase::Upper)] = util::toHex(i.second);
350289

351290
for (size_t i = 0; i < m_subs.size(); ++i)
352291
{
353292
std::stringstream hexStr;
354293
hexStr << hex << i;
355-
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices);
294+
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices, /*_includeSourceList = */false);
356295
}
357296
}
358297

359-
if (m_auxiliaryData.size() > 0)
298+
if (!m_auxiliaryData.empty())
360299
root[".auxdata"] = util::toHex(m_auxiliaryData);
361300

362301
return root;

libevmasm/Assembly.h

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <sstream>
4040
#include <memory>
4141
#include <map>
42+
#include <utility>
4243

4344
namespace solidity::evmasm
4445
{
@@ -147,7 +148,8 @@ class Assembly
147148

148149
/// Create a JSON representation of the assembly.
149150
Json::Value assemblyJSON(
150-
std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>()
151+
std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>(),
152+
bool _includeSourceList = true
151153
) const;
152154

153155
/// Mark this assembly as invalid. Calling ``assemble`` on it will throw.
@@ -167,16 +169,6 @@ class Assembly
167169
unsigned codeSize(unsigned subTagSize) const;
168170

169171
private:
170-
static Json::Value createJsonValue(
171-
std::string _name,
172-
int _source,
173-
int _begin,
174-
int _end,
175-
std::string _value = std::string(),
176-
std::string _jumpType = std::string()
177-
);
178-
static std::string toStringInHex(u256 _value);
179-
180172
bool m_invalid = false;
181173

182174
Assembly const* subAssemblyById(size_t _subId) const;
@@ -222,6 +214,7 @@ class Assembly
222214
std::string m_name;
223215

224216
langutil::SourceLocation m_currentSourceLocation;
217+
225218
public:
226219
size_t m_currentModifierDepth = 0;
227220
};

libevmasm/AssemblyItem.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <libevmasm/Assembly.h>
2222

2323
#include <libsolutil/CommonData.h>
24+
#include <libsolutil/CommonIO.h>
2425
#include <libsolutil/Numeric.h>
2526
#include <libsolutil/StringUtils.h>
2627
#include <libsolutil/FixedHash.h>
@@ -36,6 +37,18 @@ using namespace solidity::langutil;
3637

3738
static_assert(sizeof(size_t) <= 8, "size_t must be at most 64-bits wide");
3839

40+
namespace
41+
{
42+
43+
string toStringInHex(u256 _value)
44+
{
45+
std::stringstream hexStr;
46+
hexStr << std::uppercase << hex << _value;
47+
return hexStr.str();
48+
}
49+
50+
}
51+
3952
AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const
4053
{
4154
assertThrow(data() < (u256(1) << 64), util::Exception, "Tag already has subassembly set.");
@@ -56,6 +69,44 @@ pair<size_t, size_t> AssemblyItem::splitForeignPushTag() const
5669
return make_pair(subId, tag);
5770
}
5871

72+
pair<string, string> AssemblyItem::nameAndData() const
73+
{
74+
switch (type())
75+
{
76+
case Operation:
77+
return {instructionInfo(instruction()).name, m_data != nullptr ? toStringInHex(*m_data) : ""};
78+
case Push:
79+
return {"PUSH", toStringInHex(data())};
80+
case PushTag:
81+
if (data() == 0)
82+
return {"PUSH [ErrorTag]", ""};
83+
else
84+
return {"PUSH [tag]", util::toString(data())};
85+
case PushSub:
86+
return {"PUSH [$]", toString(util::h256(data()))};
87+
case PushSubSize:
88+
return {"PUSH #[$]", toString(util::h256(data()))};
89+
case PushProgramSize:
90+
return {"PUSHSIZE", ""};
91+
case PushLibraryAddress:
92+
return {"PUSHLIB", toString(util::h256(data()))};
93+
case PushDeployTimeAddress:
94+
return {"PUSHDEPLOYADDRESS", ""};
95+
case PushImmutable:
96+
return {"PUSHIMMUTABLE", toString(util::h256(data()))};
97+
case AssignImmutable:
98+
return {"ASSIGNIMMUTABLE", toString(util::h256(data()))};
99+
case Tag:
100+
return {"tag", util::toString(data())};
101+
case PushData:
102+
return {"PUSH data", toStringInHex(data())};
103+
case VerbatimBytecode:
104+
return {"VERBATIM", util::toHex(verbatimData())};
105+
default:
106+
assertThrow(false, InvalidOpcode, "");
107+
}
108+
}
109+
59110
void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag)
60111
{
61112
assertThrow(m_type == PushTag || m_type == Tag, util::Exception, "");

libevmasm/AssemblyItem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ class AssemblyItem
106106
u256 const& data() const { assertThrow(m_type != Operation, util::Exception, ""); return *m_data; }
107107
void setData(u256 const& _data) { assertThrow(m_type != Operation, util::Exception, ""); m_data = std::make_shared<u256>(_data); }
108108

109+
/// This function is used in `Assembly::assemblyJSON`.
110+
/// It returns the name & data of the current assembly item.
111+
/// @returns a pair, where the first element is the json-assembly
112+
/// item name, where second element is the string representation
113+
/// of it's data.
114+
std::pair<std::string, std::string> nameAndData() const;
115+
109116
bytes const& verbatimData() const { assertThrow(m_type == VerbatimBytecode, util::Exception, ""); return std::get<2>(*m_verbatimBytecode); }
110117

111118
/// @returns the instruction of this item (only valid if type() == Operation)

0 commit comments

Comments
 (0)