Skip to content

Commit 6d62ea0

Browse files
authored
Merge pull request #16012 from ethereum/evmasm-test-case
evmasm test case
2 parents 5baa791 + fe39551 commit 6d62ea0

33 files changed

+980
-13
lines changed

libevmasm/EVMAssemblyStack.cpp

+17-7
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ void EVMAssemblyStack::assemble()
7474
LinkerObject const& EVMAssemblyStack::object(std::string const& _contractName) const
7575
{
7676
solAssert(_contractName == m_name);
77-
return m_object;
77+
return object();
7878
}
7979

8080
LinkerObject const& EVMAssemblyStack::runtimeObject(std::string const& _contractName) const
8181
{
8282
solAssert(_contractName == m_name);
83-
return m_runtimeObject;
83+
return runtimeObject();
8484
}
8585

8686
std::map<std::string, unsigned> EVMAssemblyStack::sourceIndices() const
@@ -95,13 +95,13 @@ std::map<std::string, unsigned> EVMAssemblyStack::sourceIndices() const
9595
std::string const* EVMAssemblyStack::sourceMapping(std::string const& _contractName) const
9696
{
9797
solAssert(_contractName == m_name);
98-
return &m_sourceMapping;
98+
return &sourceMapping();
9999
}
100100

101101
std::string const* EVMAssemblyStack::runtimeSourceMapping(std::string const& _contractName) const
102102
{
103103
solAssert(_contractName == m_name);
104-
return &m_runtimeSourceMapping;
104+
return &runtimeSourceMapping();
105105
}
106106

107107
Json EVMAssemblyStack::ethdebug(std::string const& _contractName) const
@@ -123,20 +123,30 @@ Json EVMAssemblyStack::ethdebug() const
123123
return {};
124124
}
125125

126-
Json EVMAssemblyStack::assemblyJSON(std::string const& _contractName) const
126+
Json EVMAssemblyStack::assemblyJSON() const
127127
{
128-
solAssert(_contractName == m_name);
129128
solAssert(m_evmAssembly);
130129
return m_evmAssembly->assemblyJSON(sourceIndices());
131130
}
132131

133-
std::string EVMAssemblyStack::assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const
132+
Json EVMAssemblyStack::assemblyJSON(std::string const& _contractName) const
134133
{
135134
solAssert(_contractName == m_name);
135+
return assemblyJSON();
136+
}
137+
138+
std::string EVMAssemblyStack::assemblyString(StringMap const& _sourceCodes) const
139+
{
136140
solAssert(m_evmAssembly);
137141
return m_evmAssembly->assemblyString(m_debugInfoSelection, _sourceCodes);
138142
}
139143

