Skip to content

Commit 1f645f1

Browse files
committed
[libsolidity] Add support to import evm assembly json.
1 parent d534134 commit 1f645f1

File tree

3 files changed

+113
-53
lines changed

3 files changed

+113
-53
lines changed

libsolidity/interface/CompilerStack.cpp

Lines changed: 104 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ void CompilerStack::setModelCheckerSettings(ModelCheckerSettings _settings)
232232
m_modelCheckerSettings = _settings;
233233
}
234234

235-
void CompilerStack::setLibraries(std::map<std::string, util::h160> const& _libraries)
235+
void CompilerStack::setLibraries(map<string, util::h160> const& _libraries)
236236
{
237237
if (m_stackState >= ParsedAndImported)
238238
solThrow(CompilerError, "Must set libraries before parsing.");
@@ -242,6 +242,7 @@ void CompilerStack::setLibraries(std::map<std::string, util::h160> const& _libra
242242
void CompilerStack::setOptimiserSettings(bool _optimize, size_t _runs)
243243
{
244244
OptimiserSettings settings = _optimize ? OptimiserSettings::standard() : OptimiserSettings::minimal();
245+
settings.enabled = _optimize;
245246
settings.expectedExecutionsPerDeployment = _runs;
246247
setOptimiserSettings(std::move(settings));
247248
}
@@ -416,6 +417,29 @@ void CompilerStack::importASTs(map<string, Json::Value> const& _sources)
416417
storeContractDefinitions();
417418
}
418419

420+
void CompilerStack::importEvmAssemblyJson(map<string, Json::Value> const& _sources)
421+
{
422+
solAssert(_sources.size() == 1, "");
423+
solAssert(m_sourceJsons.empty(), "");
424+
solAssert(m_sourceOrder.empty(), "");
425+
if (m_stackState != Empty)
426+
solThrow(CompilerError, "Must call importEvmAssemblyJson only before the SourcesSet state.");
427+
428+
m_sourceJsons = _sources;
429+
Json::Value jsonValue = _sources.begin()->second;
430+
if (jsonValue.isMember("sourceList"))
431+
for (auto const& item: jsonValue["sourceList"])
432+
{
433+
Source source;
434+
source.charStream = make_shared<CharStream>(item.asString(), "");
435+
m_sources.emplace(make_pair(item.asString(), source));
436+
m_sourceOrder.push_back(&m_sources[item.asString()]);
437+
}
438+
m_sourceJsons[_sources.begin()->first] = std::move(jsonValue);
439+
m_compilationSourceType = CompilationSourceType::EvmAssemblyJson;
440+
m_stackState = SourcesSet;
441+
}
442+
419443
bool CompilerStack::analyze()
420444
{
421445
if (m_stackState != ParsedAndImported || m_stackState >= AnalysisPerformed)
@@ -605,6 +629,9 @@ bool CompilerStack::parseAndAnalyze(State _stopAfter)
605629
{
606630
m_stopAfter = _stopAfter;
607631

632+
if (m_compilationSourceType == CompilationSourceType::EvmAssemblyJson)
633+
return true;
634+
608635
bool success = parse();
609636
if (m_stackState >= m_stopAfter)
610637
return success;
@@ -654,53 +681,73 @@ bool CompilerStack::compile(State _stopAfter)
654681
// Only compile contracts individually which have been requested.
655682
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
656683

657-
for (Source const* source: m_sourceOrder)
658-
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
659-
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
660-
if (isRequestedContract(*contract))
661-
{
662-
try
684+
if (m_compilationSourceType == CompilationSourceType::EvmAssemblyJson)
685+
{
686+
solAssert(m_sourceJsons.size() == 1);
687+
688+
string const evmSourceName = m_sourceJsons.begin()->first;
689+
Json::Value const evmJson = m_sourceJsons.begin()->second;
690+
691+
evmasm::Assembly::OptimiserSettings optimiserSettings =
692+
evmasm::Assembly::OptimiserSettings::translateSettings(m_optimiserSettings, m_evmVersion);
693+
694+
m_contracts[evmSourceName].evmAssembly = evmasm::Assembly::loadFromAssemblyJSON(m_sourceJsons[evmSourceName]);
695+
if (m_optimiserSettings.enabled)
696+
m_contracts[evmSourceName].evmAssembly->optimise(optimiserSettings);
697+
m_contracts[evmSourceName].object = m_contracts[evmSourceName].evmAssembly->assemble();
698+
699+
m_contracts[evmSourceName].evmRuntimeAssembly = make_shared<evmasm::Assembly>(m_contracts[evmSourceName].evmAssembly->sub(0));
700+
solAssert(m_contracts[evmSourceName].evmRuntimeAssembly->isCreation() == false);
701+
if (m_optimiserSettings.enabled)
702+
m_contracts[evmSourceName].evmRuntimeAssembly->optimise(optimiserSettings);
703+
m_contracts[evmSourceName].runtimeObject = m_contracts[evmSourceName].evmRuntimeAssembly->assemble();
704+
}
705+
else
706+
{
707+
for (Source const* source: m_sourceOrder)
708+
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
709+
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
710+
if (isRequestedContract(*contract))
663711
{
664-
if (m_viaIR || m_generateIR || m_generateEwasm)
665-
generateIR(*contract);
666-
if (m_generateEvmBytecode)
712+
try
667713
{
668-
if (m_viaIR)
669-
generateEVMFromIR(*contract);
670-
else
671-
compileContract(*contract, otherCompilers);
714+
if (m_viaIR || m_generateIR || m_generateEwasm)
715+
generateIR(*contract);
716+
if (m_generateEvmBytecode)
717+
{
718+
if (m_viaIR)
719+
generateEVMFromIR(*contract);
720+
else
721+
compileContract(*contract, otherCompilers);
722+
}
723+
if (m_generateEwasm)
724+
generateEwasm(*contract);
672725
}
673-
if (m_generateEwasm)
674-
generateEwasm(*contract);
675-
}
676-
catch (Error const& _error)
677-
{
678-
if (_error.type() != Error::Type::CodeGenerationError)
679-
throw;
680-
m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what());
681-
return false;
682-
}
683-
catch (UnimplementedFeatureError const& _unimplementedError)
684-
{
685-
if (
686-
SourceLocation const* sourceLocation =
687-
boost::get_error_info<langutil::errinfo_sourceLocation>(_unimplementedError)
688-
)
726+
catch (Error const& _error)
689727
{
690-
string const* comment = _unimplementedError.comment();
691-
m_errorReporter.error(
692-
1834_error,
693-
Error::Type::CodeGenerationError,
694-
*sourceLocation,
695-
"Unimplemented feature error" +
696-
((comment && !comment->empty()) ? ": " + *comment : string{}) +
697-
" in " +
698-
_unimplementedError.lineInfo()
699-
);
728+
if (_error.type() != Error::Type::CodeGenerationError)
729+
throw;
730+
m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what());
700731
return false;
701732
}
702-
else
703-
throw;
733+
catch (UnimplementedFeatureError const& _unimplementedError)
734+
{
735+
if (SourceLocation const* sourceLocation
736+
= boost::get_error_info<langutil::errinfo_sourceLocation>(_unimplementedError))
737+
{
738+
string const* comment = _unimplementedError.comment();
739+
m_errorReporter.error(
740+
1834_error,
741+
Error::Type::CodeGenerationError,
742+
*sourceLocation,
743+
"Unimplemented feature error"
744+
+ ((comment && !comment->empty()) ? ": " + *comment : string{}) + " in "
745+
+ _unimplementedError.lineInfo());
746+
return false;
747+
}
748+
else
749+
throw;
750+
}
704751
}
705752
}
706753
m_stackState = CompilationSuccessful;
@@ -795,7 +842,6 @@ Json::Value CompilerStack::generatedSources(string const& _contractName, bool _r
795842
sources[0]["id"] = sourceIndex;
796843
sources[0]["language"] = "Yul";
797844
sources[0]["contents"] = std::move(source);
798-
799845
}
800846
}
801847
return sources;
@@ -832,7 +878,7 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c
832878
return c.runtimeSourceMapping ? &*c.runtimeSourceMapping : nullptr;
833879
}
834880

