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 * @author Christian <c@ethdev.com>
19 * @date 2016
20 * Unit tests for inline assembly.
21 */
22
23 #include <test/Common.h>
24
25 #include <test/libsolidity/ErrorCheck.h>
26
27 #include <libsolidity/ast/AST.h>
28
29 #include <libyul/AssemblyStack.h>
30
31 #include <liblangutil/DebugInfoSelection.h>
32 #include <liblangutil/Exceptions.h>
33 #include <liblangutil/Scanner.h>
34 #include <liblangutil/SourceReferenceFormatter.h>
35
36 #include <libevmasm/Assembly.h>
37
38 #include <boost/algorithm/string/replace.hpp>
39 #include <boost/test/unit_test.hpp>
40
41 #include <memory>
42 #include <optional>
43 #include <string>
44
45 using namespace std;
46 using namespace solidity::langutil;
47 using namespace solidity::yul;
48
49 namespace solidity::frontend::test
50 {
51
52 namespace
53 {
54
parseAndReturnFirstError(string const & _source,bool _assemble=false,bool _allowWarnings=true,AssemblyStack::Language _language=AssemblyStack::Language::Assembly,AssemblyStack::Machine _machine=AssemblyStack::Machine::EVM)55 std::optional<Error> parseAndReturnFirstError(
56 string const& _source,
57 bool _assemble = false,
58 bool _allowWarnings = true,
59 AssemblyStack::Language _language = AssemblyStack::Language::Assembly,
60 AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM
61 )
62 {
63 AssemblyStack stack(
64 solidity::test::CommonOptions::get().evmVersion(),
65 _language,
66 solidity::frontend::OptimiserSettings::none(),
67 DebugInfoSelection::None()
68 );
69 bool success = false;
70 try
71 {
72 success = stack.parseAndAnalyze("", _source);
73 if (success && _assemble)
74 stack.assemble(_machine);
75 }
76 catch (FatalError const&)
77 {
78 BOOST_FAIL("Fatal error leaked.");
79 success = false;
80 }
81 shared_ptr<Error const> error;
82 for (auto const& e: stack.errors())
83 {
84 if (_allowWarnings && e->type() == Error::Type::Warning)
85 continue;
86 if (error)
87 {
88 string errors;
89 for (auto const& err: stack.errors())
90 errors += SourceReferenceFormatter::formatErrorInformation(*err, stack);
91 BOOST_FAIL("Found more than one error:\n" + errors);
92 }
93 error = e;
94 }
95 if (!success)
96 BOOST_REQUIRE(error);
97 if (error)
98 return *error;
99 return {};
100 }
101
successParse(string const & _source,bool _assemble=false,bool _allowWarnings=true,AssemblyStack::Language _language=AssemblyStack::Language::Assembly,AssemblyStack::Machine _machine=AssemblyStack::Machine::EVM)102 bool successParse(
103 string const& _source,
104 bool _assemble = false,
105 bool _allowWarnings = true,
106 AssemblyStack::Language _language = AssemblyStack::Language::Assembly,
107 AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM
108 )
109 {
110 return !parseAndReturnFirstError(_source, _assemble, _allowWarnings, _language, _machine);
111 }
112
successAssemble(string const & _source,bool _allowWarnings=true,AssemblyStack::Language _language=AssemblyStack::Language::Assembly)113 bool successAssemble(string const& _source, bool _allowWarnings = true, AssemblyStack::Language _language = AssemblyStack::Language::Assembly)
114 {
115 return
116 successParse(_source, true, _allowWarnings, _language, AssemblyStack::Machine::EVM);
117 }
118
expectError(std::string const & _source,bool _assemble,bool _allowWarnings=false,AssemblyStack::Language _language=AssemblyStack::Language::Assembly)119 Error expectError(
120 std::string const& _source,
121 bool _assemble,
122 bool _allowWarnings = false,
123 AssemblyStack::Language _language = AssemblyStack::Language::Assembly
124 )
125 {
126
127 auto error = parseAndReturnFirstError(_source, _assemble, _allowWarnings, _language);
128 BOOST_REQUIRE(error);
129 return *error;
130 }
131
parsePrintCompare(string const & _source,bool _canWarn=false)132 void parsePrintCompare(string const& _source, bool _canWarn = false)
133 {
134 AssemblyStack stack(
135 solidity::test::CommonOptions::get().evmVersion(),
136 AssemblyStack::Language::Assembly,
137 OptimiserSettings::none(),
138 DebugInfoSelection::None()
139 );
140 BOOST_REQUIRE(stack.parseAndAnalyze("", _source));
141 if (_canWarn)
142 BOOST_REQUIRE(!Error::containsErrors(stack.errors()));
143 else
144 BOOST_REQUIRE(stack.errors().empty());
145 string expectation = "object \"object\" {\n code " + boost::replace_all_copy(_source, "\n", "\n ") + "\n}\n";
146 BOOST_CHECK_EQUAL(stack.print(), expectation);
147 }
148
149 }
150
151 #define CHECK_ERROR_LANG(text, assemble, typ, substring, warnings, language) \
152 do \
153 { \
154 Error err = expectError((text), (assemble), warnings, (language)); \
155 BOOST_CHECK(err.type() == (Error::Type::typ)); \
156 BOOST_CHECK(searchErrorMessage(err, (substring))); \
157 } while(0)
158
159 #define CHECK_ERROR(text, assemble, typ, substring, warnings) \
160 CHECK_ERROR_LANG(text, assemble, typ, substring, warnings, AssemblyStack::Language::Assembly)
161
162 #define CHECK_PARSE_ERROR(text, type, substring) \
163 CHECK_ERROR(text, false, type, substring, false)
164
165 #define CHECK_PARSE_WARNING(text, type, substring) \
166 CHECK_ERROR(text, false, type, substring, false)
167
168 #define CHECK_ASSEMBLE_ERROR(text, type, substring) \
169 CHECK_ERROR(text, true, type, substring, false)
170
171 #define CHECK_STRICT_ERROR(text, type, substring) \
172 CHECK_ERROR_LANG(text, false, type, substring, false, AssemblyStack::Language::StrictAssembly)
173
174 #define CHECK_STRICT_WARNING(text, type, substring) \
175 CHECK_ERROR(text, false, type, substring, false, AssemblyStack::Language::StrictAssembly)
176
177 #define SUCCESS_STRICT(text) \
178 do { successParse((text), false, false, AssemblyStack::Language::StrictAssembly); } while (false)
179
180
181 BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly)
182
BOOST_AUTO_TEST_SUITE(Printing)183 BOOST_AUTO_TEST_SUITE(Printing) // {{{
184
185 BOOST_AUTO_TEST_CASE(print_smoke)
186 {
187 parsePrintCompare("{ }");
188 }
189
BOOST_AUTO_TEST_CASE(print_instructions)190 BOOST_AUTO_TEST_CASE(print_instructions)
191 {
192 parsePrintCompare("{ pop(7) }");
193 }
194
BOOST_AUTO_TEST_CASE(print_subblock)195 BOOST_AUTO_TEST_CASE(print_subblock)
196 {
197 parsePrintCompare("{ { pop(7) } }");
198 }
199
BOOST_AUTO_TEST_CASE(print_functional)200 BOOST_AUTO_TEST_CASE(print_functional)
201 {
202 parsePrintCompare("{ let x := mul(sload(0x12), 7) }");
203 }
204
BOOST_AUTO_TEST_CASE(print_assignments)205 BOOST_AUTO_TEST_CASE(print_assignments)
206 {
207 parsePrintCompare("{\n let x := mul(2, 3)\n pop(7)\n x := add(1, 2)\n}");
208 }
209
BOOST_AUTO_TEST_CASE(print_multi_assignments)210 BOOST_AUTO_TEST_CASE(print_multi_assignments)
211 {
212 parsePrintCompare("{\n function f() -> x, y\n { }\n let x, y := f()\n}");
213 }
214
BOOST_AUTO_TEST_CASE(print_string_literals)215 BOOST_AUTO_TEST_CASE(print_string_literals)
216 {
217 parsePrintCompare("{ let x := \"\\n'\\xab\\x95\\\"\" }");
218 }
219
BOOST_AUTO_TEST_CASE(print_string_literal_unicode)220 BOOST_AUTO_TEST_CASE(print_string_literal_unicode)
221 {
222 string source = "{ let x := \"\\u1bac\" }";
223 string parsed = "object \"object\" {\n code { let x := \"\\xe1\\xae\\xac\" }\n}\n";
224 AssemblyStack stack(
225 solidity::test::CommonOptions::get().evmVersion(),
226 AssemblyStack::Language::Assembly,
227 OptimiserSettings::none(),
228 DebugInfoSelection::None()
229 );
230 BOOST_REQUIRE(stack.parseAndAnalyze("", source));
231 BOOST_REQUIRE(stack.errors().empty());
232 BOOST_CHECK_EQUAL(stack.print(), parsed);
233
234 string parsedInner = "{ let x := \"\\xe1\\xae\\xac\" }";
235 parsePrintCompare(parsedInner);
236 }
237
BOOST_AUTO_TEST_CASE(print_if)238 BOOST_AUTO_TEST_CASE(print_if)
239 {
240 parsePrintCompare("{ if 2 { pop(mload(0)) } }");
241 }
242
BOOST_AUTO_TEST_CASE(print_switch)243 BOOST_AUTO_TEST_CASE(print_switch)
244 {
245 parsePrintCompare("{\n switch 42\n case 1 { }\n case 2 { }\n default { }\n}");
246 }
247
BOOST_AUTO_TEST_CASE(print_for)248 BOOST_AUTO_TEST_CASE(print_for)
249 {
250 parsePrintCompare("{\n let ret := 5\n for { let i := 1 } lt(i, 15) { i := add(i, 1) }\n { ret := mul(ret, i) }\n}");
251 }
252
BOOST_AUTO_TEST_CASE(function_definitions_multiple_args)253 BOOST_AUTO_TEST_CASE(function_definitions_multiple_args)
254 {
255 parsePrintCompare("{\n function f(a, d)\n { mstore(a, d) }\n function g(a, d) -> x, y\n { }\n}");
256 }
257
BOOST_AUTO_TEST_CASE(function_calls)258 BOOST_AUTO_TEST_CASE(function_calls)
259 {
260 string source = R"({
261 function y()
262 { }
263 function f(a) -> b
264 { }
265 function g(a, b, c)
266 { }
267 g(1, mul(2, address()), f(mul(2, caller())))
268 y()
269 })";
270 boost::replace_all(source, "\t", " ");
271 parsePrintCompare(source);
272 }
273
274 BOOST_AUTO_TEST_SUITE_END()
275 // }}}
276
BOOST_AUTO_TEST_SUITE(Analysis)277 BOOST_AUTO_TEST_SUITE(Analysis) // {{{
278
279 BOOST_AUTO_TEST_CASE(string_literals)
280 {
281 BOOST_CHECK(successAssemble("{ let x := \"12345678901234567890123456789012\" }"));
282 }
283
BOOST_AUTO_TEST_CASE(oversize_string_literals)284 BOOST_AUTO_TEST_CASE(oversize_string_literals)
285 {
286 CHECK_ASSEMBLE_ERROR("{ let x := \"123456789012345678901234567890123\" }", TypeError, "String literal too long");
287 }
288
BOOST_AUTO_TEST_CASE(magic_variables)289 BOOST_AUTO_TEST_CASE(magic_variables)
290 {
291 CHECK_ASSEMBLE_ERROR("{ pop(this) }", DeclarationError, "Identifier \"this\" not found");
292 CHECK_ASSEMBLE_ERROR("{ pop(ecrecover) }", DeclarationError, "Identifier \"ecrecover\" not found");
293 BOOST_CHECK(successAssemble("{ let ecrecover := 1 pop(ecrecover) }"));
294 }
295
BOOST_AUTO_TEST_CASE(stack_variables)296 BOOST_AUTO_TEST_CASE(stack_variables)
297 {
298 BOOST_CHECK(successAssemble("{ let y := 3 { let z := 2 { let x := y } } }"));
299 }
300
BOOST_AUTO_TEST_CASE(designated_invalid_instruction)301 BOOST_AUTO_TEST_CASE(designated_invalid_instruction)
302 {
303 BOOST_CHECK(successAssemble("{ invalid() }"));
304 }
305
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration)306 BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration)
307 {
308 CHECK_ASSEMBLE_ERROR("{ let gas := 1 }", ParserError, "Cannot use builtin");
309 }
310
BOOST_AUTO_TEST_CASE(revert)311 BOOST_AUTO_TEST_CASE(revert)
312 {
313 BOOST_CHECK(successAssemble("{ revert(0, 0) }"));
314 }
315
BOOST_AUTO_TEST_CASE(large_constant)316 BOOST_AUTO_TEST_CASE(large_constant)
317 {
318 auto source = R"({
319 switch mul(1, 2)
320 case 0x0000000000000000000000000000000000000000000000000000000026121ff0 {
321 }
322 })";
323 BOOST_CHECK(successAssemble(source));
324 }
325
BOOST_AUTO_TEST_CASE(keccak256)326 BOOST_AUTO_TEST_CASE(keccak256)
327 {
328 BOOST_CHECK(successAssemble("{ pop(keccak256(0, 0)) }"));
329 }
330
BOOST_AUTO_TEST_CASE(returndatasize)331 BOOST_AUTO_TEST_CASE(returndatasize)
332 {
333 if (!solidity::test::CommonOptions::get().evmVersion().supportsReturndata())
334 return;
335 BOOST_CHECK(successAssemble("{ let r := returndatasize() }"));
336 }
337
BOOST_AUTO_TEST_CASE(returndatacopy)338 BOOST_AUTO_TEST_CASE(returndatacopy)
339 {
340 if (!solidity::test::CommonOptions::get().evmVersion().supportsReturndata())
341 return;
342 BOOST_CHECK(successAssemble("{ returndatacopy(0, 32, 64) }"));
343 }
344
BOOST_AUTO_TEST_CASE(staticcall)345 BOOST_AUTO_TEST_CASE(staticcall)
346 {
347 if (!solidity::test::CommonOptions::get().evmVersion().hasStaticCall())
348 return;
349 BOOST_CHECK(successAssemble("{ pop(staticcall(10000, 0x123, 64, 0x10, 128, 0x10)) }"));
350 }
351
BOOST_AUTO_TEST_CASE(create2)352 BOOST_AUTO_TEST_CASE(create2)
353 {
354 if (!solidity::test::CommonOptions::get().evmVersion().hasCreate2())
355 return;
356 BOOST_CHECK(successAssemble("{ pop(create2(10, 0x123, 32, 64)) }"));
357 }
358
BOOST_AUTO_TEST_CASE(shift)359 BOOST_AUTO_TEST_CASE(shift)
360 {
361 if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting())
362 return;
363 BOOST_CHECK(successAssemble("{ pop(shl(10, 32)) }"));
364 BOOST_CHECK(successAssemble("{ pop(shr(10, 32)) }"));
365 BOOST_CHECK(successAssemble("{ pop(sar(10, 32)) }"));
366 }
367
BOOST_AUTO_TEST_CASE(shift_constantinople_warning)368 BOOST_AUTO_TEST_CASE(shift_constantinople_warning)
369 {
370 if (solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting())
371 return;
372 CHECK_PARSE_WARNING("{ shl(10, 32) }", TypeError, "The \"shl\" instruction is only available for Constantinople-compatible VMs");
373 CHECK_PARSE_WARNING("{ shr(10, 32) }", TypeError, "The \"shr\" instruction is only available for Constantinople-compatible VMs");
374 CHECK_PARSE_WARNING("{ sar(10, 32) }", TypeError, "The \"sar\" instruction is only available for Constantinople-compatible VMs");
375 }
376
377 BOOST_AUTO_TEST_SUITE_END() // }}}
378
379 BOOST_AUTO_TEST_SUITE_END()
380
381 } // end namespaces
382