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