835-
std::string const CompilerStack::filesystemFriendlyName(string const& _contractName) const
881+
string const CompilerStack::filesystemFriendlyName(string const& _contractName) const
836882
{
837883
if (m_stackState < AnalysisPerformed)
838884
solThrow(CompilerError, "No compiled contracts found.");
@@ -846,7 +892,7 @@ std::string const CompilerStack::filesystemFriendlyName(string const& _contractN
846892
contract.second.contract != matchContract.contract)
847893
{
848894
// If it does, then return its fully-qualified name, made fs-friendly
849-
std::string friendlyName = boost::algorithm::replace_all_copy(_contractName, "/", "_");
895+
string friendlyName = boost::algorithm::replace_all_copy(_contractName, "/", "_");
850896
boost::algorithm::replace_all(friendlyName, ":", "_");
851897
boost::algorithm::replace_all(friendlyName, ".", "_");
852898
return friendlyName;
@@ -943,9 +989,10 @@ map<string, unsigned> CompilerStack::sourceIndices() const
943989
map<string, unsigned> indices;
944990
unsigned index = 0;
945991
for (auto const& s: m_sources)
946-
indices[s.first] = index++;
947-
solAssert(!indices.count(CompilerContext::yulUtilityFileName()), "");
948-
indices[CompilerContext::yulUtilityFileName()] = index++;
992+
if (s.first != CompilerContext::yulUtilityFileName())
993+
indices[s.first] = index++;
994+
if (indices.find(CompilerContext::yulUtilityFileName()) == indices.end())
995+
indices[CompilerContext::yulUtilityFileName()] = index++;
949996
return indices;
950997
}
951998

@@ -1098,7 +1145,7 @@ ContractDefinition const& CompilerStack::contractDefinition(string const& _contr
10981145
}
10991146

11001147
size_t CompilerStack::functionEntryPoint(
1101-
std::string const& _contractName,
1148+
string const& _contractName,
11021149
FunctionDefinition const& _function
11031150
) const
11041151
{
@@ -1240,8 +1287,8 @@ bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& featu
12401287

12411288
void CompilerStack::assemble(
12421289
ContractDefinition const& _contract,
1243-
std::shared_ptr<evmasm::Assembly> _assembly,
1244-
std::shared_ptr<evmasm::Assembly> _runtimeAssembly
1290+
shared_ptr<evmasm::Assembly> _assembly,
1291+
shared_ptr<evmasm::Assembly> _runtimeAssembly
12451292
)
12461293
{
12471294
solAssert(m_stackState >= AnalysisPerformed, "");
@@ -1492,6 +1539,11 @@ string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) con
14921539
case CompilationSourceType::SolidityAST:
14931540
sourceType = "SolidityAST";
14941541
break;
1542+
case CompilationSourceType::EvmAssemblyJson:
1543+
sourceType = "EvmAssemblyJson";
1544+
break;
1545+
default:
1546+
solAssert(false);
14951547
}
14961548
meta["language"] = sourceType;
14971549
meta["compiler"]["version"] = VersionStringStrict;
@@ -1523,7 +1575,7 @@ string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) con
15231575
}
15241576

