[libevmasm] Add support to import evm assembly json.

This commit is contained in:
Alexander Arlt 2022-11-01 23:58:45 -05:00 committed by r0qs
parent fe1f9c640e
commit 025422b220
No known key found for this signature in database
GPG Key ID: 61503DBA6667276C
89 changed files with 4181 additions and 136 deletions

View File

@ -0,0 +1,54 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include <libevmasm/LinkerObject.h>
#include <libsolutil/Common.h>
#include <libsolutil/JSON.h>
#include <string>
#include <vector>
namespace solidity::evmasm
{
class AbstractAssemblyStack
{
public:
virtual ~AbstractAssemblyStack() {}
virtual LinkerObject const& object(std::string const& _contractName) const = 0;
virtual LinkerObject const& runtimeObject(std::string const& _contractName) const = 0;
virtual std::string const* sourceMapping(std::string const& _contractName) const = 0;
virtual std::string const* runtimeSourceMapping(std::string const& _contractName) const = 0;
virtual Json::Value assemblyJSON(std::string const& _contractName) const = 0;
virtual std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const = 0;
virtual std::string const filesystemFriendlyName(std::string const& _contractName) const = 0;
virtual std::vector<std::string> contractNames() const = 0;
virtual std::vector<std::string> sourceNames() const = 0;
virtual bool compilationSuccessful() const = 0;
};
} // namespace solidity::evmasm

View File

@ -34,13 +34,15 @@
#include <liblangutil/CharStream.h>
#include <liblangutil/Exceptions.h>
#include <json/json.h>
#include <libsolutil/JSON.h>
#include <libsolutil/StringUtils.h>
#include <range/v3/algorithm/any_of.hpp>
#include <range/v3/view/enumerate.hpp>
#include <fstream>
#include <limits>
#include <iterator>
using namespace solidity;
using namespace solidity::evmasm;
@ -73,6 +75,210 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
}
}
void Assembly::importAssemblyItemsFromJSON(Json::Value const& _code, std::vector<std::string> const& _sourceList)
{
solAssert(m_items.empty());
solRequire(_code.isArray(), AssemblyImportException, "Supplied JSON is not an array.");
for (auto current = std::begin(_code); current != std::end(_code); ++current)
{
auto const& item = m_items.emplace_back(createAssemblyItemFromJSON(*current, _sourceList));
if (item == Instruction::JUMPDEST)
solThrow(AssemblyImportException, "JUMPDEST instruction without a tag");
else if (item.type() == AssemblyItemType::Tag)
{
++current;
if (current != std::end(_code) && createAssemblyItemFromJSON(*current, _sourceList) != Instruction::JUMPDEST)
solThrow(AssemblyImportException, "JUMPDEST expected after tag.");
}
}
}
AssemblyItem Assembly::createAssemblyItemFromJSON(Json::Value const& _json, std::vector<std::string> const& _sourceList)
{
solRequire(_json.isObject(), AssemblyImportException, "Supplied JSON is not an object.");
static std::set<std::string> const validMembers{"name", "begin", "end", "source", "value", "modifierDepth", "jumpType"};
for (auto const& member: _json.getMemberNames())
solRequire(
validMembers.count(member),
AssemblyImportException,
"Unknown member '" + member + "'. Valid members are " +
solidity::util::joinHumanReadable(validMembers, ", ") + "."
);
solRequire(isOfType<std::string>(_json["name"]), AssemblyImportException, "Member 'name' missing or not of type string.");
solRequire(isOfTypeIfExists<int>(_json, "begin"), AssemblyImportException, "Optional member 'begin' not of type int.");
solRequire(isOfTypeIfExists<int>(_json, "end"), AssemblyImportException, "Optional member 'end' not of type int.");
solRequire(isOfTypeIfExists<int>(_json, "source"), AssemblyImportException, "Optional member 'source' not of type int.");
solRequire(isOfTypeIfExists<std::string>(_json, "value"), AssemblyImportException, "Optional member 'value' not of type string.");
solRequire(
isOfTypeIfExists<int>(_json, "modifierDepth"),
AssemblyImportException,
"Optional member 'modifierDepth' not of type int."
);
solRequire(
isOfTypeIfExists<std::string>(_json, "jumpType"),
AssemblyImportException,
"Optional member 'jumpType' not of type string."
);
std::string name = get<std::string>(_json["name"]);
solRequire(!name.empty(), AssemblyImportException, "Member 'name' was empty.");
SourceLocation location;
location.start = get<int>(_json["begin"]);
location.end = get<int>(_json["end"]);
int srcIndex = getOrDefault<int>(_json["source"], -1);
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 = std::max(m_usedTags, static_cast<unsigned>(data) + 1);
return data;
};
auto storeImmutableHash = [&](std::string const& _immutableName) -> h256
{
h256 hash(util::keccak256(_immutableName));
solAssert(m_immutables.count(hash) == 0 || m_immutables[hash] == _immutableName);
m_immutables[hash] = _immutableName;
return hash;
};
auto storeLibraryHash = [&](std::string const& _libraryName) -> h256
{
h256 hash(util::keccak256(_libraryName));
solAssert(m_libraries.count(hash) == 0 || m_libraries[hash] == _libraryName);
m_libraries[hash] = _libraryName;
return hash;
};
auto requireValueDefinedForInstruction = [&](std::string const& _name, std::string const& _value)
{
solRequire(
!_value.empty(),
AssemblyImportException,
"Member 'value' was not defined for instruction '" + _name + "', but the instruction needs a value."
);
};
auto requireValueUndefinedForInstruction = [&](std::string const& _name, std::string const& _value)
{
solRequire(
_value.empty(),
AssemblyImportException,
"Member 'value' defined for instruction '" + _name + "', but the instruction does not need a value."
);
};
solRequire(srcIndex >= -1 && srcIndex < static_cast<int>(_sourceList.size()), AssemblyImportException, "srcIndex out of bound.");
if (srcIndex != -1)
{
static std::map<std::string, std::shared_ptr<std::string const>> sharedSourceNames;
if (sharedSourceNames.find(_sourceList[static_cast<size_t>(srcIndex)]) == sharedSourceNames.end())
sharedSourceNames[_sourceList[static_cast<size_t>(srcIndex)]] = std::make_shared<std::string>(_sourceList[static_cast<size_t>(srcIndex)]);
location.sourceName = sharedSourceNames[_sourceList[static_cast<size_t>(srcIndex)]];
}
AssemblyItem result(0);
if (c_instructions.count(name))
{
AssemblyItem item{c_instructions.at(name), location};
if (!jumpType.empty())
{
if (item.instruction() == Instruction::JUMP || item.instruction() == Instruction::JUMPI)
item.setJumpType(jumpType);
else
solThrow(
AssemblyImportException,
"Member 'jumpType' set on instruction different from JUMP or JUMPI (was set on instruction '" + name + "')"
);
}
requireValueUndefinedForInstruction(name, value);
result = item;
}
else
{
solRequire(
jumpType.empty(),
AssemblyImportException,
"Member 'jumpType' set on instruction different from JUMP or JUMPI (was set on instruction '" + name + "')"
);
if (name == "PUSH")
{
requireValueDefinedForInstruction(name, value);
result = {AssemblyItemType::Push, u256("0x" + value)};
}
else if (name == "PUSH [ErrorTag]")
{
requireValueUndefinedForInstruction(name, value);
result = {AssemblyItemType::PushTag, 0};
}
else if (name == "PUSH [tag]")
{
requireValueDefinedForInstruction(name, value);
result = {AssemblyItemType::PushTag, updateUsedTags(u256(value))};
}
else if (name == "PUSH [$]")
{
requireValueDefinedForInstruction(name, value);
result = {AssemblyItemType::PushSub, u256("0x" + value)};
}
else if (name == "PUSH #[$]")
{
requireValueDefinedForInstruction(name, value);
result = {AssemblyItemType::PushSubSize, u256("0x" + value)};
}
else if (name == "PUSHSIZE")
{
requireValueUndefinedForInstruction(name, value);
result = {AssemblyItemType::PushProgramSize, 0};
}
else if (name == "PUSHLIB")
{
requireValueDefinedForInstruction(name, value);
result = {AssemblyItemType::PushLibraryAddress, storeLibraryHash(value)};
}
else if (name == "PUSHDEPLOYADDRESS")
{
requireValueUndefinedForInstruction(name, value);
result = {AssemblyItemType::PushDeployTimeAddress, 0};
}
else if (name == "PUSHIMMUTABLE")
{
requireValueDefinedForInstruction(name, value);
result = {AssemblyItemType::PushImmutable, storeImmutableHash(value)};
}
else if (name == "ASSIGNIMMUTABLE")
{
requireValueDefinedForInstruction(name, value);
result = {AssemblyItemType::AssignImmutable, storeImmutableHash(value)};
}
else if (name == "tag")
{
requireValueDefinedForInstruction(name, value);
result = {AssemblyItemType::Tag, updateUsedTags(u256(value))};
}
else if (name == "PUSH data")
{
requireValueDefinedForInstruction(name, value);
result = {AssemblyItemType::PushData, u256("0x" + value)};
}
else if (name == "VERBATIM")
{
requireValueDefinedForInstruction(name, value);
AssemblyItem item(fromHex(value), 0, 0);
result = item;
}
else
solThrow(InvalidOpcode, "Invalid opcode: " + name);
}
result.setLocation(location);
result.m_modifierDepth = modifierDepth;
return result;
}
namespace
{
@ -221,7 +427,7 @@ std::string Assembly::assemblyString(
return tmp.str();
}
Json::Value Assembly::assemblyJSON(std::map<std::string, unsigned> const& _sourceIndices, bool _includeSourceList) const
Json::Value Assembly::assemblyJSON(std::vector<std::string> const& _sources, bool _includeSourceList) const
{
Json::Value root;
root[".code"] = Json::arrayValue;
@ -230,11 +436,12 @@ Json::Value Assembly::assemblyJSON(std::map<std::string, unsigned> const& _sourc
{
int sourceIndex = -1;
if (item.location().sourceName)
{
auto iter = _sourceIndices.find(*item.location().sourceName);
if (iter != _sourceIndices.end())
sourceIndex = static_cast<int>(iter->second);
}
for (size_t index = 0; index < _sources.size(); ++index)
if (_sources[index] == *item.location().sourceName)
{
sourceIndex = static_cast<int>(index);
break;
}
auto [name, data] = item.nameAndData(m_evmVersion);
Json::Value jsonItem;
@ -267,12 +474,12 @@ Json::Value Assembly::assemblyJSON(std::map<std::string, unsigned> const& _sourc
code.append(std::move(jumpdest));
}
}
if (_includeSourceList)
if (!_sources.empty() && _includeSourceList)
{
root["sourceList"] = Json::arrayValue;
Json::Value& jsonSourceList = root["sourceList"];
for (auto const& [name, index]: _sourceIndices)
jsonSourceList[index] = name;
for (int index = 0; index < static_cast<int>(_sources.size()); ++index)
jsonSourceList[index] = _sources[static_cast<size_t>(index)];
}
if (!m_data.empty() || !m_subs.empty())
@ -287,7 +494,8 @@ Json::Value Assembly::assemblyJSON(std::map<std::string, unsigned> const& _sourc
{
std::stringstream hexStr;
hexStr << std::hex << i;
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices, /*_includeSourceList = */false);
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sources, false);
data[hexStr.str()]["index"] = static_cast<int>(i);
}
}
@ -297,6 +505,130 @@ Json::Value Assembly::assemblyJSON(std::map<std::string, unsigned> const& _sourc
return root;
}
std::pair<std::shared_ptr<Assembly>, std::vector<std::string>> Assembly::fromJSON(Json::Value const& _json, std::vector<std::string> const& _sourceList, int _level)
{
solRequire(_json.isObject(), AssemblyImportException, "Supplied JSON is not an object.");
static std::set<std::string> const validMembers{".code", ".data", ".auxdata", "sourceList", "index"};
for (auto const& attribute: _json.getMemberNames())
solRequire(validMembers.count(attribute), AssemblyImportException, "Unknown attribute '" + attribute + "'.");
solRequire(_json.isMember(".code"), AssemblyImportException, "Member '.code' does not exist.");
solRequire(_json[".code"].isArray(), AssemblyImportException, "Member '.code' is not an array.");
for (auto const& codeItem: _json[".code"])
solRequire(codeItem.isObject(), AssemblyImportException, "Item of '.code' array is not an object.");
if (_level == 0)
{
if (_json.isMember("sourceList"))
{
solRequire(_json["sourceList"].isArray(), AssemblyImportException, "Optional member 'sourceList' is not an array.");
for (auto const& sourceListItem: _json["sourceList"])
solRequire(sourceListItem.isString(), AssemblyImportException, "Item of 'sourceList' array is not of type string.");
}
} else
solRequire(
!_json.isMember("sourceList"),
AssemblyImportException,
"Member 'sourceList' is only allowed in root JSON object."
);
std::shared_ptr<Assembly> result = std::make_shared<Assembly>(langutil::EVMVersion(), _level == 0, "");
std::vector<std::string> sourceList;
if (_json.isMember("sourceList"))
{
solAssert(_level == 0);
for (auto const& it: _json["sourceList"])
{
solRequire(
std::find(sourceList.begin(), sourceList.end(), it.asString()) == sourceList.end(),
AssemblyImportException,
"Items in 'sourceList' array are not unique."
);
sourceList.emplace_back(it.asString());
}
} else
sourceList = _sourceList;
result->importAssemblyItemsFromJSON(_json[".code"], sourceList);
if (_json[".auxdata"])
{
solRequire(_json[".auxdata"].isString(), AssemblyImportException, "Optional member '.auxdata' is not of type string.");
bytes auxdata{fromHex(_json[".auxdata"].asString())};
solRequire(!auxdata.empty(), AssemblyImportException, "Optional member '.auxdata' is not a valid hexadecimal string.");
result->m_auxiliaryData = auxdata;
}
if (_json.isMember(".data"))
{
solRequire(_json[".data"].isObject(), AssemblyImportException, "Optional member '.data' is not an object.");
Json::Value const& data = _json[".data"];
for (Json::ValueConstIterator dataIter = data.begin(); dataIter != data.end(); dataIter++)
{
solRequire(dataIter.key().isString(), AssemblyImportException, "Key inside '.data' is not of type string.");
std::string dataItemID = dataIter.key().asString();
Json::Value const& code = data[dataItemID];
if (code.isString())
{
if (!code.asString().empty())
{
bytes data_value{fromHex(code.asString())};
solRequire(
!data_value.empty(),
AssemblyImportException,
"Member '.data' contains a value for '" + dataItemID + "' that is not a valid hexadecimal string.");
}
result->m_data[h256(fromHex(dataItemID))] = fromHex(code.asString());
}
else if (code.isObject())
{
solAssert(code.isMember("index"));
size_t index = static_cast<size_t>(code["index"].asInt());
if (result->m_subs.size() <= index)
result->m_subs.resize(index + 1);
std::shared_ptr<Assembly> subassembly(Assembly::fromJSON(code, sourceList, _level + 1).first);
solAssert(subassembly);
result->m_subs[index] = subassembly;
}
else
solThrow(AssemblyImportException, "Key inside '.data' '" + dataItemID + "' can only be a valid hex-string or an object.");
}
}
if (_level == 0)
result->updatePaths();
return std::make_pair(result, sourceList);
}
void Assembly::updatePaths(std::vector<Assembly*> const& _parents, std::vector<size_t> const& _absolutePathFromRoot)
{
size_t subId = 0;
for (auto& assembly: this->m_subs)
{
std::vector<Assembly*> parents{_parents};
parents.push_back(this);
std::vector<size_t> absolutePathFromRoot{_absolutePathFromRoot};
absolutePathFromRoot.emplace_back(subId);
int pindex = 0;
for (auto& parent: parents)
{
if (pindex == 0)
parent->encodeSubPath(absolutePathFromRoot);
else
{
std::vector<size_t> relativePath{absolutePathFromRoot};
for (int i = 0; i < pindex; ++i)
relativePath.erase(relativePath.begin());
parent->encodeSubPath(relativePath);
}
++pindex;
}
assembly->updatePaths(parents, absolutePathFromRoot);
++subId;
}
}
AssemblyItem Assembly::namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID)
{
assertThrow(!_name.empty(), AssemblyException, "Empty named tag.");

View File

@ -150,10 +150,18 @@ public:
) const;
/// Create a JSON representation of the assembly.
Json::Value assemblyJSON(
std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>(),
bool _includeSourceList = true
) const;
Json::Value assemblyJSON(std::vector<std::string> const& _sources, bool _includeSourceList = true) const;
/// Loads the JSON representation of assembly.
/// @param _json JSON object containing assembly in the format produced by assemblyJSON().
/// @param _sourceList list of source names (used to supply the source-list from the root-assembly object to sub-assemblies).
/// @param _level this function might be called recursively, _level reflects the nesting level.
/// @returns Resulting Assembly object loaded from given json.
static std::pair<std::shared_ptr<Assembly>, std::vector<std::string>> fromJSON(
Json::Value const& _json,
std::vector<std::string> const& _sourceList = {},
int _level = 0
);
/// Mark this assembly as invalid. Calling ``assemble`` on it will throw.
void markAsInvalid() { m_invalid = true; }
@ -171,11 +179,25 @@ protected:
unsigned codeSize(unsigned subTagSize) const;
/// Add all assembly items from given JSON array. This function imports the items by iterating through
/// the code array. This method only works on clean Assembly objects that don't have any items defined yet.
/// @param _json JSON array that contains assembly items (e.g. json['.code'])
/// @param _sourceList list of source names.
void importAssemblyItemsFromJSON(Json::Value const& _code, std::vector<std::string> const& _sourceList);
/// Creates an AssemblyItem from a given JSON representation.
/// @param _json JSON object that consists a single assembly item
/// @param _sourceList list of source names.
/// @returns AssemblyItem of _json argument.
AssemblyItem createAssemblyItemFromJSON(Json::Value const& _json, std::vector<std::string> const& _sourceList);
private:
bool m_invalid = false;
Assembly const* subAssemblyById(size_t _subId) const;
void updatePaths(std::vector<Assembly *> const& _parents = {}, std::vector<size_t> const& _absolutePathFromRoot = {});
protected:
/// 0 is reserved for exception
unsigned m_usedTags = 1;
@ -217,7 +239,6 @@ protected:
/// Internal name of the assembly object, only used with the Yul backend
/// currently
std::string m_name;
langutil::SourceLocation m_currentSourceLocation;
public:

