mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[libevmasm] Add support to import evm assembly json.
This commit is contained in:
parent
69c034b1c0
commit
108490e630
@ -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,207 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
|
||||
}
|
||||
}
|
||||
|
||||
void Assembly::importAssemblyItemsFromJSON(Json::Value const& _code)
|
||||
{
|
||||
solAssert(m_items.empty());
|
||||
solRequire(_code.isArray(), AssemblyImportException, "Supplied JSON is not an array.");
|
||||
for (auto current = begin(_code); current != end(_code); ++current)
|
||||
{
|
||||
auto const& item = m_items.emplace_back(createAssemblyItemFromJSON(*current, m_sourceList));
|
||||
if (item == Instruction::JUMPDEST)
|
||||
solThrow(AssemblyImportException, "JUMPDEST instruction without a tag");
|
||||
else if (item.type() == AssemblyItemType::Tag)
|
||||
{
|
||||
++current;
|
||||
if (current != end(_code) && createAssemblyItemFromJSON(*current, m_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 set<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<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<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<string>(_json, "jumpType"),
|
||||
AssemblyImportException,
|
||||
"Optional member 'jumpType' not of type string."
|
||||
);
|
||||
|
||||
string name = get<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));
|
||||
string value = getOrDefault<string>(_json["value"], "");
|
||||
string jumpType = getOrDefault<string>(_json["jumpType"], "");
|
||||
|
||||
auto updateUsedTags = [&](u256 const& data)
|
||||
{
|
||||
m_usedTags = max(m_usedTags, static_cast<unsigned>(data) + 1);
|
||||
return data;
|
||||
};
|
||||
|
||||
auto storeImmutableHash = [&](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 = [&](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 = [&](string const& _name, string const& _value)
|
||||
{
|
||||
solRequire(
|
||||
!_value.empty(),
|
||||
AssemblyImportException,
|
||||
"Member 'value' was not defined for instruction '" + _name + "', but the instruction needs a value."
|
||||
);
|
||||
};
|
||||
|
||||
auto requireValueUndefinedForInstruction = [&](string const& _name, 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)
|
||||
location.sourceName = std::make_shared<std::string>(_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 + "')"
|
||||
);
|
||||
}
|
||||
InstructionInfo info = instructionInfo(item.instruction(), m_evmVersion);
|
||||
if (info.args == 0)
|
||||
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")
|
||||
{
|
||||
requireValueUndefinedForInstruction(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 +424,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(bool _includeSourceList) const
|
||||
{
|
||||
Json::Value root;
|
||||
root[".code"] = Json::arrayValue;
|
||||
@ -231,9 +434,9 @@ 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 < m_sourceList.size(); ++index)
|
||||
if (m_sourceList[index] == *item.location().sourceName)
|
||||
sourceIndex = static_cast<int>(index);
|
||||
}
|
||||
|
||||
auto [name, data] = item.nameAndData(m_evmVersion);
|
||||
@ -271,8 +474,8 @@ Json::Value Assembly::assemblyJSON(std::map<std::string, unsigned> const& _sourc
|
||||
{
|
||||
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>(m_sourceList.size()); ++index)
|
||||
jsonSourceList[index] = m_sourceList[static_cast<size_t>(index)];
|
||||
}
|
||||
|
||||
if (!m_data.empty() || !m_subs.empty())
|
||||
@ -285,9 +488,10 @@ Json::Value Assembly::assemblyJSON(std::map<std::string, unsigned> const& _sourc
|
||||
|
||||
for (size_t i = 0; i < m_subs.size(); ++i)
|
||||
{
|
||||
std::stringstream hexStr;
|
||||
hexStr << std::hex << i;
|
||||
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices, /*_includeSourceList = */false);
|
||||
stringstream hexStr;
|
||||
hexStr << hex << i;
|
||||
m_subs[i]->setSourceList(m_sourceList);
|
||||
data[hexStr.str()] = m_subs[i]->assemblyJSON(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,7 +501,84 @@ Json::Value Assembly::assemblyJSON(std::map<std::string, unsigned> const& _sourc
|
||||
return root;
|
||||
}
|
||||
|
||||
AssemblyItem Assembly::namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID)
|
||||
shared_ptr<Assembly> Assembly::fromJSON(Json::Value const& _json, vector<string> const& _sourceList, int _level)
|
||||
{
|
||||
solRequire(_json.isObject(), AssemblyImportException, "Supplied JSON is not an object.");
|
||||
static set<string> const validMembers{".code", ".data", ".auxdata", "sourceList"};
|
||||
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."
|
||||
);
|
||||
|
||||
shared_ptr<Assembly> result = make_shared<Assembly>(langutil::EVMVersion(), _level == 0, "");
|
||||
if (_json.isMember("sourceList"))
|
||||
{
|
||||
solAssert(_level == 0);
|
||||
for (auto const& it: _json["sourceList"])
|
||||
result->m_sourceList.emplace_back(it.asString());
|
||||
} else
|
||||
result->m_sourceList = _sourceList;
|
||||
|
||||
result->importAssemblyItemsFromJSON(_json[".code"]);
|
||||
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.");
|
||||
string dataItemID = dataIter.key().asString();
|
||||
Json::Value const& code = data[dataItemID];
|
||||
if (code.isString())
|
||||
{
|
||||
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())
|
||||
{
|
||||
shared_ptr<Assembly> subassembly(Assembly::fromJSON(code, result->m_sourceList, _level + 1));
|
||||
solAssert(subassembly);
|
||||
result->m_subs.emplace_back(make_shared<Assembly>(*subassembly));
|
||||
// TODO: this shouldn't be enough for the general case.
|
||||
result->encodeSubPath({0, 0});
|
||||
}
|
||||
else
|
||||
solThrow(AssemblyImportException, "Key inside '.data' '" + dataItemID + "' can only be a valid hex-string or an object.");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
AssemblyItem Assembly::namedTag(string const& _name, size_t _params, size_t _returns, optional<uint64_t> _sourceID)
|
||||
{
|
||||
assertThrow(!_name.empty(), AssemblyException, "Empty named tag.");
|
||||
if (m_namedTags.count(_name))
|
||||
|
@ -151,10 +151,30 @@ public:
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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::shared_ptr<Assembly> fromJSON(
|
||||
Json::Value const& _json,
|
||||
std::vector<std::string> const& _sourceList = {},
|
||||
int _level = 0
|
||||
);
|
||||
|
||||
void setSourceList(std::vector<std::string> const& _sourceList)
|
||||
{
|
||||
m_sourceList = _sourceList;
|
||||
}
|
||||
|
||||
std::vector<std::string> const& sourceList()
|
||||
{
|
||||
return m_sourceList;
|
||||
}
|
||||
|
||||
/// Mark this assembly as invalid. Calling ``assemble`` on it will throw.
|
||||
void markAsInvalid() { m_invalid = true; }
|
||||
|
||||
@ -171,6 +191,18 @@ 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);
|
||||
|
||||
/// 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;
|
||||
|
||||
@ -217,7 +249,7 @@ protected:
|
||||
/// Internal name of the assembly object, only used with the Yul backend
|
||||
/// currently
|
||||
std::string m_name;
|
||||
|
||||
std::vector<std::string> m_sourceList;
|
||||
langutil::SourceLocation m_currentSourceLocation;
|
||||
|
||||
public:
|
||||
|
@ -247,6 +247,18 @@ std::string AssemblyItem::getJumpTypeAsString() const
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyItem::setJumpType(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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -3,6 +3,8 @@ set(sources
|
||||
Assembly.h
|
||||
AssemblyItem.cpp
|
||||
AssemblyItem.h
|
||||
EVMAssemblyStack.cpp
|
||||
EVMAssemblyStack.h
|
||||
BlockDeduplicator.cpp
|
||||
BlockDeduplicator.h
|
||||
CommonSubexpressionEliminator.cpp
|
||||
|
58
libevmasm/EVMAssemblyStack.cpp
Normal file
58
libevmasm/EVMAssemblyStack.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
#include <utility>
|
||||
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::frontend;
|
||||
using namespace std;
|
||||
|
||||
namespace solidity::evmasm
|
||||
{
|
||||
|
||||
bool EVMAssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source)
|
||||
{
|
||||
m_name = _sourceName;
|
||||
if (jsonParseStrict(_source, m_json))
|
||||
{
|
||||
m_evmAssembly = evmasm::Assembly::fromJSON(m_json);
|
||||
return m_evmAssembly != nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EVMAssemblyStack::assemble()
|
||||
{
|
||||
solAssert(m_evmAssembly->isCreation());
|
||||
m_object = m_evmAssembly->assemble();
|
||||
if (m_evmAssembly->numSubs() > 0)
|
||||
{
|
||||
m_evmRuntimeAssembly = make_shared<evmasm::Assembly>(m_evmAssembly->sub(0));
|
||||
solAssert(m_evmRuntimeAssembly && !m_evmRuntimeAssembly->isCreation());
|
||||
m_runtimeObject = m_evmRuntimeAssembly->assemble();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace solidity::evmasm
|
68
libevmasm/EVMAssemblyStack.h
Normal file
68
libevmasm/EVMAssemblyStack.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
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 <map>
|
||||
#include <string>
|
||||
|
||||
#include <libsolutil/JSON.h>
|
||||
|
||||
#include <libevmasm/Assembly.h>
|
||||
#include <libevmasm/LinkerObject.h>
|
||||
|
||||
namespace solidity::evmasm
|
||||
{
|
||||
|
||||
class EVMAssemblyStack
|
||||
{
|
||||
public:
|
||||
explicit EVMAssemblyStack(langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) {}
|
||||
|
||||
/// Runs parsing and analysis, returns false if input cannot be assembled.
|
||||
/// Multiple calls overwrite the previous state.
|
||||
bool parseAndAnalyze(std::string const& _sourceName, std::string const& _source);
|
||||
|
||||
void assemble();
|
||||
|
||||
std::string const& name() const { return m_name; }
|
||||
|
||||
evmasm::LinkerObject const& object() const { return m_object; }
|
||||
|
||||
std::shared_ptr<evmasm::Assembly> const& evmAssembly() const { return m_evmAssembly; }
|
||||
|
||||
evmasm::LinkerObject const& runtimeObject() const { return m_runtimeObject; }
|
||||
|
||||
std::shared_ptr<evmasm::Assembly> const& evmRuntimeAssembly() const { return m_evmRuntimeAssembly; }
|
||||
|
||||
Json::Value json() const
|
||||
{
|
||||
return m_json;
|
||||
}
|
||||
|
||||
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.
|
||||
};
|
||||
|
||||
} // namespace solidity::evmasm
|
@ -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 {};
|
||||
|
@ -72,6 +72,7 @@
|
||||
#include <liblangutil/SemVerHandler.h>
|
||||
#include <liblangutil/SourceReferenceFormatter.h>
|
||||
|
||||
#include <libevmasm/EVMAssemblyStack.h>
|
||||
#include <libevmasm/Exceptions.h>
|
||||
|
||||
#include <libsolutil/SwarmHash.h>
|
||||
@ -438,6 +439,27 @@ void CompilerStack::importASTs(map<string, Json::Value> const& _sources)
|
||||
storeContractDefinitions();
|
||||
}
|
||||
|
||||
void CompilerStack::importFromEVMAssemblyStack(std::string const& _sourceName, std::string const& _source)
|
||||
{
|
||||
solRequire(m_stackState == Empty, CompilerError, "");
|
||||
m_evmAssemblyStack = std::make_unique<evmasm::EVMAssemblyStack>(m_evmVersion);
|
||||
Json::Value evmAsmJson;
|
||||
if (m_evmAssemblyStack->parseAndAnalyze(_sourceName, _source))
|
||||
{
|
||||
m_evmAssemblyStack->assemble();
|
||||
string const name{m_evmAssemblyStack->name()};
|
||||
Json::Value const& json = m_evmAssemblyStack->json();
|
||||
m_sourceJsons[name] = json;
|
||||
Contract& contract = m_contracts[name];
|
||||
contract.evmAssembly = m_evmAssemblyStack->evmAssembly();
|
||||
contract.evmRuntimeAssembly= m_evmAssemblyStack->evmRuntimeAssembly();
|
||||
contract.object = m_evmAssemblyStack->object();
|
||||
contract.runtimeObject = m_evmAssemblyStack->runtimeObject();
|
||||
|
||||
m_stackState = CompilationSuccessful;
|
||||
}
|
||||
}
|
||||
|
||||
bool CompilerStack::analyze()
|
||||
{
|
||||
if (m_stackState != ParsedAndImported || m_stackState >= AnalysisPerformed)
|
||||
@ -674,6 +696,7 @@ bool CompilerStack::isRequestedContract(ContractDefinition const& _contract) con
|
||||
|
||||
bool CompilerStack::compile(State _stopAfter)
|
||||
{
|
||||
solAssert(m_compilationSourceType != CompilationSourceType::EvmAssemblyJSON);
|
||||
m_stopAfter = _stopAfter;
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
if (!parseAndAnalyze(_stopAfter))
|
||||
@ -957,7 +980,13 @@ Json::Value CompilerStack::assemblyJSON(string const& _contractName) const
|
||||
|
||||
Contract const& currentContract = contract(_contractName);
|
||||
if (currentContract.evmAssembly)
|
||||
return currentContract.evmAssembly->assemblyJSON(sourceIndices());
|
||||
{
|
||||
std::vector<std::string> sources = sourceNames();
|
||||
if (std::find(sources.begin(), sources.end(), CompilerContext::yulUtilityFileName()) == sources.end())
|
||||
sources.emplace_back(CompilerContext::yulUtilityFileName());
|
||||
currentContract.evmAssembly->setSourceList(sources);
|
||||
return currentContract.evmAssembly->assemblyJSON();
|
||||
}
|
||||
else
|
||||
return Json::Value();
|
||||
}
|
||||
@ -971,9 +1000,20 @@ map<string, unsigned> CompilerStack::sourceIndices() const
|
||||
{
|
||||
map<string, unsigned> indices;
|
||||
unsigned index = 0;
|
||||
if (m_evmAssemblyStack)
|
||||
{
|
||||
for (auto const& s: m_evmAssemblyStack->evmAssembly()->sourceList())
|
||||
if (s != CompilerContext::yulUtilityFileName())
|
||||
indices[s] = index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto const& s: m_sources)
|
||||
if (s.first != CompilerContext::yulUtilityFileName())
|
||||
indices[s.first] = index++;
|
||||
solAssert(!indices.count(CompilerContext::yulUtilityFileName()), "");
|
||||
}
|
||||
|
||||
if (indices.find(CompilerContext::yulUtilityFileName()) == indices.end())
|
||||
indices[CompilerContext::yulUtilityFileName()] = index++;
|
||||
return indices;
|
||||
}
|
||||
@ -1589,6 +1629,11 @@ string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) con
|
||||
case CompilationSourceType::SolidityAST:
|
||||
sourceType = "SolidityAST";
|
||||
break;
|
||||
case CompilationSourceType::EvmAssemblyJSON:
|
||||
sourceType = "EvmAssemblyJson";
|
||||
break;
|
||||
default:
|
||||
solAssert(false);
|
||||
}
|
||||
meta["language"] = sourceType;
|
||||
meta["compiler"]["version"] = VersionStringStrict;
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
|
||||
#include <libevmasm/LinkerObject.h>
|
||||
#include <libevmasm/EVMAssemblyStack.h>
|
||||
|
||||
#include <libsolutil/Common.h>
|
||||
#include <libsolutil/FixedHash.h>
|
||||
@ -121,7 +122,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.
|
||||
@ -230,6 +233,8 @@ public:
|
||||
/// Will throw errors if the import fails
|
||||
void importASTs(std::map<std::string, Json::Value> const& _sources);
|
||||
|
||||
void importFromEVMAssemblyStack(std::string const& _sourceName, std::string const& _source);
|
||||
|
||||
/// Performs the analysis steps (imports, scopesetting, syntaxCheck, referenceResolving,
|
||||
/// typechecking, staticAnalysis) on previously parsed sources.
|
||||
/// @returns false on error.
|
||||
@ -524,6 +529,7 @@ private:
|
||||
/// If this is true, the stack will refuse to generate code.
|
||||
bool m_hasError = false;
|
||||
MetadataFormat m_metadataFormat = defaultMetadataFormat();
|
||||
std::unique_ptr<evmasm::EVMAssemblyStack> m_evmAssemblyStack;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,18 @@
|
||||
# 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
|
||||
|
||||
@ -40,7 +52,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
|
||||
@ -81,6 +93,7 @@ for PARAM in "$@"
|
||||
do
|
||||
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)" ;;
|
||||
@ -89,6 +102,7 @@ done
|
||||
|
||||
SYNTAXTESTS_DIR="${REPO_ROOT}/test/libsolidity/syntaxTests"
|
||||
ASTJSONTESTS_DIR="${REPO_ROOT}/test/libsolidity/ASTJSON"
|
||||
SEMANTICTESTS_DIR="${REPO_ROOT}/test/libsolidity/semanticTests"
|
||||
|
||||
FAILED=0
|
||||
UNCOMPILABLE=0
|
||||
@ -153,6 +167,125 @@ function test_ast_import_export_equivalence
|
||||
TESTED=$((TESTED + 1))
|
||||
}
|
||||
|
||||
function test_evmjson_import_export_equivalence
|
||||
{
|
||||
local sol_file="$1"
|
||||
local input_files=( "${@:2}" )
|
||||
local outputs=( "asm" "bin" "bin-runtime" "opcodes" "srcmap" "srcmap-runtime" )
|
||||
local export_command=("$SOLC" --combined-json "$(IFS=, ; echo "${outputs[*]}")" --pretty-json --json-indent 4 "${input_files[@]}")
|
||||
local success=1
|
||||
if ! "${export_command[@]}" > expected.json 2> expected.error
|
||||
then
|
||||
success=0
|
||||
printError "ERROR: (export) EVM Assembly JSON reimport failed for ${sol_file}"
|
||||
if (( EXIT_ON_ERROR == 1 ))
|
||||
then
|
||||
print_used_commands "$(pwd)" "${export_command[*]}" ""
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
if ! "${export_command[@]}" "--optimize" > expected.optimize.json 2> expected.optimize.error
|
||||
then
|
||||
success=0
|
||||
printError "ERROR: (export with --optimize) EVM Assembly JSON reimport failed for ${sol_file}"
|
||||
if (( EXIT_ON_ERROR == 1 ))
|
||||
then
|
||||
print_used_commands "$(pwd)" "${export_command[*]} --optimize" ""
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
for input_json in "expected.json" "expected.optimize.json"
|
||||
do
|
||||
local optimize_param=""
|
||||
if [[ "$input_json" == "expected.optimize.json" ]]
|
||||
then
|
||||
optimize_param="--optimize"
|
||||
fi
|
||||
|
||||
# Note that we have some test files, that only consist of free functions.
|
||||
# Those files don't define any contracts, so the resulting JSON does not have any
|
||||
# keys. In this case `jq` returns an error like `jq: error: null (null) has no keys`.
|
||||
# To not get spammed by these errors, errors are redirected to /dev/null.
|
||||
for contract in $(jq '.contracts | keys | .[]' "$input_json" 2> /dev/null)
|
||||
do
|
||||
for output in "${outputs[@]}"
|
||||
do
|
||||
jq --raw-output ".contracts.${contract}.\"${output}\"" "$input_json" > "expected.${output}.json"
|
||||
done
|
||||
|
||||
assembly=$(cat expected.asm.json)
|
||||
[[ $assembly != "" && $assembly != "null" ]] || continue
|
||||
|
||||
local import_command=("${SOLC}" --combined-json "bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime" --pretty-json --json-indent 4 --import-asm-json expected.asm.json)
|
||||
if ! "${import_command[@]}" > obtained.json 2> obtained.error
|
||||
then
|
||||
success=0
|
||||
printError "ERROR: (import) EVM Assembly JSON reimport failed for ${sol_file}"
|
||||
if (( EXIT_ON_ERROR == 1 ))
|
||||
then
|
||||
print_used_commands "$(pwd)" "${export_command[*]} ${optimize_param}" "${import_command[*]}"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
for output in "${outputs[@]}"
|
||||
do
|
||||
for obtained_contract in $(jq '.contracts | keys | .[]' obtained.json 2> /dev/null)
|
||||
do
|
||||
jq --raw-output ".contracts.${obtained_contract}.\"${output}\"" obtained.json > "obtained.${output}.json"
|
||||
# compare expected and obtained evm assembly json
|
||||
if ! diff_files "expected.${output}.json" "obtained.${output}.json"
|
||||
then
|
||||
success=0
|
||||
printError "ERROR: (${output}) EVM Assembly JSON reimport failed for ${sol_file}"
|
||||
if (( EXIT_ON_ERROR == 1 ))
|
||||
then
|
||||
print_used_commands "$(pwd)" "${export_command[*]} ${optimize_param}" "${import_command[*]}"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# direct export via --asm-json, if imported with --import-asm-json.
|
||||
if ! "${SOLC}" --asm-json --import-asm-json expected.asm.json --pretty-json --json-indent 4 | tail -n+2 > obtained_direct_import_export.json 2> obtained_direct_import_export.error
|
||||
then
|
||||
success=0
|
||||
printError "ERROR: (direct) EVM Assembly JSON reimport failed for ${sol_file}"
|
||||
if (( EXIT_ON_ERROR == 1 ))
|
||||
then
|
||||
print_used_commands "$(pwd)" "${SOLC} --asm-json --import-asm-json expected.asm.json --pretty-json --json-indent 4 | tail -n+4" ""
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# reformat json files using jq.
|
||||
jq . expected.asm.json > expected.asm.json.pretty
|
||||
jq . obtained_direct_import_export.json > obtained_direct_import_export.json.pretty
|
||||
|
||||
# compare expected and obtained evm assembly.
|
||||
if ! diff_files expected.asm.json.pretty obtained_direct_import_export.json.pretty
|
||||
then
|
||||
success=0
|
||||
printError "ERROR: EVM Assembly JSON reimport failed for ${sol_file}"
|
||||
if (( EXIT_ON_ERROR == 1 ))
|
||||
then
|
||||
print_used_commands "$(pwd)" "${export_command[*]} ${optimize_param}" "${import_command[*]}"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
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
|
||||
@ -168,6 +301,7 @@ function test_import_export_equivalence {
|
||||
|
||||
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
|
||||
|
||||
@ -181,6 +315,7 @@ function test_import_export_equivalence {
|
||||
then
|
||||
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
|
||||
@ -190,10 +325,23 @@ function test_import_export_equivalence {
|
||||
# This should normally not happen, so we terminate the test execution here
|
||||
# and print some details about the corresponding solc invocation.
|
||||
if (( solc_return_code == 2 ))
|
||||
then
|
||||
# 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
|
||||
@ -203,6 +351,7 @@ command_available jq --version
|
||||
|
||||
case "$IMPORT_TEST_TYPE" in
|
||||
ast) TEST_DIRS=("${SYNTAXTESTS_DIR}" "${ASTJSONTESTS_DIR}") ;;
|
||||
evm-assembly) TEST_DIRS=("${SEMANTICTESTS_DIR}") ;;
|
||||
*) assertFail "Import test type not defined. $(print_usage)" ;;
|
||||
esac
|
||||
|
||||
|
@ -93,6 +93,7 @@ namespace
|
||||
set<frontend::InputMode> const CompilerInputModes{
|
||||
frontend::InputMode::Compiler,
|
||||
frontend::InputMode::CompilerWithASTImport,
|
||||
frontend::InputMode::EVMAssemblerJSON
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
@ -167,28 +168,58 @@ static bool coloredOutput(CommandLineOptions const& _options)
|
||||
(_options.formatting.coloredOutput.has_value() && _options.formatting.coloredOutput.value());
|
||||
}
|
||||
|
||||
void CommandLineInterface::handleEVMAssembly(string const& _contract)
|
||||
{
|
||||
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
|
||||
|
||||
if (m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asmJson)
|
||||
{
|
||||
string assembly;
|
||||
if (m_options.compiler.outputs.asmJson)
|
||||
assembly = util::jsonPrint(removeNullMembers(m_compiler->assemblyJSON(_contract)), m_options.formatting.json);
|
||||
else
|
||||
assembly = 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"),
|
||||
assembly
|
||||
);
|
||||
else
|
||||
sout() << "EVM assembly:" << endl << assembly << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void CommandLineInterface::handleBinary(string const& _contract)
|
||||
{
|
||||
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
|
||||
|
||||
string binary;
|
||||
string binaryRuntime;
|
||||
if (m_options.compiler.outputs.binary)
|
||||
binary = objectWithLinkRefsHex(m_compiler->object(_contract));
|
||||
if (m_options.compiler.outputs.binaryRuntime)
|
||||
binaryRuntime = objectWithLinkRefsHex(m_compiler->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_compiler->filesystemFriendlyName(_contract) + ".bin", binary);
|
||||
else
|
||||
{
|
||||
sout() << "Binary:" << endl;
|
||||
sout() << objectWithLinkRefsHex(m_compiler->object(_contract)) << endl;
|
||||
sout() << binary << 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_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", binaryRuntime);
|
||||
else
|
||||
{
|
||||
sout() << "Binary of the runtime part:" << endl;
|
||||
sout() << objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)) << endl;
|
||||
sout() << binaryRuntime << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -197,12 +228,14 @@ void CommandLineInterface::handleOpcode(string const& _contract)
|
||||
{
|
||||
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
|
||||
|
||||
string opcodes{evmasm::disassemble(m_compiler->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_compiler->filesystemFriendlyName(_contract) + ".opcode", opcodes);
|
||||
else
|
||||
{
|
||||
sout() << "Opcodes:" << endl;
|
||||
sout() << uppercase << evmasm::disassemble(m_compiler->object(_contract).bytecode, m_options.output.evmVersion);
|
||||
sout() << uppercase << opcodes;
|
||||
sout() << endl;
|
||||
}
|
||||
}
|
||||
@ -702,6 +735,12 @@ void CommandLineInterface::processInput()
|
||||
compile();
|
||||
outputCompilationResults();
|
||||
break;
|
||||
case InputMode::EVMAssemblerJSON:
|
||||
assembleFromEvmAssemblyJson();
|
||||
handleCombinedJSON();
|
||||
handleBytecode("");
|
||||
handleEVMAssembly("");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -718,6 +757,23 @@ void CommandLineInterface::printLicense()
|
||||
sout() << licenseText << endl;
|
||||
}
|
||||
|
||||
void CommandLineInterface::assembleFromEvmAssemblyJson()
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::EVMAssemblerJSON);
|
||||
try
|
||||
{
|
||||
solAssert(m_fileReader.sourceUnits().size() == 1);
|
||||
auto const iter = m_fileReader.sourceUnits().begin();
|
||||
m_compiler = make_unique<CompilerStack>(m_universalCallback.callback());
|
||||
m_compiler->importFromEVMAssemblyStack(iter->first, iter->second);
|
||||
}
|
||||
catch (evmasm::AssemblyImportException const& _exception)
|
||||
{
|
||||
solThrow(CommandLineExecutionError, "Assembly Import Error: "s + _exception.what());
|
||||
}
|
||||
solRequire(m_compiler != nullptr, CommandLineExecutionError, "Assembly Import Error: Could not create compiler object.");
|
||||
}
|
||||
|
||||
void CommandLineInterface::compile()
|
||||
{
|
||||
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
|
||||
@ -884,8 +940,7 @@ void CommandLineInterface::handleCombinedJSON()
|
||||
);
|
||||
if (m_options.compiler.combinedJsonRequests->funDebugRuntime && m_compiler->compilationSuccessful())
|
||||
contractData[g_strFunDebugRuntime] = StandardCompiler::formatFunctionDebugData(
|
||||
m_compiler->runtimeObject(contractName).functionDebugData
|
||||
);
|
||||
m_compiler->runtimeObject(contractName).functionDebugData);
|
||||
if (m_options.compiler.combinedJsonRequests->signatureHashes)
|
||||
contractData[g_strSignatureHashes] = m_compiler->interfaceSymbols(contractName)["methods"];
|
||||
if (m_options.compiler.combinedJsonRequests->natspecDev)
|
||||
@ -894,8 +949,7 @@ void CommandLineInterface::handleCombinedJSON()
|
||||
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)
|
||||
@ -1152,6 +1206,53 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y
|
||||
else
|
||||
serr() << "No text representation found." << endl;
|
||||
}
|
||||
|
||||
if (m_options.compiler.outputs.asmJson)
|
||||
{
|
||||
shared_ptr<evmasm::Assembly> assembly{stack.assembleEVMWithDeployed().first};
|
||||
if (assembly)
|
||||
{
|
||||
std::function<map<string, unsigned>(yul::Object const&)> collectSourceIndices =
|
||||
[&](yul::Object const& _object) -> map<string, unsigned> {
|
||||
map<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)
|
||||
{
|
||||
map<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;
|
||||
vector<string> sourceList(max_index + 1, "<unknown>");
|
||||
for (auto const& [name, index]: sourceIndices)
|
||||
sourceList[index] = name;
|
||||
assembly->setSourceList(sourceList);
|
||||
sout() << util::jsonPrint(
|
||||
removeNullMembers(
|
||||
assembly->assemblyJSON()
|
||||
),
|
||||
m_options.formatting.json
|
||||
) << endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
serr() << "Could not create Assembly JSON representation." << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1180,19 +1281,7 @@ void CommandLineInterface::outputCompilationResults()
|
||||
sout() << endl << "======= " << contract << " =======" << endl;
|
||||
|
||||
// do we need EVM assembly?
|
||||
if (m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asmJson)
|
||||
{
|
||||
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:" << endl << ret << endl;
|
||||
}
|
||||
handleEVMAssembly(contract);
|
||||
|
||||
if (m_options.compiler.estimateGas)
|
||||
handleGasEstimation(contract);
|
||||
|
@ -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);
|
||||
|
@ -52,6 +52,7 @@ static string const g_strExperimentalViaIR = "experimental-via-ir";
|
||||
static string const g_strGas = "gas";
|
||||
static string const g_strHelp = "help";
|
||||
static string const g_strImportAst = "import-ast";
|
||||
static string const g_strImportEvmAssemblerJson = "import-asm-json";
|
||||
static string const g_strInputFile = "input-file";
|
||||
static string const g_strYul = "yul";
|
||||
static string const g_strYulDialect = "yul-dialect";
|
||||
@ -142,6 +143,7 @@ static map<InputMode, 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(vector<string> const& _optionNames)
|
||||
@ -468,8 +470,15 @@ void CommandLineParser::parseOutputSelection()
|
||||
CompilerOutputs::componentName(&CompilerOutputs::binary),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::irOptimized),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::astCompactJson),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::asmJson),
|
||||
};
|
||||
static set<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:
|
||||
@ -480,6 +489,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:
|
||||
@ -664,6 +675,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 "
|
||||
@ -932,6 +947,7 @@ void CommandLineParser::processArgs()
|
||||
g_strYul,
|
||||
g_strImportAst,
|
||||
g_strLSP,
|
||||
g_strImportEvmAssemblerJson,
|
||||
});
|
||||
|
||||
if (m_args.count(g_strHelp) > 0)
|
||||
@ -950,6 +966,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;
|
||||
|
||||
@ -1010,9 +1028,35 @@ void CommandLineParser::processArgs()
|
||||
if (option != CompilerOutputs::componentName(&CompilerOutputs::astCompactJson))
|
||||
checkMutuallyExclusive({g_strStopAfter, option});
|
||||
|
||||
if (m_options.input.mode == InputMode::EVMAssemblerJSON)
|
||||
{
|
||||
static set<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,
|
||||
"Option --" + option.first + " is not supported with --"+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
|
||||
)
|
||||
{
|
||||
@ -1372,7 +1416,10 @@ void CommandLineParser::processArgs()
|
||||
if (m_options.input.mode == InputMode::Compiler)
|
||||
m_options.input.errorRecovery = (m_args.count(g_strErrorRecovery) > 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()
|
||||
@ -1388,6 +1435,29 @@ 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,
|
||||
"Invalid option to --" + g_strCombinedJson + ": " + CombinedJsonRequests::componentName(invalidOption) + " for --" + g_strImportEvmAssemblerJson);
|
||||
}
|
||||
}
|
||||
|
||||
size_t CommandLineParser::countEnabledOptions(vector<string> const& _optionNames) const
|
||||
|
@ -57,6 +57,7 @@ enum class InputMode
|
||||
Linker,
|
||||
Assembler,
|
||||
LanguageServer,
|
||||
EVMAssemblerJSON
|
||||
};
|
||||
|
||||
struct CompilerOutputs
|
||||
|
File diff suppressed because one or more lines are too long
1
test/cmdlineTests/asm_json_import_all_valid_flags/args
Normal file
1
test/cmdlineTests/asm_json_import_all_valid_flags/args
Normal 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 -
|
91
test/cmdlineTests/asm_json_import_all_valid_flags/output
Normal file
91
test/cmdlineTests/asm_json_import_all_valid_flags/output
Normal file
@ -0,0 +1,91 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sourceList":
|
||||
[
|
||||
"contract.sol",
|
||||
"#utility.yul"
|
||||
]
|
||||
},
|
||||
"bin": "6000fe",
|
||||
"bin-runtime": "6001",
|
||||
"opcodes": "PUSH1 0x0 INVALID ",
|
||||
"srcmap": "0:0::-:0",
|
||||
"srcmap-runtime": "0:0::-:0"
|
||||
}
|
||||
},
|
||||
"sourceList":
|
||||
[
|
||||
"contract.sol",
|
||||
"#utility.yul"
|
||||
],
|
||||
"version": "<VERSION REMOVED>"
|
||||
}
|
||||
Binary:
|
||||
6000fe
|
||||
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"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sourceList":
|
||||
[
|
||||
"contract.sol",
|
||||
"#utility.yul"
|
||||
]
|
||||
}
|
22
test/cmdlineTests/asm_json_import_all_valid_flags/stdin
Normal file
22
test/cmdlineTests/asm_json_import_all_valid_flags/stdin
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
".code": [
|
||||
{
|
||||
"name": "PUSH",
|
||||
"value": "0"
|
||||
}
|
||||
],
|
||||
".data": {
|
||||
"0": {
|
||||
".code": [
|
||||
{
|
||||
"name": "PUSH",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sourceList": [
|
||||
"contract.sol",
|
||||
"#utility.yul"
|
||||
]
|
||||
}
|
@ -0,0 +1 @@
|
||||
--import-asm-json - --opcodes --asm
|
@ -0,0 +1,6 @@
|
||||
Opcodes:
|
||||
PREVRANDAO PREVRANDAO
|
||||
EVM assembly:
|
||||
/* */
|
||||
prevrandao
|
||||
prevrandao
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
".code": [
|
||||
{ "name": "DIFFICULTY" },
|
||||
{ "name": "PREVRANDAO" }
|
||||
]
|
||||
}
|
@ -0,0 +1 @@
|
||||
--import-asm-json - --opcodes --asm
|
@ -0,0 +1 @@
|
||||
Assembly Import Error: Member '.data' contains a value for '0' that is not a valid hexadecimal string.
|
@ -0,0 +1 @@
|
||||
1
|
11
test/cmdlineTests/asm_json_import_invalid_data_no_hex/stdin
Normal file
11
test/cmdlineTests/asm_json_import_invalid_data_no_hex/stdin
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
".code": [
|
||||
{
|
||||
"name": "PUSH",
|
||||
"value": "0"
|
||||
}
|
||||
],
|
||||
".data": {
|
||||
"0": "no-hex-string"
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
--import-asm-json - --opcodes --asm
|
@ -0,0 +1 @@
|
||||
Assembly Import Error: Key inside '.data' '0' can only be a valid hex-string or an object.
|
@ -0,0 +1 @@
|
||||
1
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
".code": [
|
||||
{
|
||||
"name": "PUSH",
|
||||
"value": "0"
|
||||
}
|
||||
],
|
||||
".data": {
|
||||
"0": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
--import-asm-json - --opcodes --asm
|
@ -0,0 +1 @@
|
||||
Assembly Import Error: Member 'jumpType' set on instruction different from JUMP or JUMPI (was set on instruction 'PUSH')
|
@ -0,0 +1 @@
|
||||
1
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
".code": [
|
||||
{
|
||||
"name": "PUSH",
|
||||
"value": "0",
|
||||
"jumpType": "[in]"
|
||||
}
|
||||
]
|
||||
}
|
1
test/cmdlineTests/asm_json_import_invalid_value/args
Normal file
1
test/cmdlineTests/asm_json_import_invalid_value/args
Normal file
@ -0,0 +1 @@
|
||||
--import-asm-json - --opcodes --asm
|
1
test/cmdlineTests/asm_json_import_invalid_value/err
Normal file
1
test/cmdlineTests/asm_json_import_invalid_value/err
Normal file
@ -0,0 +1 @@
|
||||
Assembly Import Error: Member 'value' defined for instruction 'DIFFICULTY', but the instruction does not need a value.
|
1
test/cmdlineTests/asm_json_import_invalid_value/exit
Normal file
1
test/cmdlineTests/asm_json_import_invalid_value/exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
8
test/cmdlineTests/asm_json_import_invalid_value/stdin
Normal file
8
test/cmdlineTests/asm_json_import_invalid_value/stdin
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
".code": [
|
||||
{
|
||||
"name": "DIFFICULTY",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
}
|
1
test/cmdlineTests/asm_json_import_no_optimiser/args
Normal file
1
test/cmdlineTests/asm_json_import_no_optimiser/args
Normal file
@ -0,0 +1 @@
|
||||
--optimize --import-asm-json - --opcodes --asm
|
1
test/cmdlineTests/asm_json_import_no_optimiser/err
Normal file
1
test/cmdlineTests/asm_json_import_no_optimiser/err
Normal file
@ -0,0 +1 @@
|
||||
Option --optimize is not supported with --import-asm-json.
|
1
test/cmdlineTests/asm_json_import_no_optimiser/exit
Normal file
1
test/cmdlineTests/asm_json_import_no_optimiser/exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
6
test/cmdlineTests/asm_json_import_no_optimiser/stdin
Normal file
6
test/cmdlineTests/asm_json_import_no_optimiser/stdin
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
".code": [
|
||||
{ "name": "DIFFICULTY" },
|
||||
{ "name": "PREVRANDAO" }
|
||||
]
|
||||
}
|
1
test/cmdlineTests/asm_json_import_no_value/args
Normal file
1
test/cmdlineTests/asm_json_import_no_value/args
Normal file
@ -0,0 +1 @@
|
||||
--import-asm-json - --opcodes --asm
|
1
test/cmdlineTests/asm_json_import_no_value/err
Normal file
1
test/cmdlineTests/asm_json_import_no_value/err
Normal file
@ -0,0 +1 @@
|
||||
Assembly Import Error: Member 'value' was not defined for instruction 'PUSH', but the instruction needs a value.
|
1
test/cmdlineTests/asm_json_import_no_value/exit
Normal file
1
test/cmdlineTests/asm_json_import_no_value/exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
5
test/cmdlineTests/asm_json_import_no_value/stdin
Normal file
5
test/cmdlineTests/asm_json_import_no_value/stdin
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
".code": [
|
||||
{ "name": "PUSH" }
|
||||
]
|
||||
}
|
1
test/cmdlineTests/asm_json_import_other_fields/args
Normal file
1
test/cmdlineTests/asm_json_import_other_fields/args
Normal file
@ -0,0 +1 @@
|
||||
--import-asm-json - --opcodes --asm
|
1
test/cmdlineTests/asm_json_import_other_fields/err
Normal file
1
test/cmdlineTests/asm_json_import_other_fields/err
Normal file
@ -0,0 +1 @@
|
||||
Assembly Import Error: Unknown member '_name'. Valid members are begin, end, jumpType, modifierDepth, name, source, value.
|
1
test/cmdlineTests/asm_json_import_other_fields/exit
Normal file
1
test/cmdlineTests/asm_json_import_other_fields/exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
5
test/cmdlineTests/asm_json_import_other_fields/stdin
Normal file
5
test/cmdlineTests/asm_json_import_other_fields/stdin
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
".code": [
|
||||
{ "_name": "DIFFICULTY" }
|
||||
]
|
||||
}
|
1
test/cmdlineTests/asm_json_import_untagged_jumpdest/args
Normal file
1
test/cmdlineTests/asm_json_import_untagged_jumpdest/args
Normal file
@ -0,0 +1 @@
|
||||
--import-asm-json - --opcodes --asm
|
1
test/cmdlineTests/asm_json_import_untagged_jumpdest/err
Normal file
1
test/cmdlineTests/asm_json_import_untagged_jumpdest/err
Normal file
@ -0,0 +1 @@
|
||||
Assembly Import Error: JUMPDEST instruction found that was not followed by tag.
|
1
test/cmdlineTests/asm_json_import_untagged_jumpdest/exit
Normal file
1
test/cmdlineTests/asm_json_import_untagged_jumpdest/exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
10
test/cmdlineTests/asm_json_import_untagged_jumpdest/stdin
Normal file
10
test/cmdlineTests/asm_json_import_untagged_jumpdest/stdin
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
".code": [
|
||||
{
|
||||
"name": "tag",
|
||||
"value": "0x00"
|
||||
},
|
||||
{ "name": "JUMPDEST" },
|
||||
{ "name": "JUMPDEST" }
|
||||
]
|
||||
}
|
@ -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.
|
||||
|
@ -55,13 +55,9 @@ BOOST_AUTO_TEST_SUITE(Assembler)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
{
|
||||
map<string, unsigned> indices = {
|
||||
{ "root.asm", 0 },
|
||||
{ "sub.asm", 1 },
|
||||
{ "verbatim.asm", 2 }
|
||||
};
|
||||
EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion();
|
||||
Assembly _assembly{evmVersion, false, {}};
|
||||
_assembly.setSourceList({"root.asm", "sub.asm", "verbatim.asm"});
|
||||
auto root_asm = make_shared<string>("root.asm");
|
||||
_assembly.setSourceLocation({1, 3, root_asm});
|
||||
|
||||
@ -212,7 +208,7 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
};
|
||||
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()), util::jsonCompactPrint(jsonValue));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
|
||||
@ -301,12 +297,9 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(immutable)
|
||||
{
|
||||
map<string, unsigned> indices = {
|
||||
{ "root.asm", 0 },
|
||||
{ "sub.asm", 1 }
|
||||
};
|
||||
EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion();
|
||||
Assembly _assembly{evmVersion, true, {}};
|
||||
_assembly.setSourceList({"root.asm", "sub.asm"});
|
||||
auto root_asm = make_shared<string>("root.asm");
|
||||
_assembly.setSourceLocation({1, 3, root_asm});
|
||||
|
||||
@ -383,7 +376,7 @@ BOOST_AUTO_TEST_CASE(immutable)
|
||||
"}\n"
|
||||
);
|
||||
BOOST_CHECK_EQUAL(
|
||||
util::jsonCompactPrint(_assembly.assemblyJSON(indices)),
|
||||
util::jsonCompactPrint(_assembly.assemblyJSON()),
|
||||
"{\".code\":["
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2A\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"},"
|
||||
|
@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(version)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(multiple_input_modes)
|
||||
{
|
||||
array<string, 9> inputModeOptions = {
|
||||
array<string, 10> inputModeOptions = {
|
||||
"--help",
|
||||
"--license",
|
||||
"--version",
|
||||
@ -158,10 +158,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)
|
||||
|
Loading…
Reference in New Issue
Block a user