15251577
static_assert(sizeof(m_optimiserSettings.expectedExecutionsPerDeployment) <= sizeof(Json::LargestUInt), "Invalid word size.");
1526-
solAssert(static_cast<Json::LargestUInt>(m_optimiserSettings.expectedExecutionsPerDeployment) < std::numeric_limits<Json::LargestUInt>::max(), "");
1578+
solAssert(static_cast<Json::LargestUInt>(m_optimiserSettings.expectedExecutionsPerDeployment) < numeric_limits<Json::LargestUInt>::max(), "");
15271579
meta["settings"]["optimizer"]["runs"] = Json::Value(Json::LargestUInt(m_optimiserSettings.expectedExecutionsPerDeployment));
15281580

15291581
/// Backwards compatibility: If set to one of the default settings, do not provide details.

libsolidity/interface/CompilerStack.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ class CompilerStack: public langutil::CharStreamProvider
121121
/// Regular compilation from Solidity source files.
122122
Solidity,
123123
/// Compilation from an imported Solidity AST.
124-
SolidityAST
124+
SolidityAST,
125+
/// Compilation from an imported EVM Assembly JSON
126+
EvmAssemblyJson
125127
};
126128

127129
/// Creates a new compiler stack.
@@ -229,6 +231,10 @@ class CompilerStack: public langutil::CharStreamProvider
229231
/// Will throw errors if the import fails
230232
void importASTs(std::map<std::string, Json::Value> const& _sources);
231233

234+
/// Imports given Evm Assembly Json. Leads to the same internal state as parse().
235+
/// Will throw errors if the import fails
236+
void importEvmAssemblyJson(std::map<std::string, Json::Value> const& _sources);
237+
232238
/// Performs the analysis steps (imports, scopesetting, syntaxCheck, referenceResolving,
233239
/// typechecking, staticAnalysis) on previously parsed sources.
234240
/// @returns false on error.

libsolidity/interface/OptimiserSettings.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ struct OptimiserSettings
155155
/// This specifies an estimate on how often each opcode in this assembly will be executed,
156156
/// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage.
157157
size_t expectedExecutionsPerDeployment = 200;
158+
/// Flag reflecting whether optimizer is enabled.
159+
bool enabled = false;
158160
};
159161

160162
}

0 commit comments

Comments
 (0)