View File

@ -247,6 +247,18 @@ std::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
solThrow(AssemblyImportException, "Invalid jump type.");
}
std::string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
{
std::string text;

View File

@ -174,6 +174,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;

View File

@ -1,8 +1,11 @@
set(sources
AbstractAssemblyStack.h
Assembly.cpp
Assembly.h
AssemblyItem.cpp
AssemblyItem.h
EVMAssemblyStack.cpp
EVMAssemblyStack.h
BlockDeduplicator.cpp
BlockDeduplicator.h
CommonSubexpressionEliminator.cpp

View File

@ -0,0 +1,126 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <libevmasm/EVMAssemblyStack.h>
#include <libsolutil/JSON.h>
#include <liblangutil/Exceptions.h>
#include <libsolidity/codegen/CompilerContext.h>
using namespace solidity::util;
using namespace solidity::langutil;
using namespace solidity::frontend;
namespace solidity::evmasm
{
void EVMAssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source)
{
solAssert(!m_evmAssembly);
m_name = _sourceName;
solRequire(jsonParseStrict(_source, m_json), AssemblyImportException, "Could not parse JSON file.");
auto result = evmasm::Assembly::fromJSON(m_json);
m_evmAssembly = result.first;
m_sourceList = result.second;
solRequire(m_evmAssembly != nullptr, AssemblyImportException, "Could not create evm assembly object.");
}
void EVMAssemblyStack::assemble()
{
solAssert(m_evmAssembly);
solAssert(m_evmAssembly->isCreation());
solAssert(!m_evmRuntimeAssembly);
m_object = m_evmAssembly->assemble();
m_sourceMapping = AssemblyItem::computeSourceMapping(m_evmAssembly->items(), sourceIndices());
if (m_evmAssembly->numSubs() > 0)
{
m_evmRuntimeAssembly = std::make_shared<evmasm::Assembly>(m_evmAssembly->sub(0));
solAssert(m_evmRuntimeAssembly && !m_evmRuntimeAssembly->isCreation());
m_runtimeSourceMapping = AssemblyItem::computeSourceMapping(m_evmRuntimeAssembly->items(), sourceIndices());
m_runtimeObject = m_evmRuntimeAssembly->assemble();
}
}
LinkerObject const& EVMAssemblyStack::object(std::string const& _contractName) const
{
solAssert(_contractName == m_name);
return m_object;
}
LinkerObject const& EVMAssemblyStack::runtimeObject(std::string const& _contractName) const
{
solAssert(_contractName == m_name);
return m_runtimeObject;
}
std::map<std::string, unsigned> EVMAssemblyStack::sourceIndices() const
{
solAssert(m_evmAssembly);
std::map<std::string, unsigned> indices;
unsigned index = 0;
for (auto const& s: m_sourceList)
if (s != CompilerContext::yulUtilityFileName())
indices[s] = index++;
if (indices.find(CompilerContext::yulUtilityFileName()) == indices.end())
indices[CompilerContext::yulUtilityFileName()] = index++;
return indices;
}
std::string const* EVMAssemblyStack::sourceMapping(std::string const& _contractName) const
{
solAssert(_contractName == m_name);
return &m_sourceMapping;
}
std::string const* EVMAssemblyStack::runtimeSourceMapping(std::string const& _contractName) const
{
solAssert(_contractName == m_name);
return &m_runtimeSourceMapping;
}
Json::Value EVMAssemblyStack::assemblyJSON(std::string const& _contractName) const
{
solAssert(_contractName == m_name);
solAssert(m_evmAssembly);
std::vector<std::string> sources = sourceNames();
if (find(sources.begin(), sources.end(), CompilerContext::yulUtilityFileName()) == sources.end())
sources.emplace_back(CompilerContext::yulUtilityFileName());
return m_evmAssembly->assemblyJSON(sources);
}
std::string EVMAssemblyStack::assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const
{
solAssert(_contractName == m_name);
solAssert(m_evmAssembly);
return m_evmAssembly->assemblyString(m_debugInfoSelection, _sourceCodes);
}
std::string const EVMAssemblyStack::filesystemFriendlyName(std::string const& _contractName) const
{
solAssert(_contractName == m_name);
return m_name;
}
std::vector<std::string> EVMAssemblyStack::sourceNames() const
{
return m_sourceList;
}
} // namespace solidity::evmasm

View File

@ -0,0 +1,86 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include <libevmasm/AbstractAssemblyStack.h>
#include <libevmasm/Assembly.h>
#include <libevmasm/LinkerObject.h>
#include <libsolutil/JSON.h>
#include <map>
#include <string>
namespace solidity::evmasm
{
class EVMAssemblyStack: public AbstractAssemblyStack
{
public:
explicit EVMAssemblyStack(langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) {}
/// Runs parsing and analysis steps.
/// Multiple calls overwrite the previous state.
/// @throws AssemblyImportException, if JSON could not be validated.
void parseAndAnalyze(std::string const& _sourceName, std::string const& _source);
void assemble();
std::string const& name() const { return m_name; }
virtual LinkerObject const& object(std::string const& _contractName) const override;
virtual LinkerObject const& runtimeObject(std::string const& _contractName) const override;
std::shared_ptr<evmasm::Assembly> const& evmAssembly() const { return m_evmAssembly; }
std::shared_ptr<evmasm::Assembly> const& evmRuntimeAssembly() const { return m_evmRuntimeAssembly; }
virtual std::string const* sourceMapping(std::string const& _contractName) const override;
virtual std::string const* runtimeSourceMapping(std::string const& _contractName) const override;
virtual Json::Value assemblyJSON(std::string const& _contractName) const override;
virtual std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const override;
virtual std::string const filesystemFriendlyName(std::string const& _contractName) const override;
virtual std::vector<std::string> contractNames() const override { return {m_name}; }
virtual std::vector<std::string> sourceNames() const override;
std::map<std::string, unsigned> sourceIndices() const;
virtual bool compilationSuccessful() const override { return m_evmAssembly != nullptr; }
void selectDebugInfo(langutil::DebugInfoSelection _debugInfoSelection)
{
m_debugInfoSelection = _debugInfoSelection;
}
private:
langutil::EVMVersion m_evmVersion;
std::string m_name;
Json::Value m_json;
std::shared_ptr<evmasm::Assembly> m_evmAssembly;
std::shared_ptr<evmasm::Assembly> m_evmRuntimeAssembly;
evmasm::LinkerObject m_object; ///< Deployment object (includes the runtime sub-object).
evmasm::LinkerObject m_runtimeObject; ///< Runtime object.
std::vector<std::string> m_sourceList;
langutil::DebugInfoSelection m_debugInfoSelection = langutil::DebugInfoSelection::Default();
std::string m_sourceMapping;
std::string m_runtimeSourceMapping;
};
} // namespace solidity::evmasm

View File

@ -28,6 +28,7 @@ namespace solidity::evmasm
{
struct AssemblyException: virtual util::Exception {};
struct AssemblyImportException: virtual AssemblyException {};
struct OptimizerException: virtual AssemblyException {};
struct StackTooDeepException: virtual OptimizerException {};
struct ItemNotAvailableException: virtual OptimizerException {};

View File

@ -74,7 +74,6 @@
#include <liblangutil/SemVerHandler.h>
#include <liblangutil/SourceReferenceFormatter.h>
#include <libevmasm/Exceptions.h>
#include <libsolutil/SwarmHash.h>
#include <libsolutil/IpfsHash.h>
@ -702,6 +701,7 @@ bool CompilerStack::isRequestedContract(ContractDefinition const& _contract) con
bool CompilerStack::compile(State _stopAfter)
{
solAssert(m_compilationSourceType != CompilationSourceType::EvmAssemblyJSON);
m_stopAfter = _stopAfter;
if (m_stackState < AnalysisSuccessful)
if (!parseAndAnalyze(_stopAfter))
@ -986,7 +986,12 @@ Json::Value CompilerStack::assemblyJSON(std::string const& _contractName) const
Contract const& currentContract = contract(_contractName);
if (currentContract.evmAssembly)
return currentContract.evmAssembly->assemblyJSON(sourceIndices());
{
std::vector<std::string> sources = sourceNames();
if (find(sources.begin(), sources.end(), CompilerContext::yulUtilityFileName()) == sources.end())
sources.emplace_back(CompilerContext::yulUtilityFileName());
return currentContract.evmAssembly->assemblyJSON(sources);
}
else
return Json::Value();
}
@ -1001,9 +1006,11 @@ std::map<std::string, unsigned> CompilerStack::sourceIndices() const
std::map<std::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 (s.first != CompilerContext::yulUtilityFileName())
indices[s.first] = index++;
if (indices.find(CompilerContext::yulUtilityFileName()) == indices.end())
indices[CompilerContext::yulUtilityFileName()] = index++;
return indices;
}
@ -1611,6 +1618,11 @@ std::string CompilerStack::createMetadata(Contract const& _contract, bool _forIR
case CompilationSourceType::SolidityAST:
sourceType = "SolidityAST";
break;
case CompilationSourceType::EvmAssemblyJSON:
sourceType = "EvmAssemblyJson";
break;
default:
solAssert(false);
}
meta["language"] = sourceType;
meta["compiler"]["version"] = VersionStringStrict;

View File

@ -41,6 +41,7 @@
#include <liblangutil/EVMVersion.h>
#include <liblangutil/SourceLocation.h>
#include <libevmasm/AbstractAssemblyStack.h>
#include <libevmasm/LinkerObject.h>
#include <libsolutil/Common.h>
@ -91,7 +92,7 @@ class Analysis;
* It holds state and can be used to either step through the compilation stages (and abort e.g.
* before compilation to bytecode) or run the whole compilation in one call.
*/
class CompilerStack: public langutil::CharStreamProvider
class CompilerStack: public langutil::CharStreamProvider, public evmasm::AbstractAssemblyStack
{
public:
/// Noncopyable.
@ -123,7 +124,9 @@ public:
/// Regular compilation from Solidity source files.
Solidity,
/// Compilation from an imported Solidity AST.
SolidityAST
SolidityAST,
/// Compilation from an imported EVM Assembly JSON.
EvmAssemblyJSON
};
/// Creates a new compiler stack.
@ -139,6 +142,8 @@ public:
/// @returns the current state.
State state() const { return m_stackState; }
virtual bool compilationSuccessful() const override { return m_stackState >= CompilationSuccessful; }
/// Resets the compiler to an empty state. Unless @a _keepSettings is set to true,
/// all settings are reset as well.
void reset(bool _keepSettings = false);
@ -234,7 +239,7 @@ public:
bool compile(State _stopAfter = State::CompilationSuccessful);
/// @returns the list of sources (paths) used
std::vector<std::string> sourceNames() const;
virtual std::vector<std::string> sourceNames() const override;
/// @returns a mapping assigning each source name its index inside the vector returned
/// by sourceNames().
@ -255,13 +260,13 @@ public:
std::vector<std::string> const& unhandledSMTLib2Queries() const { return m_unhandledSMTLib2Queries; }
/// @returns a list of the contract names in the sources.
std::vector<std::string> contractNames() const;
virtual std::vector<std::string> contractNames() const override;
/// @returns the name of the last contract. If _sourceName is defined the last contract of that source will be returned.
std::string const lastContractName(std::optional<std::string> const& _sourceName = std::nullopt) const;
/// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use
std::string const filesystemFriendlyName(std::string const& _contractName) const;
virtual std::string const filesystemFriendlyName(std::string const& _contractName) const override;
/// @returns the IR representation of a contract.
std::string const& yulIR(std::string const& _contractName) const;
@ -276,10 +281,10 @@ public:
Json::Value const& yulIROptimizedAst(std::string const& _contractName) const;
/// @returns the assembled object for a contract.
evmasm::LinkerObject const& object(std::string const& _contractName) const;
virtual evmasm::LinkerObject const& object(std::string const& _contractName) const override;
/// @returns the runtime object for the contract.
evmasm::LinkerObject const& runtimeObject(std::string const& _contractName) const;
virtual evmasm::LinkerObject const& runtimeObject(std::string const& _contractName) const override;
/// @returns normal contract assembly items
evmasm::AssemblyItems const* assemblyItems(std::string const& _contractName) const;
@ -293,21 +298,21 @@ public:
/// @returns the string that provides a mapping between bytecode and sourcecode or a nullptr
/// if the contract does not (yet) have bytecode.
std::string const* sourceMapping(std::string const& _contractName) const;
virtual std::string const* sourceMapping(std::string const& _contractName) const override;
/// @returns the string that provides a mapping between runtime bytecode and sourcecode.
/// if the contract does not (yet) have bytecode.
std::string const* runtimeSourceMapping(std::string const& _contractName) const;
virtual std::string const* runtimeSourceMapping(std::string const& _contractName) const override;
/// @return a verbose text representation of the assembly.
/// @arg _sourceCodes is the map of input files to source code strings
/// Prerequisite: Successful compilation.
std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes = StringMap()) const;
virtual std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes = StringMap()) const override;
/// @returns a JSON representation of the assembly.
/// @arg _sourceCodes is the map of input files to source code strings
/// Prerequisite: Successful compilation.
Json::Value assemblyJSON(std::string const& _contractName) const;
virtual Json::Value assemblyJSON(std::string const& _contractName) const override;
/// @returns a JSON representing the contract ABI.
/// Prerequisite: Successful call to parse or compile.

View File

