/src/solidity/test/tools/yulInterpreter/Interpreter.cpp
Line | Count | Source |
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 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | /** |
19 | | * Yul interpreter. |
20 | | */ |
21 | | |
22 | | #include <test/tools/yulInterpreter/Interpreter.h> |
23 | | |
24 | | #include <test/tools/yulInterpreter/EVMInstructionInterpreter.h> |
25 | | |
26 | | #include <libyul/AST.h> |
27 | | #include <libyul/Dialect.h> |
28 | | #include <libyul/Utilities.h> |
29 | | #include <libyul/backends/evm/EVMDialect.h> |
30 | | |
31 | | #include <liblangutil/Exceptions.h> |
32 | | |
33 | | #include <libsolutil/FixedHash.h> |
34 | | |
35 | | #include <range/v3/view/reverse.hpp> |
36 | | |
37 | | #include <ostream> |
38 | | #include <variant> |
39 | | |
40 | | using namespace solidity; |
41 | | using namespace solidity::yul; |
42 | | using namespace solidity::yul::test; |
43 | | |
44 | | using solidity::util::h256; |
45 | | |
46 | | void InterpreterState::dumpStorage(std::ostream& _out) const |
47 | 99.5k | { |
48 | 99.5k | for (auto const& [slot, value]: storage) |
49 | 149k | if (value != h256{}) |
50 | 85.8k | _out << " " << slot.hex() << ": " << value.hex() << std::endl; |
51 | 99.5k | } |
52 | | |
53 | | void InterpreterState::dumpTransientStorage(std::ostream& _out) const |
54 | 99.5k | { |
55 | 99.5k | for (auto const& [slot, value]: transientStorage) |
56 | 17.4k | if (value != h256{}) |
57 | 6.53k | _out << " " << slot.hex() << ": " << value.hex() << std::endl; |
58 | 99.5k | } |
59 | | |
60 | | void InterpreterState::dumpTraceAndState(std::ostream& _out, bool _disableMemoryTrace) const |
61 | 99.5k | { |
62 | 99.5k | _out << "Trace:" << std::endl; |
63 | 99.5k | for (auto const& line: trace) |
64 | 80.5k | _out << " " << line << std::endl; |
65 | 99.5k | if (!_disableMemoryTrace) |
66 | 0 | { |
67 | 0 | _out << "Memory dump:\n"; |
68 | 0 | std::map<u256, u256> words; |
69 | 0 | for (auto const& [offset, value]: memory) |
70 | 0 | words[(offset / 0x20) * 0x20] |= u256(uint32_t(value)) << (256 - 8 - 8 * static_cast<size_t>(offset % 0x20)); |
71 | 0 | for (auto const& [offset, value]: words) |
72 | 0 | if (value != 0) |
73 | 0 | _out << " " << std::uppercase << std::hex << std::setw(4) << offset << ": " << h256(value).hex() << std::endl; |
74 | 0 | } |
75 | 99.5k | _out << "Storage dump:" << std::endl; |
76 | 99.5k | dumpStorage(_out); |
77 | | |
78 | 99.5k | _out << "Transient storage dump:" << std::endl; |
79 | 99.5k | dumpTransientStorage(_out); |
80 | | |
81 | 99.5k | if (!calldata.empty()) |
82 | 99.5k | { |
83 | 99.5k | _out << "Calldata dump:"; |
84 | | |
85 | 6.47M | for (size_t offset = 0; offset < calldata.size(); ++offset) |
86 | 6.37M | if (calldata[offset] != 0) |
87 | 6.37M | { |
88 | 6.37M | if (offset % 32 == 0) |
89 | 199k | _out << |
90 | 199k | std::endl << |
91 | 199k | " " << |
92 | 199k | std::uppercase << |
93 | 199k | std::hex << |
94 | 199k | std::setfill(' ') << |
95 | 199k | std::setw(4) << |
96 | 199k | offset << |
97 | 199k | ": "; |
98 | | |
99 | 6.37M | _out << |
100 | 6.37M | std::hex << |
101 | 6.37M | std::setw(2) << |
102 | 6.37M | std::setfill('0') << |
103 | 6.37M | static_cast<int>(calldata[offset]); |
104 | 6.37M | } |
105 | | |
106 | 99.5k | _out << std::endl; |
107 | 99.5k | } |
108 | 99.5k | } |
109 | | |
110 | | void Interpreter::run( |
111 | | InterpreterState& _state, |
112 | | AST const& _ast, |
113 | | bool _disableExternalCalls, |
114 | | bool _disableMemoryTrace |
115 | | ) |
116 | 99.5k | { |
117 | 99.5k | Scope scope; |
118 | 99.5k | Interpreter{_state, _ast.dialect(), scope, _disableExternalCalls, _disableMemoryTrace}(_ast.root()); |
119 | 99.5k | } |
120 | | |
121 | | void Interpreter::operator()(ExpressionStatement const& _expressionStatement) |
122 | 729k | { |
123 | 729k | evaluateMulti(_expressionStatement.expression); |
124 | 729k | } |
125 | | |
126 | | void Interpreter::operator()(Assignment const& _assignment) |
127 | 212k | { |
128 | 212k | solAssert(_assignment.value, ""); |
129 | 212k | std::vector<u256> values = evaluateMulti(*_assignment.value); |
130 | 212k | solAssert(values.size() == _assignment.variableNames.size(), ""); |
131 | 421k | for (size_t i = 0; i < values.size(); ++i) |
132 | 209k | { |
133 | 209k | YulName varName = _assignment.variableNames.at(i).name; |
134 | 209k | solAssert(m_variables.count(varName), ""); |
135 | 209k | m_variables[varName] = values.at(i); |
136 | 209k | } |
137 | 212k | } |
138 | | |
139 | | void Interpreter::operator()(VariableDeclaration const& _declaration) |
140 | 341k | { |
141 | 341k | std::vector<u256> values(_declaration.variables.size(), 0); |
142 | 341k | if (_declaration.value) |
143 | 294k | values = evaluateMulti(*_declaration.value); |
144 | | |
145 | 341k | solAssert(values.size() == _declaration.variables.size(), ""); |
146 | 778k | for (size_t i = 0; i < values.size(); ++i) |
147 | 436k | { |
148 | 436k | YulName varName = _declaration.variables.at(i).name; |
149 | 436k | solAssert(!m_variables.count(varName), ""); |
150 | 436k | m_variables[varName] = values.at(i); |
151 | 436k | m_scope->names.emplace(varName, nullptr); |
152 | 436k | } |
153 | 341k | } |
154 | | |
155 | | void Interpreter::operator()(If const& _if) |
156 | 69.1k | { |
157 | 69.1k | solAssert(_if.condition, ""); |
158 | 69.1k | if (evaluate(*_if.condition) != 0) |
159 | 56.7k | (*this)(_if.body); |
160 | 69.1k | } |
161 | | |
162 | | void Interpreter::operator()(Switch const& _switch) |
163 | 63.3k | { |
164 | 63.3k | solAssert(_switch.expression, ""); |
165 | 63.3k | u256 val = evaluate(*_switch.expression); |
166 | 63.3k | solAssert(!_switch.cases.empty(), ""); |
167 | 63.3k | for (auto const& c: _switch.cases) |
168 | | // Default case has to be last. |
169 | 140k | if (!c.value || evaluate(*c.value) == val) |
170 | 59.4k | { |
171 | 59.4k | (*this)(c.body); |
172 | 59.4k | break; |
173 | 59.4k | } |
174 | 63.3k | } |
175 | | |
176 | | void Interpreter::operator()(FunctionDefinition const&) |
177 | 100k | { |
178 | 100k | } |
179 | | |
180 | | void Interpreter::operator()(ForLoop const& _forLoop) |
181 | 106k | { |
182 | 106k | solAssert(_forLoop.condition, ""); |
183 | | |
184 | 106k | enterScope(_forLoop.pre); |
185 | 106k | ScopeGuard g([this]{ leaveScope(); }); |
186 | | |
187 | 106k | for (auto const& statement: _forLoop.pre.statements) |
188 | 62.9k | { |
189 | 62.9k | visit(statement); |
190 | 62.9k | if (m_state.controlFlowState == ControlFlowState::Leave) |
191 | 582 | return; |
192 | 62.9k | } |
193 | 379k | while (evaluate(*_forLoop.condition) != 0) |
194 | 289k | { |
195 | | // Increment step for each loop iteration for loops with |
196 | | // an empty body and post blocks to prevent a deadlock. |
197 | 289k | if (_forLoop.body.statements.size() == 0 && _forLoop.post.statements.size() == 0) |
198 | 12.6k | incrementStep(); |
199 | | |
200 | 289k | m_state.controlFlowState = ControlFlowState::Default; |
201 | 289k | (*this)(_forLoop.body); |
202 | 289k | if (m_state.controlFlowState == ControlFlowState::Break || m_state.controlFlowState == ControlFlowState::Leave) |
203 | 11.4k | break; |
204 | | |
205 | 278k | m_state.controlFlowState = ControlFlowState::Default; |
206 | 278k | (*this)(_forLoop.post); |
207 | 278k | if (m_state.controlFlowState == ControlFlowState::Leave) |
208 | 4.43k | break; |
209 | 278k | } |
210 | 105k | if (m_state.controlFlowState != ControlFlowState::Leave) |
211 | 86.4k | m_state.controlFlowState = ControlFlowState::Default; |
212 | 105k | } |
213 | | |
214 | | void Interpreter::operator()(Break const&) |
215 | 7.55k | { |
216 | 7.55k | m_state.controlFlowState = ControlFlowState::Break; |
217 | 7.55k | } |
218 | | |
219 | | void Interpreter::operator()(Continue const&) |
220 | 33.9k | { |
221 | 33.9k | m_state.controlFlowState = ControlFlowState::Continue; |
222 | 33.9k | } |
223 | | |
224 | | void Interpreter::operator()(Leave const&) |
225 | 34.5k | { |
226 | 34.5k | m_state.controlFlowState = ControlFlowState::Leave; |
227 | 34.5k | } |
228 | | |
229 | | void Interpreter::operator()(Block const& _block) |
230 | 1.18M | { |
231 | 1.18M | enterScope(_block); |
232 | | // Register functions. |
233 | 1.18M | for (auto const& statement: _block.statements) |
234 | 2.30M | if (std::holds_alternative<FunctionDefinition>(statement)) |
235 | 129k | { |
236 | 129k | FunctionDefinition const& funDef = std::get<FunctionDefinition>(statement); |
237 | 129k | m_scope->names.emplace(funDef.name, &funDef); |
238 | 129k | } |
239 | | |
240 | 1.18M | for (auto const& statement: _block.statements) |
241 | 1.80M | { |
242 | 1.80M | incrementStep(); |
243 | 1.80M | visit(statement); |
244 | 1.80M | if (m_state.controlFlowState != ControlFlowState::Default) |
245 | 104k | break; |
246 | 1.80M | } |
247 | | |
248 | 1.18M | leaveScope(); |
249 | 1.18M | } |
250 | | |
251 | | u256 Interpreter::evaluate(Expression const& _expression) |
252 | 585k | { |
253 | 585k | ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableExternalCalls, m_disableMemoryTrace); |
254 | 585k | ev.visit(_expression); |
255 | 585k | return ev.value(); |
256 | 585k | } |
257 | | |
258 | | std::vector<u256> Interpreter::evaluateMulti(Expression const& _expression) |
259 | 1.23M | { |
260 | 1.23M | ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableExternalCalls, m_disableMemoryTrace); |
261 | 1.23M | ev.visit(_expression); |
262 | 1.23M | return ev.values(); |
263 | 1.23M | } |
264 | | |
265 | | void Interpreter::enterScope(Block const& _block) |
266 | 1.29M | { |
267 | 1.29M | if (!m_scope->subScopes.count(&_block)) |
268 | 611k | m_scope->subScopes[&_block] = std::make_unique<Scope>(Scope{ |
269 | 611k | {}, |
270 | 611k | {}, |
271 | 611k | m_scope |
272 | 611k | }); |
273 | 1.29M | m_scope = m_scope->subScopes[&_block].get(); |
274 | 1.29M | } |
275 | | |
276 | | void Interpreter::leaveScope() |
277 | 1.16M | { |
278 | 1.16M | for (auto const& [var, funDeclaration]: m_scope->names) |
279 | 483k | if (!funDeclaration) |
280 | 397k | m_variables.erase(var); |
281 | 1.16M | m_scope = m_scope->parent; |
282 | 1.16M | yulAssert(m_scope, ""); |
283 | 1.16M | } |
284 | | |
285 | | void Interpreter::incrementStep() |
286 | 1.81M | { |
287 | 1.81M | m_state.numSteps++; |
288 | 1.81M | if (m_state.maxSteps > 0 && m_state.numSteps >= m_state.maxSteps) |
289 | 3.80k | { |
290 | 3.80k | m_state.trace.emplace_back("Interpreter execution step limit reached."); |
291 | 3.80k | BOOST_THROW_EXCEPTION(StepLimitReached()); |
292 | 3.80k | } |
293 | 1.81M | } |
294 | | |
295 | | void ExpressionEvaluator::operator()(Literal const& _literal) |
296 | 4.72M | { |
297 | 4.72M | incrementStep(); |
298 | 4.72M | setValue(_literal.value.value()); |
299 | 4.72M | } |
300 | | |
301 | | void ExpressionEvaluator::operator()(Identifier const& _identifier) |
302 | 907k | { |
303 | 907k | solAssert(m_variables.count(_identifier.name), ""); |
304 | 907k | incrementStep(); |
305 | 907k | setValue(m_variables.at(_identifier.name)); |
306 | 907k | } |
307 | | |
308 | | void ExpressionEvaluator::operator()(FunctionCall const& _funCall) |
309 | 4.21M | { |
310 | 4.21M | std::vector<std::optional<LiteralKind>> const* literalArguments = nullptr; |
311 | 4.21M | if (BuiltinFunction const* builtin = resolveBuiltinFunction(_funCall.functionName, m_dialect)) |
312 | 3.96M | if (!builtin->literalArguments.empty()) |
313 | 75.1k | literalArguments = &builtin->literalArguments; |
314 | 4.21M | evaluateArgs(_funCall.arguments, literalArguments); |
315 | | |
316 | 4.21M | if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&m_dialect)) |
317 | 4.12M | { |
318 | 4.12M | if (BuiltinFunctionForEVM const* fun = resolveBuiltinFunctionForEVM(_funCall.functionName, *dialect)) |
319 | 3.87M | { |
320 | 3.87M | EVMInstructionInterpreter interpreter(dialect->evmVersion(), m_state, m_disableMemoryTrace); |
321 | | |
322 | 3.87M | u256 const value = interpreter.evalBuiltin(*fun, _funCall.arguments, values()); |
323 | | |
324 | 3.87M | if ( |
325 | 3.87M | !m_disableExternalCalls && |
326 | 0 | fun->instruction && |
327 | 0 | evmasm::isCallInstruction(*fun->instruction) |
328 | 3.87M | ) |
329 | 0 | runExternalCall(*fun->instruction); |
330 | | |
331 | 3.87M | setValue(value); |
332 | 3.87M | return; |
333 | 3.87M | } |
334 | 4.12M | } |
335 | | |
336 | 334k | yulAssert(!isBuiltinFunctionCall(_funCall)); |
337 | 334k | Scope* scope = &m_scope; |
338 | 4.37M | for (; scope; scope = scope->parent) |
339 | 4.28M | if (scope->names.count(std::get<Identifier>(_funCall.functionName).name)) |
340 | 242k | break; |
341 | 334k | yulAssert(scope, ""); |
342 | | |
343 | 334k | FunctionDefinition const* fun = scope->names.at(std::get<Identifier>(_funCall.functionName).name); |
344 | 334k | yulAssert(fun, "Function not found."); |
345 | 334k | yulAssert(m_values.size() == fun->parameters.size(), ""); |
346 | 334k | std::map<YulName, u256> variables; |
347 | 849k | for (size_t i = 0; i < fun->parameters.size(); ++i) |
348 | 515k | variables[fun->parameters.at(i).name] = m_values.at(i); |
349 | 657k | for (size_t i = 0; i < fun->returnVariables.size(); ++i) |
350 | 323k | variables[fun->returnVariables.at(i).name] = 0; |
351 | | |
352 | 334k | m_state.controlFlowState = ControlFlowState::Default; |
353 | 334k | std::unique_ptr<Interpreter> interpreter = makeInterpreterCopy(std::move(variables)); |
354 | 334k | (*interpreter)(fun->body); |
355 | 334k | m_state.controlFlowState = ControlFlowState::Default; |
356 | | |
357 | 334k | m_values.clear(); |
358 | 334k | for (auto const& retVar: fun->returnVariables) |
359 | 143k | m_values.emplace_back(interpreter->valueOfVariable(retVar.name)); |
360 | 334k | } |
361 | | |
362 | | u256 ExpressionEvaluator::value() const |
363 | 8.58M | { |
364 | 8.58M | solAssert(m_values.size() == 1, ""); |
365 | 8.58M | return m_values.front(); |
366 | 8.58M | } |
367 | | |
368 | | void ExpressionEvaluator::setValue(u256 _value) |
369 | 9.50M | { |
370 | 9.50M | m_values.clear(); |
371 | 9.50M | m_values.emplace_back(std::move(_value)); |
372 | 9.50M | } |
373 | | |
374 | | void ExpressionEvaluator::evaluateArgs( |
375 | | std::vector<Expression> const& _expr, |
376 | | std::vector<std::optional<LiteralKind>> const* _literalArguments |
377 | | ) |
378 | 4.21M | { |
379 | 4.21M | incrementStep(); |
380 | 4.21M | std::vector<u256> values; |
381 | 4.21M | size_t i = 0; |
382 | | /// Function arguments are evaluated in reverse. |
383 | 4.21M | for (auto const& expr: _expr | ranges::views::reverse) |
384 | 8.09M | { |
385 | 8.09M | if (!_literalArguments || !_literalArguments->at(_expr.size() - i - 1)) |
386 | 8.02M | visit(expr); |
387 | 75.1k | else |
388 | 75.1k | { |
389 | 75.1k | if (std::get<Literal>(expr).value.unlimited()) |
390 | 17.0k | { |
391 | 17.0k | yulAssert(std::get<Literal>(expr).kind == LiteralKind::String); |
392 | 17.0k | m_values = {0xdeadbeef}; |
393 | 17.0k | } |
394 | 58.0k | else |
395 | 58.0k | m_values = {std::get<Literal>(expr).value.value()}; |
396 | 75.1k | } |
397 | | |
398 | 8.09M | values.push_back(value()); |
399 | 8.09M | ++i; |
400 | 8.09M | } |
401 | 4.21M | m_values = std::move(values); |
402 | 4.21M | std::reverse(m_values.begin(), m_values.end()); |
403 | 4.21M | } |
404 | | |
405 | | void ExpressionEvaluator::incrementStep() |
406 | 9.84M | { |
407 | 9.84M | m_nestingLevel++; |
408 | 9.84M | if (m_state.maxExprNesting > 0 && m_nestingLevel > m_state.maxExprNesting) |
409 | 358 | { |
410 | 358 | m_state.trace.emplace_back("Maximum expression nesting level reached."); |
411 | 358 | BOOST_THROW_EXCEPTION(ExpressionNestingLimitReached()); |
412 | 358 | } |
413 | 9.84M | } |
414 | | |
415 | | void ExpressionEvaluator::runExternalCall(evmasm::Instruction _instruction) |
416 | 0 | { |
417 | 0 | u256 memOutOffset = 0; |
418 | 0 | u256 memOutSize = 0; |
419 | 0 | u256 callvalue = 0; |
420 | 0 | u256 memInOffset = 0; |
421 | 0 | u256 memInSize = 0; |
422 | | |
423 | | // Setup memOut* values |
424 | 0 | if ( |
425 | 0 | _instruction == evmasm::Instruction::CALL || |
426 | 0 | _instruction == evmasm::Instruction::CALLCODE |
427 | 0 | ) |
428 | 0 | { |
429 | 0 | memOutOffset = values()[5]; |
430 | 0 | memOutSize = values()[6]; |
431 | 0 | callvalue = values()[2]; |
432 | 0 | memInOffset = values()[3]; |
433 | 0 | memInSize = values()[4]; |
434 | 0 | } |
435 | 0 | else if ( |
436 | 0 | _instruction == evmasm::Instruction::DELEGATECALL || |
437 | 0 | _instruction == evmasm::Instruction::STATICCALL |
438 | 0 | ) |
439 | 0 | { |
440 | 0 | memOutOffset = values()[4]; |
441 | 0 | memOutSize = values()[5]; |
442 | 0 | memInOffset = values()[2]; |
443 | 0 | memInSize = values()[3]; |
444 | 0 | } |
445 | 0 | else |
446 | 0 | yulAssert(false); |
447 | | |
448 | | // Don't execute external call if it isn't our own address |
449 | 0 | if (values()[1] != util::h160::Arith(m_state.address)) |
450 | 0 | return; |
451 | | |
452 | 0 | Scope tmpScope; |
453 | 0 | InterpreterState tmpState; |
454 | 0 | tmpState.calldata = m_state.readMemory(memInOffset, memInSize); |
455 | 0 | tmpState.callvalue = callvalue; |
456 | 0 | tmpState.numInstance = m_state.numInstance + 1; |
457 | |
|
458 | 0 | yulAssert(tmpState.numInstance < 1024, "Detected more than 1024 recursive calls, aborting..."); |
459 | | |
460 | | // Create new interpreter for the called contract |
461 | 0 | std::unique_ptr<Interpreter> newInterpreter = makeInterpreterNew(tmpState, tmpScope); |
462 | |
|
463 | 0 | Scope* abstractRootScope = &m_scope; |
464 | 0 | Scope* fileScope = nullptr; |
465 | 0 | Block const* ast = nullptr; |
466 | | |
467 | | // Find file scope |
468 | 0 | while (abstractRootScope->parent) |
469 | 0 | { |
470 | 0 | fileScope = abstractRootScope; |
471 | 0 | abstractRootScope = abstractRootScope->parent; |
472 | 0 | } |
473 | | |
474 | | // Get AST for file scope |
475 | 0 | for (auto&& [block, scope]: abstractRootScope->subScopes) |
476 | 0 | if (scope.get() == fileScope) |
477 | 0 | { |
478 | 0 | ast = block; |
479 | 0 | break; |
480 | 0 | } |
481 | |
|
482 | 0 | yulAssert(ast); |
483 | |
|
484 | 0 | try |
485 | 0 | { |
486 | 0 | (*newInterpreter)(*ast); |
487 | 0 | } |
488 | 0 | catch (ExplicitlyTerminatedWithReturn const&) |
489 | 0 | { |
490 | | // Copy return data to our memory |
491 | 0 | copyZeroExtended( |
492 | 0 | m_state.memory, |
493 | 0 | newInterpreter->returnData(), |
494 | 0 | memOutOffset.convert_to<size_t>(), |
495 | 0 | 0, |
496 | 0 | memOutSize.convert_to<size_t>() |
497 | 0 | ); |
498 | 0 | m_state.returndata = newInterpreter->returnData(); |
499 | 0 | } |
500 | 0 | } |