144+
std::string EVMAssemblyStack::assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const
145+
{
146+
solAssert(_contractName == m_name);
147+
return assemblyString(_sourceCodes);
148+
}
149+
140150
std::string const EVMAssemblyStack::filesystemFriendlyName(std::string const& _contractName) const
141151
{
142152
solAssert(_contractName == m_name);

libevmasm/EVMAssemblyStack.h

+6
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,26 @@ class EVMAssemblyStack: public AbstractAssemblyStack
5858

5959
std::string const& name() const { return m_name; }
6060

61+
LinkerObject const& object() const { return m_object; }
6162
LinkerObject const& object(std::string const& _contractName) const override;
63+
LinkerObject const& runtimeObject() const { return m_runtimeObject; }
6264
LinkerObject const& runtimeObject(std::string const& _contractName) const override;
6365

6466
std::shared_ptr<evmasm::Assembly> const& evmAssembly() const { return m_evmAssembly; }
6567
std::shared_ptr<evmasm::Assembly> const& evmRuntimeAssembly() const { return m_evmRuntimeAssembly; }
6668

69+
std::string const& sourceMapping() const { return m_sourceMapping; }
6770
std::string const* sourceMapping(std::string const& _contractName) const override;
71+
std::string const& runtimeSourceMapping() const { return m_runtimeSourceMapping; }
6872
std::string const* runtimeSourceMapping(std::string const& _contractName) const override;
6973

7074
Json ethdebug(std::string const& _contractName) const override;
7175
Json ethdebugRuntime(std::string const& _contractName) const override;
7276
Json ethdebug() const override;
7377

78+
Json assemblyJSON() const;
7479
Json assemblyJSON(std::string const& _contractName) const override;
80+
std::string assemblyString(StringMap const& _sourceCodes) const;
7581
std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const override;
7682

7783
std::string const filesystemFriendlyName(std::string const& _contractName) const override;

libevmasm/Instruction.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ using namespace solidity;
2626
using namespace solidity::util;
2727
using namespace solidity::evmasm;
2828

29-
std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
29+
std::map<std::string, Instruction, std::less<>> const solidity::evmasm::c_instructions =
3030
{
3131
{ "STOP", Instruction::STOP },
3232
{ "ADD", Instruction::ADD },

libevmasm/Instruction.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,6 @@ InstructionInfo instructionInfo(Instruction _inst, langutil::EVMVersion _evmVers
322322
bool isValidInstruction(Instruction _inst);
323323

324324
/// Convert from string mnemonic to Instruction type.
325-
extern const std::map<std::string, Instruction> c_instructions;
325+
extern const std::map<std::string, Instruction, std::less<>> c_instructions;
326326

327327
}

test/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ detect_stray_source_files("${libsolutil_sources}" "libsolutil/")
5151

5252
set(libevmasm_sources
5353
libevmasm/Assembler.cpp
54+
libevmasm/EVMAssemblyTest.cpp
55+
libevmasm/EVMAssemblyTest.h
5456
libevmasm/Optimiser.cpp
57+
libevmasm/PlainAssemblyParser.cpp
58+
libevmasm/PlainAssemblyParser.h
5559
)
5660
detect_stray_source_files("${libevmasm_sources}" "libevmasm/")
5761

test/InteractiveTests.h

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
#include <test/libyul/StackShufflingTest.h>
4343
#include <test/libyul/SyntaxTest.h>
4444

45+
#include <test/libevmasm/EVMAssemblyTest.h>
46+
4547
#include <boost/filesystem.hpp>
4648

4749
namespace solidity::frontend::test
@@ -64,6 +66,7 @@ struct Testsuite
6466
Testsuite const g_interactiveTestsuites[] = {
6567
/*
6668
Title Path Subpath SMT NeedsVM Creator function */
69+
{"EVM Assembly", "libevmasm", "evmAssemblyTests", false, false, &evmasm::test::EVMAssemblyTest::create},
6770
{"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create},
6871
{"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create},
6972
{"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create},

test/TestCase.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@ void TestCase::printUpdatedSettings(std::ostream& _stream, std::string const& _l
5151
bool TestCase::isTestFilename(boost::filesystem::path const& _filename)
5252
{
5353
std::string extension = _filename.extension().string();
54-
return (extension == ".sol" || extension == ".yul" || extension == ".stack") &&
55-
!boost::starts_with(_filename.string(), "~") &&
56-
!boost::starts_with(_filename.string(), ".");
54+
// NOTE: .asmjson rather than .json because JSON files that do not represent test cases exist in some test dirs.
55+
return (extension == ".sol" || extension == ".yul" || extension == ".asm" || extension == ".asmjson" || extension == ".stack") &&
56+
!_filename.string().starts_with('~') &&
57+
!_filename.string().starts_with('.');
5758
}
5859

5960
bool TestCase::shouldRun()

test/TestCaseReader.h

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class TestCaseReader
5959
std::size_t lineNumber() const { return m_lineNumber; }
6060
std::map<std::string, std::string> const& settings() const { return m_settings; }
6161
std::ifstream& stream() { return m_fileStream; }
62+
boost::filesystem::path const& fileName() const { return m_fileName; }
6263

6364
std::string simpleExpectations();
6465

test/libevmasm/EVMAssemblyTest.cpp

+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
#include <test/libevmasm/EVMAssemblyTest.h>
19+
20+
#include <test/libevmasm/PlainAssemblyParser.h>
21+
22+
#include <test/Common.h>
23+
24+
#include <libevmasm/Disassemble.h>
25+
#include <libevmasm/EVMAssemblyStack.h>
26+
27+
#include <boost/algorithm/string/predicate.hpp>
28+
#include <boost/algorithm/string/split.hpp>
29+
#include <boost/algorithm/string/trim.hpp>
30+
31+
#include <range/v3/view/map.hpp>
32+
33+
using namespace std::string_literals;
34+
using namespace solidity;
35+
using namespace solidity::test;
36+
using namespace solidity::evmasm;
37+
using namespace solidity::evmasm::test;
38+
using namespace solidity::frontend;
39+
using namespace solidity::frontend::test;
40+
using namespace solidity::langutil;
41+
using namespace solidity::util;
42+
43+
std::vector<std::string> const EVMAssemblyTest::c_outputLabels = {
44+
"InputAssemblyJSON",
45+
"Assembly",
46+
"Bytecode",
47+
"Opcodes",
48+
"SourceMappings",
49+
};
50+
51+
std::unique_ptr<TestCase> EVMAssemblyTest::create(Config const& _config)
52+
{
53+
return std::make_unique<EVMAssemblyTest>(_config.filename);
54+
}
55+
56+
EVMAssemblyTest::EVMAssemblyTest(std::string const& _filename):
57+
EVMVersionRestrictedTestCase(_filename)
58+
{
59+
m_source = m_reader.source();
60+
m_expectation = m_reader.simpleExpectations();
61+
62+
if (_filename.ends_with(".asmjson"))
63+
m_assemblyFormat = AssemblyFormat::JSON;
64+
else if (_filename.ends_with(".asm"))
65+
m_assemblyFormat = AssemblyFormat::Plain;
66+
else
67+
BOOST_THROW_EXCEPTION(std::runtime_error("Not an assembly test: \"" + _filename + "\". Allowed extensions: .asm, .asmjson."));
68+
69+
m_selectedOutputs = m_reader.stringSetting("outputs", "Assembly,Bytecode,Opcodes,SourceMappings");
70+
OptimisationPreset optimizationPreset = m_reader.enumSetting<OptimisationPreset>(
71+
"optimizationPreset",
72+
{
73+
{"none", OptimisationPreset::None},
74+
{"minimal", OptimisationPreset::Minimal},
75+
{"standard", OptimisationPreset::Standard},
76+
{"full", OptimisationPreset::Full},
77+
},
78+
"none"
79+
);
80+
m_optimizerSettings = Assembly::OptimiserSettings::translateSettings(OptimiserSettings::preset(optimizationPreset));
81+
m_optimizerSettings.expectedExecutionsPerDeployment = m_reader.sizetSetting(
82+
"optimizer.expectedExecutionsPerDeployment",
83+
m_optimizerSettings.expectedExecutionsPerDeployment
84+
);
85+
86+
auto const optimizerComponentSetting = [&](std::string const& _component, bool& _setting) {
87+
_setting = m_reader.boolSetting("optimizer." + _component, _setting);
88+
};
89+
optimizerComponentSetting("inliner", m_optimizerSettings.runInliner);
90+
optimizerComponentSetting("jumpdestRemover", m_optimizerSettings.runJumpdestRemover);
91+
optimizerComponentSetting("peephole", m_optimizerSettings.runPeephole);
92+
optimizerComponentSetting("deduplicate", m_optimizerSettings.runDeduplicate);
93+
optimizerComponentSetting("cse", m_optimizerSettings.runCSE);
94+
optimizerComponentSetting("constantOptimizer", m_optimizerSettings.runConstantOptimiser);
95+
96+
// TODO: Enable when assembly import for EOF is implemented.
97+
if (CommonOptions::get().eofVersion().has_value())
98+
m_shouldRun = false;
99+
}
100+
101+
TestCase::TestResult EVMAssemblyTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted)
102+
{
103+
EVMAssemblyStack evmAssemblyStack(
104+
CommonOptions::get().evmVersion(),
105+
CommonOptions::get().eofVersion(),
106+
m_optimizerSettings
107+
);
108+
109+
evmAssemblyStack.selectDebugInfo(DebugInfoSelection::AllExceptExperimental());
110+
111+
std::string assemblyJSON;
112+
switch (m_assemblyFormat)
113+
{
114+
case AssemblyFormat::JSON:
115+
assemblyJSON = m_source;
116+
break;
117+
case AssemblyFormat::Plain:
118+
assemblyJSON = jsonPrint(
119+
PlainAssemblyParser{}.parse(m_reader.fileName().filename().string(), m_source),
120+
{JsonFormat::Pretty, 4}
121+
);
122+
break;
123+
}
124+
125+
try
126+
{
127+
evmAssemblyStack.parseAndAnalyze(m_reader.fileName().filename().string(), assemblyJSON);
128+
}
129+
catch (AssemblyImportException const& _exception)
130+
{
131+
m_obtainedResult = "AssemblyImportException: "s + _exception.what() + "\n";
132+
return checkResult(_stream, _linePrefix, _formatted);
133+
}
134+
135+
try
136+
{
137+
evmAssemblyStack.assemble();
138+
}
139+
catch (Error const& _error)
140+
{
141+
// TODO: EVMAssemblyStack should catch these on its own and provide an error reporter.
142+
soltestAssert(_error.comment(), "Errors must include a message for the user.");
143+
m_obtainedResult = Error::formatErrorType(_error.type()) + ": " + *_error.comment() + "\n";
144+
return checkResult(_stream, _linePrefix, _formatted);
145+
}
146+
soltestAssert(evmAssemblyStack.compilationSuccessful());
147+
148+
auto const produceOutput = [&](std::string const& _output) {
149+
if (_output == "InputAssemblyJSON")
150+
return assemblyJSON;
151+
if (_output == "Assembly")
152+
return evmAssemblyStack.assemblyString({{m_reader.fileName().filename().string(), m_source}});
153+
if (_output == "Bytecode")
154+
return util::toHex(evmAssemblyStack.object().bytecode);
155+
if (_output == "Opcodes")
156+
return disassemble(evmAssemblyStack.object().bytecode, CommonOptions::get().evmVersion());
157+
if (_output == "SourceMappings")
158+
return evmAssemblyStack.sourceMapping();
159+
soltestAssert(false);
160+
unreachable();
161+
};
162+
163+
std::set<std::string> selectedOutputSet;
164+
boost::split(selectedOutputSet, m_selectedOutputs, boost::is_any_of(","));
165+
for (std::string const& output: c_outputLabels)
166+
if (selectedOutputSet.contains(output))
167+
{
168+
if (!m_obtainedResult.empty() && m_obtainedResult.back() != '\n')
169+
m_obtainedResult += "\n";
170+
171+
// Don't trim on the left to avoid stripping indentation.
172+
std::string content = produceOutput(output);
173+
boost::trim_right(content);
174+
std::string separator = (content.empty() ? "" : (output == "Assembly" ? "\n" : " "));
175+
m_obtainedResult += output + ":" + separator + content;
176+
}
177+
if (!m_obtainedResult.empty() && m_obtainedResult.back() != '\n')
178+
m_obtainedResult += "\n";
179+
180+
return checkResult(_stream, _linePrefix, _formatted);
181+
}

0 commit comments

Comments
 (0)