@ -23,13 +23,30 @@
# ast import/export tests:
# - first exporting a .sol file to JSON, then loading it into the compiler
# and exporting it again. The second JSON should be identical to the first.
#
# evm-assembly import/export tests:
# - first a .sol file will be exported to a combined json file, containing outputs
# for "asm" "bin" "bin-runtime" "opcodes" "srcmap" and "srcmap-runtime" (expected output).
# The "asm" output will then be used as import, where its output "bin" "bin-runtime"
# "opcodes" "srcmap" "srcmap-runtime" (obtained output) will be compared with the expected output.
# The expected output needs to be identical with the obtained output.
#
# Additionally to this, the direct import/export is tested by importing an
# evm-assembly json with --import-asm-json and directly exporting it again with
# --asm-json using the same solc invocation. The original asm json file used for the
# import and the resulting exported asm json file need to be identical.
set -euo pipefail
READLINK=readlink
if [[ "$OSTYPE" == "darwin"* ]]; then
READLINK=greadlink
READLINK="readlink"
if [[ "${OSTYPE}" == "darwin"* ]]; then
READLINK="greadlink"
fi
EXPR="expr"
if [[ "${OSTYPE}" == "darwin"* ]]; then
EXPR="gexpr"
fi
REPO_ROOT=$(${READLINK} -f "$(dirname "$0")"/..)
SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-${REPO_ROOT}/build}
SOLC="${SOLIDITY_BUILD_DIR}/solc/solc"
@ -40,7 +57,7 @@ source "${REPO_ROOT}/scripts/common.sh"
function print_usage
{
echo "Usage: ${0} ast [--exit-on-error|--help]."
echo "Usage: ${0} ast|evm-assembly [--exit-on-error|--help]."
}
function print_used_commands
@ -48,6 +65,7 @@ function print_used_commands
local test_directory="$1"
local export_command="$2"
local import_command="$3"
echo
printError "You can find the files used for this test here: ${test_directory}"
printError "Used commands for test:"
printError "# export"
@ -61,34 +79,38 @@ function print_stderr_stdout
local error_message="$1"
local stderr_file="$2"
local stdout_file="$3"
printError "$error_message"
printError "${error_message}"
printError ""
printError "stderr:"
cat "$stderr_file" >&2
cat "${stderr_file}" >&2
printError ""
printError "stdout:"
cat "$stdout_file" >&2
cat "${stdout_file}" >&2
}
function check_import_test_type_unset
{
[[ -z "$IMPORT_TEST_TYPE" ]] || fail "ERROR: Import test type can only be set once. Aborting."
[[ -z "${IMPORT_TEST_TYPE}" ]] || fail "ERROR: Import test type can only be set once. Aborting."
}
IMPORT_TEST_TYPE=
EXIT_ON_ERROR=0
for PARAM in "$@"
do
case "$PARAM" in
case "${PARAM}" in
ast) check_import_test_type_unset ; IMPORT_TEST_TYPE="ast" ;;
evm-assembly) check_import_test_type_unset ; IMPORT_TEST_TYPE="evm-assembly" ;;
--help) print_usage ; exit 0 ;;
--exit-on-error) EXIT_ON_ERROR=1 ;;
*) fail "Unknown option '$PARAM'. Aborting. $(print_usage)" ;;
*) fail "Unknown option '${PARAM}'. Aborting. $(print_usage || true)" ;;
esac
done
SYNTAXTESTS_DIR="${REPO_ROOT}/test/libsolidity/syntaxTests"
ASTJSONTESTS_DIR="${REPO_ROOT}/test/libsolidity/ASTJSON"
SEMANTICTESTS_DIR="${REPO_ROOT}/test/libsolidity/semanticTests"
YULTESTS_DIR="${REPO_ROOT}/test/libyul"
FAILED=0
UNCOMPILABLE=0
@ -153,6 +175,281 @@ function test_ast_import_export_equivalence
TESTED=$((TESTED + 1))
}
function split_combined_json
{
local json_file="$1"
local output_path="$2"
local prefix="${3:-}"
for path_with_contract in $(jq '.contracts | keys | .[]' "${json_file}" 2> /dev/null)
do
local path=${path_with_contract}
local contract=""
local delimiter
delimiter=$("${EXPR}" index "${path}" ":") || true
if [[ -z "${prefix}" ]]
then
path=${path_with_contract:0:((${delimiter} - 1))}
contract=${path_with_contract:((${delimiter})):((${#path_with_contract} - ${delimiter} - 1))}
else
path=${path_with_contract}
contract=""
fi
for type in $(jq --raw-output ".contracts.${path_with_contract} | keys | .[]" "${json_file}" 2> /dev/null)
do
local output
output=$(jq --raw-output ".contracts.${path_with_contract}.\"${type}\"" "${json_file}")
if [[ -n "${output}" ]]
then
echo "${output}" > "${output_path}/${prefix}${contract}.${type}"
fi
done
done
rm "${json_file}"
}
function run_solc
{
local parameters=( "${@}" )
if ! "${SOLC}" "${parameters[@]}" > /dev/null 2> solc_stderr
then
printError "ERROR: ${parameters[*]}"
printError "${PWD}"
local SOLC_STDERR
SOLC_STDERR=$(cat "solc_stderr")
printError "${SOLC_STDERR}"
exit 1
fi
rm -f solc_stderr
return 0
}
function run_solc_store_stdout
{
local output_file=$1
local parameters=( "${@:2}" )
if ! "${SOLC}" "${parameters[@]}" > "${output_file}" 2> "${output_file}.error"
then
printError "ERROR: ${parameters[*]}"
printError "${PWD}"
local SOLC_STDERR
SOLC_STDERR=$(cat "${output_file}.error")
printError "${SOLC_STDERR}"
exit 1
fi
rm -f "${output_file}.error"
return 0
}
function test_evmjson_via_ir_and_yul_import_export
{
local sol_file="$1"
local input_files=( "${@:2}" )
mkdir yul
# export found solidity contracts to yul.
run_solc --optimize --via-ir --ir-optimized "${input_files[@]}" --no-optimize-yul -o yul/
for filename in yul/*
do
if [[ -s "${filename}" ]]
then
# remove '_opt' part of '<contract-name>_opt.yul'
mv "${filename}" "${filename/_opt/}"
else
# if file was empty, delete it.
rm -f "${filename}"
fi
done
local export_command
local import_command
mkdir sol
# create from a combined json from the supplied solidity contracts.
export_command=("${SOLC}" --combined-json "bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime" --optimize --via-ir --pretty-json --json-indent 4 --no-optimize-yul "${input_files[@]}" -o sol/)
run_solc --combined-json "bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime" --optimize --via-ir --pretty-json --json-indent 4 "${input_files[@]}" --no-optimize-yul -o sol/
mkdir input
# save the original supplied solidity contract sources for potential debugging purposes.
for file in "${input_files[@]}"
do
cat "${file}" >> "input/$(basename "${file}")"
done
# split the combined json into different files.
split_combined_json sol/combined.json sol/
# iterate through all yul files.
for yulfile in yul/*
do
# take the yul file and export it as evm assembly json. save the result in "$yulfile.asm.json".
run_solc_store_stdout "${yulfile}.asm.json" --strict-assembly "${yulfile}" --optimize --asm-json --pretty-json --json-indent 4 --no-optimize-yul
# remove the lines containing '=======', so that we just have a nice json file.
grep -v '^=======' "${yulfile}.asm.json" > tmpfile && mv tmpfile "${yulfile}.asm.json"
# import_command will just contain the last file in yul/*.asm - but it's still a good starting point ;)
import_command=("${SOLC}" --combined-json "bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime" --pretty-json --json-indent 4 --import-asm-json "${yulfile}.asm.json")
# import the created evm assembly json file and create a combined json out of it.
run_solc_store_stdout "${yulfile}.combined.json" --combined-json "bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime" --pretty-json --json-indent 4 --import-asm-json "${yulfile}.asm.json"
# split the combined json into different files.
split_combined_json "${yulfile}.combined.json" . "${yulfile}"
done
# now iterate over all files in the sol/ output folder.
# the files stored here will normally look like e.g. "sol/C.asm", "sol/C.bin"
# they were generated by the split_combined_json call above and contain the contract
# name and the type (e.g. bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime) in the
# file-name.
for file in sol/*
do
local type
local delimiter
local yul
# first just remove all path parts from the file and put it in the type.
# - "sol/C.asm" -> "C.asm"
type="$(basename "${file}")"
# delimiter is just the position of that dot that is delimiting the contract
# name from it's type.
delimiter=$("${EXPR}" index "${type}" ".")
# extract the type: for e.g. "C.asm" -> type will be "asm".
type=${type:((${delimiter})):((${#type} - ${delimiter}))}
# now we want to know which is the corresponding yul file, that should have exactly
# the same content. e.g. file="sol/C.srcmap-runtime" -> yul="yul/C.yul.srcmap-runtime"
yul=${file/sol/yul}
yul=${yul/${type}/yul.${type}}
# remember that we first exported the yul file from solidity contract files.
# then we generated and split the corresponding combined-json of that exported yul file.
# we also generated and split another combined-json file from the original
# solidity contract files. if the yul export to asm evm json and it's re-import
# will lead to the same content of the combined-json of the original solidity file,
# the yul export to asm evm json and it's reimport seem to work correctly.
# we can ignore "asm" and "json" files here. "asm" is the exported evm asm json.
# between the yul and the sol assembly jsons we may have some subtile differences,
# e.g. in the source-lists.
# however, if the yul/sol outputs of e.g. bin,bin-runtime,opcodes,srcmap,srcmap-runtime
# is matching, we consider that the reimport was done correctly.
if [[ "${type}" == "asm" ]] || [[ "${type}" == "json" ]]
then
continue
fi
# compare the files. e.g. "sol/C.srcmap-runtime" with "yul/C.yul.srcmap-runtime"
if ! diff_files "${file}" "${yul}" > diff_error
then
local diff_error
diff_error=$(cat diff_error)
printError "ERROR: diff failed ${file} ${yul}:\n ${diff_error}"
if (( EXIT_ON_ERROR == 1 ))
then
print_used_commands "${PWD}" "${export_command[*]}" "${import_command[*]}"
exit 1
fi
return 1
fi
done
rm -rf sol
rm -rf yul
rm -rf input
return 0
}
function test_evmjson_sol_import_export
{
local sol_file="$1"
local input_files=( "${@:2}" )
mkdir -p sol
# create from a combined json from the supplied solidity contracts.
local export_command
local import_command
export_command=("${SOLC}" --combined-json "bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime" --pretty-json --json-indent 4 "${input_files[@]}" -o sol/)
run_solc --combined-json "bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime" --pretty-json --json-indent 4 "${input_files[@]}" -o sol/
mkdir input
# save the original supplied solidity contract sources for potential debugging purposes.
for file in "${input_files[@]}"
do
cat "${file}" >> "input/$(basename "${file}")"
done
# split the combined json into different files.
split_combined_json sol/combined.json sol/
mkdir -p imported-from-sol
for file in sol/*.asm
do
name=$(basename "${file}" .asm)
# import_command will just contain the last file in sol/*.asm - but it's still a good starting point ;)
import_command=("${SOLC}" --import-ast --combined-json "bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime" --pretty-json --json-indent 4 --import-asm-json "${file}")
run_solc_store_stdout "imported-from-sol/combined.json" --combined-json "bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime" --pretty-json --json-indent 4 --import-asm-json "${file}"
split_combined_json imported-from-sol/combined.json imported-from-sol/ "${name}"
done
for file in sol/*
do
local imported
imported=${file/sol/imported-from-sol}
if ! diff_files "${file}" "${imported}" > diff_error
then
local diff_error
diff_error=$(cat diff_error)
printError "ERROR: diff failed ${file} ${imported}:\n ${diff_error}"
if (( EXIT_ON_ERROR == 1 ))
then
print_used_commands "${PWD}" "${export_command[*]}" "${import_command[*]}"
exit 1
fi
return 1
fi
done
rm -rf sol
rm -rf imported-from-sol
rm -rf input
return 0
}
function test_evmjson_import_export_equivalence
{
local sol_file="$1"
local input_files=( "${@:2}" )
local success=1
# export sol to yul. generate artefacts from sol and convert yul to asm json.
# import the yul asm json and check whether the sol artefacts are the same as for yul.
if ! test_evmjson_via_ir_and_yul_import_export "${sol_file}" "${input_files[@]}"
then
success=0
fi
# only run the next test-step, if the previous test was run correctly.
if (( success == 1 ))
then
# generate artefacts from sol. export sol to asm json. import that asm json and
# check whether the sol artefacts are the same as created by the asm json import.
if ! test_evmjson_sol_import_export "${sol_file}" "${input_files[@]}"
then
success=0
fi
fi
if (( success == 1 ))
then
TESTED=$((TESTED + 1))
else
FAILED=$((FAILED + 1))
fi
}
# function tests whether exporting and importing again is equivalent.
# Results are recorded by incrementing the FAILED or UNCOMPILABLE global variable.
# Also, in case of a mismatch a diff is printed
@ -166,21 +463,23 @@ function test_import_export_equivalence {
local solc_return_code
local compile_test
case "$IMPORT_TEST_TYPE" in
case "${IMPORT_TEST_TYPE}" in
ast) compile_test="--ast-compact-json" ;;
evm-assembly) compile_test="--bin" ;;
*) assertFail "Unknown import test type '${IMPORT_TEST_TYPE}'. Aborting." ;;
esac
set +e
output=$("$SOLC" "${compile_test}" "${input_files[@]}" 2>&1)
output=$("${SOLC}" "${compile_test}" "${input_files[@]}" 2>&1)
solc_return_code=$?
set -e
# if input files where compilable with success
if (( solc_return_code == 0 ))
then
case "$IMPORT_TEST_TYPE" in
case "${IMPORT_TEST_TYPE}" in
ast) test_ast_import_export_equivalence "${sol_file}" "${input_files[@]}" ;;
evm-assembly) test_evmjson_import_export_equivalence "${sol_file}" "${input_files[@]}" ;;
*) assertFail "Unknown import test type '${IMPORT_TEST_TYPE}'. Aborting." ;;
esac
else
@ -191,70 +490,163 @@ function test_import_export_equivalence {
# and print some details about the corresponding solc invocation.
if (( solc_return_code == 2 ))
then
fail "\n\nERROR: Uncaught Exception while executing '$SOLC ${compile_test} ${input_files[*]}':\n${output}\n"
# For the evm-assembly import/export tests, this script uses only the
# old code generator. Some semantic test can only be compiled with
# --via-ir (some need to be additionally compiled with --optimize).
# The tests that are meant to be compiled with --via-ir are throwing
# an UnimplementedFeatureError exception (e.g. Copying of type struct C.S
# memory[] memory to storage not yet supported, Copying nested calldata
# dynamic arrays to storage is not implemented in the old code generator.)
# We will just ignore these kind of exceptions for now.
# However, any other exception will be treated as a fatal error and the
# script execution will be terminated with an error.
if [[ "${output}" != *"UnimplementedFeatureError"* ]]
then
fail "\n\nERROR: Uncaught Exception while executing '${SOLC} ${compile_test} ${input_files[*]}':\n${output}\n"
fi
fi
fi
}
WORKINGDIR=$PWD
function test_evmjson_import_from_yul_export
{
local files="${*}"
for yulfile in $files
do
local export_command
local import_command
echo -n "·"
command_available "$SOLC" --version
# create a temporary sub-directory
FILETMP=$(mktemp -d)
cd "${FILETMP}"
set +e
output=$("${SOLC}" "--strict-assembly" "${yulfile}" "--bin" "--optimize" 2>/dev/null)
solc_return_code=$?
set -e
# if yul file got compiled with success.
if (( solc_return_code == 0 ))
then
yul_bin="$(basename "$yulfile").bin"
yul_json="$(basename "$yulfile").asm.json"
echo "$output" > "${yul_bin}"
# remove all lines starting with '=======' and 'Binary representation:'.
grep -v -e '^=======' -e '^Binary representation:' "${yul_bin}" > tmpfile && mv tmpfile "${yul_bin}"
# remove all white-spaces. we only want the binary.
tr -d '[:space:]' < "${yul_bin}" > tmpfile && mv tmpfile "${yul_bin}"
# only compare bytecode, if bytecode got generated.
if [[ -s "${yul_bin}" ]]
then
# take the yul file and export it as evm assembly json. save the result in "$yul_json".
export_command=("${SOLC}" --strict-assembly "${yulfile}" --optimize --asm-json --pretty-json --json-indent 4)
run_solc_store_stdout "${yul_json}" --strict-assembly "${yulfile}" --optimize --asm-json --pretty-json --json-indent 4
# remove the lines containing '=======', so that we just have a nice json file.
grep -v '^=======' "${yul_json}" > tmpfile && mv tmpfile "${yul_json}"
# import the created evm assembly json file and create a combined json out of it.
import_command=("${SOLC}" --combined-json "bin" --pretty-json --json-indent 4 --import-asm-json "${yul_json}")
run_solc_store_stdout "${yul_json}.combined.json" --combined-json "bin" --pretty-json --json-indent 4 --import-asm-json "${yul_json}"
# split the combined json into different files.
split_combined_json "${yul_json}.combined.json" . "compiled"
tr -d '[:space:]' < "compiled.bin" > tmpfile && mv tmpfile "compiled.bin"
if ! diff_files compiled.bin "${yul_bin}" > diff_error
then
diff_error=$(cat diff_error)
printError "ERROR: diff failed ${yulfile}:\n ${diff_error}"
if (( EXIT_ON_ERROR == 1 ))
then
print_used_commands "${PWD}" "${export_command[*]}" "${import_command[*]}"
exit 1
fi
FAILED=$((FAILED + 1))
else
TESTED=$((TESTED + 1))
fi
fi
fi
cd "${WORKINGDIR}"
# Delete temporary files
rm -rf "${FILETMP}"
done
echo
}
WORKINGDIR=${PWD}
command_available "${SOLC}" --version
command_available jq --version
command_available "${EXPR}" --version
command_available "${READLINK}" --version
case "$IMPORT_TEST_TYPE" in
case "${IMPORT_TEST_TYPE}" in
ast) TEST_DIRS=("${SYNTAXTESTS_DIR}" "${ASTJSONTESTS_DIR}") ;;
*) assertFail "Import test type not defined. $(print_usage)" ;;
evm-assembly) TEST_DIRS=("${SEMANTICTESTS_DIR}") ;;
*) assertFail "Import test type not defined. $(print_usage || true)}" ;;
esac
YUL_NSOURCES=0
if [[ "${IMPORT_TEST_TYPE}" == "evm-assembly" ]]
then
IMPORT_TEST_FILES=$(find "${YULTESTS_DIR}" -name "*.yul")
YUL_NSOURCES="$(echo "${IMPORT_TEST_FILES}" | wc -l)"
echo "Looking at ${YUL_NSOURCES} .yul files..."
test_evmjson_import_from_yul_export "${IMPORT_TEST_FILES[@]}"
fi
# boost_filesystem_bug specifically tests a local fix for a boost::filesystem
# bug. Since the test involves a malformed path, there is no point in running
# tests on it. See https://github.com/boostorg/filesystem/issues/176
IMPORT_TEST_FILES=$(find "${TEST_DIRS[@]}" -name "*.sol" -and -not -name "boost_filesystem_bug.sol" -not -path "*/experimental/*")
NSOURCES="$(echo "${IMPORT_TEST_FILES}" | wc -l)"
echo "Looking at ${NSOURCES} .sol files..."
SOL_NSOURCES="$(echo "${IMPORT_TEST_FILES}" | wc -l)"
echo "Looking at ${SOL_NSOURCES} .sol files..."
for solfile in $IMPORT_TEST_FILES
NSOURCES=$(( YUL_NSOURCES + SOL_NSOURCES ))
for solfile in ${IMPORT_TEST_FILES}
do
echo -n "·"
# create a temporary sub-directory
FILETMP=$(mktemp -d)
cd "$FILETMP"
cd "${FILETMP}"
set +e
OUTPUT=$("$SPLITSOURCES" "$solfile")
OUTPUT=$("${SPLITSOURCES}" "${solfile}")
SPLITSOURCES_RC=$?
set -e
if (( SPLITSOURCES_RC == 0 ))
then
IFS=' ' read -ra OUTPUT_ARRAY <<< "$OUTPUT"
test_import_export_equivalence "$solfile" "${OUTPUT_ARRAY[@]}"
IFS=' ' read -ra OUTPUT_ARRAY <<< "${OUTPUT}"
test_import_export_equivalence "${solfile}" "${OUTPUT_ARRAY[@]}"
elif (( SPLITSOURCES_RC == 1 ))
then
test_import_export_equivalence "$solfile" "$solfile"
test_import_export_equivalence "${solfile}" "${solfile}"
elif (( SPLITSOURCES_RC == 2 ))
then
# The script will exit with return code 2, if an UnicodeDecodeError occurred.
# This is the case if e.g. some tests are using invalid utf-8 sequences. We will ignore
# these errors, but print the actual output of the script.
printError "\n\n${OUTPUT}\n\n"
test_import_export_equivalence "$solfile" "$solfile"
test_import_export_equivalence "${solfile}" "${solfile}"
else
# All other return codes will be treated as critical errors. The script will exit.
printError "\n\nGot unexpected return code ${SPLITSOURCES_RC} from '${SPLITSOURCES} ${solfile}'. Aborting."
printError "\n\n${OUTPUT}\n\n"
cd "$WORKINGDIR"
cd "${WORKINGDIR}"
# Delete temporary files
rm -rf "$FILETMP"
rm -rf "${FILETMP}"
exit 1
fi
cd "$WORKINGDIR"
cd "${WORKINGDIR}"
# Delete temporary files
rm -rf "$FILETMP"
rm -rf "${FILETMP}"
done
echo

View File

@ -92,6 +92,7 @@ namespace
std::set<frontend::InputMode> const CompilerInputModes{
frontend::InputMode::Compiler,
frontend::InputMode::CompilerWithASTImport,
frontend::InputMode::EVMAssemblerJSON
};
} // anonymous namespace
@ -166,42 +167,77 @@ static bool coloredOutput(CommandLineOptions const& _options)
(_options.formatting.coloredOutput.has_value() && _options.formatting.coloredOutput.value());
}
void CommandLineInterface::handleEVMAssembly(std::string const& _contract)
{
solAssert(m_assemblyStack);
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
if (m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asmJson)
{
std::string assembly;
if (m_options.compiler.outputs.asmJson)
assembly = util::jsonPrint(removeNullMembers(m_assemblyStack->assemblyJSON(_contract)), m_options.formatting.json);
else
assembly = m_assemblyStack->assemblyString(_contract, m_fileReader.sourceUnits());
if (!m_options.output.dir.empty())
createFile(
m_compiler->filesystemFriendlyName(_contract) +
(m_options.compiler.outputs.asmJson ? "_evm.json" : ".evm"),
assembly
);
else
sout() << "EVM assembly:" << std::endl << assembly << std::endl;
}
}
void CommandLineInterface::handleBinary(std::string const& _contract)
{
solAssert(m_assemblyStack);
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
std::string binary;
std::string binaryRuntime;
if (m_options.compiler.outputs.binary)
binary = objectWithLinkRefsHex(m_assemblyStack->object(_contract));
if (m_options.compiler.outputs.binaryRuntime)
binaryRuntime = objectWithLinkRefsHex(m_assemblyStack->runtimeObject(_contract));
if (m_options.compiler.outputs.binary)
{
if (!m_options.output.dir.empty())
createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", objectWithLinkRefsHex(m_compiler->object(_contract)));
createFile(m_assemblyStack->filesystemFriendlyName(_contract) + ".bin", binary);
else
{
sout() << "Binary:" << std::endl;
sout() << objectWithLinkRefsHex(m_compiler->object(_contract)) << std::endl;
sout() << binary << std::endl;
}
}
if (m_options.compiler.outputs.binaryRuntime)
{
if (!m_options.output.dir.empty())
createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)));
createFile(m_assemblyStack->filesystemFriendlyName(_contract) + ".bin-runtime", binaryRuntime);
else
{
sout() << "Binary of the runtime part:" << std::endl;
sout() << objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)) << std::endl;
sout() << binaryRuntime << std::endl;
}
}
}
void CommandLineInterface::handleOpcode(std::string const& _contract)
{
solAssert(m_assemblyStack);
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
std::string opcodes{evmasm::disassemble(m_assemblyStack->object(_contract).bytecode, m_options.output.evmVersion)};
if (!m_options.output.dir.empty())
createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode, m_options.output.evmVersion));
createFile(m_assemblyStack->filesystemFriendlyName(_contract) + ".opcode", opcodes);
else
{
sout() << "Opcodes:" << std::endl;
sout() << std::uppercase << evmasm::disassemble(m_compiler->object(_contract).bytecode, m_options.output.evmVersion);
sout() << std::uppercase << opcodes;
sout() << std::endl;
}
}
@ -701,6 +737,12 @@ void CommandLineInterface::processInput()
compile();
outputCompilationResults();
break;
case InputMode::EVMAssemblerJSON:
assembleFromEvmAssemblyJson();
handleCombinedJSON();
handleBytecode(m_assemblyStack->contractNames().front());
handleEVMAssembly(m_assemblyStack->contractNames().front());
break;
}
}
@ -717,11 +759,38 @@ void CommandLineInterface::printLicense()
sout() << licenseText << std::endl;
}
void CommandLineInterface::assembleFromEvmAssemblyJson()
{
solAssert(m_options.input.mode == InputMode::EVMAssemblerJSON);
solAssert(!m_assemblyStack);
solAssert(!m_evmAssemblyStack && !m_compiler);
std::unique_ptr<evmasm::EVMAssemblyStack> evmAssemblyStack;
solAssert(m_fileReader.sourceUnits().size() == 1);
auto&& [sourceUnitName, source] = *m_fileReader.sourceUnits().begin();
try
{
evmAssemblyStack = std::make_unique<evmasm::EVMAssemblyStack>(m_options.output.evmVersion);
evmAssemblyStack->parseAndAnalyze(sourceUnitName, source);
if (m_options.output.debugInfoSelection.has_value())
evmAssemblyStack->selectDebugInfo(m_options.output.debugInfoSelection.value());
evmAssemblyStack->assemble();
m_evmAssemblyStack = std::move(evmAssemblyStack);
m_assemblyStack = m_evmAssemblyStack.get();
}
catch (evmasm::AssemblyImportException const& _exception)
{
solThrow(CommandLineExecutionError, "Assembly Import Error: "s + _exception.what());
}
}
void CommandLineInterface::compile()
{
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
solAssert(!m_assemblyStack);
solAssert(!m_evmAssemblyStack && !m_compiler);
m_compiler = std::make_unique<CompilerStack>(m_universalCallback.callback());
m_assemblyStack = m_compiler.get();
SourceReferenceFormatter formatter(serr(false), *m_compiler, coloredOutput(m_options), m_options.formatting.withErrorIds);
@ -831,6 +900,7 @@ void CommandLineInterface::compile()
void CommandLineInterface::handleCombinedJSON()
{
solAssert(m_assemblyStack);
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
if (!m_options.compiler.combinedJsonRequests.has_value())
@ -839,63 +909,59 @@ void CommandLineInterface::handleCombinedJSON()
Json::Value output(Json::objectValue);
output[g_strVersion] = frontend::VersionString;
std::vector<std::string> contracts = m_compiler->contractNames();
// NOTE: The state checks here are more strict that in Standard JSON. There we allow
// requesting certain outputs even if compilation fails as long as analysis went ok.
bool compilationSuccess = m_compiler->state() >= CompilerStack::State::CompilationSuccessful;
std::vector<std::string> contracts = m_assemblyStack->contractNames();
if (!contracts.empty())
output[g_strContracts] = Json::Value(Json::objectValue);
for (std::string const& contractName: contracts)
{
Json::Value& contractData = output[g_strContracts][contractName] = Json::objectValue;
if (m_options.compiler.combinedJsonRequests->abi && compilationSuccess)
if (m_options.compiler.combinedJsonRequests->abi && m_compiler->compilationSuccessful())
contractData[g_strAbi] = m_compiler->contractABI(contractName);
if (m_options.compiler.combinedJsonRequests->metadata && compilationSuccess)
if (m_options.compiler.combinedJsonRequests->metadata && m_compiler->compilationSuccessful())
contractData["metadata"] = m_compiler->metadata(contractName);
if (m_options.compiler.combinedJsonRequests->binary && compilationSuccess)
contractData[g_strBinary] = m_compiler->object(contractName).toHex();
if (m_options.compiler.combinedJsonRequests->binaryRuntime && compilationSuccess)
contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex();
if (m_options.compiler.combinedJsonRequests->opcodes && compilationSuccess)
contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode, m_options.output.evmVersion);
if (m_options.compiler.combinedJsonRequests->asm_ && compilationSuccess)
contractData[g_strAsm] = m_compiler->assemblyJSON(contractName);
if (m_options.compiler.combinedJsonRequests->storageLayout && compilationSuccess)
if (m_options.compiler.combinedJsonRequests->binary && m_assemblyStack->compilationSuccessful())
contractData[g_strBinary] = m_assemblyStack->object(contractName).toHex();
if (m_options.compiler.combinedJsonRequests->binaryRuntime && m_assemblyStack->compilationSuccessful())
contractData[g_strBinaryRuntime] = m_assemblyStack->runtimeObject(contractName).toHex();
if (m_options.compiler.combinedJsonRequests->opcodes && m_assemblyStack->compilationSuccessful())
contractData[g_strOpcodes] = evmasm::disassemble(m_assemblyStack->object(contractName).bytecode, m_options.output.evmVersion);
if (m_options.compiler.combinedJsonRequests->asm_ && m_assemblyStack->compilationSuccessful())
contractData[g_strAsm] = m_assemblyStack->assemblyJSON(contractName);
if (m_options.compiler.combinedJsonRequests->storageLayout && m_compiler->compilationSuccessful())
contractData[g_strStorageLayout] = m_compiler->storageLayout(contractName);
if (m_options.compiler.combinedJsonRequests->generatedSources && compilationSuccess)
if (m_options.compiler.combinedJsonRequests->generatedSources && m_compiler->compilationSuccessful())
contractData[g_strGeneratedSources] = m_compiler->generatedSources(contractName, false);
if (m_options.compiler.combinedJsonRequests->generatedSourcesRuntime && compilationSuccess)
if (m_options.compiler.combinedJsonRequests->generatedSourcesRuntime && m_compiler->compilationSuccessful())
contractData[g_strGeneratedSourcesRuntime] = m_compiler->generatedSources(contractName, true);
if (m_options.compiler.combinedJsonRequests->srcMap && compilationSuccess)
if (m_options.compiler.combinedJsonRequests->srcMap && m_assemblyStack->compilationSuccessful())
{
auto map = m_compiler->sourceMapping(contractName);
auto map = m_assemblyStack->sourceMapping(contractName);
contractData[g_strSrcMap] = map ? *map : "";
}
if (m_options.compiler.combinedJsonRequests->srcMapRuntime && compilationSuccess)
if (m_options.compiler.combinedJsonRequests->srcMapRuntime && m_assemblyStack->compilationSuccessful())
{
auto map = m_compiler->runtimeSourceMapping(contractName);
auto map = m_assemblyStack->runtimeSourceMapping(contractName);
contractData[g_strSrcMapRuntime] = map ? *map : "";
}
if (m_options.compiler.combinedJsonRequests->funDebug && compilationSuccess)
if (m_options.compiler.combinedJsonRequests->funDebug && m_assemblyStack->compilationSuccessful())
contractData[g_strFunDebug] = StandardCompiler::formatFunctionDebugData(
m_compiler->object(contractName).functionDebugData
m_assemblyStack->object(contractName).functionDebugData
);
if (m_options.compiler.combinedJsonRequests->funDebugRuntime && compilationSuccess)
if (m_options.compiler.combinedJsonRequests->funDebugRuntime && m_assemblyStack->compilationSuccessful())
contractData[g_strFunDebugRuntime] = StandardCompiler::formatFunctionDebugData(
m_compiler->runtimeObject(contractName).functionDebugData
m_assemblyStack->runtimeObject(contractName).functionDebugData
);
if (m_options.compiler.combinedJsonRequests->signatureHashes && compilationSuccess)
if (m_options.compiler.combinedJsonRequests->signatureHashes && m_compiler->compilationSuccessful())
contractData[g_strSignatureHashes] = m_compiler->interfaceSymbols(contractName)["methods"];
if (m_options.compiler.combinedJsonRequests->natspecDev && compilationSuccess)
if (m_options.compiler.combinedJsonRequests->natspecDev && m_compiler->compilationSuccessful())
contractData[g_strNatspecDev] = m_compiler->natspecDev(contractName);
if (m_options.compiler.combinedJsonRequests->natspecUser && compilationSuccess)
if (m_options.compiler.combinedJsonRequests->natspecUser && m_compiler->compilationSuccessful())
contractData[g_strNatspecUser] = m_compiler->natspecUser(contractName);
}
bool needsSourceList =
m_options.compiler.combinedJsonRequests->ast ||
bool needsSourceList = m_options.compiler.combinedJsonRequests->ast ||
m_options.compiler.combinedJsonRequests->srcMap ||
m_options.compiler.combinedJsonRequests->srcMapRuntime;
if (needsSourceList)
@ -903,7 +969,7 @@ void CommandLineInterface::handleCombinedJSON()
// Indices into this array are used to abbreviate source names in source locations.
output[g_strSourceList] = Json::Value(Json::arrayValue);
for (auto const& source: m_compiler->sourceNames())
for (auto const& source: m_assemblyStack->sourceNames())
output[g_strSourceList].append(source);
}
@ -1152,6 +1218,55 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y
else
serr() << "No text representation found." << std::endl;
}
if (m_options.compiler.outputs.asmJson)
{
std::shared_ptr<evmasm::Assembly> assembly{stack.assembleEVMWithDeployed().first};
if (assembly)
{
std::function<std::map<std::string, unsigned>(yul::Object const&)> collectSourceIndices =
[&](yul::Object const& _object) -> std::map<std::string, unsigned> {
std::map<std::string, unsigned> sourceIndices;
if (_object.debugData && _object.debugData->sourceNames.has_value())
for (auto const& iter: *_object.debugData->sourceNames)
sourceIndices[*iter.second] = iter.first;
for (auto const& sub: _object.subObjects)
{
auto subObject = dynamic_cast<yul::Object const*>(sub.get());
if (subObject)
for (auto const& [name, index]: collectSourceIndices(*subObject))
sourceIndices[name] = index;
}
return sourceIndices;
};
if (stack.parserResult() && stack.parserResult()->debugData)
{
std::map<std::string, unsigned> sourceIndices = collectSourceIndices(*stack.parserResult());
// if sourceIndices are empty here, there were no source locations annotated in the yul source.
// in this case, we just add the filename of the yul file itself here.
if (sourceIndices.empty())
sourceIndices[src.first] = 0;
size_t max_index = 0;
for (auto const& [name, index]: sourceIndices)
if (max_index < index)
max_index = index;
std::vector<std::string> sourceList(max_index + 1);
uint32_t counter{0};
for (auto& source: sourceList)
source = "unknown-source-" + std::to_string(counter++);
for (auto const& [name, index]: sourceIndices)
sourceList[index] = name;
sout() << util::jsonPrint(
removeNullMembers(
assembly->assemblyJSON(sourceList)
),
m_options.formatting.json
) << std::endl;
return;
}
}
serr() << "Could not create Assembly JSON representation." << std::endl;
}
}
}
@ -1177,20 +1292,7 @@ void CommandLineInterface::outputCompilationResults()
if (needsHumanTargetedStdout(m_options))
sout() << std::endl << "======= " << contract << " =======" << std::endl;
// do we need EVM assembly?
if (m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asmJson)
{
std::string ret;
if (m_options.compiler.outputs.asmJson)
ret = util::jsonPrint(removeNullMembers(m_compiler->assemblyJSON(contract)), m_options.formatting.json);
else
ret = m_compiler->assemblyString(contract, m_fileReader.sourceUnits());
if (!m_options.output.dir.empty())
createFile(m_compiler->filesystemFriendlyName(contract) + (m_options.compiler.outputs.asmJson ? "_evm.json" : ".evm"), ret);
else
sout() << "EVM assembly:" << std::endl << ret << std::endl;
}
handleEVMAssembly(contract);
if (m_options.compiler.estimateGas)
handleGasEstimation(contract);

View File

@ -24,6 +24,7 @@
#include <solc/CommandLineParser.h>
#include <libevmasm/EVMAssemblyStack.h>
#include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/interface/DebugSettings.h>
#include <libsolidity/interface/FileReader.h>
@ -84,6 +85,7 @@ private:
void printVersion();
void printLicense();
void compile();
void assembleFromEvmAssemblyJson();
void serveLSP();
void link();
void writeLinkedFiles();
@ -98,6 +100,7 @@ private:
void handleCombinedJSON();
void handleAst();
void handleEVMAssembly(std::string const& _contract);
void handleBinary(std::string const& _contract);
void handleOpcode(std::string const& _contract);
void handleIR(std::string const& _contract);
@ -145,6 +148,8 @@ private:
UniversalCallback m_universalCallback{m_fileReader, m_solverCommand};
std::optional<std::string> m_standardJsonInput;
std::unique_ptr<frontend::CompilerStack> m_compiler;
std::unique_ptr<evmasm::EVMAssemblyStack> m_evmAssemblyStack;
evmasm::AbstractAssemblyStack* m_assemblyStack = nullptr;
CommandLineOptions m_options;
};

View File

@ -30,6 +30,8 @@
#include <range/v3/view/filter.hpp>
#include <range/v3/range/conversion.hpp>
#include <fmt/format.h>
using namespace solidity::langutil;
namespace po = boost::program_options;
@ -50,6 +52,7 @@ static std::string const g_strExperimentalViaIR = "experimental-via-ir";
static std::string const g_strGas = "gas";
static std::string const g_strHelp = "help";
static std::string const g_strImportAst = "import-ast";
static std::string const g_strImportEvmAssemblerJson = "import-asm-json";
static std::string const g_strInputFile = "input-file";
static std::string const g_strYul = "yul";
static std::string const g_strYulDialect = "yul-dialect";
@ -140,6 +143,7 @@ static std::map<InputMode, std::string> const g_inputModeName = {
{InputMode::StandardJson, "standard JSON"},
{InputMode::Linker, "linker"},
{InputMode::LanguageServer, "language server (LSP)"},
{InputMode::EVMAssemblerJSON, "EVM assembler (JSON format)"},
};
void CommandLineParser::checkMutuallyExclusive(std::vector<std::string> const& _optionNames)
@ -465,8 +469,15 @@ void CommandLineParser::parseOutputSelection()
CompilerOutputs::componentName(&CompilerOutputs::binary),
CompilerOutputs::componentName(&CompilerOutputs::irOptimized),
CompilerOutputs::componentName(&CompilerOutputs::astCompactJson),
CompilerOutputs::componentName(&CompilerOutputs::asmJson),
};
static std::set<std::string> const evmAssemblyJsonImportModeOutputs = {
CompilerOutputs::componentName(&CompilerOutputs::asm_),
CompilerOutputs::componentName(&CompilerOutputs::binary),
CompilerOutputs::componentName(&CompilerOutputs::binaryRuntime),
CompilerOutputs::componentName(&CompilerOutputs::opcodes),
CompilerOutputs::componentName(&CompilerOutputs::asmJson),
};
switch (_mode)
{
case InputMode::Help:
@ -477,6 +488,8 @@ void CommandLineParser::parseOutputSelection()
case InputMode::Compiler:
case InputMode::CompilerWithASTImport:
return util::contains(compilerModeOutputs, _outputName);
case InputMode::EVMAssemblerJSON:
return util::contains(evmAssemblyJsonImportModeOutputs, _outputName);
case InputMode::Assembler:
return util::contains(assemblerModeOutputs, _outputName);
case InputMode::StandardJson:
@ -657,6 +670,10 @@ General Information)").c_str(),
"Supported Inputs is the output of the --" + g_strStandardJSON + " or the one produced by "
"--" + g_strCombinedJson + " " + CombinedJsonRequests::componentName(&CombinedJsonRequests::ast)).c_str()
)
(
g_strImportEvmAssemblerJson.c_str(),
"Import EVM assembly from JSON. Assumes input is in the format used by --asm-json."
)
(
g_strLSP.c_str(),
"Switch to language server mode (\"LSP\"). Allows the compiler to be used as an analysis backend "
@ -925,6 +942,7 @@ void CommandLineParser::processArgs()
g_strYul,
g_strImportAst,
g_strLSP,
g_strImportEvmAssemblerJson,
});
if (m_args.count(g_strHelp) > 0)
@ -943,6 +961,8 @@ void CommandLineParser::processArgs()
m_options.input.mode = InputMode::Linker;
else if (m_args.count(g_strImportAst) > 0)
m_options.input.mode = InputMode::CompilerWithASTImport;
else if (m_args.count(g_strImportEvmAssemblerJson) > 0)
m_options.input.mode = InputMode::EVMAssemblerJSON;
else
m_options.input.mode = InputMode::Compiler;
@ -1002,9 +1022,39 @@ void CommandLineParser::processArgs()
if (option != CompilerOutputs::componentName(&CompilerOutputs::astCompactJson))
checkMutuallyExclusive({g_strStopAfter, option});
if (m_options.input.mode == InputMode::EVMAssemblerJSON)
{
static std::set<std::string> const supportedByEvmAsmJsonImport{
g_strImportEvmAssemblerJson,
CompilerOutputs::componentName(&CompilerOutputs::asm_),
CompilerOutputs::componentName(&CompilerOutputs::binary),
CompilerOutputs::componentName(&CompilerOutputs::binaryRuntime),
CompilerOutputs::componentName(&CompilerOutputs::asmJson),
CompilerOutputs::componentName(&CompilerOutputs::opcodes),
g_strCombinedJson,
g_strInputFile,
g_strJsonIndent,
g_strPrettyJson,
"srcmap",
"srcmap-runtime",
};
for (auto const& option: m_args)
if (!option.second.defaulted() && !supportedByEvmAsmJsonImport.count(option.first))
solThrow(
CommandLineValidationError,
fmt::format(
"Option --{} is not supported with --{}.",
option.first,
g_strImportEvmAssemblerJson
)
);
}
if (
m_options.input.mode != InputMode::Compiler &&
m_options.input.mode != InputMode::CompilerWithASTImport &&
m_options.input.mode != InputMode::EVMAssemblerJSON &&
m_options.input.mode != InputMode::Assembler
)
{
@ -1362,7 +1412,11 @@ void CommandLineParser::processArgs()
m_args.count(g_strModelCheckerTimeout);
m_options.output.viaIR = (m_args.count(g_strExperimentalViaIR) > 0 || m_args.count(g_strViaIR) > 0);
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::EVMAssemblerJSON
);
}
void CommandLineParser::parseCombinedJsonOption()
@ -1378,6 +1432,35 @@ void CommandLineParser::parseCombinedJsonOption()
m_options.compiler.combinedJsonRequests = CombinedJsonRequests{};
for (auto&& [componentName, component]: CombinedJsonRequests::componentMap())
m_options.compiler.combinedJsonRequests.value().*component = (requests.count(componentName) > 0);
if (m_options.input.mode == InputMode::EVMAssemblerJSON && m_options.compiler.combinedJsonRequests.has_value())
{
static bool CombinedJsonRequests::* invalidOptions[]{
&CombinedJsonRequests::abi,
&CombinedJsonRequests::ast,
&CombinedJsonRequests::funDebug,
&CombinedJsonRequests::funDebugRuntime,
&CombinedJsonRequests::generatedSources,
&CombinedJsonRequests::generatedSourcesRuntime,
&CombinedJsonRequests::metadata,
&CombinedJsonRequests::natspecDev,
&CombinedJsonRequests::natspecUser,
&CombinedJsonRequests::signatureHashes,
&CombinedJsonRequests::storageLayout
};
for (auto const invalidOption: invalidOptions)
if (m_options.compiler.combinedJsonRequests.value().*invalidOption)
solThrow(
CommandLineValidationError,
fmt::format(
"Invalid option to --{}: {} for --{}",
g_strCombinedJson,
CombinedJsonRequests::componentName(invalidOption),
g_strImportEvmAssemblerJson
)
);
}
}
size_t CommandLineParser::countEnabledOptions(std::vector<std::string> const& _optionNames) const

View File

@ -57,6 +57,7 @@ enum class InputMode
Linker,
Assembler,
LanguageServer,
EVMAssemblerJSON
};
struct CompilerOutputs

View File

@ -1567,7 +1567,8 @@ EVM assembly:
"name": "JUMP",
"source": 1
}
]
],
"index": 0
}
},
"sourceList":

View File

@ -0,0 +1 @@
--strict-assembly - --asm-json --pretty-json

View File

@ -0,0 +1,388 @@
======= <stdin> (EVM) =======
{
".code":
[
{
"begin": 37,
"end": 51,
"name": "PUSHSIZE",
"source": 0
},
{
"begin": 34,
"end": 35,
"name": "PUSH",
"source": 0,
"value": "0"
},
{
"begin": 27,
"end": 52,
"name": "SSTORE",
"source": 0
},
{
"begin": 67,
"end": 81,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 64,
"end": 65,
"name": "PUSH",
"source": 0,
"value": "1"
},
{
"begin": 57,
"end": 82,
"name": "SSTORE",
"source": 0
},
{
"begin": 97,
"end": 114,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000ffffffffffffffff"
},
{
"begin": 94,
"end": 95,
"name": "PUSH",
"source": 0,
"value": "2"
},
{
"begin": 87,
"end": 115,
"name": "SSTORE",
"source": 0
},
{
"begin": 130,
"end": 152,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffe"
},
{
"begin": 127,
"end": 128,
"name": "PUSH",
"source": 0,
"value": "3"
},
{
"begin": 120,
"end": 153,
"name": "SSTORE",
"source": 0
},
{
"begin": 168,
"end": 190,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffd"
},
{
"begin": 165,
"end": 166,
"name": "PUSH",
"source": 0,
"value": "4"
},
{
"begin": 158,
"end": 191,
"name": "SSTORE",
"source": 0
},
{
"begin": 206,
"end": 228,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffc"
},
{
"begin": 203,
"end": 204,
"name": "PUSH",
"source": 0,
"value": "5"
},
{
"begin": 196,
"end": 229,
"name": "SSTORE",
"source": 0
},
{
"begin": 21,
"end": 233,
"name": "STOP",
"source": 0
}
],
".data":
{
"0":
{
".code":
[
{
"begin": 278,
"end": 292,
"name": "PUSHSIZE",
"source": 0
},
{
"begin": 275,
"end": 276,
"name": "PUSH",
"source": 0,
"value": "6"
},
{
"begin": 268,
"end": 293,
"name": "SSTORE",
"source": 0
},
{
"begin": 310,
"end": 324,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 307,
"end": 308,
"name": "PUSH",
"source": 0,
"value": "7"
},
{
"begin": 300,
"end": 325,
"name": "SSTORE",
"source": 0
},
{
"begin": 342,
"end": 361,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000ffffffffffffffff"
},
{
"begin": 339,
"end": 340,
"name": "PUSH",
"source": 0,
"value": "8"
},
{
"begin": 332,
"end": 362,
"name": "SSTORE",
"source": 0
},
{
"begin": 379,
"end": 398,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffe"
},
{
"begin": 376,
"end": 377,
"name": "PUSH",
"source": 0,
"value": "9"
},
{
"begin": 369,
"end": 399,
"name": "SSTORE",
"source": 0
},
{
"begin": 417,
"end": 436,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffd"
},
{
"begin": 413,
"end": 415,
"name": "PUSH",
"source": 0,
"value": "A"
},
{
"begin": 406,
"end": 437,
"name": "SSTORE",
"source": 0
},
{
"begin": 260,
"end": 443,
"name": "STOP",
"source": 0
}
],
".data":
{
"0":
{
".code":
[
{
"begin": 494,
"end": 508,
"name": "PUSHSIZE",
"source": 0
},
{
"begin": 490,
"end": 492,
"name": "PUSH",
"source": 0,
"value": "B"
},
{
"begin": 483,
"end": 509,
"name": "SSTORE",
"source": 0
},
{
"begin": 529,
"end": 545,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 525,
"end": 527,
"name": "PUSH",
"source": 0,
"value": "C"
},
{
"begin": 518,
"end": 546,
"name": "SSTORE",
"source": 0
},
{
"begin": 566,
"end": 582,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000001"
},
{
"begin": 562,
"end": 564,
"name": "PUSH",
"source": 0,
"value": "D"
},
{
"begin": 555,
"end": 583,
"name": "SSTORE",
"source": 0
},
{
"begin": 603,
"end": 619,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000002"
},
{
"begin": 599,
"end": 601,
"name": "PUSH",
"source": 0,
"value": "E"
},
{
"begin": 592,
"end": 620,
"name": "SSTORE",
"source": 0
},
{
"begin": 473,
"end": 628,
"name": "STOP",
"source": 0
}
],
".data":
{
"0":
{
".code":
[
{
"begin": 676,
"end": 685,
"name": "INVALID",
"source": 0
}
],
"index": 0
},
"1":
{
".code":
[
{
"begin": 751,
"end": 760,
"name": "INVALID",
"source": 0
}
],
"index": 1
},
"2":
{
".code":
[
{
"begin": 826,
"end": 835,
"name": "INVALID",
"source": 0
}
],
"index": 2
}
},
"index": 0
}
},
"index": 0
}
},
"sourceList":
[
"<stdin>"
]
}

View File

@ -0,0 +1,43 @@
object "L0" {
code {
sstore(0, datasize("L0"))
sstore(1, datasize("L1"))
sstore(2, datasize("L1.L2"))
sstore(3, datasize("L1.L2.L2_0"))
sstore(4, datasize("L1.L2.L2_1"))
sstore(5, datasize("L1.L2.L2_2"))
}
object "L1" {
code {
sstore(6, datasize("L1"))
sstore(7, datasize("L2"))
sstore(8, datasize("L2.L2_0"))
sstore(9, datasize("L2.L2_1"))
sstore(10, datasize("L2.L2_2"))
}
object "L2" {
code {
sstore(11, datasize("L2"))
sstore(12, datasize("L2_0"))
sstore(13, datasize("L2_1"))
sstore(14, datasize("L2_2"))
}
object "L2_0" {
code {
invalid()
}
}
object "L2_1" {
code {
invalid()
}
}
object "L2_2" {
code {
invalid()
}
}
}
}
}

View File

@ -0,0 +1 @@
--strict-assembly - --asm-json --pretty-json

View File

@ -0,0 +1,437 @@
======= <stdin> (EVM) =======
{
".code":
[
{
"begin": 36,
"end": 51,
"name": "PUSH",
"source": 0,
"value": "0"
},
{
"begin": 33,
"end": 34,
"name": "DUP1",
"source": 0
},
{
"begin": 26,
"end": 52,
"name": "SSTORE",
"source": 0
},
{
"begin": 67,
"end": 80,
"name": "PUSHSIZE",
"source": 0
},
{
"begin": 64,
"end": 65,
"name": "PUSH",
"source": 0,
"value": "1"
},
{
"begin": 57,
"end": 81,
"name": "SSTORE",
"source": 0
},
{
"begin": 96,
"end": 111,
"name": "PUSH [$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 93,
"end": 94,
"name": "PUSH",
"source": 0,
"value": "2"
},
{
"begin": 86,
"end": 112,
"name": "SSTORE",
"source": 0
},
{
"begin": 127,
"end": 140,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 124,
"end": 125,
"name": "PUSH",
"source": 0,
"value": "3"
},
{
"begin": 117,
"end": 141,
"name": "SSTORE",
"source": 0
},
{
"begin": 156,
"end": 173,
"name": "PUSH [$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000ffffffffffffffff"
},
{
"begin": 153,
"end": 154,
"name": "PUSH",
"source": 0,
"value": "4"
},
{
"begin": 146,
"end": 174,
"name": "SSTORE",
"source": 0
},
{
"begin": 189,
"end": 204,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000ffffffffffffffff"
},
{
"begin": 186,
"end": 187,
"name": "PUSH",
"source": 0,
"value": "5"
},
{
"begin": 179,
"end": 205,
"name": "SSTORE",
"source": 0
},
{
"begin": 220,
"end": 237,
"name": "PUSH [$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffe"
},
{
"begin": 217,
"end": 218,
"name": "PUSH",
"source": 0,
"value": "6"
},
{
"begin": 210,
"end": 238,
"name": "SSTORE",
"source": 0
},
{
"begin": 253,
"end": 268,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffe"
},
{
"begin": 250,
"end": 251,
"name": "PUSH",
"source": 0,
"value": "7"
},
{
"begin": 243,
"end": 269,
"name": "SSTORE",
"source": 0
},
{
"begin": 284,
"end": 303,
"name": "PUSH [$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffd"
},
{
"begin": 281,
"end": 282,
"name": "PUSH",
"source": 0,
"value": "8"
},
{
"begin": 274,
"end": 304,
"name": "SSTORE",
"source": 0
},
{
"begin": 319,
"end": 336,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffd"
},
{
"begin": 316,
"end": 317,
"name": "PUSH",
"source": 0,
"value": "9"
},
{
"begin": 309,
"end": 337,
"name": "SSTORE",
"source": 0
},
{
"begin": 20,
"end": 341,
"name": "STOP",
"source": 0
}
],
".data":
{
"0":
{
".code":
[
{
"begin": 418,
"end": 433,
"name": "PUSH [$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 414,
"end": 416,
"name": "PUSH",
"source": 0,
"value": "A"
},
{
"begin": 407,
"end": 434,
"name": "SSTORE",
"source": 0
},
{
"begin": 452,
"end": 465,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 448,
"end": 450,
"name": "PUSH",
"source": 0,
"value": "B"
},
{
"begin": 441,
"end": 466,
"name": "SSTORE",
"source": 0
},
{
"begin": 484,
"end": 499,
"name": "PUSH [$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000001"
},
{
"begin": 480,
"end": 482,
"name": "PUSH",
"source": 0,
"value": "C"
},
{
"begin": 473,
"end": 500,
"name": "SSTORE",
"source": 0
},
{
"begin": 518,
"end": 531,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000001"
},
{
"begin": 514,
"end": 516,
"name": "PUSH",
"source": 0,
"value": "D"
},
{
"begin": 507,
"end": 532,
"name": "SSTORE",
"source": 0
},
{
"begin": 550,
"end": 567,
"name": "PUSH [$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000ffffffffffffffff"
},
{
"begin": 546,
"end": 548,
"name": "PUSH",
"source": 0,
"value": "E"
},
{
"begin": 539,
"end": 568,
"name": "SSTORE",
"source": 0
},
{
"begin": 586,
"end": 601,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000ffffffffffffffff"
},
{
"begin": 582,
"end": 584,
"name": "PUSH",
"source": 0,
"value": "F"
},
{
"begin": 575,
"end": 602,
"name": "SSTORE",
"source": 0
},
{
"begin": 399,
"end": 608,
"name": "STOP",
"source": 0
}
],
".data":
{
"0":
{
".code":
[
{
"begin": 658,
"end": 673,
"name": "PUSH [$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 654,
"end": 656,
"name": "PUSH",
"source": 0,
"value": "10"
},
{
"begin": 647,
"end": 674,
"name": "SSTORE",
"source": 0
},
{
"begin": 694,
"end": 707,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 690,
"end": 692,
"name": "PUSH",
"source": 0,
"value": "11"
},
{
"begin": 683,
"end": 708,
"name": "SSTORE",
"source": 0
},
{
"begin": 637,
"end": 716,
"name": "STOP",
"source": 0
}
],
".data":
{
"0":
{
".code":
[
{
"begin": 761,
"end": 770,
"name": "INVALID",
"source": 0
}
],
"index": 0
}
},
"index": 0
},
"1":
{
".code":
[
{
"begin": 833,
"end": 842,
"name": "INVALID",
"source": 0
}
],
"index": 1
}
},
"index": 0
},
"ACAF3289D7B601CBD114FB36C4D29C85BBFD5E133F14CB355C3FD8D99367964F": "48656c6c6f2c20576f726c6421"
},
"sourceList":
[
"<stdin>"
]
}

View File

@ -0,0 +1,43 @@
object "A" {
code {
sstore(0, dataoffset("A"))
sstore(1, datasize("A"))
sstore(2, dataoffset("B"))
sstore(3, datasize("B"))
sstore(4, dataoffset("B.C"))
sstore(5, datasize("B.C"))
sstore(6, dataoffset("B.E"))
sstore(7, datasize("B.E"))
sstore(8, dataoffset("B.C.D"))
sstore(9, datasize("B.C.D"))
}
data "data1" "Hello, World!"
object "B" {
code {
sstore(10, dataoffset("C"))
sstore(11, datasize("C"))
sstore(12, dataoffset("E"))
sstore(13, datasize("E"))
sstore(14, dataoffset("C.D"))
sstore(15, datasize("C.D"))
}
object "C" {
code {
sstore(16, dataoffset("D"))
sstore(17, datasize("D"))
}
object "D" {
code {
invalid()
}
}
}
object "E" {
code {
invalid()
}
}
}
}

View File

@ -0,0 +1 @@
--strict-assembly - --asm-json

View File

@ -0,0 +1,3 @@
======= <stdin> (EVM) =======
{".code":[{"begin":28,"end":29,"name":"PUSH","source":0,"value":"0"},{"begin":15,"end":30,"name":"CALLDATALOAD","source":0},{"begin":44,"end":46,"name":"PUSH","source":0,"value":"14"},{"begin":61,"end":62,"name":"DUP1","source":0},{"begin":58,"end":59,"name":"DUP3","source":0},{"begin":51,"end":63,"name":"SSTORE","source":0},{"begin":84,"end":86,"name":"PUSH","source":0,"value":"20"},{"begin":71,"end":87,"name":"CALLDATALOAD","source":0},{"begin":68,"end":188,"name":"ISZERO","source":0},{"begin":68,"end":188,"name":"PUSH [tag]","source":0,"value":"1"},{"begin":68,"end":188,"name":"JUMPI","source":0},{"begin":108,"end":109,"name":"DUP1","source":0},{"begin":105,"end":106,"name":"DUP3","source":0},{"begin":98,"end":110,"name":"SSTORE","source":0},{"begin":149,"end":150,"name":"PUSH","source":0,"value":"0"},{"begin":146,"end":147,"name":"DUP1","source":0},{"begin":143,"end":144,"name":"PUSH","source":0,"value":"0"},{"begin":140,"end":141,"name":"DUP1","source":0},{"begin":137,"end":138,"name":"PUSH","source":0,"value":"0"},{"begin":134,"end":135,"name":"DUP1","source":0},{"begin":123,"end":151,"name":"STATICCALL","source":0},{"begin":119,"end":152,"name":"POP","source":0},{"begin":161,"end":182,"name":"VERBATIM","source":0,"value":"78797a"},{"begin":68,"end":188,"name":"tag","source":0,"value":"1"},{"begin":68,"end":188,"name":"JUMPDEST","source":0},{"begin":203,"end":204,"name":"DUP1","source":0},{"begin":200,"end":201,"name":"DUP3","source":0},{"begin":193,"end":205,"name":"SSTORE","source":0},{"begin":0,"end":207,"name":"POP","source":0},{"begin":0,"end":207,"name":"POP","source":0}],"sourceList":["<stdin>"]}

View File

@ -0,0 +1,11 @@
{
let a := calldataload(0)
let b := 20
sstore(a, b)
if calldataload(32) {
sstore(a, b)
pop(staticcall(0, 0, 0, 0, 0, 0))
verbatim_0i_0o("xyz")
}
sstore(a, b)
}

View File

@ -0,0 +1 @@
--strict-assembly - --asm-json

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,50 @@
/// @use-src 0:"state_var_initialization.sol"
object "C_23" {
code {
{
/// @src 0:0:125 "contract C {..."
let _1 := memoryguard(0x80)
mstore(64, _1)
if callvalue() { revert(0, 0) }
sstore(/** @src 0:33:34 "1" */ 0x00, /** @src 0:56:57 "2" */ 0x02)
/// @src 0:0:125 "contract C {..."
sstore(/** @src 0:33:34 "1" */ 0x01, 0x00)
/// @src 0:0:125 "contract C {..."
let _2 := datasize("C_23_deployed")
codecopy(_1, dataoffset("C_23_deployed"), _2)
return(_1, _2)
}
}
/// @use-src 0:"state_var_initialization.sol"
object "C_23_deployed" {
code {
{
/// @src 0:0:125 "contract C {..."
let _1 := memoryguard(0x80)
mstore(64, _1)
if iszero(lt(calldatasize(), 4))
{
let _2 := 0
switch shr(224, calldataload(_2))
case 0xb4f40c61 {
if callvalue() { revert(_2, _2) }
if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
mstore(_1, sload(/** @src 0:40:57 "uint public k = 2" */ 1))
/// @src 0:0:125 "contract C {..."
return(_1, 32)
}
case 0xe5aa3d58 {
if callvalue() { revert(_2, _2) }
if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
let _3 := sload(_2)
let memPos := mload(64)
mstore(memPos, _3)
return(memPos, 32)
}
}
revert(0, 0)
}
}
data ".metadata" hex"a2646970667358221220f04f800086d1bd95dea7e66c53d041963124c118e4dc70bc4b39a50865c2483f64736f6c63430008130033"
}
}

View File

@ -0,0 +1 @@
--strict-assembly - --asm-json

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,43 @@
object "C_23" {
code {
{
let _1 := memoryguard(0x80)
mstore(64, _1)
if callvalue() { revert(0, 0) }
sstore(0x00, 0x02)
sstore(0x01, 0x00)
let _2 := datasize("C_23_deployed")
codecopy(_1, dataoffset("C_23_deployed"), _2)
return(_1, _2)
}
}
object "C_23_deployed" {
code {
{
let _1 := memoryguard(0x80)
mstore(64, _1)
if iszero(lt(calldatasize(), 4))
{
let _2 := 0
switch shr(224, calldataload(_2))
case 0xb4f40c61 {
if callvalue() { revert(_2, _2) }
if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
mstore(_1, sload(/** @src 0:40:57 "uint public k = 2" */ 1))
return(_1, 32)
}
case 0xe5aa3d58 {
if callvalue() { revert(_2, _2) }
if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
let _3 := sload(_2)
let memPos := mload(64)
mstore(memPos, _3)
return(memPos, 32)
}
}
revert(0, 0)
}
}
data ".metadata" hex"a2646970667358221220f04f800086d1bd95dea7e66c53d041963124c118e4dc70bc4b39a50865c2483f64736f6c63430008130033"
}
}

View File

@ -0,0 +1 @@
--pretty-json --json-indent 4 --combined-json bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime --asm --bin --bin-runtime --asm-json --import-asm-json -

View File

@ -0,0 +1,93 @@
{
"contracts":
{
"<stdin>":
{
"asm":
{
".code":
[
{
"begin": 0,
"end": 0,
"name": "PUSH",
"source": -1,
"value": "0"
}
],
".data":
{
"0":
{
".code":
[
{
"begin": 0,
"end": 0,
"name": "PUSH",
"source": -1,
"value": "1"
}
],
"index": 0
}
},
"sourceList":
[
"contract.sol",
"#utility.yul"
]
},
"bin": "5ffe",
"bin-runtime": "6001",
"opcodes": "PUSH0 INVALID ",
"srcmap": "0:0::-:0",
"srcmap-runtime": "0:0::-:0"
}
},
"sourceList":
[
"contract.sol",
"#utility.yul"
],
"version": "<VERSION REMOVED>"
}
Binary:
5ffe
Binary of the runtime part:
6001
EVM assembly:
{
".code":
[
{
"begin": 0,
"end": 0,
"name": "PUSH",
"source": -1,
"value": "0"
}
],
".data":
{
"0":
{
".code":
[
{
"begin": 0,
"end": 0,
"name": "PUSH",
"source": -1,
"value": "1"
}
],
"index": 0
}
},
"sourceList":
[
"contract.sol",
"#utility.yul"
]
}

View File

@ -0,0 +1,23 @@
{
".code": [
{
"name": "PUSH",
"value": "0"
}
],
".data": {
"0": {
".code": [
{
"name": "PUSH",
"value": "1"
}
],
"index": 0
}
},
"sourceList": [
"contract.sol",
"#utility.yul"
]
}

View File

@ -0,0 +1 @@
--import-asm-json - --opcodes --asm

View File

@ -0,0 +1,6 @@
Opcodes:
PREVRANDAO PREVRANDAO
EVM assembly:
/* */
prevrandao
prevrandao

View File

@ -0,0 +1,6 @@
{
".code": [
{ "name": "DIFFICULTY" },
{ "name": "PREVRANDAO" }
]
}

View File

@ -0,0 +1 @@
--import-asm-json - --opcodes --asm

View File

@ -0,0 +1 @@
Assembly Import Error: Member '.data' contains a value for '0' that is not a valid hexadecimal string.

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,11 @@
{
".code": [
{
"name": "PUSH",
"value": "0"
}
],
".data": {
"0": "no-hex-string"
}
}

View File

@ -0,0 +1 @@
--import-asm-json - --opcodes --asm

View File

@ -0,0 +1 @@
Assembly Import Error: Key inside '.data' '0' can only be a valid hex-string or an object.

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,16 @@
{
".code": [
{
"name": "PUSH",
"value": "0"
}
],
".data": {
"0": [
0,
1,
2,
3
]
}
}

View File

@ -0,0 +1 @@
--import-asm-json - --opcodes --asm

View File

@ -0,0 +1 @@
Assembly Import Error: Member 'jumpType' set on instruction different from JUMP or JUMPI (was set on instruction 'PUSH')

View File

@ -0,0 +1,9 @@
{
".code": [
{
"name": "PUSH",
"value": "0",
"jumpType": "[in]"
}
]
}

View File

@ -0,0 +1 @@
--import-asm-json - --opcodes --asm

View File

@ -0,0 +1 @@
Assembly Import Error: Member 'value' defined for instruction 'DIFFICULTY', but the instruction does not need a value.

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,8 @@
{
".code": [
{
"name": "DIFFICULTY",
"value": "0"
}
]
}

View File

@ -0,0 +1 @@
--optimize --import-asm-json - --opcodes --asm

View File

@ -0,0 +1 @@
Option --optimize is not supported with --import-asm-json.

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,6 @@
{
".code": [
{ "name": "DIFFICULTY" },
{ "name": "PREVRANDAO" }
]
}

View File

@ -0,0 +1 @@
--import-asm-json - --opcodes --asm

View File

@ -0,0 +1 @@
Assembly Import Error: Member 'value' was not defined for instruction 'PUSH', but the instruction needs a value.

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,5 @@
{
".code": [
{ "name": "PUSH" }
]
}

View File

@ -0,0 +1 @@
--asm-json --import-asm-json -

View File

@ -0,0 +1 @@
Assembly Import Error: Items in 'sourceList' array are not unique.

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,23 @@
{
".code": [
{
"name": "PUSH",
"value": "0"
}
],
".data": {
"0": {
".code": [
{
"name": "PUSH",
"value": "1"
}
]
}
},
"sourceList": [
"contract.sol",
"contract.sol",
"#utility.yul"
]
}

View File

@ -0,0 +1 @@
--import-asm-json - --opcodes --asm

View File

@ -0,0 +1 @@
Assembly Import Error: Unknown member '_name'. Valid members are begin, end, jumpType, modifierDepth, name, source, value.

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,5 @@
{
".code": [
{ "_name": "DIFFICULTY" }
]
}

View File

@ -0,0 +1 @@
--import-asm-json - --opcodes --asm

View File

@ -0,0 +1 @@
Assembly Import Error: JUMPDEST instruction without a tag

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,10 @@
{
".code": [
{
"name": "tag",
"value": "0x00"
},
{ "name": "JUMPDEST" },
{ "name": "JUMPDEST" }
]
}

View File

@ -0,0 +1 @@
--import-asm-json - --opcodes --asm

View File

@ -0,0 +1,58 @@
Opcodes:
PUSH0 CALLDATALOAD PUSH1 0x14 DUP1 DUP3 SSTORE PUSH1 0x20 CALLDATALOAD ISZERO PUSH1 0x1C JUMPI DUP1 DUP3 SSTORE PUSH0 DUP1 PUSH0 DUP1 PUSH0 DUP1 STATICCALL POP PUSH25 0x797A5B80825550500000000000000000000000000000000000
EVM assembly:
/* "<stdin>":28:29 b */
0x00
/* "<stdin>":15:30 {... */
calldataload
/* "<stdin>":44:46 */
0x14
/* "<stdin>":61:62 */
dup1
/* "<stdin>":58:59 */
dup3
/* "<stdin>":51:63 : 29,... */
sstore
/* "<stdin>":84:86 " */
0x20
/* "<stdin>":71:87 "PUSH",... */
calldataload
/* "<stdin>":68:188 ": "PUSH",... */
iszero
tag_1
jumpi
/* "<stdin>":108:109 u */
dup1
/* "<stdin>":105:106 v */
dup3
/* "<stdin>":98:110 "value */
sstore
/* "<stdin>":149:150 */
0x00
/* "<stdin>":146:147 5 */
dup1
/* "<stdin>":143:144 : */
0x00
/* "<stdin>":140:141 i */
dup1
/* "<stdin>":137:138 b */
0x00
/* "<stdin>":134:135 */
dup1
/* "<stdin>":123:151 ... */
staticcall
/* "<stdin>":119:152 },... */
pop
/* "<stdin>":161:182 30,... */
verbatimbytecode_78797a
/* "<stdin>":68:188 ": "PUSH",... */
tag_1:
/* "<stdin>":203:204 s */
dup1
/* "<stdin>":200:201 */
dup3
/* "<stdin>":193:205 ",... */
sstore
/* "<stdin>":0:207 {... */
pop
pop

View File

@ -0,0 +1,196 @@
{
".code": [
{
"begin": 28,
"end": 29,
"name": "PUSH",
"source": 0,
"value": "0"
},
{
"begin": 15,
"end": 30,
"name": "CALLDATALOAD",
"source": 0
},
{
"begin": 44,
"end": 46,
"name": "PUSH",
"source": 0,
"value": "14"
},
{
"begin": 61,
"end": 62,
"name": "DUP1",
"source": 0
},
{
"begin": 58,
"end": 59,
"name": "DUP3",
"source": 0
},
{
"begin": 51,
"end": 63,
"name": "SSTORE",
"source": 0
},
{
"begin": 84,
"end": 86,
"name": "PUSH",
"source": 0,
"value": "20"
},
{
"begin": 71,
"end": 87,
"name": "CALLDATALOAD",
"source": 0
},
{
"begin": 68,
"end": 188,
"name": "ISZERO",
"source": 0
},
{
"begin": 68,
"end": 188,
"name": "PUSH [tag]",
"source": 0,
"value": "1"
},
{
"begin": 68,
"end": 188,
"name": "JUMPI",
"source": 0
},
{
"begin": 108,
"end": 109,
"name": "DUP1",
"source": 0
},
{
"begin": 105,
"end": 106,
"name": "DUP3",
"source": 0
},
{
"begin": 98,
"end": 110,
"name": "SSTORE",
"source": 0
},
{
"begin": 149,
"end": 150,
"name": "PUSH",
"source": 0,
"value": "0"
},
{
"begin": 146,
"end": 147,
"name": "DUP1",
"source": 0
},
{
"begin": 143,
"end": 144,
"name": "PUSH",
"source": 0,
"value": "0"
},
{
"begin": 140,
"end": 141,
"name": "DUP1",
"source": 0
},
{
"begin": 137,
"end": 138,
"name": "PUSH",
"source": 0,
"value": "0"
},
{
"begin": 134,
"end": 135,
"name": "DUP1",
"source": 0
},
{
"begin": 123,
"end": 151,
"name": "STATICCALL",
"source": 0
},
{
"begin": 119,
"end": 152,
"name": "POP",
"source": 0
},
{
"begin": 161,
"end": 182,
"name": "VERBATIM",
"source": 0,
"value": "78797a"
},
{
"begin": 68,
"end": 188,
"name": "tag",
"source": 0,
"value": "1"
},
{
"begin": 68,
"end": 188,
"name": "JUMPDEST",
"source": 0
},
{
"begin": 203,
"end": 204,
"name": "DUP1",
"source": 0
},
{
"begin": 200,
"end": 201,
"name": "DUP3",
"source": 0
},
{
"begin": 193,
"end": 205,
"name": "SSTORE",
"source": 0
},
{
"begin": 0,
"end": 207,
"name": "POP",
"source": 0
},
{
"begin": 0,
"end": 207,
"name": "POP",
"source": 0
}
],
"sourceList": [
"<stdin>"
]
}

View File

@ -0,0 +1 @@
--import-asm-json - --opcodes

View File

@ -0,0 +1,2 @@
Opcodes:
PUSH1 0x1E PUSH0 SSTORE PUSH1 0x1A PUSH1 0x1 SSTORE PUSH1 0x15 PUSH1 0x2 SSTORE PUSH1 0x1 PUSH1 0x3 SSTORE PUSH1 0x1 PUSH1 0x4 SSTORE PUSH1 0x1 PUSH1 0x5 SSTORE INVALID

View File

@ -0,0 +1,368 @@
{
".code":
[
{
"begin": 37,
"end": 51,
"name": "PUSHSIZE",
"source": 0
},
{
"begin": 34,
"end": 35,
"name": "PUSH",
"source": 0,
"value": "0"
},
{
"begin": 27,
"end": 52,
"name": "SSTORE",
"source": 0
},
{
"begin": 67,
"end": 81,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 64,
"end": 65,
"name": "PUSH",
"source": 0,
"value": "1"
},
{
"begin": 57,
"end": 82,
"name": "SSTORE",
"source": 0
},
{
"begin": 97,
"end": 114,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000ffffffffffffffff"
},
{
"begin": 94,
"end": 95,
"name": "PUSH",
"source": 0,
"value": "2"
},
{
"begin": 87,
"end": 115,
"name": "SSTORE",
"source": 0
},
{
"begin": 130,
"end": 152,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffe"
},
{
"begin": 127,
"end": 128,
"name": "PUSH",
"source": 0,
"value": "3"
},
{
"begin": 120,
"end": 153,
"name": "SSTORE",
"source": 0
},
{
"begin": 168,
"end": 190,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffd"
},
{
"begin": 165,
"end": 166,
"name": "PUSH",
"source": 0,
"value": "4"
},
{
"begin": 158,
"end": 191,
"name": "SSTORE",
"source": 0
},
{
"begin": 206,
"end": 228,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffc"
},
{
"begin": 203,
"end": 204,
"name": "PUSH",
"source": 0,
"value": "5"
},
{
"begin": 196,
"end": 229,
"name": "SSTORE",
"source": 0
}
],
".data":
{
"0":
{
".code":
[
{
"begin": 278,
"end": 292,
"name": "PUSHSIZE",
"source": 0
},
{
"begin": 275,
"end": 276,
"name": "PUSH",
"source": 0,
"value": "6"
},
{
"begin": 268,
"end": 293,
"name": "SSTORE",
"source": 0
},
{
"begin": 310,
"end": 324,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 307,
"end": 308,
"name": "PUSH",
"source": 0,
"value": "7"
},
{
"begin": 300,
"end": 325,
"name": "SSTORE",
"source": 0
},
{
"begin": 342,
"end": 361,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000ffffffffffffffff"
},
{
"begin": 339,
"end": 340,
"name": "PUSH",
"source": 0,
"value": "8"
},
{
"begin": 332,
"end": 362,
"name": "SSTORE",
"source": 0
},
{
"begin": 379,
"end": 398,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffe"
},
{
"begin": 376,
"end": 377,
"name": "PUSH",
"source": 0,
"value": "9"
},
{
"begin": 369,
"end": 399,
"name": "SSTORE",
"source": 0
},
{
"begin": 417,
"end": 436,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffd"
},
{
"begin": 413,
"end": 415,
"name": "PUSH",
"source": 0,
"value": "A"
},
{
"begin": 406,
"end": 437,
"name": "SSTORE",
"source": 0
}
],
"index": 0,
".data":
{
"0":
{
".code":
[
{
"begin": 494,
"end": 508,
"name": "PUSHSIZE",
"source": 0
},
{
"begin": 490,
"end": 492,
"name": "PUSH",
"source": 0,
"value": "B"
},
{
"begin": 483,
"end": 509,
"name": "SSTORE",
"source": 0
},
{
"begin": 529,
"end": 545,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 525,
"end": 527,
"name": "PUSH",
"source": 0,
"value": "C"
},
{
"begin": 518,
"end": 546,
"name": "SSTORE",
"source": 0
},
{
"begin": 566,
"end": 582,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000001"
},
{
"begin": 562,
"end": 564,
"name": "PUSH",
"source": 0,
"value": "D"
},
{
"begin": 555,
"end": 583,
"name": "SSTORE",
"source": 0
},
{
"begin": 603,
"end": 619,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000002"
},
{
"begin": 599,
"end": 601,
"name": "PUSH",
"source": 0,
"value": "E"
},
{
"begin": 592,
"end": 620,
"name": "SSTORE",
"source": 0
}
],
"index": 0,
".data":
{
"0":
{
".code":
[
{
"begin": 676,
"end": 685,
"name": "INVALID",
"source": 0
}
],
"index": 0
},
"1":
{
".code":
[
{
"begin": 751,
"end": 760,
"name": "INVALID",
"source": 0
}
],
"index": 1
},
"2":
{
".code":
[
{
"begin": 826,
"end": 835,
"name": "INVALID",
"source": 0
}
],
"index": 2
}
}
}
}
}
},
"sourceList":
[
"<stdin>"
]
}

View File

@ -0,0 +1 @@
--import-asm-json - --opcodes

View File

@ -0,0 +1,2 @@
Opcodes:
PUSH0 DUP1 SSTORE PUSH1 0x6A PUSH1 0x1 SSTORE PUSH1 0x31 PUSH1 0x2 SSTORE PUSH1 0x2C PUSH1 0x3 SSTORE PUSH1 0x5E PUSH1 0x4 SSTORE PUSH1 0xC PUSH1 0x5 SSTORE PUSH1 0x5D PUSH1 0x6 SSTORE PUSH1 0x1 PUSH1 0x7 SSTORE PUSH1 0x5D PUSH1 0x8 SSTORE PUSH1 0x1 PUSH1 0x9 SSTORE INVALID PUSH1 0x1F PUSH1 0xA SSTORE PUSH1 0xC PUSH1 0xB SSTORE PUSH1 0x2B PUSH1 0xC SSTORE PUSH1 0x1 PUSH1 0xD SSTORE PUSH1 0x2B PUSH1 0xE SSTORE PUSH1 0x1 PUSH1 0xF SSTORE INVALID PUSH1 0xB PUSH1 0x10 SSTORE PUSH1 0x1 PUSH1 0x11 SSTORE INVALID INVALID INVALID INVALID PUSH1 0xB PUSH1 0x10 SSTORE PUSH1 0x1 PUSH1 0x11 SSTORE INVALID INVALID

View File

@ -0,0 +1,417 @@
{
".code":
[
{
"begin": 36,
"end": 51,
"name": "PUSH",
"source": 0,
"value": "0"
},
{
"begin": 33,
"end": 34,
"name": "DUP1",
"source": 0
},
{
"begin": 26,
"end": 52,
"name": "SSTORE",
"source": 0
},
{
"begin": 67,
"end": 80,
"name": "PUSHSIZE",
"source": 0
},
{
"begin": 64,
"end": 65,
"name": "PUSH",
"source": 0,
"value": "1"
},
{
"begin": 57,
"end": 81,
"name": "SSTORE",
"source": 0
},
{
"begin": 96,
"end": 111,
"name": "PUSH [$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 93,
"end": 94,
"name": "PUSH",
"source": 0,
"value": "2"
},
{
"begin": 86,
"end": 112,
"name": "SSTORE",
"source": 0
},
{
"begin": 127,
"end": 140,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 124,
"end": 125,
"name": "PUSH",
"source": 0,
"value": "3"
},
{
"begin": 117,
"end": 141,
"name": "SSTORE",
"source": 0
},
{
"begin": 156,
"end": 173,
"name": "PUSH [$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000ffffffffffffffff"
},
{
"begin": 153,
"end": 154,
"name": "PUSH",
"source": 0,
"value": "4"
},
{
"begin": 146,
"end": 174,
"name": "SSTORE",
"source": 0
},
{
"begin": 189,
"end": 204,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000ffffffffffffffff"
},
{
"begin": 186,
"end": 187,
"name": "PUSH",
"source": 0,
"value": "5"
},
{
"begin": 179,
"end": 205,
"name": "SSTORE",
"source": 0
},
{
"begin": 220,
"end": 237,
"name": "PUSH [$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffe"
},
{
"begin": 217,
"end": 218,
"name": "PUSH",
"source": 0,
"value": "6"
},
{
"begin": 210,
"end": 238,
"name": "SSTORE",
"source": 0
},
{
"begin": 253,
"end": 268,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffe"
},
{
"begin": 250,
"end": 251,
"name": "PUSH",
"source": 0,
"value": "7"
},
{
"begin": 243,
"end": 269,
"name": "SSTORE",
"source": 0
},
{
"begin": 284,
"end": 303,
"name": "PUSH [$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffd"
},
{
"begin": 281,
"end": 282,
"name": "PUSH",
"source": 0,
"value": "8"
},
{
"begin": 274,
"end": 304,
"name": "SSTORE",
"source": 0
},
{
"begin": 319,
"end": 336,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000fffffffffffffffd"
},
{
"begin": 316,
"end": 317,
"name": "PUSH",
"source": 0,
"value": "9"
},
{
"begin": 309,
"end": 337,
"name": "SSTORE",
"source": 0
}
],
".data":
{
"0":
{
".code":
[
{
"begin": 418,
"end": 433,
"name": "PUSH [$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 414,
"end": 416,
"name": "PUSH",
"source": 0,
"value": "A"
},
{
"begin": 407,
"end": 434,
"name": "SSTORE",
"source": 0
},
{
"begin": 452,
"end": 465,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 448,
"end": 450,
"name": "PUSH",
"source": 0,
"value": "B"
},
{
"begin": 441,
"end": 466,
"name": "SSTORE",
"source": 0
},
{
"begin": 484,
"end": 499,
"name": "PUSH [$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000001"
},
{
"begin": 480,
"end": 482,
"name": "PUSH",
"source": 0,
"value": "C"
},
{
"begin": 473,
"end": 500,
"name": "SSTORE",
"source": 0
},
{
"begin": 518,
"end": 531,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000001"
},
{
"begin": 514,
"end": 516,
"name": "PUSH",
"source": 0,
"value": "D"
},
{
"begin": 507,
"end": 532,
"name": "SSTORE",
"source": 0
},
{
"begin": 550,
"end": 567,
"name": "PUSH [$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000ffffffffffffffff"
},
{
"begin": 546,
"end": 548,
"name": "PUSH",
"source": 0,
"value": "E"
},
{
"begin": 539,
"end": 568,
"name": "SSTORE",
"source": 0
},
{
"begin": 586,
"end": 601,
"name": "PUSH #[$]",
"source": 0,
"value": "000000000000000000000000000000000000000000000000ffffffffffffffff"
},
{
"begin": 582,
"end": 584,
"name": "PUSH",
"source": 0,
"value": "F"
},
{
"begin": 575,
"end": 602,
"name": "SSTORE",
"source": 0
}
],
"index": 0,
".data":
{
"0":
{
".code":
[
{
"begin": 658,
"end": 673,
"name": "PUSH [$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 654,
"end": 656,
"name": "PUSH",
"source": 0,
"value": "10"
},
{
"begin": 647,
"end": 674,
"name": "SSTORE",
"source": 0
},
{
"begin": 694,
"end": 707,
"name": "PUSH #[$]",
"source": 0,
"value": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"begin": 690,
"end": 692,
"name": "PUSH",
"source": 0,
"value": "11"
},
{
"begin": 683,
"end": 708,
"name": "SSTORE",
"source": 0
}
],
"index": 0,
".data":
{
"0":
{
".code":
[
{
"begin": 761,
"end": 770,
"name": "INVALID",
"source": 0
}
],
"index": 0
}
}
},
"1":
{
".code":
[
{
"begin": 833,
"end": 842,
"name": "INVALID",
"source": 0
}
],
"index": 1
}
}
},
"ACAF3289D7B601CBD114FB36C4D29C85BBFD5E133F14CB355C3FD8D99367964F": "48656c6c6f2c20576f726c6421"
},
"sourceList":
[
"<stdin>"
]
}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
The following outputs are not supported in assembler mode: --abi, --asm-json, --bin-runtime, --devdoc, --hashes, --ir, --metadata, --opcodes, --storage-layout, --userdoc.
The following outputs are not supported in assembler mode: --abi, --bin-runtime, --devdoc, --hashes, --ir, --metadata, --opcodes, --storage-layout, --userdoc.

View File

@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -euo pipefail
# shellcheck source=scripts/common.sh
source "${REPO_ROOT}/scripts/common.sh"
SOLTMPDIR=$(mktemp -d -t "cmdline-test-evmasm-import-export-XXXXXX")
(
cd "$SOLTMPDIR"
if ! "$REPO_ROOT/scripts/ASTImportTest.sh" evm-assembly
then
rm -r "$SOLTMPDIR"
fail
fi
)
rm -r "$SOLTMPDIR"

View File

@ -55,11 +55,6 @@ BOOST_AUTO_TEST_SUITE(Assembler)
BOOST_AUTO_TEST_CASE(all_assembly_items)
{
std::map<std::string, unsigned> indices = {
{ "root.asm", 0 },
{ "sub.asm", 1 },
{ "verbatim.asm", 2 }
};
EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion();
Assembly _assembly{evmVersion, false, {}};
auto root_asm = std::make_shared<std::string>("root.asm");
@ -203,16 +198,16 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
"{\"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}"
"]},"
"],\"index\":0},"
"\"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\"]}"
"],\"index\":1},\"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));
BOOST_CHECK_EQUAL(util::jsonCompactPrint(_assembly.assemblyJSON({"root.asm", "sub.asm", "verbatim.asm"})), util::jsonCompactPrint(jsonValue));
}
BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
@ -301,10 +296,6 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
BOOST_AUTO_TEST_CASE(immutable)
{
std::map<std::string, unsigned> indices = {
{ "root.asm", 0 },
{ "sub.asm", 1 }
};
EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion();
Assembly _assembly{evmVersion, true, {}};
auto root_asm = std::make_shared<std::string>("root.asm");
@ -383,7 +374,7 @@ BOOST_AUTO_TEST_CASE(immutable)
"}\n"
);
BOOST_CHECK_EQUAL(
util::jsonCompactPrint(_assembly.assemblyJSON(indices)),
util::jsonCompactPrint(_assembly.assemblyJSON({"root.asm", "sub.asm"})),
"{\".code\":["
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2A\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"},"
@ -397,7 +388,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\"]}"
"],\"index\":0}},\"sourceList\":[\"root.asm\",\"sub.asm\"]}"
);
}

