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
5f63b3ca21
commit
f042c6c32f
@ -74,6 +74,117 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
|
||||
}
|
||||
}
|
||||
|
||||
void Assembly::addAssemblyItemsFromJSON(Json::Value const& _code)
|
||||
{
|
||||
solAssert(_code.isArray(), "");
|
||||
for (auto const& jsonItem: _code)
|
||||
m_items.emplace_back(loadItemFromJSON(jsonItem));
|
||||
|
||||
for (auto current = m_items.begin(); current != m_items.end(); ++current)
|
||||
{
|
||||
// During the assembly json export a `JUMPDEST` is always generated after a `tag`.
|
||||
// So we just ignore exactly these `JUMPDEST`'s.
|
||||
auto const next = std::next(current);
|
||||
if (
|
||||
next != m_items.end() &&
|
||||
current->type() == AssemblyItemType::Tag &&
|
||||
next->type() == AssemblyItemType::Operation &&
|
||||
next->instruction() == Instruction::JUMPDEST
|
||||
)
|
||||
m_items.erase(next);
|
||||
}
|
||||
}
|
||||
|
||||
AssemblyItem Assembly::loadItemFromJSON(Json::Value const& _json)
|
||||
{
|
||||
std::string name = _json["name"].isString() ? _json["name"].asString() : "";
|
||||
int begin = _json["begin"].isInt() ? _json["begin"].asInt() : -1;
|
||||
int end = _json["end"].isInt() ? _json["end"].asInt() : -1;
|
||||
int srcIndex = _json["source"].isInt() ? _json["source"].asInt() : -1;
|
||||
size_t modifierDepth = _json["modifierDepth"].isInt() ? static_cast<size_t>(_json["modifierDepth"].asInt()) : 0;
|
||||
std::string value = _json["value"].isString() ? _json["value"].asString() : "";
|
||||
std::string jumpType = _json["jumpType"].isString() ? _json["jumpType"].asString() : "";
|
||||
solAssert(!name.empty(), "");
|
||||
|
||||
auto updateUsedTags = [&](u256 const& data) {
|
||||
auto tag = static_cast<unsigned>(data);
|
||||
if (m_usedTags <= tag)
|
||||
m_usedTags = tag + 1;
|
||||
return data;
|
||||
};
|
||||
|
||||
auto immutableHash = [&](string const& _immutableName) -> h256 {
|
||||
h256 hash(util::keccak256(value));
|
||||
m_immutables[hash] = _immutableName;
|
||||
return hash;
|
||||
};
|
||||
|
||||
auto libraryHash = [&](string const& _libraryName) -> h256 {
|
||||
h256 hash(util::keccak256(value));
|
||||
m_libraries[hash] = _libraryName;
|
||||
return hash;
|
||||
};
|
||||
|
||||
SourceLocation location;
|
||||
location.start = begin;
|
||||
location.end = end;
|
||||
if (srcIndex > -1 && srcIndex < static_cast<int>(sources().size()))
|
||||
location.sourceName = sources()[static_cast<size_t>(srcIndex)];
|
||||
|
||||
AssemblyItem result(0);
|
||||
|
||||
if (c_instructions.find(name) != c_instructions.end())
|
||||
{
|
||||
AssemblyItem item{c_instructions.at(name), location};
|
||||
item.m_modifierDepth = modifierDepth;
|
||||
if (!jumpType.empty())
|
||||
item.setJumpType(jumpType);
|
||||
result = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (name == "PUSH")
|
||||
{
|
||||
AssemblyItem item{AssemblyItemType::Push, u256("0x" + value), location};
|
||||
if (!jumpType.empty())
|
||||
item.setJumpType(jumpType);
|
||||
result = item;
|
||||
}
|
||||
else if (name == "PUSH [ErrorTag]")
|
||||
result = {AssemblyItemType::PushTag, 0, location};
|
||||
else if (name == "PUSH [tag]")
|
||||
result = {AssemblyItemType::PushTag, updateUsedTags(u256(value)), location};
|
||||
else if (name == "PUSH [$]")
|
||||
result = {AssemblyItemType::PushSub, u256("0x" + value), location};
|
||||
else if (name == "PUSH #[$]")
|
||||
result = {AssemblyItemType::PushSubSize, u256("0x" + value), location};
|
||||
else if (name == "PUSHSIZE")
|
||||
result = {AssemblyItemType::PushProgramSize, 0, location};
|
||||
else if (name == "PUSHLIB")
|
||||
result = {AssemblyItemType::PushLibraryAddress, libraryHash(value), location};
|
||||
else if (name == "PUSHDEPLOYADDRESS")
|
||||
result = {AssemblyItemType::PushDeployTimeAddress, 0, location};
|
||||
else if (name == "PUSHIMMUTABLE")
|
||||
result = {AssemblyItemType::PushImmutable, immutableHash(value), location};
|
||||
else if (name == "ASSIGNIMMUTABLE")
|
||||
result = {AssemblyItemType::AssignImmutable, immutableHash(value), location};
|
||||
else if (name == "tag")
|
||||
result = {AssemblyItemType::Tag, updateUsedTags(u256(value)), location};
|
||||
else if (name == "PUSH data")
|
||||
result = {AssemblyItemType::PushData, u256("0x" + value), location};
|
||||
else if (name == "VERBATIM")
|
||||
{
|
||||
AssemblyItem item(fromHex(value), 0, 0);
|
||||
item.setLocation(location);
|
||||
result = item;
|
||||
}
|
||||
else
|
||||
assertThrow(false, InvalidOpcode, "");
|
||||
}
|
||||
result.m_modifierDepth = modifierDepth;
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
@ -298,6 +409,43 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices,
|
||||
return root;
|
||||
}
|
||||
|
||||
bool Assembly::loadFromAssemblyJSON(Json::Value const& _json, bool _loadSources /* = true */)
|
||||
{
|
||||
if (!_json[".code"].isArray())
|
||||
return false;
|
||||
bool success{true};
|
||||
|
||||
if (_loadSources)
|
||||
{
|
||||
vector<string> sourceList;
|
||||
if (_json.isMember("sourceList"))
|
||||
for (auto const& it: _json["sourceList"])
|
||||
sourceList.emplace_back(it.asString());
|
||||
setSources(sourceList);
|
||||
}
|
||||
|
||||
addAssemblyItemsFromJSON(_json[".code"]);
|
||||
if (_json[".auxdata"].isString())
|
||||
m_auxiliaryData = fromHex(_json[".auxdata"].asString());
|
||||
Json::Value const& data = _json[".data"];
|
||||
for (Json::ValueConstIterator itr = data.begin(); itr != data.end(); itr++)
|
||||
{
|
||||
solAssert(itr.key().isString(), "");
|
||||
std::string key = itr.key().asString();
|
||||
Json::Value const& code = data[key];
|
||||
if (code.isString())
|
||||
m_data[h256(fromHex(key))] = fromHex(code.asString());
|
||||
else
|
||||
{
|
||||
shared_ptr<Assembly> subassembly = make_shared<Assembly>(false, "");
|
||||
subassembly->setSources(sources());
|
||||
success &= subassembly->loadFromAssemblyJSON(code, false);
|
||||
m_subs.emplace_back(subassembly);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
AssemblyItem Assembly::namedTag(string const& _name, size_t _params, size_t _returns, optional<uint64_t> _sourceID)
|
||||
{
|
||||
assertThrow(!_name.empty(), AssemblyException, "Empty named tag.");
|
||||
|
@ -152,6 +152,12 @@ public:
|
||||
bool _includeSourceList = true
|
||||
) const;
|
||||
|
||||
/// Loads the JSON representation of assembly.
|
||||
/// @param _json JSON object containing assembly
|
||||
/// @param _loadSources true, if source list should be included, false otherwise.
|
||||
/// @returns true on success, false otherwise
|
||||
bool loadFromAssemblyJSON(Json::Value const& _json, bool _loadSources = true);
|
||||
|
||||
/// Mark this assembly as invalid. Calling ``assemble`` on it will throw.
|
||||
void markAsInvalid() { m_invalid = true; }
|
||||
|
||||
@ -160,6 +166,22 @@ public:
|
||||
|
||||
bool isCreation() const { return m_creation; }
|
||||
|
||||
/// Set the source list.
|
||||
void setSources(std::vector<std::shared_ptr<std::string const>> _sources)
|
||||
{
|
||||
m_sources = std::move(_sources);
|
||||
}
|
||||
|
||||
/// Set the source list from simple vector<string>.
|
||||
void setSources(std::vector<std::string> const& _sources)
|
||||
{
|
||||
for (auto const& item: _sources)
|
||||
m_sources.emplace_back(std::make_shared<std::string>(item));
|
||||
}
|
||||
|
||||
/// @returns List of sources.
|
||||
std::vector<std::shared_ptr<std::string const>> sources() const& { return m_sources; }
|
||||
|
||||
protected:
|
||||
/// Does the same operations as @a optimise, but should only be applied to a sub and
|
||||
/// returns the replaced tags. Also takes an argument containing the tags of this assembly
|
||||
@ -168,6 +190,14 @@ protected:
|
||||
|
||||
unsigned codeSize(unsigned subTagSize) const;
|
||||
|
||||
/// Add all assembly items from given JSON array.
|
||||
void addAssemblyItemsFromJSON(Json::Value const& _code);
|
||||
|
||||
/// Creates an AssemblyItem from a given JSON representation.
|
||||
/// @param _json JSON representation of an assembly item
|
||||
/// @returns AssemblyItem from a given JSON representation.
|
||||
AssemblyItem loadItemFromJSON(Json::Value const& _json);
|
||||
|
||||
private:
|
||||
bool m_invalid = false;
|
||||
|
||||
@ -214,6 +244,7 @@ protected:
|
||||
std::string m_name;
|
||||
|
||||
langutil::SourceLocation m_currentSourceLocation;
|
||||
std::vector<std::shared_ptr<std::string const>> m_sources;
|
||||
|
||||
public:
|
||||
size_t m_currentModifierDepth = 0;
|
||||
|
@ -243,6 +243,18 @@ string AssemblyItem::getJumpTypeAsString() const
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyItem::setJumpType(std::string const& _jumpType)
|
||||
{
|
||||
if (_jumpType == "[in]")
|
||||
m_jumpType = JumpType::IntoFunction;
|
||||
else if (_jumpType == "[out]")
|
||||
m_jumpType = JumpType::OutOfFunction;
|
||||
else if (_jumpType.empty())
|
||||
m_jumpType = JumpType::Ordinary;
|
||||
else
|
||||
assertThrow(false, AssemblyException, "Invalid jump type.");
|
||||
}
|
||||
|
||||
string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
|
||||
{
|
||||
string text;
|
||||
|
@ -173,6 +173,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;
|
||||
|
||||
|
@ -415,6 +415,28 @@ void CompilerStack::importASTs(map<string, Json::Value> const& _sources)
|
||||
storeContractDefinitions();
|
||||
}
|
||||
|
||||
void CompilerStack::importEvmAssemblyJson(std::map<std::string, Json::Value> const& _sources)
|
||||
{
|
||||
solAssert(_sources.size() == 1, "");
|
||||
solAssert(m_sources.empty(), "");
|
||||
solAssert(m_sourceOrder.empty(), "");
|
||||
if (m_stackState != Empty)
|
||||
solThrow(CompilerError, "Must call importEvmAssemblyJson only before the SourcesSet state.");
|
||||
|
||||
Json::Value jsonValue = _sources.begin()->second;
|
||||
if (jsonValue.isMember("sourceList"))
|
||||
for (auto const& item: jsonValue["sourceList"])
|
||||
{
|
||||
Source source;
|
||||
source.charStream = std::make_shared<CharStream>(item.asString(), "");
|
||||
m_sources.emplace(std::make_pair(item.asString(), source));
|
||||
m_sourceOrder.push_back(&m_sources[item.asString()]);
|
||||
}
|
||||
m_evmAssemblyJson[_sources.begin()->first] = jsonValue;
|
||||
m_importedSources = true;
|
||||
m_stackState = SourcesSet;
|
||||
}
|
||||
|
||||
bool CompilerStack::analyze()
|
||||
{
|
||||
if (m_stackState != ParsedAndImported || m_stackState >= AnalysisPerformed)
|
||||
@ -604,6 +626,9 @@ bool CompilerStack::parseAndAnalyze(State _stopAfter)
|
||||
{
|
||||
m_stopAfter = _stopAfter;
|
||||
|
||||
if (!m_evmAssemblyJson.empty())
|
||||
return true;
|
||||
|
||||
bool success = parse();
|
||||
if (m_stackState >= m_stopAfter)
|
||||
return success;
|
||||
@ -653,55 +678,85 @@ bool CompilerStack::compile(State _stopAfter)
|
||||
// Only compile contracts individually which have been requested.
|
||||
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
|
||||
|
||||
for (Source const* source: m_sourceOrder)
|
||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
|
||||
if (isRequestedContract(*contract))
|
||||
{
|
||||
try
|
||||
if (!m_evmAssemblyJson.empty())
|
||||
{
|
||||
solAssert(m_importedSources, "");
|
||||
solAssert(m_evmAssemblyJson.size() == 1, "");
|
||||
|
||||
string const evmSourceName = m_evmAssemblyJson.begin()->first;
|
||||
Json::Value const evmJson = m_evmAssemblyJson.begin()->second;
|
||||
|
||||
evmasm::Assembly::OptimiserSettings optimiserSettings;
|
||||
optimiserSettings.evmVersion = m_evmVersion;
|
||||
optimiserSettings.expectedExecutionsPerDeployment = m_optimiserSettings.expectedExecutionsPerDeployment;
|
||||
optimiserSettings.runCSE = m_optimiserSettings.runCSE;
|
||||
optimiserSettings.runConstantOptimiser = m_optimiserSettings.runConstantOptimiser;
|
||||
optimiserSettings.runDeduplicate = m_optimiserSettings.runDeduplicate;
|
||||
optimiserSettings.runInliner = m_optimiserSettings.runInliner;
|
||||
optimiserSettings.runJumpdestRemover = m_optimiserSettings.runJumpdestRemover;
|
||||
optimiserSettings.runPeephole = m_optimiserSettings.runPeephole;
|
||||
|
||||
m_contracts[evmSourceName].evmAssembly = make_shared<evmasm::Assembly>(true, evmSourceName);
|
||||
m_contracts[evmSourceName].evmAssembly->loadFromAssemblyJSON(m_evmAssemblyJson[evmSourceName]);
|
||||
if (m_optimiserSettings.enabled)
|
||||
m_contracts[evmSourceName].evmAssembly->optimise(optimiserSettings);
|
||||
m_contracts[evmSourceName].object = m_contracts[evmSourceName].evmAssembly->assemble();
|
||||
|
||||
m_contracts[evmSourceName].evmRuntimeAssembly = make_shared<evmasm::Assembly>(false, evmSourceName);
|
||||
m_contracts[evmSourceName].evmRuntimeAssembly->setSources(m_contracts[evmSourceName].evmAssembly->sources());
|
||||
m_contracts[evmSourceName].evmRuntimeAssembly->loadFromAssemblyJSON(m_evmAssemblyJson[evmSourceName][".data"]["0"], false);
|
||||
if (m_optimiserSettings.enabled)
|
||||
m_contracts[evmSourceName].evmRuntimeAssembly->optimise(optimiserSettings);
|
||||
m_contracts[evmSourceName].runtimeObject = m_contracts[evmSourceName].evmRuntimeAssembly->assemble();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Source const* source: m_sourceOrder)
|
||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
|
||||
if (isRequestedContract(*contract))
|
||||
{
|
||||
if (m_viaIR || m_generateIR || m_generateEwasm)
|
||||
generateIR(*contract);
|
||||
if (m_generateEvmBytecode)
|
||||
try
|
||||
{
|
||||
if (m_viaIR)
|
||||
generateEVMFromIR(*contract);
|
||||
else
|
||||
compileContract(*contract, otherCompilers);
|
||||
if (m_viaIR || m_generateIR || m_generateEwasm)
|
||||
generateIR(*contract);
|
||||
if (m_generateEvmBytecode)
|
||||
{
|
||||
if (m_viaIR)
|
||||
generateEVMFromIR(*contract);
|
||||
else
|
||||
compileContract(*contract, otherCompilers);
|
||||
}
|
||||
if (m_generateEwasm)
|
||||
generateEwasm(*contract);
|
||||
}
|
||||
if (m_generateEwasm)
|
||||
generateEwasm(*contract);
|
||||
}
|
||||
catch (Error const& _error)
|
||||
{
|
||||
if (_error.type() != Error::Type::CodeGenerationError)
|
||||
throw;
|
||||
m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what());
|
||||
return false;
|
||||
}
|
||||
catch (UnimplementedFeatureError const& _unimplementedError)
|
||||
{
|
||||
if (
|
||||
SourceLocation const* sourceLocation =
|
||||
boost::get_error_info<langutil::errinfo_sourceLocation>(_unimplementedError)
|
||||
)
|
||||
catch (Error const& _error)
|
||||
{
|
||||
string const* comment = _unimplementedError.comment();
|
||||
m_errorReporter.error(
|
||||
1834_error,
|
||||
Error::Type::CodeGenerationError,
|
||||
*sourceLocation,
|
||||
"Unimplemented feature error" +
|
||||
((comment && !comment->empty()) ? ": " + *comment : string{}) +
|
||||
" in " +
|
||||
_unimplementedError.lineInfo()
|
||||
);
|
||||
if (_error.type() != Error::Type::CodeGenerationError)
|
||||
throw;
|
||||
m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
throw;
|
||||
catch (UnimplementedFeatureError const& _unimplementedError)
|
||||
{
|
||||
if (SourceLocation const* sourceLocation
|
||||
= boost::get_error_info<langutil::errinfo_sourceLocation>(_unimplementedError))
|
||||
{
|
||||
string const* comment = _unimplementedError.comment();
|
||||
m_errorReporter.error(
|
||||
1834_error,
|
||||
Error::Type::CodeGenerationError,
|
||||
*sourceLocation,
|
||||
"Unimplemented feature error"
|
||||
+ ((comment && !comment->empty()) ? ": " + *comment : string{}) + " in "
|
||||
+ _unimplementedError.lineInfo());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_stackState = CompilationSuccessful;
|
||||
this->link();
|
||||
return true;
|
||||
@ -937,14 +992,21 @@ vector<string> CompilerStack::sourceNames() const
|
||||
return names;
|
||||
}
|
||||
|
||||
map<string, unsigned> CompilerStack::sourceIndices() const
|
||||
map<string, unsigned> CompilerStack::sourceIndices(bool _includeInternalSources /* = true */) const
|
||||
{
|
||||
map<string, unsigned> indices;
|
||||
unsigned index = 0;
|
||||
for (auto const& s: m_sources)
|
||||
indices[s.first] = index++;
|
||||
solAssert(!indices.count(CompilerContext::yulUtilityFileName()), "");
|
||||
indices[CompilerContext::yulUtilityFileName()] = index++;
|
||||
if (m_evmAssemblyJson.empty())
|
||||
{
|
||||
for (auto const& s: m_sources)
|
||||
indices[s.first] = index++;
|
||||
solAssert(!indices.count(CompilerContext::yulUtilityFileName()), "");
|
||||
if (_includeInternalSources)
|
||||
indices[CompilerContext::yulUtilityFileName()] = index++;
|
||||
}
|
||||
else
|
||||
for (auto const& s: m_sourceOrder)
|
||||
indices[s->charStream->source()] = index++;
|
||||
return indices;
|
||||
}
|
||||
|
||||
|
@ -222,6 +222,10 @@ public:
|
||||
/// Will throw errors if the import fails
|
||||
void importASTs(std::map<std::string, Json::Value> const& _sources);
|
||||
|
||||
/// Imports given Evm Assembly Json. Leads to the same internal state as parse().
|
||||
/// Will throw errors if the import fails
|
||||
void importEvmAssemblyJson(std::map<std::string, Json::Value> const& _sources);
|
||||
|
||||
/// Performs the analysis steps (imports, scopesetting, syntaxCheck, referenceResolving,
|
||||
/// typechecking, staticAnalysis) on previously parsed sources.
|
||||
/// @returns false on error.
|
||||
@ -240,7 +244,7 @@ public:
|
||||
|
||||
/// @returns a mapping assigning each source name its index inside the vector returned
|
||||
/// by sourceNames().
|
||||
std::map<std::string, unsigned> sourceIndices() const;
|
||||
std::map<std::string, unsigned> sourceIndices(bool _includeInternalSources = true) const;
|
||||
|
||||
/// @returns the previously used character stream, useful for counting lines during error reporting.
|
||||
langutil::CharStream const& charStream(std::string const& _sourceName) const override;
|
||||
@ -498,6 +502,7 @@ private:
|
||||
std::map<std::string const, Source> m_sources;
|
||||
// if imported, store AST-JSONS for each filename
|
||||
std::map<std::string, Json::Value> m_sourceJsons;
|
||||
std::map<std::string, Json::Value> m_evmAssemblyJson;
|
||||
std::vector<std::string> m_unhandledSMTLib2Queries;
|
||||
std::map<util::h256, std::string> m_smtlib2Responses;
|
||||
std::shared_ptr<GlobalContext> m_globalContext;
|
||||
|
@ -155,6 +155,8 @@ struct OptimiserSettings
|
||||
/// This specifies an estimate on how often each opcode in this assembly will be executed,
|
||||
/// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage.
|
||||
size_t expectedExecutionsPerDeployment = 200;
|
||||
/// Flag reflecting whether optimizer is enabled.
|
||||
bool enabled = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -161,7 +161,11 @@ static bool coloredOutput(CommandLineOptions const& _options)
|
||||
|
||||
void CommandLineInterface::handleBinary(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (m_options.compiler.outputs.binary)
|
||||
{
|
||||
@ -187,7 +191,11 @@ void CommandLineInterface::handleBinary(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleOpcode(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.output.dir.empty())
|
||||
createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode));
|
||||
@ -201,7 +209,11 @@ void CommandLineInterface::handleOpcode(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleIR(string const& _contractName)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.ir)
|
||||
return;
|
||||
@ -217,7 +229,11 @@ void CommandLineInterface::handleIR(string const& _contractName)
|
||||
|
||||
void CommandLineInterface::handleIROptimized(string const& _contractName)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.irOptimized)
|
||||
return;
|
||||
@ -233,7 +249,11 @@ void CommandLineInterface::handleIROptimized(string const& _contractName)
|
||||
|
||||
void CommandLineInterface::handleEwasm(string const& _contractName)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.ewasm)
|
||||
return;
|
||||
@ -256,7 +276,11 @@ void CommandLineInterface::handleEwasm(string const& _contractName)
|
||||
|
||||
void CommandLineInterface::handleBytecode(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (m_options.compiler.outputs.opcodes)
|
||||
handleOpcode(_contract);
|
||||
@ -266,7 +290,11 @@ void CommandLineInterface::handleBytecode(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleSignatureHashes(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.signatureHashes)
|
||||
return;
|
||||
@ -298,7 +326,11 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleMetadata(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.metadata)
|
||||
return;
|
||||
@ -312,7 +344,11 @@ void CommandLineInterface::handleMetadata(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleABI(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.abi)
|
||||
return;
|
||||
@ -326,7 +362,11 @@ void CommandLineInterface::handleABI(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleStorageLayout(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.storageLayout)
|
||||
return;
|
||||
@ -340,7 +380,11 @@ void CommandLineInterface::handleStorageLayout(string const& _contract)
|
||||
|
||||
void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
bool enabled = false;
|
||||
std::string suffix;
|
||||
@ -383,7 +427,11 @@ void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contra
|
||||
|
||||
void CommandLineInterface::handleGasEstimation(string const& _contract)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
Json::Value estimates = m_compiler->gasEstimates(_contract);
|
||||
sout() << "Gas estimation:" << endl;
|
||||
@ -556,6 +604,25 @@ map<string, Json::Value> CommandLineInterface::parseAstFromInput()
|
||||
return sourceJsons;
|
||||
}
|
||||
|
||||
map<string, Json::Value> CommandLineInterface::parseEvmAssemblyJsonFromInput()
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, "");
|
||||
solAssert(m_fileReader.sourceUnits().size() == 1, "");
|
||||
|
||||
map<string, Json::Value> sourceJsons;
|
||||
|
||||
for (auto const& iter: m_fileReader.sourceUnits())
|
||||
{
|
||||
Json::Value evmAsmJson;
|
||||
astAssert(jsonParseStrict(iter.second, evmAsmJson), "Input file could not be parsed to JSON");
|
||||
astAssert(evmAsmJson.isMember(".code"), "Invalid Format for assembly-JSON: Must have '.code'-object");
|
||||
astAssert(evmAsmJson.isMember(".data"), "Invalid Format for assembly-JSON: Must have '.data'-object");
|
||||
sourceJsons[iter.first] = evmAsmJson;
|
||||
}
|
||||
|
||||
return sourceJsons;
|
||||
}
|
||||
|
||||
void CommandLineInterface::createFile(string const& _fileName, string const& _data)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
@ -659,6 +726,7 @@ void CommandLineInterface::processInput()
|
||||
break;
|
||||
case InputMode::Compiler:
|
||||
case InputMode::CompilerWithASTImport:
|
||||
case InputMode::CompilerWithEvmAssemblyJsonImport:
|
||||
compile();
|
||||
outputCompilationResults();
|
||||
}
|
||||
@ -679,7 +747,11 @@ void CommandLineInterface::printLicense()
|
||||
|
||||
void CommandLineInterface::compile()
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
m_compiler = make_unique<CompilerStack>(m_fileReader.reader());
|
||||
|
||||
@ -726,7 +798,18 @@ void CommandLineInterface::compile()
|
||||
|
||||
m_compiler->setOptimiserSettings(m_options.optimiserSettings());
|
||||
|
||||
if (m_options.input.mode == InputMode::CompilerWithASTImport)
|
||||
if (m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_compiler->importEvmAssemblyJson(parseEvmAssemblyJsonFromInput());
|
||||
}
|
||||
catch (Exception const& _exc)
|
||||
{
|
||||
solThrow(CommandLineExecutionError, "Failed to import Evm Assembly JSON: "s + _exc.what());
|
||||
}
|
||||
}
|
||||
else if (m_options.input.mode == InputMode::CompilerWithASTImport)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -786,7 +869,11 @@ void CommandLineInterface::compile()
|
||||
|
||||
void CommandLineInterface::handleCombinedJSON()
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.combinedJsonRequests.has_value())
|
||||
return;
|
||||
@ -878,7 +965,11 @@ void CommandLineInterface::handleCombinedJSON()
|
||||
|
||||
void CommandLineInterface::handleAst()
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
if (!m_options.compiler.outputs.astCompactJson)
|
||||
return;
|
||||
@ -1120,7 +1211,11 @@ void CommandLineInterface::assemble(yul::YulStack::Language _language, yul::YulS
|
||||
|
||||
void CommandLineInterface::outputCompilationResults()
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
solAssert(
|
||||
m_options.input.mode == InputMode::Compiler ||
|
||||
m_options.input.mode == InputMode::CompilerWithASTImport ||
|
||||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
|
||||
);
|
||||
|
||||
handleCombinedJSON();
|
||||
|
||||
|
@ -115,6 +115,8 @@ private:
|
||||
/// or standard-json output
|
||||
std::map<std::string, Json::Value> parseAstFromInput();
|
||||
|
||||
std::map<std::string, Json::Value> parseEvmAssemblyJsonFromInput();
|
||||
|
||||
/// Create a file in the given directory
|
||||
/// @arg _fileName the name of the file
|
||||
/// @arg _data to be written
|
||||
|
@ -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";
|
||||
@ -138,6 +139,7 @@ static map<InputMode, string> const g_inputModeName = {
|
||||
{InputMode::StandardJson, "standard JSON"},
|
||||
{InputMode::Linker, "linker"},
|
||||
{InputMode::LanguageServer, "language server (LSP)"},
|
||||
{InputMode::CompilerWithEvmAssemblyJsonImport, "assembler (EVM ASM JSON import)"},
|
||||
};
|
||||
|
||||
void CommandLineParser::checkMutuallyExclusive(vector<string> const& _optionNames)
|
||||
@ -277,6 +279,8 @@ OptimiserSettings CommandLineOptions::optimiserSettings() const
|
||||
solAssert(settings.yulOptimiserCleanupSteps == OptimiserSettings::DefaultYulOptimiserCleanupSteps);
|
||||
}
|
||||
|
||||
settings.enabled = optimizer.enabled;
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
@ -461,6 +465,13 @@ void CommandLineParser::parseOutputSelection()
|
||||
CompilerOutputs::componentName(&CompilerOutputs::ewasm),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::ewasmIR),
|
||||
};
|
||||
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)
|
||||
{
|
||||
@ -472,6 +483,8 @@ void CommandLineParser::parseOutputSelection()
|
||||
case InputMode::Compiler:
|
||||
case InputMode::CompilerWithASTImport:
|
||||
return util::contains(compilerModeOutputs, _outputName);
|
||||
case InputMode::CompilerWithEvmAssemblyJsonImport:
|
||||
return util::contains(evmAssemblyJsonImportModeOutputs, _outputName);
|
||||
case InputMode::Assembler:
|
||||
return util::contains(assemblerModeOutputs, _outputName);
|
||||
case InputMode::StandardJson:
|
||||
@ -650,6 +663,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 JSON, assumes input holds the EVM Assembly in JSON format."
|
||||
)
|
||||
(
|
||||
g_strLSP.c_str(),
|
||||
"Switch to language server mode (\"LSP\"). Allows the compiler to be used as an analysis backend "
|
||||
@ -906,6 +923,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::CompilerWithEvmAssemblyJsonImport;
|
||||
else
|
||||
m_options.input.mode = InputMode::Compiler;
|
||||
|
||||
@ -970,9 +989,27 @@ void CommandLineParser::processArgs()
|
||||
for (auto& option: conflictingWithStopAfter)
|
||||
checkMutuallyExclusive({g_strStopAfter, option});
|
||||
|
||||
array<string, 11> const conflictingWithAsmJsonImport{
|
||||
CompilerOutputs::componentName(&CompilerOutputs::ir),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::irOptimized),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::ewasm),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::ewasmIR),
|
||||
g_strGas,
|
||||
CompilerOutputs::componentName(&CompilerOutputs::metadata),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::natspecDev),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::natspecUser),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::signatureHashes),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::storageLayout),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::astCompactJson),
|
||||
};
|
||||
|
||||
for (auto& option: conflictingWithAsmJsonImport)
|
||||
checkMutuallyExclusive({g_strImportEvmAssemblerJson, option});
|
||||
|
||||
if (
|
||||
m_options.input.mode != InputMode::Compiler &&
|
||||
m_options.input.mode != InputMode::CompilerWithASTImport &&
|
||||
m_options.input.mode != InputMode::CompilerWithEvmAssemblyJsonImport &&
|
||||
m_options.input.mode != InputMode::Assembler
|
||||
)
|
||||
{
|
||||
@ -1289,7 +1326,11 @@ 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::CompilerWithEvmAssemblyJsonImport
|
||||
);
|
||||
}
|
||||
|
||||
void CommandLineParser::parseCombinedJsonOption()
|
||||
|
@ -56,7 +56,8 @@ enum class InputMode
|
||||
StandardJson,
|
||||
Linker,
|
||||
Assembler,
|
||||
LanguageServer
|
||||
LanguageServer,
|
||||
CompilerWithEvmAssemblyJsonImport
|
||||
};
|
||||
|
||||
struct CompilerOutputs
|
||||
|
@ -673,6 +673,16 @@ SOLTMPDIR=$(mktemp -d)
|
||||
fi
|
||||
)
|
||||
rm -r "$SOLTMPDIR"
|
||||
printTask "Testing EVM Assembly JSON import/export..."
|
||||
SOLTMPDIR=$(mktemp -d)
|
||||
(
|
||||
cd "$SOLTMPDIR"
|
||||
if ! "$REPO_ROOT/scripts/ImportExportTest.sh" evm-assembly
|
||||
then
|
||||
rm -r "$SOLTMPDIR"
|
||||
fail
|
||||
fi
|
||||
)
|
||||
|
||||
printTask "Testing AST export with stop-after=parsing..."
|
||||
"$REPO_ROOT/test/stopAfterParseTests.sh"
|
||||
|
Loading…
Reference in New Issue
Block a user