mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[solc] Basic --import-asm-json import.
This commit is contained in:
parent
43fe553ff0
commit
d71de24f9c
@ -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>
|
||||
@ -222,13 +222,16 @@ string Assembly::assemblyString(
|
||||
return tmp.str();
|
||||
}
|
||||
|
||||
Json::Value Assembly::createJsonValue(string _name, int _source, int _begin, int _end, string _value, string _jumpType)
|
||||
Json::Value Assembly::createJsonValue(
|
||||
string _name, int _sourceIndex, size_t _modifierDepth, int _begin, int _end, string _value, string _jumpType)
|
||||
{
|
||||
Json::Value value{Json::objectValue};
|
||||
value["name"] = _name;
|
||||
value["source"] = _source;
|
||||
value["source"] = _sourceIndex;
|
||||
value["begin"] = _begin;
|
||||
value["end"] = _end;
|
||||
if (_modifierDepth != 0)
|
||||
value["modifierDepth"] = static_cast<int>(_modifierDepth);
|
||||
if (!_value.empty())
|
||||
value["value"] = _value;
|
||||
if (!_jumpType.empty())
|
||||
@ -243,12 +246,275 @@ string Assembly::toStringInHex(u256 _value)
|
||||
return hexStr.str();
|
||||
}
|
||||
|
||||
Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices) const
|
||||
AssemblyItem Assembly::loadItemFromJSON(Json::Value const& _json)
|
||||
{
|
||||
std::string name = _json["name"].isString() ? _json["name"].asString() : "";
|
||||
int begin = _json["begin"].isInt() ? _json["begin"].asInt() : -1;
|
||||
int end = _json["end"].isInt() ? _json["end"].asInt() : -1;
|
||||
int srcIndex = _json["source"].isInt() ? _json["source"].asInt() : -1;
|
||||
size_t modifierDepth = _json["modifierDepth"].isInt() ? static_cast<size_t>(_json["modifierDepth"].asInt()) : 0;
|
||||
std::string value = _json["value"].isString() ? _json["value"].asString() : "";
|
||||
std::string jumpType = _json["jumpType"].isString() ? _json["jumpType"].asString() : "";
|
||||
solAssert(!name.empty(), "");
|
||||
|
||||
auto updateUsedTags = [&](u256 const& data) {
|
||||
auto tag = static_cast<unsigned>(data);
|
||||
if (this->m_usedTags <= tag)
|
||||
this->m_usedTags = tag + 1;
|
||||
};
|
||||
|
||||
auto updateImmutables = [&](string const& _immutableName) -> h256 {
|
||||
h256 hash(util::keccak256(value));
|
||||
this->m_immutables[hash] = _immutableName;
|
||||
return hash;
|
||||
};
|
||||
|
||||
auto updateLibraries = [&](string const& _libraryName) -> h256 {
|
||||
h256 hash(util::keccak256(_libraryName));
|
||||
this->m_libraries[hash] = _libraryName;
|
||||
return hash;
|
||||
};
|
||||
|
||||
SourceLocation location;
|
||||
location.start = begin;
|
||||
location.end = end;
|
||||
if (srcIndex > -1 && srcIndex < (int) sources().size())
|
||||
location.sourceName = sources()[static_cast<size_t>(srcIndex)];
|
||||
|
||||
AssemblyItem result(0);
|
||||
|
||||
if (c_instructions.find(name) != c_instructions.end())
|
||||
{
|
||||
AssemblyItem item{c_instructions.at(name), location};
|
||||
item.m_modifierDepth = modifierDepth;
|
||||
if (!value.empty())
|
||||
item.setJumpType(value);
|
||||
result = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
u256 data;
|
||||
if (name == "PUSH")
|
||||
{
|
||||
if (!value.empty())
|
||||
data = u256("0x" + value);
|
||||
AssemblyItem item{AssemblyItemType::Push, data, location};
|
||||
if (!jumpType.empty())
|
||||
item.setJumpType(jumpType);
|
||||
result = item;
|
||||
}
|
||||
else if (name == "PUSH [ErrorTag]")
|
||||
result = {AssemblyItemType::PushTag, data, location};
|
||||
else if (name == "PUSH [tag]")
|
||||
{
|
||||
if (!value.empty())
|
||||
data = u256(value);
|
||||
updateUsedTags(data);
|
||||
result = {AssemblyItemType::PushTag, data, location};
|
||||
}
|
||||
else if (name == "PUSH [$]")
|
||||
{
|
||||
if (!value.empty())
|
||||
data = u256("0x" + value);
|
||||
result = {AssemblyItemType::PushSub, data, location};
|
||||
}
|
||||
else if (name == "PUSH #[$]")
|
||||
{
|
||||
if (!value.empty())
|
||||
data = u256("0x" + value);
|
||||
result = {AssemblyItemType::PushSubSize, data, location};
|
||||
}
|
||||
else if (name == "PUSHSIZE")
|
||||
result = {AssemblyItemType::PushProgramSize, data, location};
|
||||
else if (name == "PUSHLIB")
|
||||
{
|
||||
h256 hash = updateLibraries(value);
|
||||
result = {AssemblyItemType::PushLibraryAddress, hash, location};
|
||||
}
|
||||
else if (name == "PUSHDEPLOYADDRESS")
|
||||
result = {AssemblyItemType::PushDeployTimeAddress, data, location};
|
||||
else if (name == "PUSHIMMUTABLE")
|
||||
{
|
||||
h256 hash = updateImmutables(value);
|
||||
result = {AssemblyItemType::PushImmutable, hash, location};
|
||||
}
|
||||
else if (name == "ASSIGNIMMUTABLE")
|
||||
{
|
||||
h256 hash = updateImmutables(value);
|
||||
result = {AssemblyItemType::AssignImmutable, hash, location};
|
||||
}
|
||||
else if (name == "tag")
|
||||
{
|
||||
if (!value.empty())
|
||||
data = u256(value);
|
||||
result = {AssemblyItemType::Tag, data, location};
|
||||
}
|
||||
else if (name == "PUSH data")
|
||||
{
|
||||
if (!value.empty())
|
||||
data = u256("0x" + value);
|
||||
result = {AssemblyItemType::PushData, data, location};
|
||||
}
|
||||
else if (name == "VERBATIM")
|
||||
{
|
||||
AssemblyItem item(fromHex(value), 0, 0);
|
||||
item.setLocation(location);
|
||||
result = item;
|
||||
}
|
||||
else
|
||||
assertThrow(false, InvalidOpcode, "");
|
||||
}
|
||||
result.m_modifierDepth = modifierDepth;
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<Json::Value> Assembly::assemblyItemAsJSON(AssemblyItem const& _item, int _sourceIndex) const
|
||||
{
|
||||
vector<Json::Value> result;
|
||||
|
||||
switch (_item.type())
|
||||
{
|
||||
case Operation:
|
||||
result.emplace_back(createJsonValue(
|
||||
instructionInfo(_item.instruction()).name,
|
||||
_sourceIndex,
|
||||
_item.m_modifierDepth,
|
||||
_item.location().start,
|
||||
_item.location().end,
|
||||
_item.getJumpTypeAsString()));
|
||||
break;
|
||||
case Push:
|
||||
result.emplace_back(createJsonValue(
|
||||
"PUSH",
|
||||
_sourceIndex,
|
||||
_item.m_modifierDepth,
|
||||
_item.location().start,
|
||||
_item.location().end,
|
||||
toStringInHex(_item.data()),
|
||||
_item.getJumpTypeAsString()));
|
||||
break;
|
||||
case PushTag:
|
||||
if (_item.data() == 0)
|
||||
result.emplace_back(createJsonValue(
|
||||
"PUSH [ErrorTag]",
|
||||
_sourceIndex,
|
||||
_item.m_modifierDepth,
|
||||
_item.location().start,
|
||||
_item.location().end,
|
||||
""));
|
||||
else
|
||||
result.emplace_back(createJsonValue(
|
||||
"PUSH [tag]",
|
||||
_sourceIndex,
|
||||
_item.m_modifierDepth,
|
||||
_item.location().start,
|
||||
_item.location().end,
|
||||
toString(_item.data())));
|
||||
break;
|
||||
case PushSub:
|
||||
result.emplace_back(createJsonValue(
|
||||
"PUSH [$]",
|
||||
_sourceIndex,
|
||||
_item.m_modifierDepth,
|
||||
_item.location().start,
|
||||
_item.location().end,
|
||||
toString(h256(_item.data()))));
|
||||
break;
|
||||
case PushSubSize:
|
||||
result.emplace_back(createJsonValue(
|
||||
"PUSH #[$]",
|
||||
_sourceIndex,
|
||||
_item.m_modifierDepth,
|
||||
_item.location().start,
|
||||
_item.location().end,
|
||||
toString(h256(_item.data()))));
|
||||
break;
|
||||
case PushProgramSize:
|
||||
result.emplace_back(createJsonValue(
|
||||
"PUSHSIZE", _sourceIndex, _item.m_modifierDepth, _item.location().start, _item.location().end));
|
||||
break;
|
||||
case PushLibraryAddress:
|
||||
result.emplace_back(createJsonValue(
|
||||
"PUSHLIB",
|
||||
_sourceIndex,
|
||||
_item.m_modifierDepth,
|
||||
_item.location().start,
|
||||
_item.location().end,
|
||||
m_libraries.at(h256(_item.data()))));
|
||||
break;
|
||||
case PushDeployTimeAddress:
|
||||
result.emplace_back(createJsonValue(
|
||||
"PUSHDEPLOYADDRESS", _sourceIndex, _item.m_modifierDepth, _item.location().start, _item.location().end));
|
||||
break;
|
||||
case PushImmutable:
|
||||
result.emplace_back(createJsonValue(
|
||||
"PUSHIMMUTABLE",
|
||||
_sourceIndex,
|
||||
_item.m_modifierDepth,
|
||||
_item.location().start,
|
||||
_item.location().end,
|
||||
m_immutables.at(h256(_item.data()))));
|
||||
break;
|
||||
case AssignImmutable:
|
||||
result.emplace_back(createJsonValue(
|
||||
"ASSIGNIMMUTABLE",
|
||||
_sourceIndex,
|
||||
_item.m_modifierDepth,
|
||||
_item.location().start,
|
||||
_item.location().end,
|
||||
m_immutables.at(h256(_item.data()))));
|
||||
break;
|
||||
case Tag:
|
||||
result.emplace_back(createJsonValue(
|
||||
"tag",
|
||||
_sourceIndex,
|
||||
_item.m_modifierDepth,
|
||||
_item.location().start,
|
||||
_item.location().end,
|
||||
toString(_item.data())));
|
||||
result.emplace_back(createJsonValue(
|
||||
"JUMPDEST", _sourceIndex, _item.m_modifierDepth, _item.location().start, _item.location().end));
|
||||
break;
|
||||
case PushData:
|
||||
result.emplace_back(createJsonValue(
|
||||
"PUSH data",
|
||||
_sourceIndex,
|
||||
_item.m_modifierDepth,
|
||||
_item.location().start,
|
||||
_item.location().end,
|
||||
toStringInHex(_item.data())));
|
||||
break;
|
||||
case VerbatimBytecode:
|
||||
result.emplace_back(createJsonValue(
|
||||
"VERBATIM",
|
||||
_sourceIndex,
|
||||
_item.m_modifierDepth,
|
||||
_item.location().start,
|
||||
_item.location().end,
|
||||
util::toHex(_item.verbatimData())));
|
||||
break;
|
||||
default:
|
||||
assertThrow(false, InvalidOpcode, "");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices, bool _includeSourceList) const
|
||||
{
|
||||
Json::Value root;
|
||||
root[".code"] = Json::arrayValue;
|
||||
if (_includeSourceList)
|
||||
{
|
||||
root["sourceList"] = Json::arrayValue;
|
||||
Json::Value& sourceList = root["sourceList"];
|
||||
std::vector<string> sources(_sourceIndices.size());
|
||||
for (auto const& item: _sourceIndices)
|
||||
sources[item.second] = item.first;
|
||||
for (auto const& item: sources)
|
||||
sourceList.append(item);
|
||||
}
|
||||
|
||||
Json::Value& collection = root[".code"];
|
||||
root[".code"] = Json::arrayValue;
|
||||
Json::Value& code = root[".code"];
|
||||
for (AssemblyItem const& i: m_items)
|
||||
{
|
||||
int sourceIndex = -1;
|
||||
@ -259,85 +525,8 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices)
|
||||
sourceIndex = static_cast<int>(iter->second);
|
||||
}
|
||||
|
||||
switch (i.type())
|
||||
{
|
||||
case Operation:
|
||||
collection.append(
|
||||
createJsonValue(
|
||||
instructionInfo(i.instruction()).name,
|
||||
sourceIndex,
|
||||
i.location().start,
|
||||
i.location().end,
|
||||
i.getJumpTypeAsString())
|
||||
);
|
||||
break;
|
||||
case Push:
|
||||
collection.append(
|
||||
createJsonValue("PUSH", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data()), i.getJumpTypeAsString()));
|
||||
break;
|
||||
case PushTag:
|
||||
if (i.data() == 0)
|
||||
collection.append(
|
||||
createJsonValue("PUSH [ErrorTag]", sourceIndex, i.location().start, i.location().end, ""));
|
||||
else
|
||||
collection.append(
|
||||
createJsonValue("PUSH [tag]", sourceIndex, i.location().start, i.location().end, toString(i.data())));
|
||||
break;
|
||||
case PushSub:
|
||||
collection.append(
|
||||
createJsonValue("PUSH [$]", sourceIndex, i.location().start, i.location().end, toString(h256(i.data()))));
|
||||
break;
|
||||
case PushSubSize:
|
||||
collection.append(
|
||||
createJsonValue("PUSH #[$]", sourceIndex, i.location().start, i.location().end, toString(h256(i.data()))));
|
||||
break;
|
||||
case PushProgramSize:
|
||||
collection.append(
|
||||
createJsonValue("PUSHSIZE", sourceIndex, i.location().start, i.location().end));
|
||||
break;
|
||||
case PushLibraryAddress:
|
||||
collection.append(
|
||||
createJsonValue("PUSHLIB", sourceIndex, i.location().start, i.location().end, m_libraries.at(h256(i.data())))
|
||||
);
|
||||
break;
|
||||
case PushDeployTimeAddress:
|
||||
collection.append(
|
||||
createJsonValue("PUSHDEPLOYADDRESS", sourceIndex, i.location().start, i.location().end)
|
||||
);
|
||||
break;
|
||||
case PushImmutable:
|
||||
collection.append(createJsonValue(
|
||||
"PUSHIMMUTABLE",
|
||||
sourceIndex,
|
||||
i.location().start,
|
||||
i.location().end,
|
||||
m_immutables.at(h256(i.data()))
|
||||
));
|
||||
break;
|
||||
case AssignImmutable:
|
||||
collection.append(createJsonValue(
|
||||
"ASSIGNIMMUTABLE",
|
||||
sourceIndex,
|
||||
i.location().start,
|
||||
i.location().end,
|
||||
m_immutables.at(h256(i.data()))
|
||||
));
|
||||
break;
|
||||
case Tag:
|
||||
collection.append(
|
||||
createJsonValue("tag", sourceIndex, i.location().start, i.location().end, toString(i.data())));
|
||||
collection.append(
|
||||
createJsonValue("JUMPDEST", sourceIndex, i.location().start, i.location().end));
|
||||
break;
|
||||
case PushData:
|
||||
collection.append(createJsonValue("PUSH data", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data())));
|
||||
break;
|
||||
case VerbatimBytecode:
|
||||
collection.append(createJsonValue("VERBATIM", sourceIndex, i.location().start, i.location().end, util::toHex(i.verbatimData())));
|
||||
break;
|
||||
default:
|
||||
assertThrow(false, InvalidOpcode, "");
|
||||
}
|
||||
for (Json::Value const& item: assemblyItemAsJSON(i, sourceIndex))
|
||||
code.append(item);
|
||||
}
|
||||
|
||||
if (!m_data.empty() || !m_subs.empty())
|
||||
@ -352,16 +541,75 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices)
|
||||
{
|
||||
std::stringstream hexStr;
|
||||
hexStr << hex << i;
|
||||
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices);
|
||||
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_auxiliaryData.size() > 0)
|
||||
if (!m_auxiliaryData.empty())
|
||||
root[".auxdata"] = util::toHex(m_auxiliaryData);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
bool Assembly::addAssemblyItemsFromJSON(Json::Value const& _code)
|
||||
{
|
||||
solAssert(_code.isArray(), "");
|
||||
for (auto const& it: _code)
|
||||
this->m_items.emplace_back(loadItemFromJSON(it));
|
||||
|
||||
for (auto current = this->m_items.begin(); current != this->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 (
|
||||
current->type() == AssemblyItemType::Tag &&
|
||||
next->type() == AssemblyItemType::Operation &&
|
||||
next->instruction() == Instruction::JUMPDEST
|
||||
)
|
||||
this->m_items.erase(next);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Assembly::loadFromAssemblyJSON(Json::Value const& _json, bool _loadSources /* = true */)
|
||||
{
|
||||
if (!_json[".code"].isArray())
|
||||
return false;
|
||||
bool result{true};
|
||||
|
||||
if (_loadSources)
|
||||
{
|
||||
vector<string> sourceList;
|
||||
if (_json.isMember("sourceList"))
|
||||
for (auto const& it: _json["sourceList"])
|
||||
sourceList.emplace_back(it.asString());
|
||||
setSources(sourceList);
|
||||
}
|
||||
|
||||
addAssemblyItemsFromJSON(_json[".code"]);
|
||||
if (_json[".auxdata"].isString())
|
||||
this->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())
|
||||
this->m_data[h256(fromHex(key))] = fromHex(code.asString());
|
||||
else
|
||||
{
|
||||
shared_ptr<Assembly> subassembly = make_shared<Assembly>();
|
||||
subassembly->setSources(this->sources());
|
||||
result &= subassembly->loadFromAssemblyJSON(code, false);
|
||||
this->m_subs.emplace_back(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.");
|
||||
@ -397,6 +645,25 @@ AssemblyItem Assembly::newImmutableAssignment(string const& _identifier)
|
||||
return AssemblyItem{AssignImmutable, h};
|
||||
}
|
||||
|
||||
Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs)
|
||||
{
|
||||
OptimiserSettings settings;
|
||||
settings.isCreation = _isCreation;
|
||||
settings.runInliner = true;
|
||||
settings.runJumpdestRemover = true;
|
||||
settings.runPeephole = true;
|
||||
if (_enable)
|
||||
{
|
||||
settings.runDeduplicate = true;
|
||||
settings.runCSE = true;
|
||||
settings.runConstantOptimiser = true;
|
||||
}
|
||||
settings.evmVersion = _evmVersion;
|
||||
settings.expectedExecutionsPerDeployment = _runs;
|
||||
optimise(settings);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Assembly& Assembly::optimise(OptimiserSettings const& _settings)
|
||||
{
|
||||
optimiseInternal(_settings, {});
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
namespace solidity::evmasm
|
||||
{
|
||||
@ -133,6 +134,13 @@ public:
|
||||
/// is optimised according to the settings in @a _settings.
|
||||
Assembly& optimise(OptimiserSettings const& _settings);
|
||||
|
||||
/// Modify (if @a _enable is set) and return the current assembly such that creation and
|
||||
/// execution gas usage is optimised. @a _isCreation should be true for the top-level assembly.
|
||||
/// @a _runs specifes an estimate on how often each opcode in this assembly will be executed,
|
||||
/// i.e. use a small value to optimise for size and a large value to optimise for runtime.
|
||||
/// If @a _enable is not set, will perform some simple peephole optimizations.
|
||||
Assembly& optimise(bool _enable, langutil::EVMVersion _evmVersion, bool _isCreation, size_t _runs);
|
||||
|
||||
/// Create a text representation of the assembly.
|
||||
std::string assemblyString(
|
||||
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
|
||||
@ -147,9 +155,12 @@ public:
|
||||
|
||||
/// Create a JSON representation of the assembly.
|
||||
Json::Value assemblyJSON(
|
||||
std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>()
|
||||
std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>(),
|
||||
bool _includeSourceList = true
|
||||
) const;
|
||||
|
||||
bool loadFromAssemblyJSON(Json::Value const& _json, bool _loadSources = true);
|
||||
|
||||
/// Mark this assembly as invalid. Calling ``assemble`` on it will throw.
|
||||
void markAsInvalid() { m_invalid = true; }
|
||||
|
||||
@ -158,6 +169,16 @@ public:
|
||||
|
||||
bool isCreation() const { return m_creation; }
|
||||
|
||||
void setSources(std::vector<std::shared_ptr<std::string const>> _sources) {
|
||||
m_sources = std::move(_sources);
|
||||
}
|
||||
|
||||
void setSources(std::vector<std::string> const& _sources) {
|
||||
for (auto const& item: _sources)
|
||||
m_sources.emplace_back(std::make_shared<std::string>(item));
|
||||
}
|
||||
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
|
||||
@ -166,10 +187,15 @@ protected:
|
||||
|
||||
unsigned codeSize(unsigned subTagSize) const;
|
||||
|
||||
AssemblyItem loadItemFromJSON(Json::Value const& _json);
|
||||
std::vector<Json::Value> assemblyItemAsJSON(AssemblyItem const& _item, int _sourceIndex) const;
|
||||
|
||||
private:
|
||||
bool addAssemblyItemsFromJSON(Json::Value const& _code);
|
||||
static Json::Value createJsonValue(
|
||||
std::string _name,
|
||||
int _source,
|
||||
int _sourceIndex,
|
||||
size_t _modifierDepth,
|
||||
int _begin,
|
||||
int _end,
|
||||
std::string _value = std::string(),
|
||||
@ -222,6 +248,8 @@ protected:
|
||||
std::string m_name;
|
||||
|
||||
langutil::SourceLocation m_currentSourceLocation;
|
||||
std::vector<std::shared_ptr<std::string const>> m_sources;
|
||||
|
||||
public:
|
||||
size_t m_currentModifierDepth = 0;
|
||||
};
|
||||
|
@ -192,6 +192,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;
|
||||
|
@ -166,6 +166,7 @@ public:
|
||||
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;
|
||||
|
||||
|
@ -676,7 +676,34 @@ bool CompilerStack::compile(State _stopAfter)
|
||||
|
||||
if (!m_evmAssemblyJson.empty())
|
||||
{
|
||||
solAssert(m_importedSources, "");
|
||||
solAssert(m_evmAssemblyJson.size() == 1, "");
|
||||
|
||||
string const evmSourceName = m_evmAssemblyJson.begin()->first;
|
||||
Json::Value const evmJson = m_evmAssemblyJson.begin()->second;
|
||||
|
||||
evmasm::Assembly::OptimiserSettings optimiserSettings;
|
||||
optimiserSettings.evmVersion = m_evmVersion;
|
||||
optimiserSettings.expectedExecutionsPerDeployment = m_optimiserSettings.expectedExecutionsPerDeployment;
|
||||
optimiserSettings.runCSE = m_optimiserSettings.runCSE;
|
||||
optimiserSettings.runConstantOptimiser = m_optimiserSettings.runConstantOptimiser;
|
||||
optimiserSettings.runDeduplicate = m_optimiserSettings.runDeduplicate;
|
||||
optimiserSettings.runInliner = m_optimiserSettings.runInliner;
|
||||
optimiserSettings.runJumpdestRemover = m_optimiserSettings.runJumpdestRemover;
|
||||
optimiserSettings.runPeephole = m_optimiserSettings.runPeephole;
|
||||
|
||||
m_contracts[evmSourceName].evmAssembly = make_shared<evmasm::Assembly>(evmSourceName);
|
||||
m_contracts[evmSourceName].evmAssembly->loadFromAssemblyJSON(m_evmAssemblyJson[evmSourceName]);
|
||||
if (m_optimiserSettings.enabled)
|
||||
m_contracts[evmSourceName].evmAssembly->optimise(optimiserSettings);
|
||||
m_contracts[evmSourceName].object = m_contracts[evmSourceName].evmAssembly->assemble();
|
||||
|
||||
m_contracts[evmSourceName].evmRuntimeAssembly = make_shared<evmasm::Assembly>(evmSourceName);
|
||||
m_contracts[evmSourceName].evmRuntimeAssembly->setSources(m_contracts[evmSourceName].evmAssembly->sources());
|
||||
m_contracts[evmSourceName].evmRuntimeAssembly->loadFromAssemblyJSON(m_evmAssemblyJson[evmSourceName][".data"]["0"], false);
|
||||
if (m_optimiserSettings.enabled)
|
||||
m_contracts[evmSourceName].evmRuntimeAssembly->optimise(optimiserSettings);
|
||||
m_contracts[evmSourceName].runtimeObject = m_contracts[evmSourceName].evmRuntimeAssembly->assemble();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -961,14 +988,21 @@ vector<string> CompilerStack::sourceNames() const
|
||||
return names;
|
||||
}
|
||||
|
||||
map<string, unsigned> CompilerStack::sourceIndices() const
|
||||
map<string, unsigned> CompilerStack::sourceIndices(bool _includeInternalSources /* = true */) const
|
||||
{
|
||||
map<string, unsigned> indices;
|
||||
unsigned index = 0;
|
||||
for (auto const& s: m_sources)
|
||||
indices[s.first] = index++;
|
||||
solAssert(!indices.count(CompilerContext::yulUtilityFileName()), "");
|
||||
indices[CompilerContext::yulUtilityFileName()] = index++;
|
||||
if (m_evmAssemblyJson.empty())
|
||||
{
|
||||
for (auto const& s: m_sources)
|
||||
indices[s.first] = index++;
|
||||
solAssert(!indices.count(CompilerContext::yulUtilityFileName()), "");
|
||||
if (_includeInternalSources)
|
||||
indices[CompilerContext::yulUtilityFileName()] = index++;
|
||||
}
|
||||
else
|
||||
for (auto const& s: m_sourceOrder)
|
||||
indices[s->charStream->source()] = index++;
|
||||
return indices;
|
||||
}
|
||||
|
||||
|
@ -244,7 +244,7 @@ public:
|
||||
|
||||
/// @returns a mapping assigning each source name its index inside the vector returned
|
||||
/// by sourceNames().
|
||||
std::map<std::string, unsigned> sourceIndices() const;
|
||||
std::map<std::string, unsigned> sourceIndices(bool _includeInternalSources = true) const;
|
||||
|
||||
/// @returns the previously used character stream, useful for counting lines during error reporting.
|
||||
langutil::CharStream const& charStream(std::string const& _sourceName) const override;
|
||||
|
@ -201,7 +201,11 @@ void CommandLineInterface::handleOpcode(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleIR(string const& _contractName)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.ir)
|
||||
return;
|
||||
@ -217,7 +221,11 @@ void CommandLineInterface::handleIR(string const& _contractName)
|
||||
|
||||
void CommandLineInterface::handleIROptimized(string const& _contractName)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.irOptimized)
|
||||
return;
|
||||
@ -233,7 +241,11 @@ void CommandLineInterface::handleIROptimized(string const& _contractName)
|
||||
|
||||
void CommandLineInterface::handleEwasm(string const& _contractName)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.ewasm)
|
||||
return;
|
||||
@ -256,7 +268,11 @@ void CommandLineInterface::handleEwasm(string const& _contractName)
|
||||
|
||||
void CommandLineInterface::handleBytecode(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (m_options.compiler.outputs.opcodes)
|
||||
handleOpcode(_contract);
|
||||
@ -266,7 +282,11 @@ void CommandLineInterface::handleBytecode(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleSignatureHashes(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.signatureHashes)
|
||||
return;
|
||||
@ -298,7 +318,11 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleMetadata(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.metadata)
|
||||
return;
|
||||
@ -312,7 +336,11 @@ void CommandLineInterface::handleMetadata(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleABI(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.abi)
|
||||
return;
|
||||
@ -326,7 +354,11 @@ void CommandLineInterface::handleABI(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleStorageLayout(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.storageLayout)
|
||||
return;
|
||||
@ -340,7 +372,11 @@ void CommandLineInterface::handleStorageLayout(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
bool enabled = false;
|
||||
std::string suffix;
|
||||
@ -382,7 +418,11 @@ void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contra
|
||||
|
||||
void CommandLineInterface::handleGasEstimation(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
Json::Value estimates = m_compiler->gasEstimates(_contract);
|
||||
sout() << "Gas estimation:" << endl;
|
||||
|
@ -1582,5 +1582,10 @@ EVM assembly:
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceList":
|
||||
[
|
||||
"asm_json/input.sol",
|
||||
"#utility.yul"
|
||||
]
|
||||
}
|
||||
|
@ -56,7 +56,8 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
{
|
||||
map<string, unsigned> indices = {
|
||||
{ "root.asm", 0 },
|
||||
{ "sub.asm", 1 }
|
||||
{ "sub.asm", 1 },
|
||||
{ "verbatim.asm", 2 }
|
||||
};
|
||||
Assembly _assembly{false, {}};
|
||||
auto root_asm = make_shared<string>("root.asm");
|
||||
@ -65,11 +66,22 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
Assembly _subAsm{false, {}};
|
||||
auto sub_asm = make_shared<string>("sub.asm");
|
||||
_subAsm.setSourceLocation({6, 8, sub_asm});
|
||||
|
||||
Assembly _verbatimAsm;
|
||||
auto verbatim_asm = make_shared<string>("verbatim.asm");
|
||||
_verbatimAsm.setSourceLocation({8, 18, verbatim_asm});
|
||||
|
||||
// PushImmutable
|
||||
_subAsm.appendImmutable("someImmutable");
|
||||
_subAsm.append(AssemblyItem(PushTag, 0));
|
||||
_subAsm.append(Instruction::INVALID);
|
||||
shared_ptr<Assembly> _subAsmPtr = make_shared<Assembly>(_subAsm);
|
||||
|
||||
_verbatimAsm.appendVerbatim({0xff,0xff}, 0, 0);
|
||||
_verbatimAsm.appendVerbatim({0x74, 0x65, 0x73, 0x74}, 0, 1);
|
||||
_verbatimAsm.append(Instruction::MSTORE);
|
||||
shared_ptr<Assembly> _verbatimAsmPtr = make_shared<Assembly>(_verbatimAsm);
|
||||
|
||||
// Tag
|
||||
auto tag = _assembly.newTag();
|
||||
_assembly.append(tag);
|
||||
@ -90,6 +102,10 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
auto sub = _assembly.appendSubroutine(_subAsmPtr);
|
||||
// PushSub
|
||||
_assembly.pushSubroutineOffset(static_cast<size_t>(sub.data()));
|
||||
// PushSubSize
|
||||
auto verbatim_sub = _assembly.appendSubroutine(_verbatimAsmPtr);
|
||||
// PushSub
|
||||
_assembly.pushSubroutineOffset(static_cast<size_t>(verbatim_sub.data()));
|
||||
// PushDeployTimeAddress
|
||||
_assembly.append(PushDeployTimeAddress);
|
||||
// AssignImmutable.
|
||||
@ -106,12 +122,12 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
|
||||
BOOST_CHECK_EQUAL(
|
||||
_assembly.assemble().toHex(),
|
||||
"5b6001600220606f73__$bf005014d9d0f534b8fcb268bd84c491a2$__"
|
||||
"6000566067602260457300000000000000000000000000000000000000005050"
|
||||
"5b6001600220607c73__$bf005014d9d0f534b8fcb268bd84c491a2$__"
|
||||
"6000566074602460496007606d7300000000000000000000000000000000000000005050"
|
||||
"600260010152"
|
||||
"00fe"
|
||||
"7f0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"fe010203044266eeaa"
|
||||
"6000feffff7465737452010203044266eeaa"
|
||||
);
|
||||
BOOST_CHECK_EQUAL(
|
||||
_assembly.assemblyString(),
|
||||
@ -124,6 +140,8 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
" data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b\n"
|
||||
" dataSize(sub_0)\n"
|
||||
" dataOffset(sub_0)\n"
|
||||
" dataSize(sub_1)\n"
|
||||
" dataOffset(sub_1)\n"
|
||||
" deployTimeAddress()\n"
|
||||
" assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n"
|
||||
" 0x02\n"
|
||||
@ -135,13 +153,20 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
"sub_0: assembly {\n"
|
||||
" /* \"sub.asm\":6:8 */\n"
|
||||
" immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
|
||||
" tag_0\n"
|
||||
" invalid\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"sub_1: assembly {\n"
|
||||
" /* \"verbatim.asm\":8:18 */\n"
|
||||
" verbatimbytecode_ffff\n"
|
||||
" verbatimbytecode_74657374\n"
|
||||
" mstore\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"auxdata: 0x4266eeaa\n"
|
||||
);
|
||||
BOOST_CHECK_EQUAL(
|
||||
util::jsonCompactPrint(_assembly.assemblyJSON(indices)),
|
||||
string json{
|
||||
"{\".auxdata\":\"4266eeaa\",\".code\":["
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"tag\",\"source\":0,\"value\":\"1\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"JUMPDEST\",\"source\":0},"
|
||||
@ -155,6 +180,8 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH data\",\"source\":0,\"value\":\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000001\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000001\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\",\"source\":0},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"},"
|
||||
@ -162,9 +189,23 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0}"
|
||||
"],\".data\":{\"0\":{\".code\":["
|
||||
"{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"},"
|
||||
"{\"begin\":6,\"end\":8,\"name\":\"PUSH [ErrorTag]\",\"source\":1},"
|
||||
"{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}"
|
||||
"]},\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}"
|
||||
);
|
||||
"]},"
|
||||
"\"1\":{\".code\":["
|
||||
"{\"begin\":8,\"end\":18,\"name\":\"VERBATIM\",\"source\":2,\"value\":\"ffff\"},"
|
||||
"{\"begin\":8,\"end\":18,\"name\":\"VERBATIM\",\"source\":2,\"value\":\"74657374\"},"
|
||||
"{\"begin\":8,\"end\":18,\"name\":\"MSTORE\",\"source\":2}"
|
||||
"]},\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"},\"sourceList\":[\"root.asm\",\"sub.asm\",\"verbatim.asm\"]}"
|
||||
};
|
||||
Json::Value jsonValue;
|
||||
BOOST_CHECK(util::jsonParseStrict(json, jsonValue));
|
||||
BOOST_CHECK_EQUAL(util::jsonCompactPrint(_assembly.assemblyJSON(indices)), util::jsonCompactPrint(jsonValue));
|
||||
|
||||
Assembly _assemblyFromJson;
|
||||
_assemblyFromJson.loadFromAssemblyJSON(_assembly.assemblyJSON(indices));
|
||||
BOOST_CHECK_EQUAL(util::jsonCompactPrint(_assemblyFromJson.assemblyJSON(indices)), util::jsonCompactPrint(jsonValue));
|
||||
BOOST_CHECK_EQUAL(_assembly.assemble().toHex(), _assemblyFromJson.assemble().toHex());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
|
||||
@ -343,7 +384,7 @@ BOOST_AUTO_TEST_CASE(immutable)
|
||||
"{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"},"
|
||||
"{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someOtherImmutable\"},"
|
||||
"{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}"
|
||||
"]}}}"
|
||||
"]}},\"sourceList\":[\"root.asm\",\"sub.asm\"]}"
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user