View File

@ -0,0 +1,475 @@
object "root" {
code {
sstore(0, datasize("root"))
sstore(1, datasize("0"))
sstore(2, datasize("1"))
sstore(3, datasize("2"))
sstore(4, datasize("3"))
sstore(5, datasize("4"))
sstore(6, datasize("5"))
sstore(7, datasize("6"))
sstore(8, datasize("7"))
sstore(9, datasize("8"))
sstore(10, datasize("9"))
sstore(11, datasize("a"))
sstore(12, datasize("b"))
sstore(13, datasize("c"))
sstore(14, datasize("d"))
sstore(15, datasize("e"))
sstore(16, datasize("f"))
sstore(17, datasize("10"))
}
object "0" {
code {
sstore(100, 0)
sstore(200, datasize("sub0"))
}
object "sub0" {
code {
sstore(300, 0)
}
}
}
object "1" {
code {
sstore(100, 1)
}
}
object "2" {
code {
sstore(101, 2)
}
}
object "3" {
code {
sstore(102, 3)
}
}
object "4" {
code {
sstore(103, 4)
}
}
object "5" {
code {
sstore(104, 5)
}
}
object "6" {
code {
sstore(105, 6)
}
}
object "7" {
code {
sstore(106, 7)
}
}
object "8" {
code {
sstore(107, 8)
}
}
object "9" {
code {
sstore(108, 9)
}
}
object "a" {
code {
sstore(109, 10)
}
}
object "b" {
code {
sstore(110, 11)
}
}
object "c" {
code {
sstore(111, 12)
}
}
object "d" {
code {
sstore(112, 13)
}
}
object "e" {
code {
sstore(113, 14)
}
}
object "f" {
code {
sstore(114, 15)
}
}
object "10" {
code {
sstore(115, 16)
sstore(201, datasize("sub10"))
}
object "sub10" {
code {
sstore(300, 16)
}
}
}
}
// ----
// Assembly:
// /* "source":41:57 */
// bytecodeSize
// /* "source":38:39 */
// 0x00
// /* "source":31:58 */
// sstore
// /* "source":75:88 */
// dataSize(sub_0)
// /* "source":72:73 */
// 0x01
// /* "source":65:89 */
// sstore
// /* "source":106:119 */
// dataSize(sub_1)
// /* "source":103:104 */
// 0x02
// /* "source":96:120 */
// sstore
// /* "source":137:150 */
// dataSize(sub_2)
// /* "source":134:135 */
// 0x03
// /* "source":127:151 */
// sstore
// /* "source":168:181 */
// dataSize(sub_3)
// /* "source":165:166 */
// 0x04
// /* "source":158:182 */
// sstore
// /* "source":199:212 */
// dataSize(sub_4)
// /* "source":196:197 */
// 0x05
// /* "source":189:213 */
// sstore
// /* "source":230:243 */
// dataSize(sub_5)
// /* "source":227:228 */
// 0x06
// /* "source":220:244 */
// sstore
// /* "source":261:274 */
// dataSize(sub_6)
// /* "source":258:259 */
// 0x07
// /* "source":251:275 */
// sstore
// /* "source":292:305 */
// dataSize(sub_7)
// /* "source":289:290 */
// 0x08
// /* "source":282:306 */
// sstore
// /* "source":323:336 */
// dataSize(sub_8)
// /* "source":320:321 */
// 0x09
// /* "source":313:337 */
// sstore
// /* "source":355:368 */
// dataSize(sub_9)
// /* "source":351:353 */
// 0x0a
// /* "source":344:369 */
// sstore
// /* "source":387:400 */
// dataSize(sub_10)
// /* "source":383:385 */
// 0x0b
// /* "source":376:401 */
// sstore
// /* "source":419:432 */
// dataSize(sub_11)
// /* "source":415:417 */
// 0x0c
// /* "source":408:433 */
// sstore
// /* "source":451:464 */
// dataSize(sub_12)
// /* "source":447:449 */
// 0x0d
// /* "source":440:465 */
// sstore
// /* "source":483:496 */
// dataSize(sub_13)
// /* "source":479:481 */
// 0x0e
// /* "source":472:497 */
// sstore
// /* "source":515:528 */
// dataSize(sub_14)
// /* "source":511:513 */
// 0x0f
// /* "source":504:529 */
// sstore
// /* "source":547:560 */
// dataSize(sub_15)
// /* "source":543:545 */
// 0x10
// /* "source":536:561 */
// sstore
// /* "source":579:593 */
// dataSize(sub_16)
// /* "source":575:577 */
// 0x11
// /* "source":568:594 */
// sstore
// /* "source":23:598 */
// stop
// stop
//
// sub_0: assembly {
// /* "source":644:645 */
// 0x00
// /* "source":639:642 */
// 0x64
// /* "source":632:646 */
// sstore
// /* "source":665:681 */
// dataSize(sub_0)
// /* "source":660:663 */
// 0xc8
// /* "source":653:682 */
// sstore
// /* "source":624:688 */
// stop
// stop
//
// sub_0: assembly {
// /* "source":742:743 */
// 0x00
// /* "source":737:740 */
// 0x012c
// /* "source":730:744 */
// sstore
// /* "source":720:752 */
// stop
// }
// }
//
// sub_1: assembly {
// /* "source":808:809 */
// 0x01
// /* "source":803:806 */
// 0x64
// /* "source":796:810 */
// sstore
// /* "source":788:816 */
// stop
// }
//
// sub_2: assembly {
// /* "source":866:867 */
// 0x02
// /* "source":861:864 */
// 0x65
// /* "source":854:868 */
// sstore
// /* "source":846:874 */
// stop
// }
//
// sub_3: assembly {
// /* "source":924:925 */
// 0x03
// /* "source":919:922 */
// 0x66
// /* "source":912:926 */
// sstore
// /* "source":904:932 */
// stop
// }
//
// sub_4: assembly {
// /* "source":982:983 */
// 0x04
// /* "source":977:980 */
// 0x67
// /* "source":970:984 */
// sstore
// /* "source":962:990 */
// stop
// }
//
// sub_5: assembly {
// /* "source":1040:1041 */
// 0x05
// /* "source":1035:1038 */
// 0x68
// /* "source":1028:1042 */
// sstore
// /* "source":1020:1048 */
// stop
// }
//
// sub_6: assembly {
// /* "source":1098:1099 */
// 0x06
// /* "source":1093:1096 */
// 0x69
// /* "source":1086:1100 */
// sstore
// /* "source":1078:1106 */
// stop
// }
//
// sub_7: assembly {
// /* "source":1156:1157 */
// 0x07
// /* "source":1151:1154 */
// 0x6a
// /* "source":1144:1158 */
// sstore
// /* "source":1136:1164 */
// stop
// }
//
// sub_8: assembly {
// /* "source":1214:1215 */
// 0x08
// /* "source":1209:1212 */
// 0x6b
// /* "source":1202:1216 */
// sstore
// /* "source":1194:1222 */
// stop
// }
//
// sub_9: assembly {
// /* "source":1272:1273 */
// 0x09
// /* "source":1267:1270 */
// 0x6c
// /* "source":1260:1274 */
// sstore
// /* "source":1252:1280 */
// stop
// }
//
// sub_10: assembly {
// /* "source":1330:1332 */
// 0x0a
// /* "source":1325:1328 */
// 0x6d
// /* "source":1318:1333 */
// sstore
// /* "source":1310:1339 */
// stop
// }
//
// sub_11: assembly {
// /* "source":1389:1391 */
// 0x0b
// /* "source":1384:1387 */
// 0x6e
// /* "source":1377:1392 */
// sstore
// /* "source":1369:1398 */
// stop
// }
//
// sub_12: assembly {
// /* "source":1448:1450 */
// 0x0c
// /* "source":1443:1446 */
// 0x6f
// /* "source":1436:1451 */
// sstore
// /* "source":1428:1457 */
// stop
// }
//
// sub_13: assembly {
// /* "source":1507:1509 */
// 0x0d
// /* "source":1502:1505 */
// 0x70
// /* "source":1495:1510 */
// sstore
// /* "source":1487:1516 */
// stop
// }
//
// sub_14: assembly {
// /* "source":1566:1568 */
// 0x0e
// /* "source":1561:1564 */
// 0x71
// /* "source":1554:1569 */
// sstore
// /* "source":1546:1575 */
// stop
// }
//
// sub_15: assembly {
// /* "source":1625:1627 */
// 0x0f
// /* "source":1620:1623 */
// 0x72
// /* "source":1613:1628 */
// sstore
// /* "source":1605:1634 */
// stop
// }
//
// sub_16: assembly {
// /* "source":1685:1687 */
// 0x10
// /* "source":1680:1683 */
// 0x73
// /* "source":1673:1688 */
// sstore
// /* "source":1707:1724 */
// dataSize(sub_0)
// /* "source":1702:1705 */
// 0xc9
// /* "source":1695:1725 */
// sstore
// /* "source":1665:1731 */
// stop
// stop
//
// sub_0: assembly {
// /* "source":1786:1788 */
// 0x10
// /* "source":1781:1784 */
// 0x012c
// /* "source":1774:1789 */
// sstore
// /* "source":1764:1797 */
// stop
// }
// }
// Bytecode: 61005c5f55600b600155600660025560066003556006600455600660055560066006556006600755600660085560066009556006600a556006600b556006600c556006600d556006600e556006600f556006601055600c60115500fe
// Opcodes: PUSH2 0x5C PUSH0 SSTORE PUSH1 0xB PUSH1 0x1 SSTORE PUSH1 0x6 PUSH1 0x2 SSTORE PUSH1 0x6 PUSH1 0x3 SSTORE PUSH1 0x6 PUSH1 0x4 SSTORE PUSH1 0x6 PUSH1 0x5 SSTORE PUSH1 0x6 PUSH1 0x6 SSTORE PUSH1 0x6 PUSH1 0x7 SSTORE PUSH1 0x6 PUSH1 0x8 SSTORE PUSH1 0x6 PUSH1 0x9 SSTORE PUSH1 0x6 PUSH1 0xA SSTORE PUSH1 0x6 PUSH1 0xB SSTORE PUSH1 0x6 PUSH1 0xC SSTORE PUSH1 0x6 PUSH1 0xD SSTORE PUSH1 0x6 PUSH1 0xE SSTORE PUSH1 0x6 PUSH1 0xF SSTORE PUSH1 0x6 PUSH1 0x10 SSTORE PUSH1 0xC PUSH1 0x11 SSTORE STOP INVALID
// SourceMappings: 41:16:0:-:0;38:1;31:27;75:13;72:1;65:24;106:13;103:1;96:24;137:13;134:1;127:24;168:13;165:1;158:24;199:13;196:1;189:24;230:13;227:1;220:24;261:13;258:1;251:24;292:13;289:1;282:24;323:13;320:1;313:24;355:13;351:2;344:25;387:13;383:2;376:25;419:13;415:2;408:25;451:13;447:2;440:25;483:13;479:2;472:25;515:13;511:2;504:25;547:13;543:2;536:25;579:14;575:2;568:26;23:575

View File

@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE(version)
BOOST_AUTO_TEST_CASE(multiple_input_modes)
{
array<string, 9> inputModeOptions = {
array<string, 10> inputModeOptions = {
"--help",
"--license",
"--version",
@ -159,10 +159,11 @@ BOOST_AUTO_TEST_CASE(multiple_input_modes)
"--strict-assembly",
"--yul",
"--import-ast",
"--import-asm-json",
};
string expectedMessage =
"The following options are mutually exclusive: "
"--help, --license, --version, --standard-json, --link, --assemble, --strict-assembly, --yul, --import-ast, --lsp. "
"--help, --license, --version, --standard-json, --link, --assemble, --strict-assembly, --yul, --import-ast, --lsp, --import-asm-json. "
"Select at most one.";
for (string const& mode1: inputModeOptions)