/src/solidity/libsolidity/codegen/ir/IRGeneratorForStatements.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 | | * Component that translates Solidity code into Yul at statement level and below. |
20 | | */ |
21 | | |
22 | | #include <libsolidity/codegen/ir/IRGeneratorForStatements.h> |
23 | | |
24 | | #include <libsolidity/codegen/ABIFunctions.h> |
25 | | #include <libsolidity/codegen/ir/IRGenerationContext.h> |
26 | | #include <libsolidity/codegen/ir/IRLValue.h> |
27 | | #include <libsolidity/codegen/ir/IRVariable.h> |
28 | | #include <libsolidity/codegen/YulUtilFunctions.h> |
29 | | #include <libsolidity/codegen/ABIFunctions.h> |
30 | | #include <libsolidity/codegen/CompilerUtils.h> |
31 | | #include <libsolidity/codegen/ReturnInfo.h> |
32 | | #include <libsolidity/ast/TypeProvider.h> |
33 | | #include <libsolidity/ast/ASTUtils.h> |
34 | | #include <libsolidity/analysis/ConstantEvaluator.h> |
35 | | |
36 | | #include <libevmasm/GasMeter.h> |
37 | | |
38 | | #include <libyul/AsmPrinter.h> |
39 | | #include <libyul/AST.h> |
40 | | #include <libyul/Dialect.h> |
41 | | #include <libyul/Utilities.h> |
42 | | #include <libyul/optimiser/ASTCopier.h> |
43 | | |
44 | | #include <liblangutil/Exceptions.h> |
45 | | |
46 | | #include <libsolutil/Whiskers.h> |
47 | | #include <libsolutil/StringUtils.h> |
48 | | #include <libsolutil/Keccak256.h> |
49 | | #include <libsolutil/FunctionSelector.h> |
50 | | #include <libsolutil/Visitor.h> |
51 | | |
52 | | #include <range/v3/algorithm/all_of.hpp> |
53 | | #include <range/v3/view/transform.hpp> |
54 | | |
55 | | using namespace solidity; |
56 | | using namespace solidity::util; |
57 | | using namespace solidity::frontend; |
58 | | using namespace std::string_literals; |
59 | | |
60 | | namespace |
61 | | { |
62 | | |
63 | | struct CopyTranslate: public yul::ASTCopier |
64 | | { |
65 | | using ExternalRefsMap = std::map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo>; |
66 | | |
67 | | CopyTranslate(IRGenerationContext& _context, ExternalRefsMap const& _references): |
68 | 174 | m_context(_context), m_references(_references) {} |
69 | | |
70 | | using ASTCopier::operator(); |
71 | | |
72 | | yul::Expression operator()(yul::Identifier const& _identifier) override |
73 | | { |
74 | | // The operator() function is only called in lvalue context. In rvalue context, |
75 | | // only translate(yul::Identifier) is called. |
76 | | if (m_references.count(&_identifier)) |
77 | | return translateReference(_identifier); |
78 | | else |
79 | | return ASTCopier::operator()(_identifier); |
80 | | } |
81 | | |
82 | | yul::YulName translateIdentifier(yul::YulName _name) override |
83 | | { |
84 | | // Strictly, the dialect used by inline assembly could be different |
85 | | // from the Yul dialect we are compiling to. By only translating `YulName`s which correspond to Identifiers, |
86 | | // we are implicitly excluding builtins together with the assumption, that numerical builtin handles |
87 | | // stay identical. Special care has to be taken, that these numerical handles stay consistent. |
88 | | return yul::YulName{"usr$" + _name.str()}; |
89 | | } |
90 | | |
91 | | yul::Identifier translate(yul::Identifier const& _identifier) override |
92 | | { |
93 | | if (!m_references.count(&_identifier)) |
94 | | return ASTCopier::translate(_identifier); |
95 | | |
96 | | yul::Expression translated = translateReference(_identifier); |
97 | | solAssert(std::holds_alternative<yul::Identifier>(translated)); |
98 | | return std::get<yul::Identifier>(std::move(translated)); |
99 | | } |
100 | | |
101 | | private: |
102 | | |
103 | | /// Translates a reference to a local variable, potentially including |
104 | | /// a suffix. Might return a literal, which causes this to be invalid in |
105 | | /// lvalue-context. |
106 | | yul::Expression translateReference(yul::Identifier const& _identifier) |
107 | | { |
108 | | auto const& reference = m_references.at(&_identifier); |
109 | | auto const varDecl = dynamic_cast<VariableDeclaration const*>(reference.declaration); |
110 | | solUnimplementedAssert(varDecl); |
111 | | std::string const& suffix = reference.suffix; |
112 | | |
113 | | std::string value; |
114 | | if (suffix.empty() && varDecl->isLocalVariable()) |
115 | | { |
116 | | auto const& var = m_context.localVariable(*varDecl); |
117 | | solAssert(var.type().sizeOnStack() == 1); |
118 | | |
119 | | value = var.commaSeparatedList(); |
120 | | } |
121 | | else if (varDecl->isConstant()) |
122 | | { |
123 | | VariableDeclaration const* variable = rootConstVariableDeclaration(*varDecl); |
124 | | solAssert(variable); |
125 | | |
126 | | if (variable->value()->annotation().type->category() == Type::Category::RationalNumber) |
127 | | { |
128 | | u256 intValue = dynamic_cast<RationalNumberType const&>(*variable->value()->annotation().type).literalValue(nullptr); |
129 | | if (auto const* bytesType = dynamic_cast<FixedBytesType const*>(variable->type())) |
130 | | intValue <<= 256 - 8 * bytesType->numBytes(); |
131 | | else |
132 | | solAssert(variable->type()->category() == Type::Category::Integer); |
133 | | value = intValue.str(); |
134 | | } |
135 | | else if (auto const* literal = dynamic_cast<Literal const*>(variable->value().get())) |
136 | | { |
137 | | Type const* type = literal->annotation().type; |
138 | | |
139 | | switch (type->category()) |
140 | | { |
141 | | case Type::Category::Bool: |
142 | | case Type::Category::Address: |
143 | | solAssert(type->category() == variable->annotation().type->category()); |
144 | | value = toCompactHexWithPrefix(type->literalValue(literal)); |
145 | | break; |
146 | | case Type::Category::StringLiteral: |
147 | | { |
148 | | auto const& stringLiteral = dynamic_cast<StringLiteralType const&>(*type); |
149 | | solAssert(variable->type()->category() == Type::Category::FixedBytes); |
150 | | unsigned const numBytes = dynamic_cast<FixedBytesType const&>(*variable->type()).numBytes(); |
151 | | solAssert(stringLiteral.value().size() <= numBytes); |
152 | | value = formatNumber(u256(h256(stringLiteral.value(), h256::AlignLeft))); |
153 | | break; |
154 | | } |
155 | | default: |
156 | | solAssert(false); |
157 | | } |
158 | | } |
159 | | else |
160 | | solAssert(false, "Invalid constant in inline assembly."); |
161 | | } |
162 | | else if (varDecl->isStateVariable()) |
163 | | { |
164 | | if (suffix == "slot") |
165 | | value = m_context.storageLocationOfStateVariable(*varDecl).first.str(); |
166 | | else if (suffix == "offset") |
167 | | value = std::to_string(m_context.storageLocationOfStateVariable(*varDecl).second); |
168 | | else |
169 | | solAssert(false); |
170 | | } |
171 | | else if (varDecl->type()->dataStoredIn(DataLocation::Storage)) |
172 | | { |
173 | | solAssert(suffix == "slot" || suffix == "offset"); |
174 | | solAssert(varDecl->isLocalVariable()); |
175 | | solAssert(!varDecl->type()->isValueType()); |
176 | | if (suffix == "slot") |
177 | | value = IRVariable{*varDecl}.part("slot").name(); |
178 | | else |
179 | | { |
180 | | solAssert(!IRVariable{*varDecl}.hasPart("offset")); |
181 | | value = "0"s; |
182 | | } |
183 | | } |
184 | | else if (varDecl->type()->dataStoredIn(DataLocation::CallData)) |
185 | | { |
186 | | solAssert(suffix == "offset" || suffix == "length"); |
187 | | value = IRVariable{*varDecl}.part(suffix).name(); |
188 | | } |
189 | | else if ( |
190 | | auto const* functionType = dynamic_cast<FunctionType const*>(varDecl->type()); |
191 | | functionType && functionType->kind() == FunctionType::Kind::External |
192 | | ) |
193 | | { |
194 | | solAssert(suffix == "selector" || suffix == "address"); |
195 | | solAssert(varDecl->type()->sizeOnStack() == 2); |
196 | | if (suffix == "selector") |
197 | | value = IRVariable{*varDecl}.part("functionSelector").name(); |
198 | | else |
199 | | value = IRVariable{*varDecl}.part("address").name(); |
200 | | } |
201 | | else |
202 | | solAssert(false); |
203 | | |
204 | | if (isDigit(value.front())) |
205 | | return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::valueOfNumberLiteral(value)}; |
206 | | else |
207 | | return yul::Identifier{_identifier.debugData, yul::YulName{value}}; |
208 | | } |
209 | | |
210 | | IRGenerationContext& m_context; |
211 | | ExternalRefsMap const& m_references; |
212 | | }; |
213 | | |
214 | | } |
215 | | |
216 | | std::string IRGeneratorForStatementsBase::code() const |
217 | 12.3k | { |
218 | 12.3k | return m_code.str(); |
219 | 12.3k | } |
220 | | |
221 | | std::ostringstream& IRGeneratorForStatementsBase::appendCode(bool _addLocationComment) |
222 | 128k | { |
223 | 128k | if ( |
224 | 128k | _addLocationComment && |
225 | 93.5k | m_currentLocation.isValid() && |
226 | 93.5k | m_lastLocation != m_currentLocation |
227 | 128k | ) |
228 | 53.8k | m_code << dispenseLocationComment(m_currentLocation, m_context) << "\n"; |
229 | | |
230 | 128k | m_lastLocation = m_currentLocation; |
231 | | |
232 | 128k | return m_code; |
233 | 128k | } |
234 | | |
235 | | void IRGeneratorForStatementsBase::setLocation(ASTNode const& _node) |
236 | 71.7k | { |
237 | 71.7k | m_currentLocation = _node.location(); |
238 | 71.7k | } |
239 | | |
240 | | std::string IRGeneratorForStatements::code() const |
241 | 12.3k | { |
242 | 12.3k | solAssert(!m_currentLValue, "LValue not reset!"); |
243 | 12.3k | return IRGeneratorForStatementsBase::code(); |
244 | 12.3k | } |
245 | | |
246 | | void IRGeneratorForStatements::generate(Block const& _block) |
247 | 3.46k | { |
248 | 3.46k | try |
249 | 3.46k | { |
250 | 3.46k | _block.accept(*this); |
251 | 3.46k | } |
252 | 3.46k | catch (langutil::UnimplementedFeatureError const& _error) |
253 | 3.46k | { |
254 | 0 | if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error)) |
255 | 0 | _error << langutil::errinfo_sourceLocation(m_currentLocation); |
256 | 0 | BOOST_THROW_EXCEPTION(_error); |
257 | 0 | } |
258 | 3.46k | } |
259 | | |
260 | | void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _varDecl) |
261 | 1.49k | { |
262 | 1.49k | try |
263 | 1.49k | { |
264 | 1.49k | setLocation(_varDecl); |
265 | | |
266 | 1.49k | solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable."); |
267 | 1.49k | solAssert(!_varDecl.isConstant()); |
268 | 1.49k | if (!_varDecl.value()) |
269 | 1.00k | return; |
270 | 485 | solAssert(_varDecl.referenceLocation() != VariableDeclaration::Location::Transient, "Transient storage state variables cannot be initialized in place."); |
271 | | |
272 | 485 | _varDecl.value()->accept(*this); |
273 | | |
274 | 485 | writeToLValue( |
275 | 485 | _varDecl.immutable() ? |
276 | 31 | IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} : |
277 | 485 | IRLValue{*_varDecl.annotation().type, IRLValue::Storage{ |
278 | 454 | toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first), |
279 | 454 | m_context.storageLocationOfStateVariable(_varDecl).second |
280 | 454 | }}, |
281 | 485 | *_varDecl.value() |
282 | 485 | ); |
283 | 485 | } |
284 | 1.49k | catch (langutil::UnimplementedFeatureError const& _error) |
285 | 1.49k | { |
286 | 0 | if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error)) |
287 | 0 | _error << langutil::errinfo_sourceLocation(m_currentLocation); |
288 | 0 | BOOST_THROW_EXCEPTION(_error); |
289 | 0 | } |
290 | 1.49k | } |
291 | | |
292 | | void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _varDecl) |
293 | 2.53k | { |
294 | 2.53k | try |
295 | 2.53k | { |
296 | 2.53k | setLocation(_varDecl); |
297 | | |
298 | 2.53k | solAssert(m_context.isLocalVariable(_varDecl), "Must be a local variable."); |
299 | | |
300 | 2.53k | auto const* type = _varDecl.type(); |
301 | 2.53k | if (dynamic_cast<MappingType const*>(type)) |
302 | 0 | return; |
303 | 2.53k | else if (auto const* refType = dynamic_cast<ReferenceType const*>(type)) |
304 | 1.23k | if (refType->dataStoredIn(DataLocation::Storage) && refType->isPointer()) |
305 | 0 | return; |
306 | | |
307 | 2.53k | IRVariable zero = zeroValue(*type); |
308 | 2.53k | assign(m_context.localVariable(_varDecl), zero); |
309 | 2.53k | } |
310 | 2.53k | catch (langutil::UnimplementedFeatureError const& _error) |
311 | 2.53k | { |
312 | 0 | if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error)) |
313 | 0 | _error << langutil::errinfo_sourceLocation(m_currentLocation); |
314 | 0 | BOOST_THROW_EXCEPTION(_error); |
315 | 0 | } |
316 | 2.53k | } |
317 | | |
318 | | IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expression, Type const& _targetType) |
319 | 190 | { |
320 | 190 | try |
321 | 190 | { |
322 | 190 | setLocation(_expression); |
323 | | |
324 | 190 | _expression.accept(*this); |
325 | | |
326 | 190 | setLocation(_expression); |
327 | 190 | IRVariable variable{m_context.newYulVariable(), _targetType}; |
328 | 190 | define(variable, _expression); |
329 | 190 | return variable; |
330 | 190 | } |
331 | 190 | catch (langutil::UnimplementedFeatureError const& _error) |
332 | 190 | { |
333 | 0 | if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error)) |
334 | 0 | _error << langutil::errinfo_sourceLocation(m_currentLocation); |
335 | 0 | BOOST_THROW_EXCEPTION(_error); |
336 | 0 | } |
337 | 190 | } |
338 | | |
339 | | std::string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant) |
340 | 67 | { |
341 | 67 | try |
342 | 67 | { |
343 | 67 | std::string functionName = IRNames::constantValueFunction(_constant); |
344 | 67 | return m_context.functionCollector().createFunction(functionName, [&] { |
345 | 55 | Whiskers templ(R"( |
346 | 55 | <sourceLocationComment> |
347 | 55 | function <functionName>() -> <ret> { |
348 | 55 | <code> |
349 | 55 | <ret> := <value> |
350 | 55 | } |
351 | 55 | )"); |
352 | 55 | templ("sourceLocationComment", dispenseLocationComment(_constant, m_context)); |
353 | 55 | templ("functionName", functionName); |
354 | 55 | IRGeneratorForStatements generator(m_context, m_utils, m_optimiserSettings); |
355 | 55 | solAssert(_constant.value()); |
356 | 55 | Type const& constantType = *_constant.type(); |
357 | 55 | templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList()); |
358 | 55 | templ("code", generator.code()); |
359 | 55 | templ("ret", IRVariable("ret", constantType).commaSeparatedList()); |
360 | | |
361 | 55 | return templ.render(); |
362 | 55 | }); |
363 | 67 | } |
364 | 67 | catch (langutil::UnimplementedFeatureError const& _error) |
365 | 67 | { |
366 | 0 | if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error)) |
367 | 0 | _error << langutil::errinfo_sourceLocation(m_currentLocation); |
368 | 0 | BOOST_THROW_EXCEPTION(_error); |
369 | 0 | } |
370 | 67 | } |
371 | | |
372 | | void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement) |
373 | 1.18k | { |
374 | 1.18k | setLocation(_varDeclStatement); |
375 | | |
376 | 1.18k | if (Expression const* expression = _varDeclStatement.initialValue()) |
377 | 570 | { |
378 | 570 | if (_varDeclStatement.declarations().size() > 1) |
379 | 24 | { |
380 | 24 | auto const* tupleType = dynamic_cast<TupleType const*>(expression->annotation().type); |
381 | 24 | solAssert(tupleType, "Expected expression of tuple type."); |
382 | 24 | solAssert(_varDeclStatement.declarations().size() == tupleType->components().size(), "Invalid number of tuple components."); |
383 | 81 | for (size_t i = 0; i < _varDeclStatement.declarations().size(); ++i) |
384 | 57 | if (auto const& decl = _varDeclStatement.declarations()[i]) |
385 | 46 | { |
386 | 46 | solAssert(tupleType->components()[i]); |
387 | 46 | define(m_context.addLocalVariable(*decl), IRVariable(*expression).tupleComponent(i)); |
388 | 46 | } |
389 | 24 | } |
390 | 546 | else |
391 | 546 | { |
392 | 546 | VariableDeclaration const& varDecl = *_varDeclStatement.declarations().front(); |
393 | 546 | define(m_context.addLocalVariable(varDecl), *expression); |
394 | 546 | } |
395 | 570 | } |
396 | 615 | else |
397 | 615 | for (auto const& decl: _varDeclStatement.declarations()) |
398 | 615 | if (decl) |
399 | 615 | { |
400 | 615 | declare(m_context.addLocalVariable(*decl)); |
401 | 615 | initializeLocalVar(*decl); |
402 | 615 | } |
403 | 1.18k | } |
404 | | |
405 | | bool IRGeneratorForStatements::visit(Conditional const& _conditional) |
406 | 342 | { |
407 | 342 | _conditional.condition().accept(*this); |
408 | | |
409 | 342 | setLocation(_conditional); |
410 | | |
411 | 342 | std::string condition = expressionAsType(_conditional.condition(), *TypeProvider::boolean()); |
412 | 342 | declare(_conditional); |
413 | | |
414 | 342 | appendCode() << "switch " << condition << "\n" "case 0 {\n"; |
415 | | |
416 | 342 | _conditional.falseExpression().accept(*this); |
417 | 342 | setLocation(_conditional); |
418 | | |
419 | 342 | assign(_conditional, _conditional.falseExpression()); |
420 | 342 | appendCode() << "}\n" "default {\n"; |
421 | | |
422 | 342 | _conditional.trueExpression().accept(*this); |
423 | 342 | setLocation(_conditional); |
424 | | |
425 | 342 | assign(_conditional, _conditional.trueExpression()); |
426 | 342 | appendCode() << "}\n"; |
427 | | |
428 | 342 | return false; |
429 | 342 | } |
430 | | |
431 | | bool IRGeneratorForStatements::visit(Assignment const& _assignment) |
432 | 2.05k | { |
433 | 2.05k | _assignment.rightHandSide().accept(*this); |
434 | 2.05k | setLocation(_assignment); |
435 | | |
436 | 2.05k | Token assignmentOperator = _assignment.assignmentOperator(); |
437 | 2.05k | Token binaryOperator = |
438 | 2.05k | assignmentOperator == Token::Assign ? |
439 | 1.91k | assignmentOperator : |
440 | 2.05k | TokenTraits::AssignmentToBinaryOp(assignmentOperator); |
441 | | |
442 | 2.05k | if (TokenTraits::isShiftOp(binaryOperator)) |
443 | 2.05k | solAssert(type(_assignment.rightHandSide()).mobileType()); |
444 | 2.05k | IRVariable value = |
445 | 2.05k | type(_assignment.leftHandSide()).isValueType() ? |
446 | 1.53k | convert( |
447 | 1.53k | _assignment.rightHandSide(), |
448 | 1.53k | TokenTraits::isShiftOp(binaryOperator) ? *type(_assignment.rightHandSide()).mobileType() : type(_assignment) |
449 | 1.53k | ) : |
450 | 2.05k | _assignment.rightHandSide(); |
451 | | |
452 | 2.05k | _assignment.leftHandSide().accept(*this); |
453 | | |
454 | 2.05k | solAssert(!!m_currentLValue, "LValue not retrieved."); |
455 | 2.05k | setLocation(_assignment); |
456 | | |
457 | 2.05k | if (assignmentOperator != Token::Assign) |
458 | 135 | { |
459 | 135 | solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types."); |
460 | 135 | solAssert(binaryOperator != Token::Exp); |
461 | 135 | solAssert(type(_assignment) == type(_assignment.leftHandSide())); |
462 | | |
463 | 135 | IRVariable leftIntermediate = readFromLValue(*m_currentLValue); |
464 | 135 | solAssert(type(_assignment) == leftIntermediate.type()); |
465 | | |
466 | 135 | define(_assignment) << ( |
467 | 135 | TokenTraits::isShiftOp(binaryOperator) ? |
468 | 0 | shiftOperation(binaryOperator, leftIntermediate, value) : |
469 | 135 | binaryOperation(binaryOperator, type(_assignment), leftIntermediate.name(), value.name()) |
470 | 135 | ) << "\n"; |
471 | | |
472 | 135 | writeToLValue(*m_currentLValue, IRVariable(_assignment)); |
473 | 135 | } |
474 | 1.91k | else |
475 | 1.91k | { |
476 | 1.91k | writeToLValue(*m_currentLValue, value); |
477 | | |
478 | 1.91k | if (dynamic_cast<ReferenceType const*>(&m_currentLValue->type)) |
479 | 510 | define(_assignment, readFromLValue(*m_currentLValue)); |
480 | 1.40k | else if (*_assignment.annotation().type != *TypeProvider::emptyTuple()) |
481 | 1.40k | define(_assignment, value); |
482 | 1.91k | } |
483 | | |
484 | 2.05k | m_currentLValue.reset(); |
485 | 2.05k | return false; |
486 | 2.05k | } |
487 | | |
488 | | bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) |
489 | 1.66k | { |
490 | 1.66k | setLocation(_tuple); |
491 | | |
492 | 1.66k | if (_tuple.isInlineArray()) |
493 | 301 | { |
494 | 301 | auto const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type); |
495 | 301 | solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array."); |
496 | 301 | define(_tuple) << |
497 | 301 | m_utils.allocateMemoryArrayFunction(arrayType) << |
498 | 301 | "(" << |
499 | 301 | _tuple.components().size() << |
500 | 301 | ")\n"; |
501 | | |
502 | 301 | std::string mpos = IRVariable(_tuple).part("mpos").name(); |
503 | 301 | Type const& baseType = *arrayType.baseType(); |
504 | 645 | for (size_t i = 0; i < _tuple.components().size(); i++) |
505 | 344 | { |
506 | 344 | Expression const& component = *_tuple.components()[i]; |
507 | 344 | component.accept(*this); |
508 | 344 | setLocation(_tuple); |
509 | 344 | IRVariable converted = convert(component, baseType); |
510 | 344 | appendCode() << |
511 | 344 | m_utils.writeToMemoryFunction(baseType) << |
512 | 344 | "(" << |
513 | 344 | ("add(" + mpos + ", " + std::to_string(i * arrayType.memoryStride()) + ")") << |
514 | 344 | ", " << |
515 | 344 | converted.commaSeparatedList() << |
516 | 344 | ")\n"; |
517 | 344 | } |
518 | 301 | } |
519 | 1.36k | else |
520 | 1.36k | { |
521 | 1.36k | bool willBeWrittenTo = _tuple.annotation().willBeWrittenTo; |
522 | 1.36k | if (willBeWrittenTo) |
523 | 1.36k | solAssert(!m_currentLValue); |
524 | 1.36k | if (_tuple.components().size() == 1) |
525 | 726 | { |
526 | 726 | solAssert(_tuple.components().front()); |
527 | 726 | _tuple.components().front()->accept(*this); |
528 | 726 | setLocation(_tuple); |
529 | 726 | if (willBeWrittenTo) |
530 | 726 | solAssert(!!m_currentLValue); |
531 | 725 | else |
532 | 725 | define(_tuple, *_tuple.components().front()); |
533 | 726 | } |
534 | 640 | else |
535 | 640 | { |
536 | 640 | std::vector<std::optional<IRLValue>> lvalues; |
537 | 1.97k | for (size_t i = 0; i < _tuple.components().size(); ++i) |
538 | 1.33k | if (auto const& component = _tuple.components()[i]) |
539 | 1.32k | { |
540 | 1.32k | component->accept(*this); |
541 | 1.32k | setLocation(_tuple); |
542 | 1.32k | if (willBeWrittenTo) |
543 | 5 | { |
544 | 5 | solAssert(!!m_currentLValue); |
545 | 5 | lvalues.emplace_back(std::move(m_currentLValue)); |
546 | 5 | m_currentLValue.reset(); |
547 | 5 | } |
548 | 1.32k | else |
549 | 1.32k | define(IRVariable(_tuple).tupleComponent(i), *component); |
550 | 1.32k | } |
551 | 5 | else if (willBeWrittenTo) |
552 | 5 | lvalues.emplace_back(); |
553 | | |
554 | 640 | if (_tuple.annotation().willBeWrittenTo) |
555 | 5 | m_currentLValue.emplace(IRLValue{ |
556 | 5 | *_tuple.annotation().type, |
557 | 5 | IRLValue::Tuple{std::move(lvalues)} |
558 | 5 | }); |
559 | 640 | } |
560 | 1.36k | } |
561 | 1.66k | return false; |
562 | 1.66k | } |
563 | | |
564 | | bool IRGeneratorForStatements::visit(Block const& _block) |
565 | 3.65k | { |
566 | 3.65k | if (_block.unchecked()) |
567 | 10 | { |
568 | 10 | solAssert(m_context.arithmetic() == Arithmetic::Checked); |
569 | 10 | m_context.setArithmetic(Arithmetic::Wrapping); |
570 | 10 | } |
571 | 3.65k | return true; |
572 | 3.65k | } |
573 | | |
574 | | void IRGeneratorForStatements::endVisit(Block const& _block) |
575 | 3.65k | { |
576 | 3.65k | if (_block.unchecked()) |
577 | 10 | { |
578 | 10 | solAssert(m_context.arithmetic() == Arithmetic::Wrapping); |
579 | 10 | m_context.setArithmetic(Arithmetic::Checked); |
580 | 10 | } |
581 | 3.65k | } |
582 | | |
583 | | bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) |
584 | 20 | { |
585 | 20 | _ifStatement.condition().accept(*this); |
586 | 20 | setLocation(_ifStatement); |
587 | 20 | std::string condition = expressionAsType(_ifStatement.condition(), *TypeProvider::boolean()); |
588 | | |
589 | 20 | if (_ifStatement.falseStatement()) |
590 | 0 | { |
591 | 0 | appendCode() << "switch " << condition << "\n" "case 0 {\n"; |
592 | 0 | _ifStatement.falseStatement()->accept(*this); |
593 | 0 | setLocation(_ifStatement); |
594 | 0 | appendCode() << "}\n" "default {\n"; |
595 | 0 | } |
596 | 20 | else |
597 | 20 | appendCode() << "if " << condition << " {\n"; |
598 | 20 | _ifStatement.trueStatement().accept(*this); |
599 | 20 | setLocation(_ifStatement); |
600 | 20 | appendCode() << "}\n"; |
601 | | |
602 | 20 | return false; |
603 | 20 | } |
604 | | |
605 | | void IRGeneratorForStatements::endVisit(PlaceholderStatement const& _placeholder) |
606 | 100 | { |
607 | 100 | solAssert(m_placeholderCallback); |
608 | 100 | setLocation(_placeholder); |
609 | 100 | appendCode() << m_placeholderCallback(); |
610 | 100 | } |
611 | | |
612 | | bool IRGeneratorForStatements::visit(ForStatement const& _forStatement) |
613 | 90 | { |
614 | 90 | setLocation(_forStatement); |
615 | 90 | generateLoop( |
616 | 90 | _forStatement.body(), |
617 | 90 | _forStatement.condition(), |
618 | 90 | _forStatement.initializationExpression(), |
619 | 90 | _forStatement.loopExpression(), |
620 | 90 | false, // _isDoWhile |
621 | 90 | *_forStatement.annotation().isSimpleCounterLoop |
622 | 90 | ); |
623 | | |
624 | 90 | return false; |
625 | 90 | } |
626 | | |
627 | | bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement) |
628 | 16 | { |
629 | 16 | setLocation(_whileStatement); |
630 | 16 | generateLoop( |
631 | 16 | _whileStatement.body(), |
632 | 16 | &_whileStatement.condition(), |
633 | 16 | nullptr, |
634 | 16 | nullptr, |
635 | 16 | _whileStatement.isDoWhile() |
636 | 16 | ); |
637 | | |
638 | 16 | return false; |
639 | 16 | } |
640 | | |
641 | | bool IRGeneratorForStatements::visit(Continue const& _continue) |
642 | 0 | { |
643 | 0 | setLocation(_continue); |
644 | 0 | appendCode() << "continue\n"; |
645 | 0 | return false; |
646 | 0 | } |
647 | | |
648 | | bool IRGeneratorForStatements::visit(Break const& _break) |
649 | 0 | { |
650 | 0 | setLocation(_break); |
651 | 0 | appendCode() << "break\n"; |
652 | 0 | return false; |
653 | 0 | } |
654 | | |
655 | | void IRGeneratorForStatements::endVisit(Return const& _return) |
656 | 1.33k | { |
657 | 1.33k | setLocation(_return); |
658 | 1.33k | if (Expression const* value = _return.expression()) |
659 | 1.31k | { |
660 | 1.31k | solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer."); |
661 | 1.31k | std::vector<ASTPointer<VariableDeclaration>> const& returnParameters = |
662 | 1.31k | _return.annotation().functionReturnParameters->parameters(); |
663 | 1.31k | if (returnParameters.size() > 1) |
664 | 1.07k | for (size_t i = 0; i < returnParameters.size(); ++i) |
665 | 736 | assign(m_context.localVariable(*returnParameters[i]), IRVariable(*value).tupleComponent(i)); |
666 | 971 | else if (returnParameters.size() == 1) |
667 | 971 | assign(m_context.localVariable(*returnParameters.front()), *value); |
668 | 1.31k | } |
669 | 1.33k | appendCode() << "leave\n"; |
670 | 1.33k | } |
671 | | |
672 | | bool IRGeneratorForStatements::visit(UnaryOperation const& _unaryOperation) |
673 | 728 | { |
674 | 728 | setLocation(_unaryOperation); |
675 | | |
676 | 728 | FunctionDefinition const* function = *_unaryOperation.annotation().userDefinedFunction; |
677 | 728 | if (function) |
678 | 0 | { |
679 | 0 | _unaryOperation.subExpression().accept(*this); |
680 | 0 | setLocation(_unaryOperation); |
681 | |
|
682 | 0 | solAssert(function->isImplemented()); |
683 | 0 | solAssert(function->isFree()); |
684 | 0 | solAssert(function->parameters().size() == 1); |
685 | 0 | solAssert(function->returnParameters().size() == 1); |
686 | 0 | solAssert(*function->returnParameters()[0]->type() == *_unaryOperation.annotation().type); |
687 | |
|
688 | 0 | std::string argument = expressionAsType(_unaryOperation.subExpression(), *function->parameters()[0]->type()); |
689 | 0 | solAssert(!argument.empty()); |
690 | |
|
691 | 0 | solAssert(_unaryOperation.userDefinedFunctionType()->kind() == FunctionType::Kind::Internal); |
692 | 0 | define(_unaryOperation) << |
693 | 0 | m_context.enqueueFunctionForCodeGeneration(*function) << |
694 | 0 | ("(" + argument + ")\n"); |
695 | |
|
696 | 0 | return false; |
697 | 0 | } |
698 | | |
699 | 728 | Type const& resultType = type(_unaryOperation); |
700 | 728 | Token const op = _unaryOperation.getOperator(); |
701 | | |
702 | 728 | if (resultType.category() == Type::Category::RationalNumber) |
703 | 132 | { |
704 | 132 | define(_unaryOperation) << formatNumber(resultType.literalValue(nullptr)) << "\n"; |
705 | 132 | return false; |
706 | 132 | } |
707 | | |
708 | 596 | _unaryOperation.subExpression().accept(*this); |
709 | 596 | setLocation(_unaryOperation); |
710 | | |
711 | 596 | if (op == Token::Delete) |
712 | 12 | { |
713 | 12 | solAssert(!!m_currentLValue, "LValue not retrieved."); |
714 | 12 | std::visit( |
715 | 12 | util::GenericVisitor{ |
716 | 12 | [&](IRLValue::Storage const& _storage) { |
717 | 12 | appendCode() << |
718 | 12 | m_utils.storageSetToZeroFunction(m_currentLValue->type, VariableDeclaration::Location::Unspecified) << |
719 | 12 | "(" << |
720 | 12 | _storage.slot << |
721 | 12 | ", " << |
722 | 12 | _storage.offsetString() << |
723 | 12 | ")\n"; |
724 | 12 | m_currentLValue.reset(); |
725 | 12 | }, |
726 | 12 | [&](IRLValue::TransientStorage const& _transientStorage) { |
727 | 0 | appendCode() << |
728 | 0 | m_utils.storageSetToZeroFunction(m_currentLValue->type, VariableDeclaration::Location::Transient) << |
729 | 0 | "(" << |
730 | 0 | _transientStorage.slot << |
731 | 0 | ", " << |
732 | 0 | _transientStorage.offsetString() << |
733 | 0 | ")\n"; |
734 | 0 | m_currentLValue.reset(); |
735 | 0 | }, |
736 | 12 | [&](auto const&) { |
737 | 0 | IRVariable zeroValue(m_context.newYulVariable(), m_currentLValue->type); |
738 | 0 | define(zeroValue) << m_utils.zeroValueFunction(m_currentLValue->type) << "()\n"; |
739 | 0 | writeToLValue(*m_currentLValue, zeroValue); |
740 | 0 | m_currentLValue.reset(); |
741 | 0 | } Unexecuted instantiation: IRGeneratorForStatements.cpp:auto solidity::frontend::IRGeneratorForStatements::visit(solidity::frontend::UnaryOperation const&)::$_2::operator()<solidity::frontend::IRLValue::Stack>(solidity::frontend::IRLValue::Stack const&) const Unexecuted instantiation: IRGeneratorForStatements.cpp:auto solidity::frontend::IRGeneratorForStatements::visit(solidity::frontend::UnaryOperation const&)::$_2::operator()<solidity::frontend::IRLValue::Immutable>(solidity::frontend::IRLValue::Immutable const&) const Unexecuted instantiation: IRGeneratorForStatements.cpp:auto solidity::frontend::IRGeneratorForStatements::visit(solidity::frontend::UnaryOperation const&)::$_2::operator()<solidity::frontend::IRLValue::Memory>(solidity::frontend::IRLValue::Memory const&) const Unexecuted instantiation: IRGeneratorForStatements.cpp:auto solidity::frontend::IRGeneratorForStatements::visit(solidity::frontend::UnaryOperation const&)::$_2::operator()<solidity::frontend::IRLValue::Tuple>(solidity::frontend::IRLValue::Tuple const&) const |
742 | 12 | }, |
743 | 12 | m_currentLValue->kind |
744 | 12 | ); |
745 | 12 | } |
746 | 584 | else if (resultType.category() == Type::Category::Integer) |
747 | 583 | { |
748 | 583 | solAssert(resultType == type(_unaryOperation.subExpression()), "Result type doesn't match!"); |
749 | | |
750 | 583 | if (op == Token::Inc || op == Token::Dec) |
751 | 386 | { |
752 | 386 | solAssert(!!m_currentLValue, "LValue not retrieved."); |
753 | 386 | IRVariable modifiedValue(m_context.newYulVariable(), resultType); |
754 | 386 | IRVariable originalValue = readFromLValue(*m_currentLValue); |
755 | | |
756 | 386 | bool checked = m_context.arithmetic() == Arithmetic::Checked; |
757 | 386 | define(modifiedValue) << |
758 | 386 | (op == Token::Inc ? |
759 | 304 | (checked ? m_utils.incrementCheckedFunction(resultType) : m_utils.incrementWrappingFunction(resultType)) : |
760 | 386 | (checked ? m_utils.decrementCheckedFunction(resultType) : m_utils.decrementWrappingFunction(resultType)) |
761 | 386 | ) << |
762 | 386 | "(" << |
763 | 386 | originalValue.name() << |
764 | 386 | ")\n"; |
765 | 386 | writeToLValue(*m_currentLValue, modifiedValue); |
766 | 386 | m_currentLValue.reset(); |
767 | | |
768 | 386 | define(_unaryOperation, _unaryOperation.isPrefixOperation() ? modifiedValue : originalValue); |
769 | 386 | } |
770 | 197 | else if (op == Token::BitNot) |
771 | 129 | appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression()); |
772 | 68 | else if (op == Token::Add) |
773 | | // According to SyntaxChecker... |
774 | 68 | solAssert(false, "Use of unary + is disallowed."); |
775 | 68 | else if (op == Token::Sub) |
776 | 68 | { |
777 | 68 | IntegerType const& intType = *dynamic_cast<IntegerType const*>(&resultType); |
778 | 68 | define(_unaryOperation) << ( |
779 | 68 | m_context.arithmetic() == Arithmetic::Checked ? |
780 | 64 | m_utils.negateNumberCheckedFunction(intType) : |
781 | 68 | m_utils.negateNumberWrappingFunction(intType) |
782 | 68 | ) << "(" << IRVariable(_unaryOperation.subExpression()).name() << ")\n"; |
783 | 68 | } |
784 | 0 | else |
785 | 68 | solUnimplemented("Unary operator not yet implemented"); |
786 | 583 | } |
787 | 1 | else if (resultType.category() == Type::Category::FixedBytes) |
788 | 0 | { |
789 | 0 | solAssert(op == Token::BitNot, "Only bitwise negation is allowed for FixedBytes"); |
790 | 0 | solAssert(resultType == type(_unaryOperation.subExpression()), "Result type doesn't match!"); |
791 | 0 | appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression()); |
792 | 0 | } |
793 | 1 | else if (resultType.category() == Type::Category::Bool) |
794 | 1 | { |
795 | 1 | solAssert( |
796 | 1 | op != Token::BitNot, |
797 | 1 | "Bitwise Negation can't be done on bool!" |
798 | 1 | ); |
799 | | |
800 | 1 | appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression()); |
801 | 1 | } |
802 | 0 | else |
803 | 1 | solUnimplemented("Unary operator not yet implemented"); |
804 | | |
805 | 596 | return false; |
806 | 728 | } |
807 | | |
808 | | void IRGeneratorForStatements::endVisit(RevertStatement const& _revertStatement) |
809 | 2 | { |
810 | 2 | ErrorDefinition const* error = dynamic_cast<ErrorDefinition const*>(ASTNode::referencedDeclaration(_revertStatement.errorCall().expression())); |
811 | 2 | solAssert(error); |
812 | 2 | revertWithError( |
813 | 2 | error->functionType(true)->externalSignature(), |
814 | 2 | error->functionType(true)->parameterTypes(), |
815 | 2 | _revertStatement.errorCall().sortedArguments() |
816 | 2 | ); |
817 | 2 | } |
818 | | |
819 | | bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) |
820 | 6.92k | { |
821 | 6.92k | setLocation(_binOp); |
822 | | |
823 | 6.92k | FunctionDefinition const* function = *_binOp.annotation().userDefinedFunction; |
824 | 6.92k | if (function) |
825 | 0 | { |
826 | 0 | _binOp.leftExpression().accept(*this); |
827 | 0 | _binOp.rightExpression().accept(*this); |
828 | 0 | setLocation(_binOp); |
829 | |
|
830 | 0 | solAssert(function->isImplemented()); |
831 | 0 | solAssert(function->isFree()); |
832 | 0 | solAssert(function->parameters().size() == 2); |
833 | 0 | solAssert(function->returnParameters().size() == 1); |
834 | 0 | solAssert(*function->returnParameters()[0]->type() == *_binOp.annotation().type); |
835 | |
|
836 | 0 | std::string left = expressionAsType(_binOp.leftExpression(), *function->parameters()[0]->type()); |
837 | 0 | std::string right = expressionAsType(_binOp.rightExpression(), *function->parameters()[1]->type()); |
838 | 0 | solAssert(!left.empty() && !right.empty()); |
839 | |
|
840 | 0 | solAssert(_binOp.userDefinedFunctionType()->kind() == FunctionType::Kind::Internal); |
841 | 0 | define(_binOp) << |
842 | 0 | m_context.enqueueFunctionForCodeGeneration(*function) << |
843 | 0 | ("(" + left + ", " + right + ")\n"); |
844 | |
|
845 | 0 | return false; |
846 | 0 | } |
847 | | |
848 | 6.92k | solAssert(!!_binOp.annotation().commonType); |
849 | 6.92k | Type const* commonType = _binOp.annotation().commonType; |
850 | 6.92k | langutil::Token op = _binOp.getOperator(); |
851 | | |
852 | 6.92k | if (op == Token::And || op == Token::Or) |
853 | 11 | { |
854 | | // This can short-circuit! |
855 | 11 | appendAndOrOperatorCode(_binOp); |
856 | 11 | return false; |
857 | 11 | } |
858 | | |
859 | 6.91k | if (commonType->category() == Type::Category::RationalNumber) |
860 | 114 | { |
861 | 114 | define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n"; |
862 | 114 | return false; // skip sub-expressions |
863 | 114 | } |
864 | | |
865 | 6.79k | _binOp.leftExpression().accept(*this); |
866 | 6.79k | _binOp.rightExpression().accept(*this); |
867 | 6.79k | setLocation(_binOp); |
868 | | |
869 | 6.79k | if (TokenTraits::isCompareOp(op)) |
870 | 413 | { |
871 | 413 | solAssert(commonType->isValueType()); |
872 | | |
873 | 413 | bool isSigned = false; |
874 | 413 | if (auto type = dynamic_cast<IntegerType const*>(commonType)) |
875 | 384 | isSigned = type->isSigned(); |
876 | | |
877 | 413 | std::string args = expressionAsCleanedType(_binOp.leftExpression(), *commonType); |
878 | 413 | args += ", " + expressionAsCleanedType(_binOp.rightExpression(), *commonType); |
879 | | |
880 | 413 | auto functionType = dynamic_cast<FunctionType const*>(commonType); |
881 | 413 | solAssert(functionType ? (op == Token::Equal || op == Token::NotEqual) : true, "Invalid function pointer comparison!"); |
882 | | |
883 | 413 | std::string expr; |
884 | | |
885 | 413 | if (functionType && functionType->kind() == FunctionType::Kind::External) |
886 | 0 | { |
887 | 0 | solUnimplementedAssert(functionType->sizeOnStack() == 2, ""); |
888 | 0 | expr = m_utils.externalFunctionPointersEqualFunction() + |
889 | 0 | "(" + |
890 | 0 | IRVariable{_binOp.leftExpression()}.part("address").name() + "," + |
891 | 0 | IRVariable{_binOp.leftExpression()}.part("functionSelector").name() + "," + |
892 | 0 | IRVariable{_binOp.rightExpression()}.part("address").name() + "," + |
893 | 0 | IRVariable{_binOp.rightExpression()}.part("functionSelector").name() + |
894 | 0 | ")"; |
895 | 0 | if (op == Token::NotEqual) |
896 | 0 | expr = "iszero(" + expr + ")"; |
897 | 0 | } |
898 | 413 | else if (op == Token::Equal) |
899 | 139 | expr = "eq(" + std::move(args) + ")"; |
900 | 274 | else if (op == Token::NotEqual) |
901 | 55 | expr = "iszero(eq(" + std::move(args) + "))"; |
902 | 219 | else if (op == Token::GreaterThanOrEqual) |
903 | 0 | expr = "iszero(" + std::string(isSigned ? "slt(" : "lt(") + std::move(args) + "))"; |
904 | 219 | else if (op == Token::LessThanOrEqual) |
905 | 18 | expr = "iszero(" + std::string(isSigned ? "sgt(" : "gt(") + std::move(args) + "))"; |
906 | 201 | else if (op == Token::GreaterThan) |
907 | 76 | expr = (isSigned ? "sgt(" : "gt(") + std::move(args) + ")"; |
908 | 125 | else if (op == Token::LessThan) |
909 | 125 | expr = (isSigned ? "slt(" : "lt(") + std::move(args) + ")"; |
910 | 0 | else |
911 | 125 | solAssert(false, "Unknown comparison operator."); |
912 | 413 | define(_binOp) << expr << "\n"; |
913 | 413 | } |
914 | 6.38k | else if (op == Token::Exp) |
915 | 1.61k | { |
916 | 1.61k | IRVariable left = convert(_binOp.leftExpression(), *commonType); |
917 | 1.61k | IRVariable right = convert(_binOp.rightExpression(), *type(_binOp.rightExpression()).mobileType()); |
918 | | |
919 | 1.61k | if (m_context.arithmetic() == Arithmetic::Wrapping) |
920 | 0 | define(_binOp) << m_utils.wrappingIntExpFunction( |
921 | 0 | dynamic_cast<IntegerType const&>(left.type()), |
922 | 0 | dynamic_cast<IntegerType const&>(right.type()) |
923 | 0 | ) << "(" << left.name() << ", " << right.name() << ")\n"; |
924 | 1.61k | else if (auto rationalNumberType = dynamic_cast<RationalNumberType const*>(_binOp.leftExpression().annotation().type)) |
925 | 137 | { |
926 | 137 | solAssert(rationalNumberType->integerType(), "Invalid literal as the base for exponentiation."); |
927 | 137 | solAssert(dynamic_cast<IntegerType const*>(commonType)); |
928 | | |
929 | 137 | define(_binOp) << m_utils.overflowCheckedIntLiteralExpFunction( |
930 | 137 | *rationalNumberType, |
931 | 137 | dynamic_cast<IntegerType const&>(right.type()), |
932 | 137 | dynamic_cast<IntegerType const&>(*commonType) |
933 | 137 | ) << "(" << right.name() << ")\n"; |
934 | 137 | } |
935 | 1.48k | else |
936 | 1.48k | define(_binOp) << m_utils.overflowCheckedIntExpFunction( |
937 | 1.48k | dynamic_cast<IntegerType const&>(left.type()), |
938 | 1.48k | dynamic_cast<IntegerType const&>(right.type()) |
939 | 1.48k | ) << "(" << left.name() << ", " << right.name() << ")\n"; |
940 | 1.61k | } |
941 | 4.76k | else if (TokenTraits::isShiftOp(op)) |
942 | 18 | { |
943 | 18 | IRVariable left = convert(_binOp.leftExpression(), *commonType); |
944 | 18 | IRVariable right = convert(_binOp.rightExpression(), *type(_binOp.rightExpression()).mobileType()); |
945 | 18 | define(_binOp) << shiftOperation(_binOp.getOperator(), left, right) << "\n"; |
946 | 18 | } |
947 | 4.74k | else |
948 | 4.74k | { |
949 | 4.74k | std::string left = expressionAsType(_binOp.leftExpression(), *commonType); |
950 | 4.74k | std::string right = expressionAsType(_binOp.rightExpression(), *commonType); |
951 | 4.74k | define(_binOp) << binaryOperation(_binOp.getOperator(), *commonType, left, right) << "\n"; |
952 | 4.74k | } |
953 | 6.79k | return false; |
954 | 6.91k | } |
955 | | |
956 | | void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) |
957 | 3.01k | { |
958 | 3.01k | setLocation(_functionCall); |
959 | 3.01k | auto functionCallKind = *_functionCall.annotation().kind; |
960 | | |
961 | 3.01k | if (functionCallKind == FunctionCallKind::TypeConversion) |
962 | 414 | { |
963 | 414 | solAssert( |
964 | 414 | _functionCall.expression().annotation().type->category() == Type::Category::TypeType, |
965 | 414 | "Expected category to be TypeType" |
966 | 414 | ); |
967 | 414 | solAssert(_functionCall.arguments().size() == 1, "Expected one argument for type conversion"); |
968 | 414 | define(_functionCall, *_functionCall.arguments().front()); |
969 | 414 | return; |
970 | 414 | } |
971 | | |
972 | 2.60k | FunctionTypePointer functionType = nullptr; |
973 | 2.60k | if (functionCallKind == FunctionCallKind::StructConstructorCall) |
974 | 0 | { |
975 | 0 | auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type); |
976 | 0 | auto const& structType = dynamic_cast<StructType const&>(*type.actualType()); |
977 | 0 | functionType = structType.constructorType(); |
978 | 0 | } |
979 | 2.60k | else |
980 | 2.60k | functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type); |
981 | | |
982 | 2.60k | TypePointers parameterTypes = functionType->parameterTypes(); |
983 | | |
984 | 2.60k | std::vector<ASTPointer<Expression const>> const& arguments = _functionCall.sortedArguments(); |
985 | | |
986 | 2.60k | if (functionCallKind == FunctionCallKind::StructConstructorCall) |
987 | 0 | { |
988 | 0 | TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type); |
989 | 0 | auto const& structType = dynamic_cast<StructType const&>(*type.actualType()); |
990 | |
|
991 | 0 | define(_functionCall) << m_utils.allocateMemoryStructFunction(structType) << "()\n"; |
992 | |
|
993 | 0 | MemberList::MemberMap members = structType.nativeMembers(nullptr); |
994 | |
|
995 | 0 | solAssert(members.size() == arguments.size(), "Struct parameter mismatch."); |
996 | |
|
997 | 0 | for (size_t i = 0; i < arguments.size(); i++) |
998 | 0 | { |
999 | 0 | IRVariable converted = convert(*arguments[i], *parameterTypes[i]); |
1000 | 0 | appendCode() << |
1001 | 0 | m_utils.writeToMemoryFunction(*functionType->parameterTypes()[i]) << |
1002 | 0 | "(add(" << |
1003 | 0 | IRVariable(_functionCall).part("mpos").name() << |
1004 | 0 | ", " << |
1005 | 0 | structType.memoryOffsetOfMember(members[i].name) << |
1006 | 0 | "), " << |
1007 | 0 | converted.commaSeparatedList() << |
1008 | 0 | ")\n"; |
1009 | 0 | } |
1010 | |
|
1011 | 0 | return; |
1012 | 0 | } |
1013 | | |
1014 | 2.60k | switch (functionType->kind()) |
1015 | 2.60k | { |
1016 | 0 | case FunctionType::Kind::Declaration: |
1017 | 0 | solAssert(false, "Attempted to generate code for calling a function definition."); |
1018 | 0 | break; |
1019 | 198 | case FunctionType::Kind::Internal: |
1020 | 198 | { |
1021 | 198 | FunctionDefinition const* functionDef = ASTNode::resolveFunctionCall(_functionCall, &m_context.mostDerivedContract()); |
1022 | | |
1023 | 198 | solAssert(!functionType->takesArbitraryParameters()); |
1024 | | |
1025 | 198 | std::vector<std::string> args; |
1026 | 198 | if (functionType->hasBoundFirstArgument()) |
1027 | 3 | args += IRVariable(_functionCall.expression()).part("self").stackSlots(); |
1028 | | |
1029 | 339 | for (size_t i = 0; i < arguments.size(); ++i) |
1030 | 141 | args += convert(*arguments[i], *parameterTypes[i]).stackSlots(); |
1031 | | |
1032 | 198 | if (functionDef) |
1033 | 176 | { |
1034 | 176 | solAssert(functionDef->isImplemented()); |
1035 | | |
1036 | 176 | define(_functionCall) << |
1037 | 176 | m_context.enqueueFunctionForCodeGeneration(*functionDef) << |
1038 | 176 | "(" << |
1039 | 176 | joinHumanReadable(args) << |
1040 | 176 | ")\n"; |
1041 | 176 | } |
1042 | 22 | else |
1043 | 22 | { |
1044 | 22 | YulArity arity = YulArity::fromType(*functionType); |
1045 | 22 | m_context.internalFunctionCalledThroughDispatch(arity); |
1046 | | |
1047 | 22 | define(_functionCall) << |
1048 | 22 | IRNames::internalDispatch(arity) << |
1049 | 22 | "(" << |
1050 | 22 | IRVariable(_functionCall.expression()).part("functionIdentifier").name() << |
1051 | 22 | joinHumanReadablePrefixed(args) << |
1052 | 22 | ")\n"; |
1053 | 22 | } |
1054 | 198 | break; |
1055 | 0 | } |
1056 | 102 | case FunctionType::Kind::External: |
1057 | 149 | case FunctionType::Kind::DelegateCall: |
1058 | 149 | appendExternalFunctionCall(_functionCall, arguments); |
1059 | 149 | break; |
1060 | 84 | case FunctionType::Kind::BareCall: |
1061 | 84 | case FunctionType::Kind::BareDelegateCall: |
1062 | 84 | case FunctionType::Kind::BareStaticCall: |
1063 | 84 | appendBareCall(_functionCall, arguments); |
1064 | 84 | break; |
1065 | 0 | case FunctionType::Kind::BareCallCode: |
1066 | 0 | solAssert(false, "Callcode has been removed."); |
1067 | 796 | case FunctionType::Kind::Event: |
1068 | 796 | { |
1069 | 796 | auto const& event = dynamic_cast<EventDefinition const&>(functionType->declaration()); |
1070 | 796 | TypePointers paramTypes = functionType->parameterTypes(); |
1071 | 796 | ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector()); |
1072 | | |
1073 | 796 | std::vector<IRVariable> indexedArgs; |
1074 | 796 | std::vector<std::string> nonIndexedArgs; |
1075 | 796 | TypePointers nonIndexedArgTypes; |
1076 | 796 | TypePointers nonIndexedParamTypes; |
1077 | 796 | if (!event.isAnonymous()) |
1078 | 796 | define(indexedArgs.emplace_back(m_context.newYulVariable(), *TypeProvider::uint256())) << |
1079 | 796 | formatNumber(u256(h256::Arith(keccak256(functionType->externalSignature())))) << "\n"; |
1080 | 1.59k | for (size_t i = 0; i < event.parameters().size(); ++i) |
1081 | 796 | { |
1082 | 796 | Expression const& arg = *arguments[i]; |
1083 | 796 | if (event.parameters()[i]->isIndexed()) |
1084 | 0 | { |
1085 | 0 | std::string value; |
1086 | 0 | if (auto const& referenceType = dynamic_cast<ReferenceType const*>(paramTypes[i])) |
1087 | 0 | define(indexedArgs.emplace_back(m_context.newYulVariable(), *TypeProvider::uint256())) << |
1088 | 0 | m_utils.packedHashFunction({arg.annotation().type}, {referenceType}) << |
1089 | 0 | "(" << |
1090 | 0 | IRVariable(arg).commaSeparatedList() << |
1091 | 0 | ")\n"; |
1092 | 0 | else if (auto functionType = dynamic_cast<FunctionType const*>(paramTypes[i])) |
1093 | 0 | { |
1094 | 0 | solAssert( |
1095 | 0 | IRVariable(arg).type() == *functionType && |
1096 | 0 | functionType->kind() == FunctionType::Kind::External && |
1097 | 0 | !functionType->hasBoundFirstArgument(), |
1098 | 0 | "" |
1099 | 0 | ); |
1100 | 0 | define(indexedArgs.emplace_back(m_context.newYulVariable(), *TypeProvider::fixedBytes(32))) << |
1101 | 0 | m_utils.combineExternalFunctionIdFunction() << |
1102 | 0 | "(" << |
1103 | 0 | IRVariable(arg).commaSeparatedList() << |
1104 | 0 | ")\n"; |
1105 | 0 | } |
1106 | 0 | else |
1107 | 0 | { |
1108 | 0 | solAssert(parameterTypes[i]->sizeOnStack() == 1, ""); |
1109 | 0 | indexedArgs.emplace_back(convertAndCleanup(arg, *parameterTypes[i])); |
1110 | 0 | } |
1111 | 0 | } |
1112 | 796 | else |
1113 | 796 | { |
1114 | 796 | nonIndexedArgs += IRVariable(arg).stackSlots(); |
1115 | 796 | nonIndexedArgTypes.push_back(arg.annotation().type); |
1116 | 796 | nonIndexedParamTypes.push_back(paramTypes[i]); |
1117 | 796 | } |
1118 | 796 | } |
1119 | 796 | solAssert(indexedArgs.size() <= 4, "Too many indexed arguments."); |
1120 | 796 | Whiskers templ(R"({ |
1121 | 796 | let <pos> := <allocateUnbounded>() |
1122 | 796 | let <end> := <encode>(<pos> <nonIndexedArgs>) |
1123 | 796 | <log>(<pos>, sub(<end>, <pos>) <indexedArgs>) |
1124 | 796 | })"); |
1125 | 796 | templ("pos", m_context.newYulVariable()); |
1126 | 796 | templ("end", m_context.newYulVariable()); |
1127 | 796 | templ("allocateUnbounded", m_utils.allocateUnboundedFunction()); |
1128 | 796 | templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes)); |
1129 | 796 | templ("nonIndexedArgs", joinHumanReadablePrefixed(nonIndexedArgs)); |
1130 | 796 | templ("log", "log" + std::to_string(indexedArgs.size())); |
1131 | 796 | templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs | ranges::views::transform([&](auto const& _arg) { |
1132 | 796 | return _arg.commaSeparatedList(); |
1133 | 796 | }))); |
1134 | 796 | appendCode() << templ.render(); |
1135 | 796 | break; |
1136 | 0 | } |
1137 | 7 | case FunctionType::Kind::Wrap: |
1138 | 8 | case FunctionType::Kind::Unwrap: |
1139 | 8 | { |
1140 | 8 | solAssert(arguments.size() == 1); |
1141 | 8 | FunctionType::Kind kind = functionType->kind(); |
1142 | 8 | if (kind == FunctionType::Kind::Wrap) |
1143 | 8 | solAssert( |
1144 | 1 | type(*arguments.at(0)).isImplicitlyConvertibleTo( |
1145 | 1 | dynamic_cast<UserDefinedValueType const&>(type(_functionCall)).underlyingType() |
1146 | 1 | ), |
1147 | 1 | "" |
1148 | 1 | ); |
1149 | 1 | else |
1150 | 8 | solAssert(type(*arguments.at(0)).category() == Type::Category::UserDefinedValueType); |
1151 | | |
1152 | 8 | define(_functionCall, *arguments.at(0)); |
1153 | 8 | break; |
1154 | 7 | } |
1155 | 104 | case FunctionType::Kind::Assert: |
1156 | 111 | case FunctionType::Kind::Require: |
1157 | 111 | { |
1158 | 111 | solAssert(arguments.size() > 0, "Expected at least one parameter for require/assert"); |
1159 | 111 | solAssert(arguments.size() <= 2, "Expected no more than two parameters for require/assert"); |
1160 | | |
1161 | 111 | Type const* messageArgumentType = arguments.size() == 2 ? arguments[1]->annotation().type : nullptr; |
1162 | | |
1163 | 111 | auto const* magicType = dynamic_cast<MagicType const*>(messageArgumentType); |
1164 | 111 | if (magicType && magicType->kind() == MagicType::Kind::Error) |
1165 | 0 | { |
1166 | 0 | auto const& errorConstructorCall = dynamic_cast<FunctionCall const&>(*arguments[1]); |
1167 | 0 | appendCode() << m_utils.requireWithErrorFunction(errorConstructorCall) << "(" <<IRVariable(*arguments[0]).name(); |
1168 | 0 | for (auto argument: errorConstructorCall.arguments()) |
1169 | 0 | if (argument->annotation().type->sizeOnStack() > 0) |
1170 | 0 | appendCode() << ", " << IRVariable(*argument).commaSeparatedList(); |
1171 | 0 | appendCode() << ")\n"; |
1172 | 0 | } |
1173 | 111 | else |
1174 | 111 | { |
1175 | | // This option only removes strings, not custom errors |
1176 | 111 | if (m_context.revertStrings() == RevertStrings::Strip) |
1177 | 0 | messageArgumentType = nullptr; |
1178 | 111 | ASTPointer<Expression const> stringArgumentExpression = messageArgumentType ? arguments[1] : nullptr; |
1179 | 111 | std::string requireOrAssertFunction = m_utils.requireOrAssertFunction( |
1180 | 111 | functionType->kind() == FunctionType::Kind::Assert, |
1181 | 111 | messageArgumentType, |
1182 | 111 | stringArgumentExpression |
1183 | 111 | ); |
1184 | 111 | appendCode() << std::move(requireOrAssertFunction) << "(" << IRVariable(*arguments[0]).name(); |
1185 | 111 | if (messageArgumentType && messageArgumentType->sizeOnStack() > 0) |
1186 | 7 | appendCode() << ", " << IRVariable(*arguments[1]).commaSeparatedList(); |
1187 | 111 | appendCode() << ")\n"; |
1188 | 111 | } |
1189 | | |
1190 | 111 | break; |
1191 | 104 | } |
1192 | 42 | case FunctionType::Kind::ABIEncode: |
1193 | 42 | case FunctionType::Kind::ABIEncodePacked: |
1194 | 44 | case FunctionType::Kind::ABIEncodeWithSelector: |
1195 | 44 | case FunctionType::Kind::ABIEncodeCall: |
1196 | 48 | case FunctionType::Kind::ABIEncodeWithSignature: |
1197 | 48 | { |
1198 | 48 | bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked; |
1199 | 48 | solAssert(functionType->padArguments() != isPacked); |
1200 | 48 | bool const hasSelectorOrSignature = |
1201 | 48 | functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector || |
1202 | 46 | functionType->kind() == FunctionType::Kind::ABIEncodeCall || |
1203 | 46 | functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature; |
1204 | | |
1205 | 48 | TypePointers argumentTypes; |
1206 | 48 | TypePointers targetTypes; |
1207 | 48 | std::vector<std::string> argumentVars; |
1208 | 48 | std::string selector; |
1209 | 48 | std::vector<ASTPointer<Expression const>> argumentsOfEncodeFunction; |
1210 | | |
1211 | 48 | if (functionType->kind() == FunctionType::Kind::ABIEncodeCall) |
1212 | 0 | { |
1213 | 0 | solAssert(arguments.size() == 2); |
1214 | | // Account for tuples with one component which become that component |
1215 | 0 | if (type(*arguments[1]).category() == Type::Category::Tuple) |
1216 | 0 | { |
1217 | 0 | auto const& tupleExpression = dynamic_cast<TupleExpression const&>(*arguments[1]); |
1218 | 0 | for (auto component: tupleExpression.components()) |
1219 | 0 | argumentsOfEncodeFunction.push_back(component); |
1220 | 0 | } |
1221 | 0 | else |
1222 | 0 | argumentsOfEncodeFunction.push_back(arguments[1]); |
1223 | 0 | } |
1224 | 48 | else |
1225 | 87 | for (size_t i = 0; i < arguments.size(); ++i) |
1226 | 39 | { |
1227 | | // ignore selector |
1228 | 39 | if (hasSelectorOrSignature && i == 0) |
1229 | 6 | continue; |
1230 | 33 | argumentsOfEncodeFunction.push_back(arguments[i]); |
1231 | 33 | } |
1232 | | |
1233 | 48 | for (auto const& argument: argumentsOfEncodeFunction) |
1234 | 33 | { |
1235 | 33 | argumentTypes.emplace_back(&type(*argument)); |
1236 | 33 | argumentVars += IRVariable(*argument).stackSlots(); |
1237 | 33 | } |
1238 | | |
1239 | 48 | if (functionType->kind() == FunctionType::Kind::ABIEncodeCall) |
1240 | 0 | { |
1241 | 0 | auto encodedFunctionType = dynamic_cast<FunctionType const*>(arguments.front()->annotation().type); |
1242 | 0 | solAssert(encodedFunctionType); |
1243 | 0 | encodedFunctionType = encodedFunctionType->asExternallyCallableFunction(false); |
1244 | 0 | solAssert(encodedFunctionType); |
1245 | 0 | targetTypes = encodedFunctionType->parameterTypes(); |
1246 | 0 | } |
1247 | 48 | else |
1248 | 48 | for (auto const& argument: argumentsOfEncodeFunction) |
1249 | 33 | targetTypes.emplace_back(type(*argument).fullEncodingType(false, true, isPacked)); |
1250 | | |
1251 | | |
1252 | 48 | if (functionType->kind() == FunctionType::Kind::ABIEncodeCall) |
1253 | 0 | { |
1254 | 0 | auto const& selectorType = dynamic_cast<FunctionType const&>(type(*arguments.front())); |
1255 | 0 | if (selectorType.kind() == FunctionType::Kind::Declaration) |
1256 | 0 | { |
1257 | 0 | solAssert(selectorType.hasDeclaration()); |
1258 | 0 | selector = formatNumber(selectorType.externalIdentifier() << (256 - 32)); |
1259 | 0 | } |
1260 | 0 | else |
1261 | 0 | { |
1262 | 0 | selector = convert( |
1263 | 0 | IRVariable(*arguments[0]).part("functionSelector"), |
1264 | 0 | *TypeProvider::fixedBytes(4) |
1265 | 0 | ).name(); |
1266 | 0 | } |
1267 | 0 | } |
1268 | 48 | else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature) |
1269 | 4 | { |
1270 | | // hash the signature |
1271 | 4 | Type const& selectorType = type(*arguments.front()); |
1272 | 4 | if (auto const* stringType = dynamic_cast<StringLiteralType const*>(&selectorType)) |
1273 | 4 | selector = formatNumber(util::selectorFromSignatureU256(stringType->value())); |
1274 | 0 | else |
1275 | 0 | { |
1276 | | // Used to reset the free memory pointer later. |
1277 | | // TODO This is an abuse of the `allocateUnbounded` function. |
1278 | | // We might want to introduce a new set of memory handling functions here |
1279 | | // a la "setMemoryCheckPoint" and "freeUntilCheckPoint". |
1280 | 0 | std::string freeMemoryPre = m_context.newYulVariable(); |
1281 | 0 | appendCode() << "let " << freeMemoryPre << " := " << m_utils.allocateUnboundedFunction() << "()\n"; |
1282 | 0 | IRVariable array = convert(*arguments[0], *TypeProvider::bytesMemory()); |
1283 | 0 | IRVariable hashVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(32)); |
1284 | |
|
1285 | 0 | std::string dataAreaFunction = m_utils.arrayDataAreaFunction(*TypeProvider::bytesMemory()); |
1286 | 0 | std::string arrayLengthFunction = m_utils.arrayLengthFunction(*TypeProvider::bytesMemory()); |
1287 | 0 | define(hashVariable) << |
1288 | 0 | "keccak256(" << |
1289 | 0 | (dataAreaFunction + "(" + array.commaSeparatedList() + ")") << |
1290 | 0 | ", " << |
1291 | 0 | (arrayLengthFunction + "(" + array.commaSeparatedList() +")") << |
1292 | 0 | ")\n"; |
1293 | 0 | IRVariable selectorVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(4)); |
1294 | 0 | define(selectorVariable, hashVariable); |
1295 | 0 | selector = selectorVariable.name(); |
1296 | 0 | appendCode() << m_utils.finalizeAllocationFunction() << "(" << freeMemoryPre << ", 0)\n"; |
1297 | 0 | } |
1298 | 4 | } |
1299 | 44 | else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector) |
1300 | 2 | selector = convert(*arguments.front(), *TypeProvider::fixedBytes(4)).name(); |
1301 | | |
1302 | 48 | Whiskers templ(R"( |
1303 | 48 | let <data> := <allocateUnbounded>() |
1304 | 48 | let <memPtr> := add(<data>, 0x20) |
1305 | 48 | <?+selector> |
1306 | 48 | mstore(<memPtr>, <selector>) |
1307 | 48 | <memPtr> := add(<memPtr>, 4) |
1308 | 48 | </+selector> |
1309 | 48 | let <mend> := <encode>(<memPtr><arguments>) |
1310 | 48 | mstore(<data>, sub(<mend>, add(<data>, 0x20))) |
1311 | 48 | <finalizeAllocation>(<data>, sub(<mend>, <data>)) |
1312 | 48 | )"); |
1313 | 48 | templ("data", IRVariable(_functionCall).part("mpos").name()); |
1314 | 48 | templ("allocateUnbounded", m_utils.allocateUnboundedFunction()); |
1315 | 48 | templ("memPtr", m_context.newYulVariable()); |
1316 | 48 | templ("mend", m_context.newYulVariable()); |
1317 | 48 | templ("selector", selector); |
1318 | 48 | templ("encode", |
1319 | 48 | isPacked ? |
1320 | 0 | m_context.abiFunctions().tupleEncoderPacked(argumentTypes, targetTypes) : |
1321 | 48 | m_context.abiFunctions().tupleEncoder(argumentTypes, targetTypes, false) |
1322 | 48 | ); |
1323 | 48 | templ("arguments", joinHumanReadablePrefixed(argumentVars)); |
1324 | 48 | templ("finalizeAllocation", m_utils.finalizeAllocationFunction()); |
1325 | | |
1326 | 48 | appendCode() << templ.render(); |
1327 | 48 | break; |
1328 | 44 | } |
1329 | 14 | case FunctionType::Kind::ABIDecode: |
1330 | 14 | { |
1331 | 14 | Whiskers templ(R"( |
1332 | 14 | <?+retVars>let <retVars> := </+retVars> <abiDecode>(<offset>, add(<offset>, <length>)) |
1333 | 14 | )"); |
1334 | | |
1335 | 14 | Type const* firstArgType = arguments.front()->annotation().type; |
1336 | 14 | TypePointers targetTypes; |
1337 | | |
1338 | 14 | if (TupleType const* targetTupleType = dynamic_cast<TupleType const*>(_functionCall.annotation().type)) |
1339 | 0 | targetTypes = targetTupleType->components(); |
1340 | 14 | else |
1341 | 14 | targetTypes = TypePointers{_functionCall.annotation().type}; |
1342 | | |
1343 | 14 | if ( |
1344 | 14 | auto referenceType = dynamic_cast<ReferenceType const*>(firstArgType); |
1345 | 14 | referenceType && referenceType->dataStoredIn(DataLocation::CallData) |
1346 | 14 | ) |
1347 | 0 | { |
1348 | 0 | solAssert(referenceType->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata())); |
1349 | 0 | IRVariable var = convert(*arguments[0], *TypeProvider::bytesCalldata()); |
1350 | 0 | templ("abiDecode", m_context.abiFunctions().tupleDecoder(targetTypes, false)); |
1351 | 0 | templ("offset", var.part("offset").name()); |
1352 | 0 | templ("length", var.part("length").name()); |
1353 | 0 | } |
1354 | 14 | else |
1355 | 14 | { |
1356 | 14 | IRVariable var = convert(*arguments[0], *TypeProvider::bytesMemory()); |
1357 | 14 | templ("abiDecode", m_context.abiFunctions().tupleDecoder(targetTypes, true)); |
1358 | 14 | templ("offset", "add(" + var.part("mpos").name() + ", 32)"); |
1359 | 14 | templ("length", |
1360 | 14 | m_utils.arrayLengthFunction(*TypeProvider::bytesMemory()) + "(" + var.part("mpos").name() + ")" |
1361 | 14 | ); |
1362 | 14 | } |
1363 | 14 | templ("retVars", IRVariable(_functionCall).commaSeparatedList()); |
1364 | | |
1365 | 14 | appendCode() << templ.render(); |
1366 | 14 | break; |
1367 | 44 | } |
1368 | 11 | case FunctionType::Kind::Revert: |
1369 | 11 | { |
1370 | 11 | solAssert(arguments.size() == parameterTypes.size()); |
1371 | 11 | solAssert(arguments.size() <= 1); |
1372 | 11 | solAssert( |
1373 | 11 | arguments.empty() || |
1374 | 11 | arguments.front()->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()), |
1375 | 11 | ""); |
1376 | 11 | if (m_context.revertStrings() == RevertStrings::Strip || arguments.empty()) |
1377 | 0 | appendCode() << "revert(0, 0)\n"; |
1378 | 11 | else |
1379 | 11 | revertWithError( |
1380 | 11 | "Error(string)", |
1381 | 11 | {TypeProvider::stringMemory()}, |
1382 | 11 | {arguments.front()} |
1383 | 11 | ); |
1384 | 11 | break; |
1385 | 44 | } |
1386 | | // Array creation using new |
1387 | 629 | case FunctionType::Kind::ObjectCreation: |
1388 | 629 | { |
1389 | 629 | ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type); |
1390 | 629 | solAssert(arguments.size() == 1); |
1391 | | |
1392 | 629 | IRVariable value = convert(*arguments[0], *TypeProvider::uint256()); |
1393 | 629 | define(_functionCall) << |
1394 | 629 | m_utils.allocateAndInitializeMemoryArrayFunction(arrayType) << |
1395 | 629 | "(" << |
1396 | 629 | value.commaSeparatedList() << |
1397 | 629 | ")\n"; |
1398 | 629 | break; |
1399 | 44 | } |
1400 | 4 | case FunctionType::Kind::KECCAK256: |
1401 | 4 | { |
1402 | 4 | solAssert(arguments.size() == 1); |
1403 | | |
1404 | 4 | ArrayType const* arrayType = TypeProvider::bytesMemory(); |
1405 | | |
1406 | 4 | if (auto const* stringLiteral = dynamic_cast<StringLiteralType const*>(arguments.front()->annotation().type)) |
1407 | 2 | { |
1408 | | // Optimization: Compute keccak256 on string literals at compile-time. |
1409 | 2 | define(_functionCall) << |
1410 | 2 | ("0x" + keccak256(stringLiteral->value()).hex()) << |
1411 | 2 | "\n"; |
1412 | 2 | } |
1413 | 2 | else |
1414 | 2 | { |
1415 | 2 | auto array = convert(*arguments[0], *arrayType); |
1416 | | |
1417 | 2 | std::string dataAreaFunction = m_utils.arrayDataAreaFunction(*arrayType); |
1418 | 2 | std::string arrayLengthFunction = m_utils.arrayLengthFunction(*arrayType); |
1419 | 2 | define(_functionCall) << |
1420 | 2 | "keccak256(" << |
1421 | 2 | (dataAreaFunction + "(" + array.commaSeparatedList() + ")") << |
1422 | 2 | ", " << |
1423 | 2 | (arrayLengthFunction + "(" + array.commaSeparatedList() +")") << |
1424 | 2 | ")\n"; |
1425 | 2 | } |
1426 | 4 | break; |
1427 | 44 | } |
1428 | 0 | case FunctionType::Kind::ERC7201: |
1429 | 0 | { |
1430 | 0 | solAssert(arguments.size() == 1); |
1431 | 0 | Type const* argType = arguments.front()->annotation().type; |
1432 | 0 | solAssert(argType); |
1433 | 0 | if (dynamic_cast<StringLiteralType const*>(argType)) |
1434 | 0 | { |
1435 | 0 | std::optional<u256> slot = erc7201CompileTimeValue(_functionCall); |
1436 | 0 | solAssert(slot.has_value()); |
1437 | 0 | define(_functionCall) << formatNumber(*slot) << "\n"; |
1438 | 0 | } |
1439 | 0 | else |
1440 | 0 | { |
1441 | 0 | Whiskers templ(R"( |
1442 | 0 | <erc7201Builtin>(<arrayDataArea>(<namespaceID>), <arrayLength>(<namespaceID>)) |
1443 | 0 | )"); |
1444 | |
|
1445 | 0 | IRVariable stringArg = convert(*arguments[0], *TypeProvider::stringMemory()); |
1446 | 0 | solAssert(stringArg.stackSlots().size() == 1); |
1447 | 0 | std::string namespaceID = stringArg.stackSlots().front(); |
1448 | |
|
1449 | 0 | templ("arrayDataArea", m_utils.arrayDataAreaFunction(*TypeProvider::stringMemory())); |
1450 | 0 | templ("arrayLength", m_utils.arrayLengthFunction(*TypeProvider::stringMemory())); |
1451 | 0 | templ("namespaceID", namespaceID); |
1452 | 0 | templ("erc7201Builtin", m_utils.erc7201()); |
1453 | |
|
1454 | 0 | define(_functionCall) << templ.render(); |
1455 | 0 | } |
1456 | 0 | break; |
1457 | 44 | } |
1458 | 30 | case FunctionType::Kind::ArrayPop: |
1459 | 30 | { |
1460 | 30 | solAssert(functionType->hasBoundFirstArgument()); |
1461 | 30 | solAssert(functionType->parameterTypes().empty()); |
1462 | 30 | ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType()); |
1463 | 30 | solAssert(arrayType); |
1464 | 30 | define(_functionCall) << |
1465 | 30 | m_utils.storageArrayPopFunction(*arrayType) << |
1466 | 30 | "(" << |
1467 | 30 | IRVariable(_functionCall.expression()).commaSeparatedList() << |
1468 | 30 | ")\n"; |
1469 | 30 | break; |
1470 | 44 | } |
1471 | 271 | case FunctionType::Kind::ArrayPush: |
1472 | 271 | { |
1473 | 271 | ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType()); |
1474 | 271 | solAssert(arrayType); |
1475 | | |
1476 | 271 | if (arguments.empty()) |
1477 | 3 | { |
1478 | 3 | auto slotName = m_context.newYulVariable(); |
1479 | 3 | auto offsetName = m_context.newYulVariable(); |
1480 | 3 | appendCode() << "let " << slotName << ", " << offsetName << " := " << |
1481 | 3 | m_utils.storageArrayPushZeroFunction(*arrayType) << |
1482 | 3 | "(" << IRVariable(_functionCall.expression()).commaSeparatedList() << ")\n"; |
1483 | 3 | setLValue(_functionCall, IRLValue{ |
1484 | 3 | *arrayType->baseType(), |
1485 | 3 | IRLValue::Storage{ |
1486 | 3 | slotName, |
1487 | 3 | offsetName, |
1488 | 3 | } |
1489 | 3 | }); |
1490 | 3 | } |
1491 | 268 | else |
1492 | 268 | { |
1493 | 268 | IRVariable argument = |
1494 | 268 | arrayType->baseType()->isValueType() ? |
1495 | 95 | convert(*arguments.front(), *arrayType->baseType()) : |
1496 | 268 | *arguments.front(); |
1497 | | |
1498 | 268 | appendCode() << |
1499 | 268 | m_utils.storageArrayPushFunction(*arrayType, &argument.type()) << |
1500 | 268 | "(" << |
1501 | 268 | IRVariable(_functionCall.expression()).commaSeparatedList() << |
1502 | 268 | (argument.stackSlots().empty() ? "" : (", " + argument.commaSeparatedList())) << |
1503 | 268 | ")\n"; |
1504 | 268 | } |
1505 | 271 | break; |
1506 | 44 | } |
1507 | 0 | case FunctionType::Kind::StringConcat: |
1508 | 28 | case FunctionType::Kind::BytesConcat: |
1509 | 28 | { |
1510 | 28 | TypePointers argumentTypes; |
1511 | 28 | std::vector<std::string> argumentVars; |
1512 | 28 | for (ASTPointer<Expression const> const& argument: arguments) |
1513 | 55 | { |
1514 | 55 | argumentTypes.emplace_back(&type(*argument)); |
1515 | 55 | argumentVars += IRVariable(*argument).stackSlots(); |
1516 | 55 | } |
1517 | 28 | define(IRVariable(_functionCall)) << |
1518 | 28 | m_utils.bytesOrStringConcatFunction(argumentTypes, functionType->kind()) << |
1519 | 28 | "(" << |
1520 | 28 | joinHumanReadable(argumentVars) << |
1521 | 28 | ")\n"; |
1522 | 28 | break; |
1523 | 0 | } |
1524 | 2 | case FunctionType::Kind::Error: |
1525 | 53 | case FunctionType::Kind::MetaType: |
1526 | 53 | { |
1527 | 53 | break; |
1528 | 2 | } |
1529 | 8 | case FunctionType::Kind::AddMod: |
1530 | 11 | case FunctionType::Kind::MulMod: |
1531 | 11 | { |
1532 | 11 | static std::map<FunctionType::Kind, std::string> functions = { |
1533 | 11 | {FunctionType::Kind::AddMod, "addmod"}, |
1534 | 11 | {FunctionType::Kind::MulMod, "mulmod"}, |
1535 | 11 | }; |
1536 | 11 | solAssert(functions.find(functionType->kind()) != functions.end()); |
1537 | 11 | solAssert(arguments.size() == 3 && parameterTypes.size() == 3); |
1538 | | |
1539 | 11 | IRVariable modulus(m_context.newYulVariable(), *(parameterTypes[2])); |
1540 | 11 | define(modulus, *arguments[2]); |
1541 | 11 | Whiskers templ("if iszero(<modulus>) { <panic>() }\n"); |
1542 | 11 | templ("modulus", modulus.name()); |
1543 | 11 | templ("panic", m_utils.panicFunction(PanicCode::DivisionByZero)); |
1544 | 11 | appendCode() << templ.render(); |
1545 | | |
1546 | 11 | std::string args; |
1547 | 33 | for (size_t i = 0; i < 2; ++i) |
1548 | 22 | args += expressionAsType(*arguments[i], *(parameterTypes[i])) + ", "; |
1549 | 11 | args += modulus.name(); |
1550 | 11 | define(_functionCall) << functions[functionType->kind()] << "(" << args << ")\n"; |
1551 | 11 | break; |
1552 | 8 | } |
1553 | 0 | case FunctionType::Kind::GasLeft: |
1554 | 0 | case FunctionType::Kind::Selfdestruct: |
1555 | 0 | case FunctionType::Kind::BlockHash: |
1556 | 0 | case FunctionType::Kind::BlobHash: |
1557 | 0 | { |
1558 | 0 | static std::map<FunctionType::Kind, std::string> functions = { |
1559 | 0 | {FunctionType::Kind::GasLeft, "gas"}, |
1560 | 0 | {FunctionType::Kind::Selfdestruct, "selfdestruct"}, |
1561 | 0 | {FunctionType::Kind::BlockHash, "blockhash"}, |
1562 | 0 | {FunctionType::Kind::BlobHash, "blobhash"}, |
1563 | 0 | }; |
1564 | 0 | solAssert(functions.find(functionType->kind()) != functions.end()); |
1565 | |
|
1566 | 0 | std::string args; |
1567 | 0 | for (size_t i = 0; i < arguments.size(); ++i) |
1568 | 0 | args += (args.empty() ? "" : ", ") + expressionAsType(*arguments[i], *(parameterTypes[i])); |
1569 | 0 | define(_functionCall) << functions[functionType->kind()] << "(" << args << ")\n"; |
1570 | 0 | break; |
1571 | 0 | } |
1572 | 90 | case FunctionType::Kind::Creation: |
1573 | 90 | { |
1574 | 90 | solAssert(!functionType->gasSet(), "Gas limit set for contract creation."); |
1575 | 90 | solAssert( |
1576 | 90 | functionType->returnParameterTypes().size() == 1, |
1577 | 90 | "Constructor should return only one type" |
1578 | 90 | ); |
1579 | | |
1580 | 90 | TypePointers argumentTypes; |
1581 | 90 | std::vector<std::string> constructorParams; |
1582 | 90 | for (ASTPointer<Expression const> const& arg: arguments) |
1583 | 0 | { |
1584 | 0 | argumentTypes.push_back(arg->annotation().type); |
1585 | 0 | constructorParams += IRVariable{*arg}.stackSlots(); |
1586 | 0 | } |
1587 | | |
1588 | 90 | ContractDefinition const* contract = |
1589 | 90 | &dynamic_cast<ContractType const&>(*functionType->returnParameterTypes().front()).contractDefinition(); |
1590 | 90 | m_context.addSubObject(contract); |
1591 | | |
1592 | 90 | Whiskers t(R"( |
1593 | 90 | let <memPos> := <allocateUnbounded>() |
1594 | 90 | let <memEnd> := add(<memPos>, datasize("<object>")) |
1595 | 90 | if or(gt(<memEnd>, 0xffffffffffffffff), lt(<memEnd>, <memPos>)) { <panic>() } |
1596 | 90 | datacopy(<memPos>, dataoffset("<object>"), datasize("<object>")) |
1597 | 90 | <memEnd> := <abiEncode>(<memEnd><constructorParams>) |
1598 | 90 | <?saltSet> |
1599 | 90 | let <address> := create2(<value>, <memPos>, sub(<memEnd>, <memPos>), <salt>) |
1600 | 90 | <!saltSet> |
1601 | 90 | let <address> := create(<value>, <memPos>, sub(<memEnd>, <memPos>)) |
1602 | 90 | </saltSet> |
1603 | 90 | <?isTryCall> |
1604 | 90 | let <success> := iszero(iszero(<address>)) |
1605 | 90 | <!isTryCall> |
1606 | 90 | if iszero(<address>) { <forwardingRevert>() } |
1607 | 90 | </isTryCall> |
1608 | 90 | )"); |
1609 | 90 | t("memPos", m_context.newYulVariable()); |
1610 | 90 | t("memEnd", m_context.newYulVariable()); |
1611 | 90 | t("allocateUnbounded", m_utils.allocateUnboundedFunction()); |
1612 | 90 | t("object", IRNames::creationObject(*contract)); |
1613 | 90 | t("panic", m_utils.panicFunction(PanicCode::ResourceError)); |
1614 | 90 | t("abiEncode", |
1615 | 90 | m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false) |
1616 | 90 | ); |
1617 | 90 | t("constructorParams", joinHumanReadablePrefixed(constructorParams)); |
1618 | 90 | t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0"); |
1619 | 90 | t("saltSet", functionType->saltSet()); |
1620 | 90 | if (functionType->saltSet()) |
1621 | 0 | t("salt", IRVariable(_functionCall.expression()).part("salt").name()); |
1622 | 90 | solAssert(IRVariable(_functionCall).stackSlots().size() == 1); |
1623 | 90 | t("address", IRVariable(_functionCall).commaSeparatedList()); |
1624 | 90 | t("isTryCall", _functionCall.annotation().tryCall); |
1625 | 90 | if (_functionCall.annotation().tryCall) |
1626 | 0 | t("success", IRNames::trySuccessConditionVariable(_functionCall)); |
1627 | 90 | else |
1628 | 90 | t("forwardingRevert", m_utils.forwardingRevertFunction()); |
1629 | 90 | appendCode() << t.render(); |
1630 | | |
1631 | 90 | break; |
1632 | 0 | } |
1633 | 0 | case FunctionType::Kind::Send: |
1634 | 12 | case FunctionType::Kind::Transfer: |
1635 | 12 | { |
1636 | 12 | solAssert(arguments.size() == 1 && parameterTypes.size() == 1); |
1637 | 12 | std::string address{IRVariable(_functionCall.expression()).part("address").name()}; |
1638 | 12 | std::string value{expressionAsType(*arguments[0], *(parameterTypes[0]))}; |
1639 | 12 | Whiskers templ(R"( |
1640 | 12 | let <gas> := 0 |
1641 | 12 | if iszero(<value>) { <gas> := <callStipend> } |
1642 | 12 | let <success> := call(<gas>, <address>, <value>, 0, 0, 0, 0) |
1643 | 12 | <?isTransfer> |
1644 | 12 | if iszero(<success>) { <forwardingRevert>() } |
1645 | 12 | </isTransfer> |
1646 | 12 | )"); |
1647 | 12 | templ("gas", m_context.newYulVariable()); |
1648 | 12 | templ("callStipend", toString(evmasm::GasCosts::callStipend)); |
1649 | 12 | templ("address", address); |
1650 | 12 | templ("value", value); |
1651 | 12 | if (functionType->kind() == FunctionType::Kind::Transfer) |
1652 | 12 | templ("success", m_context.newYulVariable()); |
1653 | 0 | else |
1654 | 0 | templ("success", IRVariable(_functionCall).commaSeparatedList()); |
1655 | 12 | templ("isTransfer", functionType->kind() == FunctionType::Kind::Transfer); |
1656 | 12 | templ("forwardingRevert", m_utils.forwardingRevertFunction()); |
1657 | 12 | appendCode() << templ.render(); |
1658 | | |
1659 | 12 | break; |
1660 | 0 | } |
1661 | 4 | case FunctionType::Kind::ECRecover: |
1662 | 57 | case FunctionType::Kind::RIPEMD160: |
1663 | 57 | case FunctionType::Kind::SHA256: |
1664 | 57 | { |
1665 | 57 | solAssert(!_functionCall.annotation().tryCall); |
1666 | 57 | solAssert(!functionType->valueSet()); |
1667 | 57 | solAssert(!functionType->gasSet()); |
1668 | 57 | solAssert(!functionType->hasBoundFirstArgument()); |
1669 | | |
1670 | 57 | static std::map<FunctionType::Kind, std::tuple<unsigned, size_t>> precompiles = { |
1671 | 57 | {FunctionType::Kind::ECRecover, std::make_tuple(1, 0)}, |
1672 | 57 | {FunctionType::Kind::SHA256, std::make_tuple(2, 0)}, |
1673 | 57 | {FunctionType::Kind::RIPEMD160, std::make_tuple(3, 12)}, |
1674 | 57 | }; |
1675 | 57 | auto [ address, offset ] = precompiles[functionType->kind()]; |
1676 | 57 | TypePointers argumentTypes; |
1677 | 57 | std::vector<std::string> argumentStrings; |
1678 | 57 | for (auto const& arg: arguments) |
1679 | 69 | { |
1680 | 69 | argumentTypes.emplace_back(&type(*arg)); |
1681 | 69 | argumentStrings += IRVariable(*arg).stackSlots(); |
1682 | 69 | } |
1683 | 57 | Whiskers templ(R"( |
1684 | 57 | let <pos> := <allocateUnbounded>() |
1685 | 57 | let <end> := <encodeArgs>(<pos> <argumentString>) |
1686 | 57 | <?isECRecover> |
1687 | 57 | mstore(0, 0) |
1688 | 57 | </isECRecover> |
1689 | 57 | let <success> := <call>(<gas>, <address> <?isCall>, 0</isCall>, <pos>, sub(<end>, <pos>), 0, 32) |
1690 | 57 | if iszero(<success>) { <forwardingRevert>() } |
1691 | 57 | let <retVars> := <shl>(mload(0)) |
1692 | 57 | )"); |
1693 | 57 | templ("call", m_context.evmVersion().hasStaticCall() ? "staticcall" : "call"); |
1694 | 57 | templ("isCall", !m_context.evmVersion().hasStaticCall()); |
1695 | 57 | templ("shl", m_utils.shiftLeftFunction(offset * 8)); |
1696 | 57 | templ("allocateUnbounded", m_utils.allocateUnboundedFunction()); |
1697 | 57 | templ("pos", m_context.newYulVariable()); |
1698 | 57 | templ("end", m_context.newYulVariable()); |
1699 | 57 | templ("isECRecover", FunctionType::Kind::ECRecover == functionType->kind()); |
1700 | 57 | if (FunctionType::Kind::ECRecover == functionType->kind()) |
1701 | 4 | templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes)); |
1702 | 53 | else |
1703 | 53 | templ("encodeArgs", m_context.abiFunctions().tupleEncoderPacked(argumentTypes, parameterTypes)); |
1704 | 57 | templ("argumentString", joinHumanReadablePrefixed(argumentStrings)); |
1705 | 57 | templ("address", toString(address)); |
1706 | 57 | templ("success", m_context.newYulVariable()); |
1707 | 57 | templ("retVars", IRVariable(_functionCall).commaSeparatedList()); |
1708 | 57 | templ("forwardingRevert", m_utils.forwardingRevertFunction()); |
1709 | 57 | if (m_context.evmVersion().canOverchargeGasForCall()) |
1710 | | // Send all gas (requires tangerine whistle EVM) |
1711 | 49 | templ("gas", "gas()"); |
1712 | 8 | else |
1713 | 8 | { |
1714 | | // @todo The value 10 is not exact and this could be fine-tuned, |
1715 | | // but this has worked for years in the old code generator. |
1716 | 8 | u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10 + evmasm::GasCosts::callNewAccountGas; |
1717 | 8 | templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")"); |
1718 | 8 | } |
1719 | | |
1720 | 57 | appendCode() << templ.render(); |
1721 | | |
1722 | 57 | break; |
1723 | 57 | } |
1724 | 0 | default: |
1725 | 0 | solUnimplemented("FunctionKind " + toString(static_cast<int>(functionType->kind())) + " not yet implemented"); |
1726 | 2.60k | } |
1727 | 2.60k | } |
1728 | | |
1729 | | void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) |
1730 | 20 | { |
1731 | 20 | setLocation(_options); |
1732 | 20 | FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type); |
1733 | | |
1734 | 20 | solUnimplementedAssert(!previousType.hasBoundFirstArgument()); |
1735 | | |
1736 | | // Copy over existing values. |
1737 | 20 | for (auto const& item: previousType.stackItems()) |
1738 | 40 | define(IRVariable(_options).part(std::get<0>(item)), IRVariable(_options.expression()).part(std::get<0>(item))); |
1739 | | |
1740 | 40 | for (size_t i = 0; i < _options.names().size(); ++i) |
1741 | 20 | { |
1742 | 20 | std::string const& name = *_options.names()[i]; |
1743 | 20 | solAssert(name == "salt" || name == "gas" || name == "value"); |
1744 | | |
1745 | 20 | define(IRVariable(_options).part(name), *_options.options()[i]); |
1746 | 20 | } |
1747 | 20 | } |
1748 | | |
1749 | | bool IRGeneratorForStatements::visit(MemberAccess const& _memberAccess) |
1750 | 1.10k | { |
1751 | | // A shortcut for <address>.code.length. We skip visiting <address>.code and directly visit |
1752 | | // <address>. The actual code is generated in endVisit. |
1753 | 1.10k | if ( |
1754 | 1.10k | auto innerExpression = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()); |
1755 | 1.10k | _memberAccess.memberName() == "length" && |
1756 | 62 | innerExpression && |
1757 | 1 | innerExpression->memberName() == "code" && |
1758 | 0 | innerExpression->expression().annotation().type->category() == Type::Category::Address |
1759 | 1.10k | ) |
1760 | 0 | { |
1761 | 0 | solAssert(innerExpression->annotation().type->category() == Type::Category::Array); |
1762 | | // Skip visiting <address>.code |
1763 | 0 | innerExpression->expression().accept(*this); |
1764 | |
|
1765 | 0 | return false; |
1766 | 0 | } |
1767 | | |
1768 | 1.10k | return true; |
1769 | 1.10k | } |
1770 | | |
1771 | | void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) |
1772 | 1.10k | { |
1773 | 1.10k | setLocation(_memberAccess); |
1774 | | |
1775 | 1.10k | ASTString const& member = _memberAccess.memberName(); |
1776 | 1.10k | auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type); |
1777 | 1.10k | Type::Category objectCategory = _memberAccess.expression().annotation().type->category(); |
1778 | | |
1779 | 1.10k | if (memberFunctionType && memberFunctionType->hasBoundFirstArgument()) |
1780 | 345 | { |
1781 | 345 | define(IRVariable(_memberAccess).part("self"), _memberAccess.expression()); |
1782 | 345 | solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static); |
1783 | 345 | if (memberFunctionType->kind() == FunctionType::Kind::Internal) |
1784 | 3 | assignInternalFunctionIDIfNotCalledDirectly( |
1785 | 3 | _memberAccess, |
1786 | 3 | dynamic_cast<FunctionDefinition const&>(memberFunctionType->declaration()) |
1787 | 3 | ); |
1788 | 342 | else if ( |
1789 | 342 | memberFunctionType->kind() == FunctionType::Kind::ArrayPush || |
1790 | 71 | memberFunctionType->kind() == FunctionType::Kind::ArrayPop |
1791 | 342 | ) |
1792 | 301 | { |
1793 | | // Nothing to do. |
1794 | 301 | } |
1795 | 41 | else |
1796 | 41 | { |
1797 | 41 | auto const& functionDefinition = dynamic_cast<FunctionDefinition const&>(memberFunctionType->declaration()); |
1798 | 41 | solAssert(memberFunctionType->kind() == FunctionType::Kind::DelegateCall); |
1799 | 41 | auto contract = dynamic_cast<ContractDefinition const*>(functionDefinition.scope()); |
1800 | 41 | solAssert(contract && contract->isLibrary()); |
1801 | 41 | define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\n"; |
1802 | 41 | define(IRVariable(_memberAccess).part("functionSelector")) << memberFunctionType->externalIdentifier() << "\n"; |
1803 | 41 | } |
1804 | 345 | return; |
1805 | 345 | } |
1806 | | |
1807 | 762 | switch (objectCategory) |
1808 | 762 | { |
1809 | 106 | case Type::Category::Contract: |
1810 | 106 | { |
1811 | 106 | ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type); |
1812 | 106 | if (type.isSuper()) |
1813 | 106 | solAssert(false); |
1814 | | |
1815 | | // ordinary contract type |
1816 | 106 | else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) |
1817 | 106 | { |
1818 | 106 | u256 identifier; |
1819 | 106 | if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration)) |
1820 | 10 | identifier = FunctionType(*variable).externalIdentifier(); |
1821 | 96 | else if (auto const* function = dynamic_cast<FunctionDefinition const*>(declaration)) |
1822 | 96 | identifier = FunctionType(*function).externalIdentifier(); |
1823 | 0 | else |
1824 | 96 | solAssert(false, "Contract member is neither variable nor function."); |
1825 | | |
1826 | 106 | define(IRVariable(_memberAccess).part("address"), _memberAccess.expression()); |
1827 | 106 | define(IRVariable(_memberAccess).part("functionSelector")) << formatNumber(identifier) << "\n"; |
1828 | 106 | } |
1829 | 0 | else |
1830 | 106 | solAssert(false, "Invalid member access in contract"); |
1831 | 106 | break; |
1832 | 0 | } |
1833 | 0 | case Type::Category::Integer: |
1834 | 0 | { |
1835 | 0 | solAssert(false, "Invalid member access to integer"); |
1836 | 0 | break; |
1837 | 0 | } |
1838 | 109 | case Type::Category::Address: |
1839 | 109 | { |
1840 | 109 | if (member == "balance") |
1841 | 6 | define(_memberAccess) << |
1842 | 6 | "balance(" << |
1843 | 6 | expressionAsType(_memberAccess.expression(), *TypeProvider::address()) << |
1844 | 6 | ")\n"; |
1845 | 103 | else if (member == "code") |
1846 | 7 | { |
1847 | 7 | std::string externalCodeFunction = m_utils.externalCodeFunction(); |
1848 | 7 | define(_memberAccess) << |
1849 | 7 | externalCodeFunction << |
1850 | 7 | "(" << |
1851 | 7 | expressionAsType(_memberAccess.expression(), *TypeProvider::address()) << |
1852 | 7 | ")\n"; |
1853 | 7 | } |
1854 | 96 | else if (member == "codehash") |
1855 | 0 | { |
1856 | 0 | define(_memberAccess) << |
1857 | 0 | "extcodehash(" << |
1858 | 0 | expressionAsType(_memberAccess.expression(), *TypeProvider::address()) << |
1859 | 0 | ")\n"; |
1860 | 0 | } |
1861 | 96 | else if (std::set<std::string>{"send", "transfer"}.count(member)) |
1862 | 12 | { |
1863 | 12 | solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable); |
1864 | 12 | define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression()); |
1865 | 12 | } |
1866 | 84 | else if (std::set<std::string>{"call", "callcode", "delegatecall", "staticcall"}.count(member)) |
1867 | 84 | define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression()); |
1868 | 0 | else |
1869 | 84 | solAssert(false, "Invalid member access to address"); |
1870 | 109 | break; |
1871 | 0 | } |
1872 | 34 | case Type::Category::Function: |
1873 | 34 | if (member == "selector") |
1874 | 30 | { |
1875 | 30 | FunctionType const& functionType = dynamic_cast<FunctionType const&>( |
1876 | 30 | *_memberAccess.expression().annotation().type |
1877 | 30 | ); |
1878 | 30 | if ( |
1879 | 30 | functionType.kind() == FunctionType::Kind::External || |
1880 | 25 | functionType.kind() == FunctionType::Kind::DelegateCall |
1881 | 30 | ) |
1882 | 5 | define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("functionSelector")); |
1883 | 25 | else if ( |
1884 | 25 | functionType.kind() == FunctionType::Kind::Declaration || |
1885 | 6 | functionType.kind() == FunctionType::Kind::Error || |
1886 | | // In some situations, internal function types also provide the "selector" member. |
1887 | | // See Types.cpp for details. |
1888 | 6 | functionType.kind() == FunctionType::Kind::Internal |
1889 | 25 | ) |
1890 | 25 | { |
1891 | 25 | solAssert(functionType.hasDeclaration()); |
1892 | 25 | solAssert( |
1893 | 25 | functionType.kind() == FunctionType::Kind::Error || |
1894 | 25 | functionType.declaration().isPartOfExternalInterface(), |
1895 | 25 | "" |
1896 | 25 | ); |
1897 | 25 | define(IRVariable{_memberAccess}) << formatNumber( |
1898 | 25 | util::selectorFromSignatureU256(functionType.externalSignature()) |
1899 | 25 | ) << "\n"; |
1900 | 25 | } |
1901 | 0 | else if (functionType.kind() == FunctionType::Kind::Event) |
1902 | 0 | { |
1903 | 0 | solAssert(functionType.hasDeclaration()); |
1904 | 0 | solAssert(functionType.kind() == FunctionType::Kind::Event); |
1905 | 0 | solAssert( |
1906 | 0 | !(dynamic_cast<EventDefinition const&>(functionType.declaration()).isAnonymous()) |
1907 | 0 | ); |
1908 | 0 | define(IRVariable{_memberAccess}) << formatNumber( |
1909 | 0 | u256(h256::Arith(util::keccak256(functionType.externalSignature()))) |
1910 | 0 | ) << "\n"; |
1911 | 0 | } |
1912 | 0 | else |
1913 | 0 | solAssert(false, "Invalid use of .selector: " + functionType.toString(false)); |
1914 | 30 | } |
1915 | 4 | else if (member == "address") |
1916 | 4 | { |
1917 | 4 | solUnimplementedAssert( |
1918 | 4 | dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type).kind() == |
1919 | 4 | FunctionType::Kind::External |
1920 | 4 | ); |
1921 | 4 | define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("address")); |
1922 | 4 | } |
1923 | 0 | else |
1924 | 4 | solAssert( |
1925 | 34 | !!_memberAccess.expression().annotation().type->memberType(member), |
1926 | 34 | "Invalid member access to function." |
1927 | 34 | ); |
1928 | 34 | break; |
1929 | 141 | case Type::Category::Magic: |
1930 | | // we can ignore the kind of magic and only look at the name of the member |
1931 | 141 | if (member == "coinbase") |
1932 | 0 | define(_memberAccess) << "coinbase()\n"; |
1933 | 141 | else if (member == "timestamp") |
1934 | 0 | define(_memberAccess) << "timestamp()\n"; |
1935 | 141 | else if (member == "difficulty" || member == "prevrandao") |
1936 | 0 | { |
1937 | 0 | if (m_context.evmVersion().hasPrevRandao()) |
1938 | 0 | define(_memberAccess) << "prevrandao()\n"; |
1939 | 0 | else |
1940 | 0 | define(_memberAccess) << "difficulty()\n"; |
1941 | 0 | } |
1942 | 141 | else if (member == "number") |
1943 | 0 | define(_memberAccess) << "number()\n"; |
1944 | 141 | else if (member == "gaslimit") |
1945 | 0 | define(_memberAccess) << "gaslimit()\n"; |
1946 | 141 | else if (member == "sender") |
1947 | 4 | define(_memberAccess) << "caller()\n"; |
1948 | 137 | else if (member == "value") |
1949 | 0 | define(_memberAccess) << "callvalue()\n"; |
1950 | 137 | else if (member == "origin") |
1951 | 0 | define(_memberAccess) << "origin()\n"; |
1952 | 137 | else if (member == "gasprice") |
1953 | 0 | define(_memberAccess) << "gasprice()\n"; |
1954 | 137 | else if (member == "chainid") |
1955 | 0 | define(_memberAccess) << "chainid()\n"; |
1956 | 137 | else if (member == "basefee") |
1957 | 0 | define(_memberAccess) << "basefee()\n"; |
1958 | 137 | else if (member == "blobbasefee") |
1959 | 0 | define(_memberAccess) << "blobbasefee()\n"; |
1960 | 137 | else if (member == "data") |
1961 | 18 | { |
1962 | 18 | IRVariable var(_memberAccess); |
1963 | 18 | define(var.part("offset")) << "0\n"; |
1964 | 18 | define(var.part("length")) << "calldatasize()\n"; |
1965 | 18 | } |
1966 | 119 | else if (member == "sig") |
1967 | 5 | define(_memberAccess) << |
1968 | 5 | "and(calldataload(0), " << |
1969 | 5 | formatNumber(u256(0xffffffff) << (256 - 32)) << |
1970 | 5 | ")\n"; |
1971 | 114 | else if (member == "gas") |
1972 | 114 | solAssert(false, "Gas has been removed."); |
1973 | 114 | else if (member == "blockhash") |
1974 | 114 | solAssert(false, "Blockhash has been removed."); |
1975 | 114 | else if (member == "creationCode" || member == "runtimeCode") |
1976 | 0 | { |
1977 | 0 | Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument(); |
1978 | 0 | auto const& contractType = dynamic_cast<ContractType const&>(*arg); |
1979 | 0 | solAssert(!contractType.isSuper()); |
1980 | 0 | ContractDefinition const& contract = contractType.contractDefinition(); |
1981 | 0 | m_context.addSubObject(&contract); |
1982 | 0 | appendCode() << Whiskers(R"( |
1983 | 0 | let <size> := datasize("<objectName>") |
1984 | 0 | let <result> := <allocationFunction>(add(<size>, 32)) |
1985 | 0 | mstore(<result>, <size>) |
1986 | 0 | datacopy(add(<result>, 32), dataoffset("<objectName>"), <size>) |
1987 | 0 | )") |
1988 | 0 | ("allocationFunction", m_utils.allocationFunction()) |
1989 | 0 | ("size", m_context.newYulVariable()) |
1990 | 0 | ("objectName", IRNames::creationObject(contract) + (member == "runtimeCode" ? "." + IRNames::deployedObject(contract) : "")) |
1991 | 0 | ("result", IRVariable(_memberAccess).commaSeparatedList()).render(); |
1992 | 0 | } |
1993 | 114 | else if (member == "name") |
1994 | 0 | { |
1995 | 0 | Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument(); |
1996 | 0 | ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition(); |
1997 | 0 | define(IRVariable(_memberAccess)) << m_utils.copyLiteralToMemoryFunction(contract.name()) << "()\n"; |
1998 | 0 | } |
1999 | 114 | else if (member == "interfaceId") |
2000 | 0 | { |
2001 | 0 | Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument(); |
2002 | 0 | auto const& contractType = dynamic_cast<ContractType const&>(*arg); |
2003 | 0 | solAssert(!contractType.isSuper()); |
2004 | 0 | ContractDefinition const& contract = contractType.contractDefinition(); |
2005 | 0 | define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n"; |
2006 | 0 | } |
2007 | 114 | else if (member == "min" || member == "max") |
2008 | 51 | { |
2009 | 51 | MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type); |
2010 | | |
2011 | 51 | std::string requestedValue; |
2012 | 51 | if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument())) |
2013 | 51 | { |
2014 | 51 | if (member == "min") |
2015 | 35 | requestedValue = formatNumber(integerType->min()); |
2016 | 16 | else |
2017 | 16 | requestedValue = formatNumber(integerType->max()); |
2018 | 51 | } |
2019 | 0 | else if (EnumType const* enumType = dynamic_cast<EnumType const*>(arg->typeArgument())) |
2020 | 0 | { |
2021 | 0 | if (member == "min") |
2022 | 0 | requestedValue = std::to_string(enumType->minValue()); |
2023 | 0 | else |
2024 | 0 | requestedValue = std::to_string(enumType->maxValue()); |
2025 | 0 | } |
2026 | 0 | else |
2027 | 0 | solAssert(false, "min/max requested on unexpected type."); |
2028 | | |
2029 | 51 | define(_memberAccess) << requestedValue << "\n"; |
2030 | 51 | } |
2031 | 63 | else if (std::set<std::string>{"encode", "encodePacked", "encodeWithSelector", "encodeCall", "encodeWithSignature", "decode"}.count(member)) |
2032 | 63 | { |
2033 | | // no-op |
2034 | 63 | } |
2035 | 0 | else |
2036 | 63 | solAssert(false, "Unknown magic member."); |
2037 | 141 | break; |
2038 | 240 | case Type::Category::Struct: |
2039 | 240 | { |
2040 | 240 | auto const& structType = dynamic_cast<StructType const&>(*_memberAccess.expression().annotation().type); |
2041 | | |
2042 | 240 | IRVariable expression(_memberAccess.expression()); |
2043 | 240 | switch (structType.location()) |
2044 | 240 | { |
2045 | 158 | case DataLocation::Storage: |
2046 | 158 | { |
2047 | 158 | std::pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member); |
2048 | 158 | std::string slot = m_context.newYulVariable(); |
2049 | 158 | appendCode() << "let " << slot << " := " << |
2050 | 158 | ("add(" + expression.part("slot").name() + ", " + offsets.first.str() + ")\n"); |
2051 | 158 | setLValue(_memberAccess, IRLValue{ |
2052 | 158 | type(_memberAccess), |
2053 | 158 | IRLValue::Storage{slot, offsets.second} |
2054 | 158 | }); |
2055 | 158 | break; |
2056 | 0 | } |
2057 | 78 | case DataLocation::Memory: |
2058 | 78 | { |
2059 | 78 | std::string pos = m_context.newYulVariable(); |
2060 | 78 | appendCode() << "let " << pos << " := " << |
2061 | 78 | ("add(" + expression.part("mpos").name() + ", " + structType.memoryOffsetOfMember(member).str() + ")\n"); |
2062 | 78 | setLValue(_memberAccess, IRLValue{ |
2063 | 78 | type(_memberAccess), |
2064 | 78 | IRLValue::Memory{pos} |
2065 | 78 | }); |
2066 | 78 | break; |
2067 | 0 | } |
2068 | 4 | case DataLocation::CallData: |
2069 | 4 | { |
2070 | 4 | std::string baseRef = expression.part("offset").name(); |
2071 | 4 | std::string offset = m_context.newYulVariable(); |
2072 | 4 | appendCode() << "let " << offset << " := " << "add(" << baseRef << ", " << std::to_string(structType.calldataOffsetOfMember(member)) << ")\n"; |
2073 | 4 | if (_memberAccess.annotation().type->isDynamicallyEncoded()) |
2074 | 3 | define(_memberAccess) << |
2075 | 3 | m_utils.accessCalldataTailFunction(*_memberAccess.annotation().type) << |
2076 | 3 | "(" << |
2077 | 3 | baseRef << |
2078 | 3 | ", " << |
2079 | 3 | offset << |
2080 | 3 | ")\n"; |
2081 | 1 | else if ( |
2082 | 1 | dynamic_cast<ArrayType const*>(_memberAccess.annotation().type) || |
2083 | 1 | dynamic_cast<StructType const*>(_memberAccess.annotation().type) |
2084 | 1 | ) |
2085 | 0 | define(_memberAccess) << offset << "\n"; |
2086 | 1 | else |
2087 | 1 | define(_memberAccess) << |
2088 | 1 | m_utils.readFromCalldata(*_memberAccess.annotation().type) << |
2089 | 1 | "(" << |
2090 | 1 | offset << |
2091 | 1 | ")\n"; |
2092 | 4 | break; |
2093 | 0 | } |
2094 | 0 | default: |
2095 | 0 | solAssert(false, "Illegal data location for struct."); |
2096 | 240 | } |
2097 | 240 | break; |
2098 | 240 | } |
2099 | 240 | case Type::Category::Enum: |
2100 | 0 | { |
2101 | 0 | EnumType const& type = dynamic_cast<EnumType const&>(*_memberAccess.expression().annotation().type); |
2102 | 0 | define(_memberAccess) << std::to_string(type.memberValue(_memberAccess.memberName())) << "\n"; |
2103 | 0 | break; |
2104 | 240 | } |
2105 | 62 | case Type::Category::Array: |
2106 | 62 | { |
2107 | 62 | auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type); |
2108 | 62 | if (member == "length") |
2109 | 62 | { |
2110 | | // shortcut for <address>.code.length |
2111 | 62 | if ( |
2112 | 62 | auto innerExpression = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()); |
2113 | 62 | innerExpression && |
2114 | 1 | innerExpression->memberName() == "code" && |
2115 | 0 | innerExpression->expression().annotation().type->category() == Type::Category::Address |
2116 | 62 | ) |
2117 | 0 | define(_memberAccess) << |
2118 | 0 | "extcodesize(" << |
2119 | 0 | expressionAsType(innerExpression->expression(), *TypeProvider::address()) << |
2120 | 0 | ")\n"; |
2121 | 62 | else |
2122 | 62 | define(_memberAccess) << |
2123 | 62 | m_utils.arrayLengthFunction(type) << |
2124 | 62 | "(" << |
2125 | 62 | IRVariable(_memberAccess.expression()).commaSeparatedList() << |
2126 | 62 | ")\n"; |
2127 | 62 | } |
2128 | 0 | else if (member == "pop" || member == "push") |
2129 | 0 | { |
2130 | 0 | solAssert(type.location() == DataLocation::Storage); |
2131 | 0 | define(IRVariable{_memberAccess}.part("slot"), IRVariable{_memberAccess.expression()}.part("slot")); |
2132 | 0 | } |
2133 | 0 | else |
2134 | 0 | solAssert(false, "Invalid array member access."); |
2135 | | |
2136 | 62 | break; |
2137 | 240 | } |
2138 | 0 | case Type::Category::FixedBytes: |
2139 | 0 | { |
2140 | 0 | auto const& type = dynamic_cast<FixedBytesType const&>(*_memberAccess.expression().annotation().type); |
2141 | 0 | if (member == "length") |
2142 | 0 | define(_memberAccess) << std::to_string(type.numBytes()) << "\n"; |
2143 | 0 | else |
2144 | 0 | solAssert(false, "Illegal fixed bytes member."); |
2145 | 0 | break; |
2146 | 240 | } |
2147 | 68 | case Type::Category::TypeType: |
2148 | 68 | { |
2149 | 68 | Type const& actualType = *dynamic_cast<TypeType const&>( |
2150 | 68 | *_memberAccess.expression().annotation().type |
2151 | 68 | ).actualType(); |
2152 | | |
2153 | 68 | if (actualType.category() == Type::Category::Contract) |
2154 | 32 | { |
2155 | 32 | ContractType const& contractType = dynamic_cast<ContractType const&>(actualType); |
2156 | 32 | if (contractType.isSuper()) |
2157 | 0 | { |
2158 | 0 | solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); |
2159 | 0 | ContractDefinition const* super = contractType.contractDefinition().superContract(m_context.mostDerivedContract()); |
2160 | 0 | solAssert(super, "Super contract not available."); |
2161 | 0 | FunctionDefinition const& resolvedFunctionDef = |
2162 | 0 | dynamic_cast<FunctionDefinition const&>( |
2163 | 0 | *_memberAccess.annotation().referencedDeclaration |
2164 | 0 | ).resolveVirtual(m_context.mostDerivedContract(), super); |
2165 | |
|
2166 | 0 | solAssert(resolvedFunctionDef.functionType(true)); |
2167 | 0 | solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal); |
2168 | 0 | assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, resolvedFunctionDef); |
2169 | 0 | } |
2170 | 32 | else if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) |
2171 | 0 | handleVariableReference(*variable, _memberAccess); |
2172 | 32 | else if (memberFunctionType) |
2173 | 30 | { |
2174 | 30 | switch (memberFunctionType->kind()) |
2175 | 30 | { |
2176 | 19 | case FunctionType::Kind::Declaration: |
2177 | 19 | break; |
2178 | 3 | case FunctionType::Kind::Internal: |
2179 | 3 | if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration)) |
2180 | 3 | assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, *function); |
2181 | 0 | else |
2182 | 3 | solAssert(false, "Function not found in member access"); |
2183 | 3 | break; |
2184 | 2 | case FunctionType::Kind::Event: |
2185 | 2 | solAssert( |
2186 | 2 | dynamic_cast<EventDefinition const*>(_memberAccess.annotation().referencedDeclaration), |
2187 | 2 | "Event not found" |
2188 | 2 | ); |
2189 | | // the call will do the resolving |
2190 | 2 | break; |
2191 | 0 | case FunctionType::Kind::Error: |
2192 | 0 | solAssert( |
2193 | 0 | dynamic_cast<ErrorDefinition const*>(_memberAccess.annotation().referencedDeclaration), |
2194 | 0 | "Error not found" |
2195 | 0 | ); |
2196 | | // The function call will resolve the selector. |
2197 | 0 | break; |
2198 | 6 | case FunctionType::Kind::DelegateCall: |
2199 | 6 | define(IRVariable(_memberAccess).part("address"), _memberAccess.expression()); |
2200 | 6 | define(IRVariable(_memberAccess).part("functionSelector")) << formatNumber(memberFunctionType->externalIdentifier()) << "\n"; |
2201 | 6 | break; |
2202 | 0 | case FunctionType::Kind::External: |
2203 | 0 | case FunctionType::Kind::Creation: |
2204 | 0 | case FunctionType::Kind::Send: |
2205 | 0 | case FunctionType::Kind::BareCall: |
2206 | 0 | case FunctionType::Kind::BareCallCode: |
2207 | 0 | case FunctionType::Kind::BareDelegateCall: |
2208 | 0 | case FunctionType::Kind::BareStaticCall: |
2209 | 0 | case FunctionType::Kind::Transfer: |
2210 | 0 | case FunctionType::Kind::ECRecover: |
2211 | 0 | case FunctionType::Kind::SHA256: |
2212 | 0 | case FunctionType::Kind::RIPEMD160: |
2213 | 0 | default: |
2214 | 0 | solAssert(false, "unsupported member function"); |
2215 | 30 | } |
2216 | 30 | } |
2217 | 2 | else if (dynamic_cast<TypeType const*>(_memberAccess.annotation().type)) |
2218 | 2 | { |
2219 | | // no-op |
2220 | 2 | } |
2221 | 0 | else |
2222 | | // The old code generator had a generic "else" case here |
2223 | | // without any specific code being generated, |
2224 | | // but it would still be better to have an exhaustive list. |
2225 | 2 | solAssert(false); |
2226 | 32 | } |
2227 | 36 | else if (EnumType const* enumType = dynamic_cast<EnumType const*>(&actualType)) |
2228 | 0 | define(_memberAccess) << std::to_string(enumType->memberValue(_memberAccess.memberName())) << "\n"; |
2229 | 36 | else if (dynamic_cast<UserDefinedValueType const*>(&actualType)) |
2230 | 36 | solAssert(member == "wrap" || member == "unwrap"); |
2231 | 28 | else if (auto const* arrayType = dynamic_cast<ArrayType const*>(&actualType)) |
2232 | 28 | solAssert(arrayType->isByteArrayOrString() && member == "concat"); |
2233 | 0 | else |
2234 | | // The old code generator had a generic "else" case here |
2235 | | // without any specific code being generated, |
2236 | | // but it would still be better to have an exhaustive list. |
2237 | 28 | solAssert(false); |
2238 | 68 | break; |
2239 | 68 | } |
2240 | 68 | case Type::Category::Module: |
2241 | 2 | { |
2242 | 2 | Type::Category category = _memberAccess.annotation().type->category(); |
2243 | 2 | solAssert( |
2244 | 2 | dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration) || |
2245 | 2 | dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration) || |
2246 | 2 | dynamic_cast<ErrorDefinition const*>(_memberAccess.annotation().referencedDeclaration) || |
2247 | 2 | dynamic_cast<EventDefinition const*>(_memberAccess.annotation().referencedDeclaration) || |
2248 | 2 | category == Type::Category::TypeType || |
2249 | 2 | category == Type::Category::Module, |
2250 | 2 | "" |
2251 | 2 | ); |
2252 | 2 | if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) |
2253 | 2 | { |
2254 | 2 | solAssert(variable->isConstant()); |
2255 | 2 | handleVariableReference(*variable, static_cast<Expression const&>(_memberAccess)); |
2256 | 2 | } |
2257 | 0 | else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration)) |
2258 | 0 | { |
2259 | 0 | auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type); |
2260 | 0 | solAssert(function && function->isFree()); |
2261 | 0 | solAssert(function->functionType(true)); |
2262 | 0 | solAssert(function->functionType(true)->kind() == FunctionType::Kind::Internal); |
2263 | 0 | solAssert(funType->kind() == FunctionType::Kind::Internal); |
2264 | 0 | solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static); |
2265 | |
|
2266 | 0 | assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, *function); |
2267 | 0 | } |
2268 | 0 | else if (auto const* contract = dynamic_cast<ContractDefinition const*>(_memberAccess.annotation().referencedDeclaration)) |
2269 | 0 | { |
2270 | 0 | if (contract->isLibrary()) |
2271 | 0 | define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\n"; |
2272 | 0 | } |
2273 | 2 | break; |
2274 | 68 | } |
2275 | 0 | default: |
2276 | 0 | solAssert(false, "Member access to unknown type."); |
2277 | 762 | } |
2278 | 762 | } |
2279 | | |
2280 | | bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) |
2281 | 174 | { |
2282 | 174 | setLocation(_inlineAsm); |
2283 | 174 | if (*_inlineAsm.annotation().hasMemoryEffects && !_inlineAsm.annotation().markedMemorySafe) |
2284 | 76 | m_context.setMemoryUnsafeInlineAssemblySeen(); |
2285 | 174 | CopyTranslate bodyCopier{m_context, _inlineAsm.annotation().externalReferences}; |
2286 | | |
2287 | 174 | yul::Statement modified = bodyCopier(_inlineAsm.operations().root()); |
2288 | | |
2289 | 174 | solAssert(std::holds_alternative<yul::Block>(modified)); |
2290 | | |
2291 | 174 | appendCode() << yul::AsmPrinter(_inlineAsm.dialect())(std::get<yul::Block>(modified)) << "\n"; |
2292 | 174 | return false; |
2293 | 174 | } |
2294 | | |
2295 | | |
2296 | | void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) |
2297 | 7.79k | { |
2298 | 7.79k | setLocation(_indexAccess); |
2299 | 7.79k | Type const& baseType = *_indexAccess.baseExpression().annotation().type; |
2300 | | |
2301 | 7.79k | if (baseType.category() == Type::Category::Mapping) |
2302 | 399 | { |
2303 | 399 | solAssert(_indexAccess.indexExpression(), "Index expression expected."); |
2304 | | |
2305 | 399 | MappingType const& mappingType = dynamic_cast<MappingType const&>(baseType); |
2306 | 399 | Type const& keyType = *_indexAccess.indexExpression()->annotation().type; |
2307 | | |
2308 | 399 | std::string slot = m_context.newYulVariable(); |
2309 | 399 | Whiskers templ("let <slot> := <indexAccess>(<base><?+key>,<key></+key>)\n"); |
2310 | 399 | templ("slot", slot); |
2311 | 399 | templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType)); |
2312 | 399 | templ("base", IRVariable(_indexAccess.baseExpression()).commaSeparatedList()); |
2313 | 399 | templ("key", IRVariable(*_indexAccess.indexExpression()).commaSeparatedList()); |
2314 | 399 | appendCode() << templ.render(); |
2315 | 399 | setLValue(_indexAccess, IRLValue{ |
2316 | 399 | *_indexAccess.annotation().type, |
2317 | 399 | IRLValue::Storage{ |
2318 | 399 | slot, |
2319 | 399 | 0u |
2320 | 399 | } |
2321 | 399 | }); |
2322 | 399 | } |
2323 | 7.39k | else if (baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice) |
2324 | 7.39k | { |
2325 | 7.39k | ArrayType const& arrayType = |
2326 | 7.39k | baseType.category() == Type::Category::Array ? |
2327 | 7.39k | dynamic_cast<ArrayType const&>(baseType) : |
2328 | 7.39k | dynamic_cast<ArraySliceType const&>(baseType).arrayType(); |
2329 | | |
2330 | 7.39k | if (baseType.category() == Type::Category::ArraySlice) |
2331 | 7.39k | solAssert(arrayType.dataStoredIn(DataLocation::CallData) && arrayType.isDynamicallySized()); |
2332 | | |
2333 | 7.39k | solAssert(_indexAccess.indexExpression(), "Index expression expected."); |
2334 | | |
2335 | 7.39k | switch (arrayType.location()) |
2336 | 7.39k | { |
2337 | 683 | case DataLocation::Storage: |
2338 | 683 | { |
2339 | 683 | std::string slot = m_context.newYulVariable(); |
2340 | 683 | std::string offset = m_context.newYulVariable(); |
2341 | | |
2342 | 683 | appendCode() << Whiskers(R"( |
2343 | 683 | let <slot>, <offset> := <indexFunc>(<array>, <index>) |
2344 | 683 | )") |
2345 | 683 | ("slot", slot) |
2346 | 683 | ("offset", offset) |
2347 | 683 | ("indexFunc", m_utils.storageArrayIndexAccessFunction(arrayType)) |
2348 | 683 | ("array", IRVariable(_indexAccess.baseExpression()).part("slot").name()) |
2349 | 683 | ("index", IRVariable(*_indexAccess.indexExpression()).name()) |
2350 | 683 | .render(); |
2351 | | |
2352 | 683 | setLValue(_indexAccess, IRLValue{ |
2353 | 683 | *_indexAccess.annotation().type, |
2354 | 683 | IRLValue::Storage{slot, offset} |
2355 | 683 | }); |
2356 | | |
2357 | 683 | break; |
2358 | 0 | } |
2359 | 0 | case DataLocation::Transient: |
2360 | 0 | solUnimplemented("Transient data location is only supported for value types."); |
2361 | 0 | break; |
2362 | 6.70k | case DataLocation::Memory: |
2363 | 6.70k | { |
2364 | 6.70k | std::string const indexAccessFunction = m_utils.memoryArrayIndexAccessFunction(arrayType); |
2365 | 6.70k | std::string const baseRef = IRVariable(_indexAccess.baseExpression()).part("mpos").name(); |
2366 | 6.70k | std::string const indexExpression = expressionAsType( |
2367 | 6.70k | *_indexAccess.indexExpression(), |
2368 | 6.70k | *TypeProvider::uint256() |
2369 | 6.70k | ); |
2370 | 6.70k | std::string const memAddress = indexAccessFunction + "(" + baseRef + ", " + indexExpression + ")"; |
2371 | | |
2372 | 6.70k | setLValue(_indexAccess, IRLValue{ |
2373 | 6.70k | *arrayType.baseType(), |
2374 | 6.70k | IRLValue::Memory{memAddress, arrayType.isByteArrayOrString()} |
2375 | 6.70k | }); |
2376 | 6.70k | break; |
2377 | 0 | } |
2378 | 4 | case DataLocation::CallData: |
2379 | 4 | { |
2380 | 4 | std::string const indexAccessFunction = m_utils.calldataArrayIndexAccessFunction(arrayType); |
2381 | 4 | std::string const baseRef = IRVariable(_indexAccess.baseExpression()).commaSeparatedList(); |
2382 | 4 | std::string const indexExpression = expressionAsType( |
2383 | 4 | *_indexAccess.indexExpression(), |
2384 | 4 | *TypeProvider::uint256() |
2385 | 4 | ); |
2386 | 4 | std::string const calldataAddress = indexAccessFunction + "(" + baseRef + ", " + indexExpression + ")"; |
2387 | | |
2388 | 4 | if (arrayType.isByteArrayOrString()) |
2389 | 0 | define(_indexAccess) << |
2390 | 0 | m_utils.cleanupFunction(*arrayType.baseType()) << |
2391 | 0 | "(calldataload(" << |
2392 | 0 | calldataAddress << |
2393 | 0 | "))\n"; |
2394 | 4 | else if (arrayType.baseType()->isValueType()) |
2395 | 3 | define(_indexAccess) << |
2396 | 3 | m_utils.readFromCalldata(*arrayType.baseType()) << |
2397 | 3 | "(" << |
2398 | 3 | calldataAddress << |
2399 | 3 | ")\n"; |
2400 | 1 | else |
2401 | 1 | define(_indexAccess) << calldataAddress << "\n"; |
2402 | 4 | break; |
2403 | 0 | } |
2404 | 7.39k | } |
2405 | 7.39k | } |
2406 | 2 | else if (baseType.category() == Type::Category::FixedBytes) |
2407 | 0 | { |
2408 | 0 | auto const& fixedBytesType = dynamic_cast<FixedBytesType const&>(baseType); |
2409 | 0 | solAssert(_indexAccess.indexExpression(), "Index expression expected."); |
2410 | |
|
2411 | 0 | IRVariable index{m_context.newYulVariable(), *TypeProvider::uint256()}; |
2412 | 0 | define(index, *_indexAccess.indexExpression()); |
2413 | 0 | appendCode() << Whiskers(R"( |
2414 | 0 | if iszero(lt(<index>, <length>)) { <panic>() } |
2415 | 0 | let <result> := <shl248>(byte(<index>, <array>)) |
2416 | 0 | )") |
2417 | 0 | ("index", index.name()) |
2418 | 0 | ("length", std::to_string(fixedBytesType.numBytes())) |
2419 | 0 | ("panic", m_utils.panicFunction(PanicCode::ArrayOutOfBounds)) |
2420 | 0 | ("array", IRVariable(_indexAccess.baseExpression()).name()) |
2421 | 0 | ("shl248", m_utils.shiftLeftFunction(256 - 8)) |
2422 | 0 | ("result", IRVariable(_indexAccess).name()) |
2423 | 0 | .render(); |
2424 | 0 | } |
2425 | 2 | else if (baseType.category() == Type::Category::TypeType) |
2426 | 2 | { |
2427 | 2 | solAssert(baseType.sizeOnStack() == 0); |
2428 | 2 | solAssert(_indexAccess.annotation().type->sizeOnStack() == 0); |
2429 | | // no-op - this seems to be a lone array type (`structType[];`) |
2430 | 2 | } |
2431 | 0 | else |
2432 | 2 | solAssert(false, "Index access only allowed for mappings or arrays."); |
2433 | 7.79k | } |
2434 | | |
2435 | | void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess) |
2436 | 15 | { |
2437 | 15 | setLocation(_indexRangeAccess); |
2438 | 15 | Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type; |
2439 | 15 | solAssert( |
2440 | 15 | baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice, |
2441 | 15 | "Index range accesses is available only on arrays and array slices." |
2442 | 15 | ); |
2443 | | |
2444 | 15 | ArrayType const& arrayType = |
2445 | 15 | baseType.category() == Type::Category::Array ? |
2446 | 15 | dynamic_cast<ArrayType const &>(baseType) : |
2447 | 15 | dynamic_cast<ArraySliceType const &>(baseType).arrayType(); |
2448 | | |
2449 | 15 | switch (arrayType.location()) |
2450 | 15 | { |
2451 | 15 | case DataLocation::CallData: |
2452 | 15 | { |
2453 | 15 | solAssert(baseType.isDynamicallySized()); |
2454 | 15 | IRVariable sliceStart{m_context.newYulVariable(), *TypeProvider::uint256()}; |
2455 | 15 | if (_indexRangeAccess.startExpression()) |
2456 | 15 | define(sliceStart, IRVariable{*_indexRangeAccess.startExpression()}); |
2457 | 0 | else |
2458 | 0 | define(sliceStart) << u256(0) << "\n"; |
2459 | | |
2460 | 15 | IRVariable sliceEnd{ |
2461 | 15 | m_context.newYulVariable(), |
2462 | 15 | *TypeProvider::uint256() |
2463 | 15 | }; |
2464 | 15 | if (_indexRangeAccess.endExpression()) |
2465 | 15 | define(sliceEnd, IRVariable{*_indexRangeAccess.endExpression()}); |
2466 | 0 | else |
2467 | 0 | define(sliceEnd, IRVariable{_indexRangeAccess.baseExpression()}.part("length")); |
2468 | | |
2469 | 15 | IRVariable range{_indexRangeAccess}; |
2470 | 15 | define(range) << |
2471 | 15 | m_utils.calldataArrayIndexRangeAccess(arrayType) << "(" << |
2472 | 15 | IRVariable{_indexRangeAccess.baseExpression()}.commaSeparatedList() << ", " << |
2473 | 15 | sliceStart.name() << ", " << |
2474 | 15 | sliceEnd.name() << ")\n"; |
2475 | 15 | break; |
2476 | 0 | } |
2477 | 0 | default: |
2478 | 0 | solUnimplemented("Index range accesses is implemented only on calldata arrays."); |
2479 | 15 | } |
2480 | 15 | } |
2481 | | |
2482 | | void IRGeneratorForStatements::endVisit(Identifier const& _identifier) |
2483 | 13.0k | { |
2484 | 13.0k | setLocation(_identifier); |
2485 | 13.0k | Declaration const* declaration = _identifier.annotation().referencedDeclaration; |
2486 | 13.0k | if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration)) |
2487 | 518 | { |
2488 | 518 | switch (magicVar->type()->category()) |
2489 | 518 | { |
2490 | 181 | case Type::Category::Contract: |
2491 | 181 | solAssert(_identifier.name() == "this"); |
2492 | 181 | define(_identifier) << "address()\n"; |
2493 | 181 | break; |
2494 | 0 | case Type::Category::Integer: |
2495 | 0 | solAssert(_identifier.name() == "now"); |
2496 | 0 | define(_identifier) << "timestamp()\n"; |
2497 | 0 | break; |
2498 | 0 | case Type::Category::TypeType: |
2499 | 0 | { |
2500 | 0 | auto typeType = dynamic_cast<TypeType const*>(magicVar->type()); |
2501 | 0 | if (auto contractType = dynamic_cast<ContractType const*>(typeType->actualType())) |
2502 | 0 | solAssert(!contractType->isSuper() || _identifier.name() == "super"); |
2503 | 0 | break; |
2504 | 0 | } |
2505 | 337 | default: |
2506 | 337 | break; |
2507 | 518 | } |
2508 | 518 | return; |
2509 | 518 | } |
2510 | 12.5k | else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration)) |
2511 | 188 | { |
2512 | 188 | solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual); |
2513 | 188 | FunctionDefinition const& resolvedFunctionDef = functionDef->resolveVirtual(m_context.mostDerivedContract()); |
2514 | | |
2515 | 188 | solAssert(resolvedFunctionDef.functionType(true)); |
2516 | 188 | solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal); |
2517 | 188 | assignInternalFunctionIDIfNotCalledDirectly(_identifier, resolvedFunctionDef); |
2518 | 188 | } |
2519 | 12.3k | else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration)) |
2520 | 11.5k | handleVariableReference(*varDecl, _identifier); |
2521 | 844 | else if (auto const* contract = dynamic_cast<ContractDefinition const*>(declaration)) |
2522 | 40 | { |
2523 | 40 | if (contract->isLibrary()) |
2524 | 6 | define(IRVariable(_identifier).part("address")) << linkerSymbol(*contract) << "\n"; |
2525 | 40 | } |
2526 | 804 | else if (dynamic_cast<EventDefinition const*>(declaration)) |
2527 | 794 | { |
2528 | | // no-op |
2529 | 794 | } |
2530 | 10 | else if (dynamic_cast<ErrorDefinition const*>(declaration)) |
2531 | 2 | { |
2532 | | // no-op |
2533 | 2 | } |
2534 | 8 | else if (dynamic_cast<EnumDefinition const*>(declaration)) |
2535 | 0 | { |
2536 | | // no-op |
2537 | 0 | } |
2538 | 8 | else if (dynamic_cast<StructDefinition const*>(declaration)) |
2539 | 0 | { |
2540 | | // no-op |
2541 | 0 | } |
2542 | 8 | else if (dynamic_cast<ImportDirective const*>(declaration)) |
2543 | 2 | { |
2544 | | // no-op |
2545 | 2 | } |
2546 | 6 | else if (dynamic_cast<UserDefinedValueTypeDefinition const*>(declaration)) |
2547 | 6 | { |
2548 | | // no-op |
2549 | 6 | } |
2550 | 0 | else |
2551 | 0 | { |
2552 | 0 | solAssert(false, "Identifier type not expected in expression context."); |
2553 | 0 | } |
2554 | 13.0k | } |
2555 | | |
2556 | | bool IRGeneratorForStatements::visit(Literal const& _literal) |
2557 | 14.8k | { |
2558 | 14.8k | setLocation(_literal); |
2559 | 14.8k | Type const& literalType = type(_literal); |
2560 | | |
2561 | 14.8k | switch (literalType.category()) |
2562 | 14.8k | { |
2563 | 13.4k | case Type::Category::RationalNumber: |
2564 | 13.4k | case Type::Category::Bool: |
2565 | 13.4k | case Type::Category::Address: |
2566 | 13.4k | define(_literal) << toCompactHexWithPrefix(literalType.literalValue(&_literal)) << "\n"; |
2567 | 13.4k | break; |
2568 | 1.38k | case Type::Category::StringLiteral: |
2569 | 1.38k | break; // will be done during conversion |
2570 | 0 | default: |
2571 | 0 | solUnimplemented("Only integer, boolean and string literals implemented for now."); |
2572 | 14.8k | } |
2573 | 14.8k | return false; |
2574 | 14.8k | } |
2575 | | |
2576 | | void IRGeneratorForStatements::handleVariableReference( |
2577 | | VariableDeclaration const& _variable, |
2578 | | Expression const& _referencingExpression |
2579 | | ) |
2580 | 11.5k | { |
2581 | 11.5k | if ((_variable.isStateVariable() || _variable.isFileLevelVariable()) && _variable.isConstant()) |
2582 | 67 | define(_referencingExpression) << constantValueFunction(_variable) << "()\n"; |
2583 | 11.4k | else if (_variable.isStateVariable() && _variable.immutable()) |
2584 | 66 | setLValue(_referencingExpression, IRLValue{ |
2585 | 66 | *_variable.annotation().type, |
2586 | 66 | IRLValue::Immutable{&_variable} |
2587 | 66 | }); |
2588 | 11.3k | else if (m_context.isLocalVariable(_variable)) |
2589 | 9.19k | setLValue(_referencingExpression, IRLValue{ |
2590 | 9.19k | *_variable.annotation().type, |
2591 | 9.19k | IRLValue::Stack{m_context.localVariable(_variable)} |
2592 | 9.19k | }); |
2593 | 2.17k | else if (m_context.isStateVariable(_variable) && _variable.referenceLocation() == VariableDeclaration::Location::Transient) |
2594 | 0 | setLValue(_referencingExpression, IRLValue{ |
2595 | 0 | *_variable.annotation().type, |
2596 | 0 | IRLValue::TransientStorage{ |
2597 | 0 | toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_variable).first), |
2598 | 0 | m_context.storageLocationOfStateVariable(_variable).second |
2599 | 0 | } |
2600 | 0 | }); |
2601 | 2.17k | else if (m_context.isStateVariable(_variable)) |
2602 | 2.17k | { |
2603 | 2.17k | solAssert(_variable.referenceLocation() == VariableDeclaration::Location::Unspecified, "Must have storage location."); |
2604 | 2.17k | setLValue(_referencingExpression, IRLValue{ |
2605 | 2.17k | *_variable.annotation().type, |
2606 | 2.17k | IRLValue::Storage{ |
2607 | 2.17k | toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_variable).first), |
2608 | 2.17k | m_context.storageLocationOfStateVariable(_variable).second |
2609 | 2.17k | } |
2610 | 2.17k | }); |
2611 | 2.17k | } |
2612 | 0 | else |
2613 | 2.17k | solAssert(false, "Invalid variable kind."); |
2614 | 11.5k | } |
2615 | | |
2616 | | void IRGeneratorForStatements::appendExternalFunctionCall( |
2617 | | FunctionCall const& _functionCall, |
2618 | | std::vector<ASTPointer<Expression const>> const& _arguments |
2619 | | ) |
2620 | 149 | { |
2621 | 149 | FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression())); |
2622 | 149 | solAssert(!funType.takesArbitraryParameters()); |
2623 | 149 | solAssert(_arguments.size() == funType.parameterTypes().size()); |
2624 | 149 | solAssert(!funType.isBareCall()); |
2625 | 149 | FunctionType::Kind const funKind = funType.kind(); |
2626 | | |
2627 | 149 | solAssert( |
2628 | 149 | funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall, |
2629 | 149 | "Can only be used for regular external calls." |
2630 | 149 | ); |
2631 | | |
2632 | 149 | bool const isDelegateCall = funKind == FunctionType::Kind::DelegateCall; |
2633 | 149 | bool const useStaticCall = funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall(); |
2634 | | |
2635 | 149 | ReturnInfo const returnInfo{m_context.evmVersion(), funType}; |
2636 | | |
2637 | 149 | TypePointers parameterTypes = funType.parameterTypes(); |
2638 | 149 | TypePointers argumentTypes; |
2639 | 149 | std::vector<std::string> argumentStrings; |
2640 | 149 | if (funType.hasBoundFirstArgument()) |
2641 | 41 | { |
2642 | 41 | parameterTypes.insert(parameterTypes.begin(), funType.selfType()); |
2643 | 41 | argumentTypes.emplace_back(funType.selfType()); |
2644 | 41 | argumentStrings += IRVariable(_functionCall.expression()).part("self").stackSlots(); |
2645 | 41 | } |
2646 | | |
2647 | 149 | for (auto const& arg: _arguments) |
2648 | 36 | { |
2649 | 36 | argumentTypes.emplace_back(&type(*arg)); |
2650 | 36 | argumentStrings += IRVariable(*arg).stackSlots(); |
2651 | 36 | } |
2652 | | |
2653 | 149 | if (!m_context.evmVersion().canOverchargeGasForCall()) |
2654 | 14 | { |
2655 | | // Touch the end of the output area so that we do not pay for memory resize during the call |
2656 | | // (which we would have to subtract from the gas left) |
2657 | | // We could also just use MLOAD; POP right before the gas calculation, but the optimizer |
2658 | | // would remove that, so we use MSTORE here. |
2659 | 14 | if (!funType.gasSet() && returnInfo.estimatedReturnSize > 0) |
2660 | 12 | appendCode() << "mstore(add(" << m_utils.allocateUnboundedFunction() << "() , " << std::to_string(returnInfo.estimatedReturnSize) << "), 0)\n"; |
2661 | 14 | } |
2662 | | |
2663 | | // NOTE: When the expected size of returndata is static, we pass that in to the call opcode and it gets copied automatically. |
2664 | | // When it's dynamic, we get zero from estimatedReturnSize() instead and then we need an explicit returndatacopy(). |
2665 | 149 | Whiskers templ(R"( |
2666 | 149 | <?checkExtcodesize> |
2667 | 149 | if iszero(extcodesize(<address>)) { <revertNoCode>() } |
2668 | 149 | </checkExtcodesize> |
2669 | 149 | // storage for arguments and returned data |
2670 | 149 | let <pos> := <allocateUnbounded>() |
2671 | 149 | mstore(<pos>, <shl28>(<funSel>)) |
2672 | 149 | let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>) |
2673 | 149 | |
2674 | 149 | let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <staticReturndataSize>) |
2675 | 149 | <?noTryCall> |
2676 | 149 | if iszero(<success>) { <forwardingRevert>() } |
2677 | 149 | </noTryCall> |
2678 | 149 | <?+retVars> let <retVars> </+retVars> |
2679 | 149 | if <success> { |
2680 | 149 | <?isReturndataSizeDynamic> |
2681 | 149 | let <returnDataSizeVar> := returndatasize() |
2682 | 149 | returndatacopy(<pos>, 0, <returnDataSizeVar>) |
2683 | 149 | <!isReturndataSizeDynamic> |
2684 | 149 | let <returnDataSizeVar> := <staticReturndataSize> |
2685 | 149 | <?supportsReturnData> |
2686 | 149 | if gt(<returnDataSizeVar>, returndatasize()) { |
2687 | 149 | <returnDataSizeVar> := returndatasize() |
2688 | 149 | } |
2689 | 149 | </supportsReturnData> |
2690 | 149 | </isReturndataSizeDynamic> |
2691 | 149 | |
2692 | 149 | // update freeMemoryPointer according to dynamic return size |
2693 | 149 | <finalizeAllocation>(<pos>, <returnDataSizeVar>) |
2694 | 149 | |
2695 | 149 | // decode return parameters from external try-call into retVars |
2696 | 149 | <?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnDataSizeVar>)) |
2697 | 149 | } |
2698 | 149 | )"); |
2699 | 149 | templ("revertNoCode", m_utils.revertReasonIfDebugFunction("Target contract does not contain code")); |
2700 | | |
2701 | | // We do not need to check extcodesize if we expect return data: If there is no |
2702 | | // code, the call will return empty data and the ABI decoder will revert. |
2703 | 149 | size_t encodedHeadSize = 0; |
2704 | 149 | for (auto const& t: returnInfo.returnTypes) |
2705 | 153 | encodedHeadSize += t->decodingType()->calldataHeadSize(); |
2706 | 149 | bool const checkExtcodesize = |
2707 | 149 | ( |
2708 | 149 | encodedHeadSize == 0 || |
2709 | 118 | !m_context.evmVersion().supportsReturndata() || |
2710 | 87 | m_context.revertStrings() >= RevertStrings::Debug |
2711 | 149 | ); |
2712 | 149 | templ("checkExtcodesize", checkExtcodesize); |
2713 | | |
2714 | 149 | templ("pos", m_context.newYulVariable()); |
2715 | 149 | templ("end", m_context.newYulVariable()); |
2716 | 149 | if (_functionCall.annotation().tryCall) |
2717 | 53 | templ("success", IRNames::trySuccessConditionVariable(_functionCall)); |
2718 | 96 | else |
2719 | 96 | templ("success", m_context.newYulVariable()); |
2720 | 149 | templ("allocateUnbounded", m_utils.allocateUnboundedFunction()); |
2721 | 149 | templ("finalizeAllocation", m_utils.finalizeAllocationFunction()); |
2722 | 149 | templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4))); |
2723 | | |
2724 | 149 | templ("funSel", IRVariable(_functionCall.expression()).part("functionSelector").name()); |
2725 | 149 | templ("address", IRVariable(_functionCall.expression()).part("address").name()); |
2726 | | |
2727 | 149 | if (returnInfo.dynamicReturnSize) |
2728 | 149 | solAssert(m_context.evmVersion().supportsReturndata()); |
2729 | 149 | templ("returnDataSizeVar", m_context.newYulVariable()); |
2730 | 149 | templ("staticReturndataSize", std::to_string(returnInfo.estimatedReturnSize)); |
2731 | 149 | templ("supportsReturnData", m_context.evmVersion().supportsReturndata()); |
2732 | | |
2733 | 149 | std::string const retVars = IRVariable(_functionCall).commaSeparatedList(); |
2734 | 149 | templ("retVars", retVars); |
2735 | 149 | solAssert(retVars.empty() == returnInfo.returnTypes.empty()); |
2736 | | |
2737 | 149 | templ("abiDecode", m_context.abiFunctions().tupleDecoder(returnInfo.returnTypes, true)); |
2738 | 149 | templ("isReturndataSizeDynamic", returnInfo.dynamicReturnSize); |
2739 | | |
2740 | 149 | templ("noTryCall", !_functionCall.annotation().tryCall); |
2741 | | |
2742 | 149 | bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall; |
2743 | | |
2744 | 149 | solAssert(funType.padArguments()); |
2745 | 149 | templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes, encodeForLibraryCall)); |
2746 | 149 | templ("argumentString", joinHumanReadablePrefixed(argumentStrings)); |
2747 | | |
2748 | 149 | solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall"); |
2749 | 149 | solAssert(!useStaticCall || !funType.valueSet(), "Value set for staticcall"); |
2750 | | |
2751 | 149 | templ("hasValue", !isDelegateCall && !useStaticCall); |
2752 | 149 | templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0"); |
2753 | | |
2754 | 149 | if (funType.gasSet()) |
2755 | 20 | templ("gas", IRVariable(_functionCall.expression()).part("gas").name()); |
2756 | 129 | else if (m_context.evmVersion().canOverchargeGasForCall()) |
2757 | | // Send all gas (requires tangerine whistle EVM) |
2758 | 117 | templ("gas", "gas()"); |
2759 | 12 | else |
2760 | 12 | { |
2761 | | // send all gas except the amount needed to execute "SUB" and "CALL" |
2762 | | // @todo this retains too much gas for now, needs to be fine-tuned. |
2763 | 12 | u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10; |
2764 | 12 | if (funType.valueSet()) |
2765 | 0 | gasNeededByCaller += evmasm::GasCosts::callValueTransferGas; |
2766 | 12 | if (!checkExtcodesize) |
2767 | 0 | gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know |
2768 | 12 | templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")"); |
2769 | 12 | } |
2770 | | // Order is important here, STATICCALL might overlap with DELEGATECALL. |
2771 | 149 | if (isDelegateCall) |
2772 | 47 | templ("call", "delegatecall"); |
2773 | 102 | else if (useStaticCall) |
2774 | 26 | templ("call", "staticcall"); |
2775 | 76 | else |
2776 | 76 | templ("call", "call"); |
2777 | | |
2778 | 149 | templ("forwardingRevert", m_utils.forwardingRevertFunction()); |
2779 | | |
2780 | 149 | appendCode() << templ.render(); |
2781 | 149 | } |
2782 | | |
2783 | | void IRGeneratorForStatements::appendBareCall( |
2784 | | FunctionCall const& _functionCall, |
2785 | | std::vector<ASTPointer<Expression const>> const& _arguments |
2786 | | ) |
2787 | 84 | { |
2788 | 84 | FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression())); |
2789 | 84 | solAssert( |
2790 | 84 | !funType.hasBoundFirstArgument() && |
2791 | 84 | !funType.takesArbitraryParameters() && |
2792 | 84 | _arguments.size() == 1 && |
2793 | 84 | funType.parameterTypes().size() == 1, "" |
2794 | 84 | ); |
2795 | 84 | FunctionType::Kind const funKind = funType.kind(); |
2796 | | |
2797 | 84 | solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall()); |
2798 | 84 | solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed."); |
2799 | 84 | solAssert( |
2800 | 84 | funKind == FunctionType::Kind::BareCall || |
2801 | 84 | funKind == FunctionType::Kind::BareDelegateCall || |
2802 | 84 | funKind == FunctionType::Kind::BareStaticCall, "" |
2803 | 84 | ); |
2804 | | |
2805 | 84 | solAssert(!_functionCall.annotation().tryCall); |
2806 | 84 | Whiskers templ(R"( |
2807 | 84 | <?needsEncoding> |
2808 | 84 | let <pos> := <allocateUnbounded>() |
2809 | 84 | let <length> := sub(<encode>(<pos> <?+arg>,</+arg> <arg>), <pos>) |
2810 | 84 | <!needsEncoding> |
2811 | 84 | let <pos> := add(<arg>, 0x20) |
2812 | 84 | let <length> := mload(<arg>) |
2813 | 84 | </needsEncoding> |
2814 | 84 | |
2815 | 84 | let <success> := <call>(<gas>, <address>, <?+value> <value>, </+value> <pos>, <length>, 0, 0) |
2816 | 84 | |
2817 | 84 | let <returndataVar> := <extractReturndataFunction>() |
2818 | 84 | )"); |
2819 | | |
2820 | 84 | templ("allocateUnbounded", m_utils.allocateUnboundedFunction()); |
2821 | 84 | templ("pos", m_context.newYulVariable()); |
2822 | 84 | templ("length", m_context.newYulVariable()); |
2823 | | |
2824 | 84 | templ("arg", IRVariable(*_arguments.front()).commaSeparatedList()); |
2825 | 84 | Type const& argType = type(*_arguments.front()); |
2826 | 84 | if (argType == *TypeProvider::bytesMemory() || argType == *TypeProvider::stringMemory()) |
2827 | 8 | templ("needsEncoding", false); |
2828 | 76 | else |
2829 | 76 | { |
2830 | 76 | templ("needsEncoding", true); |
2831 | 76 | ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector()); |
2832 | 76 | templ("encode", abi.tupleEncoderPacked({&argType}, {TypeProvider::bytesMemory()})); |
2833 | 76 | } |
2834 | | |
2835 | 84 | templ("success", IRVariable(_functionCall).tupleComponent(0).name()); |
2836 | 84 | templ("returndataVar", IRVariable(_functionCall).tupleComponent(1).commaSeparatedList()); |
2837 | 84 | templ("extractReturndataFunction", m_utils.extractReturndataFunction()); |
2838 | | |
2839 | 84 | templ("address", IRVariable(_functionCall.expression()).part("address").name()); |
2840 | | |
2841 | 84 | if (funKind == FunctionType::Kind::BareCall) |
2842 | 84 | { |
2843 | 84 | templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0"); |
2844 | 84 | templ("call", "call"); |
2845 | 84 | } |
2846 | 0 | else |
2847 | 0 | { |
2848 | 0 | solAssert(!funType.valueSet(), "Value set for delegatecall or staticcall."); |
2849 | 0 | templ("value", ""); |
2850 | 0 | if (funKind == FunctionType::Kind::BareStaticCall) |
2851 | 0 | templ("call", "staticcall"); |
2852 | 0 | else |
2853 | 0 | templ("call", "delegatecall"); |
2854 | 0 | } |
2855 | | |
2856 | 84 | if (funType.gasSet()) |
2857 | 0 | templ("gas", IRVariable(_functionCall.expression()).part("gas").name()); |
2858 | 84 | else if (m_context.evmVersion().canOverchargeGasForCall()) |
2859 | | // Send all gas (requires tangerine whistle EVM) |
2860 | 76 | templ("gas", "gas()"); |
2861 | 8 | else |
2862 | 8 | { |
2863 | | // send all gas except the amount needed to execute "SUB" and "CALL" |
2864 | | // @todo this retains too much gas for now, needs to be fine-tuned. |
2865 | 8 | u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10; |
2866 | 8 | if (funType.valueSet()) |
2867 | 0 | gasNeededByCaller += evmasm::GasCosts::callValueTransferGas; |
2868 | 8 | gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know |
2869 | 8 | templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")"); |
2870 | 8 | } |
2871 | | |
2872 | 84 | appendCode() << templ.render(); |
2873 | 84 | } |
2874 | | |
2875 | | void IRGeneratorForStatements::assignInternalFunctionIDIfNotCalledDirectly( |
2876 | | Expression const& _expression, |
2877 | | FunctionDefinition const& _referencedFunction |
2878 | | ) |
2879 | 194 | { |
2880 | 194 | solAssert( |
2881 | 194 | dynamic_cast<MemberAccess const*>(&_expression) || |
2882 | 194 | dynamic_cast<Identifier const*>(&_expression), |
2883 | 194 | "" |
2884 | 194 | ); |
2885 | 194 | if (_expression.annotation().calledDirectly) |
2886 | 176 | return; |
2887 | | |
2888 | 18 | define(IRVariable(_expression).part("functionIdentifier")) << |
2889 | 18 | std::to_string(m_context.mostDerivedContract().annotation().internalFunctionIDs.at(&_referencedFunction)) << |
2890 | 18 | "\n"; |
2891 | 18 | m_context.addToInternalDispatch(_referencedFunction); |
2892 | 18 | } |
2893 | | |
2894 | | IRVariable IRGeneratorForStatements::convert(IRVariable const& _from, Type const& _to) |
2895 | 6.03k | { |
2896 | 6.03k | if (_from.type() == _to) |
2897 | 2.98k | return _from; |
2898 | 3.05k | else |
2899 | 3.05k | { |
2900 | 3.05k | IRVariable converted(m_context.newYulVariable(), _to); |
2901 | 3.05k | define(converted, _from); |
2902 | 3.05k | return converted; |
2903 | 3.05k | } |
2904 | 6.03k | } |
2905 | | |
2906 | | IRVariable IRGeneratorForStatements::convertAndCleanup(IRVariable const& _from, Type const& _to) |
2907 | 0 | { |
2908 | 0 | IRVariable converted(m_context.newYulVariable(), _to); |
2909 | 0 | defineAndCleanup(converted, _from); |
2910 | 0 | return converted; |
2911 | 0 | } |
2912 | | |
2913 | | std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to) |
2914 | 17.5k | { |
2915 | 17.5k | IRVariable from(_expression); |
2916 | 17.5k | if (from.type() == _to) |
2917 | 9.52k | return from.commaSeparatedList(); |
2918 | 8.01k | else |
2919 | 8.01k | return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")"; |
2920 | 17.5k | } |
2921 | | |
2922 | | std::string IRGeneratorForStatements::expressionAsCleanedType(Expression const& _expression, Type const& _to) |
2923 | 826 | { |
2924 | 826 | IRVariable from(_expression); |
2925 | 826 | if (from.type() == _to) |
2926 | 423 | return m_utils.cleanupFunction(_to) + "(" + expressionAsType(_expression, _to) + ")"; |
2927 | 403 | else |
2928 | 403 | return expressionAsType(_expression, _to) ; |
2929 | 826 | } |
2930 | | |
2931 | | std::ostream& IRGeneratorForStatements::define(IRVariable const& _var) |
2932 | 35.3k | { |
2933 | 35.3k | if (_var.type().sizeOnStack() > 0) |
2934 | 35.3k | appendCode() << "let " << _var.commaSeparatedList() << " := "; |
2935 | 35.3k | return appendCode(false); |
2936 | 35.3k | } |
2937 | | |
2938 | | void IRGeneratorForStatements::declare(IRVariable const& _var) |
2939 | 957 | { |
2940 | 957 | if (_var.type().sizeOnStack() > 0) |
2941 | 957 | appendCode() << "let " << _var.commaSeparatedList() << "\n"; |
2942 | 957 | } |
2943 | | |
2944 | | void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable const& _rhs, bool _declare, bool _forceCleanup) |
2945 | 58.1k | { |
2946 | 58.1k | std::string output; |
2947 | 58.1k | if (_lhs.type() == _rhs.type() && !_forceCleanup) |
2948 | 52.1k | for (auto const& [stackItemName, stackItemType]: _lhs.type().stackItems()) |
2949 | 52.2k | if (stackItemType) |
2950 | 16.5k | declareAssign(_lhs.part(stackItemName), _rhs.part(stackItemName), _declare); |
2951 | 35.6k | else |
2952 | 35.6k | appendCode() << (_declare ? "let ": "") << _lhs.part(stackItemName).name() << " := " << _rhs.part(stackItemName).name() << "\n"; |
2953 | 5.96k | else |
2954 | 5.96k | { |
2955 | 5.96k | if (_lhs.type().sizeOnStack() > 0) |
2956 | 5.96k | appendCode() << |
2957 | 5.96k | (_declare ? "let ": "") << |
2958 | 5.96k | _lhs.commaSeparatedList() << |
2959 | 5.96k | " := "; |
2960 | 5.96k | appendCode() << m_context.utils().conversionFunction(_rhs.type(), _lhs.type()) << |
2961 | 5.96k | "(" << |
2962 | 5.96k | _rhs.commaSeparatedList() << |
2963 | 5.96k | ")\n"; |
2964 | 5.96k | } |
2965 | 58.1k | } |
2966 | | |
2967 | | IRVariable IRGeneratorForStatements::zeroValue(Type const& _type, bool _splitFunctionTypes) |
2968 | 2.53k | { |
2969 | 2.53k | IRVariable irVar{IRNames::zeroValue(_type, m_context.newYulVariable()), _type}; |
2970 | 2.53k | define(irVar) << m_utils.zeroValueFunction(_type, _splitFunctionTypes) << "()\n"; |
2971 | 2.53k | return irVar; |
2972 | 2.53k | } |
2973 | | |
2974 | | void IRGeneratorForStatements::appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr) |
2975 | 130 | { |
2976 | 130 | std::string func; |
2977 | | |
2978 | 130 | if (_operation.getOperator() == Token::Not) |
2979 | 1 | func = "iszero"; |
2980 | 129 | else if (_operation.getOperator() == Token::BitNot) |
2981 | 129 | func = "not"; |
2982 | 0 | else |
2983 | 129 | solAssert(false, "Invalid Token!"); |
2984 | | |
2985 | 130 | define(_operation) << |
2986 | 130 | m_utils.cleanupFunction(type(_expr)) << |
2987 | 130 | "(" << |
2988 | 130 | func << |
2989 | 130 | "(" << |
2990 | 130 | IRVariable(_expr).commaSeparatedList() << |
2991 | 130 | ")" << |
2992 | 130 | ")\n"; |
2993 | 130 | } |
2994 | | |
2995 | | std::string IRGeneratorForStatements::binaryOperation( |
2996 | | langutil::Token _operator, |
2997 | | Type const& _type, |
2998 | | std::string const& _left, |
2999 | | std::string const& _right |
3000 | | ) |
3001 | 4.88k | { |
3002 | 4.88k | solAssert( |
3003 | 4.88k | !TokenTraits::isShiftOp(_operator), |
3004 | 4.88k | "Have to use specific shift operation function for shifts." |
3005 | 4.88k | ); |
3006 | 4.88k | std::string fun; |
3007 | 4.88k | if (TokenTraits::isBitOp(_operator)) |
3008 | 184 | { |
3009 | 184 | solAssert( |
3010 | 184 | _type.category() == Type::Category::Integer || |
3011 | 184 | _type.category() == Type::Category::FixedBytes, |
3012 | 184 | "" |
3013 | 184 | ); |
3014 | 184 | switch (_operator) |
3015 | 184 | { |
3016 | 4 | case Token::BitOr: fun = "or"; break; |
3017 | 48 | case Token::BitXor: fun = "xor"; break; |
3018 | 132 | case Token::BitAnd: fun = "and"; break; |
3019 | 0 | default: break; |
3020 | 184 | } |
3021 | 184 | } |
3022 | 4.69k | else if (TokenTraits::isArithmeticOp(_operator)) |
3023 | 4.69k | { |
3024 | 4.69k | solUnimplementedAssert( |
3025 | 4.69k | _type.category() != Type::Category::FixedPoint, |
3026 | 4.69k | "Not yet implemented - FixedPointType." |
3027 | 4.69k | ); |
3028 | 4.69k | IntegerType const* type = dynamic_cast<IntegerType const*>(&_type); |
3029 | 4.69k | solAssert(type); |
3030 | 4.69k | bool checked = m_context.arithmetic() == Arithmetic::Checked; |
3031 | 4.69k | switch (_operator) |
3032 | 4.69k | { |
3033 | 694 | case Token::Add: |
3034 | 694 | fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type); |
3035 | 694 | break; |
3036 | 2.35k | case Token::Sub: |
3037 | 2.35k | fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type); |
3038 | 2.35k | break; |
3039 | 1.21k | case Token::Mul: |
3040 | 1.21k | fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type); |
3041 | 1.21k | break; |
3042 | 266 | case Token::Div: |
3043 | 266 | fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type); |
3044 | 266 | break; |
3045 | 172 | case Token::Mod: |
3046 | 172 | fun = m_utils.intModFunction(*type); |
3047 | 172 | break; |
3048 | 0 | default: |
3049 | 0 | break; |
3050 | 4.69k | } |
3051 | 4.69k | } |
3052 | | |
3053 | 4.88k | solUnimplementedAssert(!fun.empty(), "Type: " + _type.toString()); |
3054 | 4.88k | return fun + "(" + _left + ", " + _right + ")\n"; |
3055 | 4.88k | } |
3056 | | |
3057 | | std::string IRGeneratorForStatements::shiftOperation( |
3058 | | langutil::Token _operator, |
3059 | | IRVariable const& _value, |
3060 | | IRVariable const& _amountToShift |
3061 | | ) |
3062 | 18 | { |
3063 | 18 | solUnimplementedAssert( |
3064 | 18 | _amountToShift.type().category() != Type::Category::FixedPoint && |
3065 | 18 | _value.type().category() != Type::Category::FixedPoint, |
3066 | 18 | "Not yet implemented - FixedPointType." |
3067 | 18 | ); |
3068 | 18 | IntegerType const* amountType = dynamic_cast<IntegerType const*>(&_amountToShift.type()); |
3069 | 18 | solAssert(amountType); |
3070 | | |
3071 | 18 | solAssert(_operator == Token::SHL || _operator == Token::SAR); |
3072 | | |
3073 | 18 | return |
3074 | 18 | Whiskers(R"( |
3075 | 18 | <shift>(<value>, <amount>) |
3076 | 18 | )") |
3077 | 18 | ("shift", |
3078 | 18 | _operator == Token::SHL ? |
3079 | 2 | m_utils.typedShiftLeftFunction(_value.type(), *amountType) : |
3080 | 18 | m_utils.typedShiftRightFunction(_value.type(), *amountType) |
3081 | 18 | ) |
3082 | 18 | ("value", _value.name()) |
3083 | 18 | ("amount", _amountToShift.name()) |
3084 | 18 | .render(); |
3085 | 18 | } |
3086 | | |
3087 | | void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp) |
3088 | 11 | { |
3089 | 11 | langutil::Token const op = _binOp.getOperator(); |
3090 | 11 | solAssert(op == Token::Or || op == Token::And); |
3091 | | |
3092 | 11 | _binOp.leftExpression().accept(*this); |
3093 | 11 | setLocation(_binOp); |
3094 | | |
3095 | 11 | IRVariable value(_binOp); |
3096 | 11 | define(value, _binOp.leftExpression()); |
3097 | 11 | if (op == Token::Or) |
3098 | 0 | appendCode() << "if iszero(" << value.name() << ") {\n"; |
3099 | 11 | else |
3100 | 11 | appendCode() << "if " << value.name() << " {\n"; |
3101 | 11 | _binOp.rightExpression().accept(*this); |
3102 | 11 | setLocation(_binOp); |
3103 | 11 | assign(value, _binOp.rightExpression()); |
3104 | 11 | appendCode() << "}\n"; |
3105 | 11 | } |
3106 | | |
3107 | | void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable const& _value) |
3108 | 2.92k | { |
3109 | 2.92k | std::visit( |
3110 | 2.92k | util::GenericVisitor{ |
3111 | 2.92k | [&](IRLValue::Storage const& _storage) { |
3112 | 1.60k | std::string offsetArgument; |
3113 | 1.60k | std::optional<unsigned> offsetStatic; |
3114 | | |
3115 | 1.60k | std::visit(GenericVisitor{ |
3116 | 1.60k | [&](unsigned _offset) { offsetStatic = _offset; }, |
3117 | 1.60k | [&](std::string const& _offset) { offsetArgument = ", " + _offset; } |
3118 | 1.60k | }, _storage.offset); |
3119 | | |
3120 | 1.60k | appendCode() << |
3121 | 1.60k | m_utils.updateStorageValueFunction(_value.type(), _lvalue.type, VariableDeclaration::Location::Unspecified, offsetStatic) << |
3122 | 1.60k | "(" << |
3123 | 1.60k | _storage.slot << |
3124 | 1.60k | offsetArgument << |
3125 | 1.60k | _value.commaSeparatedListPrefixed() << |
3126 | 1.60k | ")\n"; |
3127 | 1.60k | }, |
3128 | 2.92k | [&](IRLValue::TransientStorage const& _transientStorage) { |
3129 | 0 | std::string offsetArgument; |
3130 | 0 | std::optional<unsigned> offsetStatic; |
3131 | |
|
3132 | 0 | std::visit(GenericVisitor{ |
3133 | 0 | [&](unsigned _offset) { offsetStatic = _offset; }, |
3134 | 0 | [&](std::string const& _offset) { offsetArgument = ", " + _offset; } |
3135 | 0 | }, _transientStorage.offset); |
3136 | |
|
3137 | 0 | appendCode() << |
3138 | 0 | m_utils.updateStorageValueFunction(_value.type(), _lvalue.type, VariableDeclaration::Location::Transient, offsetStatic) << |
3139 | 0 | "(" << |
3140 | 0 | _transientStorage.slot << |
3141 | 0 | offsetArgument << |
3142 | 0 | _value.commaSeparatedListPrefixed() << |
3143 | 0 | ")\n"; |
3144 | 0 | }, |
3145 | 2.92k | [&](IRLValue::Memory const& _memory) { |
3146 | 753 | if (_lvalue.type.isValueType()) |
3147 | 647 | { |
3148 | 647 | IRVariable prepared(m_context.newYulVariable(), _lvalue.type); |
3149 | 647 | define(prepared, _value); |
3150 | | |
3151 | 647 | if (_memory.byteArrayElement) |
3152 | 5 | { |
3153 | 5 | solAssert(_lvalue.type == *TypeProvider::byte()); |
3154 | 5 | appendCode() << "mstore8(" + _memory.address + ", byte(0, " + prepared.commaSeparatedList() + "))\n"; |
3155 | 5 | } |
3156 | 642 | else |
3157 | 642 | appendCode() << m_utils.writeToMemoryFunction(_lvalue.type) << |
3158 | 642 | "(" << |
3159 | 642 | _memory.address << |
3160 | 642 | ", " << |
3161 | 642 | prepared.commaSeparatedList() << |
3162 | 642 | ")\n"; |
3163 | 647 | } |
3164 | 106 | else if (auto const* literalType = dynamic_cast<StringLiteralType const*>(&_value.type())) |
3165 | 0 | { |
3166 | 0 | std::string writeUInt = m_utils.writeToMemoryFunction(*TypeProvider::uint256()); |
3167 | 0 | appendCode() << |
3168 | 0 | writeUInt << |
3169 | 0 | "(" << |
3170 | 0 | _memory.address << |
3171 | 0 | ", " << |
3172 | 0 | m_utils.copyLiteralToMemoryFunction(literalType->value()) + "()" << |
3173 | 0 | ")\n"; |
3174 | 0 | } |
3175 | 106 | else |
3176 | 106 | { |
3177 | 106 | solAssert(_lvalue.type.sizeOnStack() == 1); |
3178 | 106 | auto const* valueReferenceType = dynamic_cast<ReferenceType const*>(&_value.type()); |
3179 | 106 | solAssert(valueReferenceType); |
3180 | 106 | if (valueReferenceType->dataStoredIn(DataLocation::Memory)) |
3181 | 106 | appendCode() << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n"; |
3182 | 0 | else |
3183 | 0 | appendCode() << "mstore(" + _memory.address + ", " + m_utils.conversionFunction(_value.type(), _lvalue.type) + "(" + _value.commaSeparatedList() + "))\n"; |
3184 | 106 | } |
3185 | 753 | }, |
3186 | 2.92k | [&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); }, |
3187 | 2.92k | [&](IRLValue::Immutable const& _immutable) |
3188 | 2.92k | { |
3189 | 71 | solUnimplementedAssert(_lvalue.type.isValueType()); |
3190 | 71 | solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1); |
3191 | 71 | solAssert(_lvalue.type == *_immutable.variable->type()); |
3192 | 71 | size_t memOffset = m_context.immutableMemoryOffset(*_immutable.variable); |
3193 | | |
3194 | 71 | IRVariable prepared(m_context.newYulVariable(), _lvalue.type); |
3195 | 71 | define(prepared, _value); |
3196 | | |
3197 | 71 | appendCode() << "mstore(" << std::to_string(memOffset) << ", " << prepared.commaSeparatedList() << ")\n"; |
3198 | 71 | }, |
3199 | 2.92k | [&](IRLValue::Tuple const& _tuple) { |
3200 | 5 | auto components = std::move(_tuple.components); |
3201 | 15 | for (size_t i = 0; i < components.size(); i++) |
3202 | 10 | { |
3203 | 10 | size_t idx = components.size() - i - 1; |
3204 | 10 | if (components[idx]) |
3205 | 5 | writeToLValue(*components[idx], _value.tupleComponent(idx)); |
3206 | 10 | } |
3207 | 5 | } |
3208 | 2.92k | }, |
3209 | 2.92k | _lvalue.kind |
3210 | 2.92k | ); |
3211 | 2.92k | } |
3212 | | |
3213 | | IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue) |
3214 | 18.0k | { |
3215 | 18.0k | IRVariable result{m_context.newYulVariable(), _lvalue.type}; |
3216 | 18.0k | std::visit(GenericVisitor{ |
3217 | 18.0k | [&](IRLValue::Storage const& _storage) { |
3218 | 2.70k | if (!_lvalue.type.isValueType()) |
3219 | 2.12k | define(result) << _storage.slot << "\n"; |
3220 | 587 | else if (std::holds_alternative<std::string>(_storage.offset)) |
3221 | 149 | define(result) << |
3222 | 149 | m_utils.readFromStorageDynamic(_lvalue.type, true, VariableDeclaration::Location::Unspecified) << |
3223 | 149 | "(" << |
3224 | 149 | _storage.slot << |
3225 | 149 | ", " << |
3226 | 149 | std::get<std::string>(_storage.offset) << |
3227 | 149 | ")\n"; |
3228 | 438 | else |
3229 | 438 | define(result) << |
3230 | 438 | m_utils.readFromStorage(_lvalue.type, std::get<unsigned>(_storage.offset), true, VariableDeclaration::Location::Unspecified) << |
3231 | 438 | "(" << |
3232 | 438 | _storage.slot << |
3233 | 438 | ")\n"; |
3234 | 2.70k | }, |
3235 | 18.0k | [&](IRLValue::TransientStorage const& _transientStorage) { |
3236 | 0 | if (!_lvalue.type.isValueType()) |
3237 | 0 | define(result) << _transientStorage.slot << "\n"; |
3238 | 0 | else if (std::holds_alternative<std::string>(_transientStorage.offset)) |
3239 | 0 | define(result) << |
3240 | 0 | m_utils.readFromStorageDynamic(_lvalue.type, true, VariableDeclaration::Location::Transient) << |
3241 | 0 | "(" << |
3242 | 0 | _transientStorage.slot << |
3243 | 0 | ", " << |
3244 | 0 | std::get<std::string>(_transientStorage.offset) << |
3245 | 0 | ")\n"; |
3246 | 0 | else |
3247 | 0 | define(result) << |
3248 | 0 | m_utils.readFromStorage(_lvalue.type, std::get<unsigned>(_transientStorage.offset), true, VariableDeclaration::Location::Transient) << |
3249 | 0 | "(" << |
3250 | 0 | _transientStorage.slot << |
3251 | 0 | ")\n"; |
3252 | 0 | }, |
3253 | 18.0k | [&](IRLValue::Memory const& _memory) { |
3254 | 6.22k | if (_lvalue.type.isValueType()) |
3255 | 2.94k | define(result) << |
3256 | 2.94k | m_utils.readFromMemory(_lvalue.type) << |
3257 | 2.94k | "(" << |
3258 | 2.94k | _memory.address << |
3259 | 2.94k | ")\n"; |
3260 | 3.28k | else |
3261 | 3.28k | define(result) << "mload(" << _memory.address << ")\n"; |
3262 | 6.22k | }, |
3263 | 18.0k | [&](IRLValue::Stack const& _stack) { |
3264 | 9.08k | define(result, _stack.variable); |
3265 | 9.08k | }, |
3266 | 18.0k | [&](IRLValue::Immutable const& _immutable) { |
3267 | 26 | solUnimplementedAssert(_lvalue.type.isValueType()); |
3268 | 26 | solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1); |
3269 | 26 | solAssert(_lvalue.type == *_immutable.variable->type()); |
3270 | 26 | if (m_context.executionContext() == IRGenerationContext::ExecutionContext::Creation) |
3271 | 0 | { |
3272 | 0 | std::string readFunction = m_utils.readFromMemory(*_immutable.variable->type()); |
3273 | 0 | define(result) << |
3274 | 0 | readFunction << |
3275 | 0 | "(" << |
3276 | 0 | std::to_string(m_context.immutableMemoryOffset(*_immutable.variable)) << |
3277 | 0 | ")\n"; |
3278 | 0 | } |
3279 | 26 | else |
3280 | 26 | define(result) << "loadimmutable(\"" << std::to_string(_immutable.variable->id()) << "\")\n"; |
3281 | 26 | }, |
3282 | 18.0k | [&](IRLValue::Tuple const&) { |
3283 | 0 | solAssert(false, "Attempted to read from tuple lvalue."); |
3284 | 0 | } |
3285 | 18.0k | }, _lvalue.kind); |
3286 | 18.0k | return result; |
3287 | 18.0k | } |
3288 | | |
3289 | | void IRGeneratorForStatements::setLValue(Expression const& _expression, IRLValue _lvalue) |
3290 | 19.4k | { |
3291 | 19.4k | solAssert(!m_currentLValue); |
3292 | | |
3293 | 19.4k | if (_expression.annotation().willBeWrittenTo) |
3294 | 2.45k | { |
3295 | 2.45k | m_currentLValue.emplace(std::move(_lvalue)); |
3296 | 2.45k | if (_lvalue.type.dataStoredIn(DataLocation::CallData)) |
3297 | 2.45k | solAssert(std::holds_alternative<IRLValue::Stack>(_lvalue.kind)); |
3298 | 2.45k | } |
3299 | 17.0k | else |
3300 | | // Only define the expression, if it will not be written to. |
3301 | 17.0k | define(_expression, readFromLValue(_lvalue)); |
3302 | 19.4k | } |
3303 | | |
3304 | | void IRGeneratorForStatements::generateLoop( |
3305 | | Statement const& _body, |
3306 | | Expression const* _conditionExpression, |
3307 | | Statement const* _initExpression, |
3308 | | ExpressionStatement const* _loopExpression, |
3309 | | bool _isDoWhile, |
3310 | | bool _isSimpleCounterLoop |
3311 | | ) |
3312 | 106 | { |
3313 | 106 | std::string firstRun; |
3314 | | |
3315 | 106 | if (_isDoWhile) |
3316 | 2 | { |
3317 | 2 | solAssert(_conditionExpression, "Expected condition for doWhile"); |
3318 | 2 | firstRun = m_context.newYulVariable(); |
3319 | 2 | appendCode() << "let " << firstRun << " := 1\n"; |
3320 | 2 | } |
3321 | | |
3322 | 106 | appendCode() << "for {\n"; |
3323 | 106 | if (_initExpression) |
3324 | 80 | _initExpression->accept(*this); |
3325 | 106 | appendCode() << "} 1 {\n"; |
3326 | 106 | if (_loopExpression) |
3327 | 80 | { |
3328 | 80 | Arithmetic previousArithmetic = m_context.arithmetic(); |
3329 | 80 | if (m_optimiserSettings.simpleCounterForLoopUncheckedIncrement && _isSimpleCounterLoop) |
3330 | 79 | m_context.setArithmetic(Arithmetic::Wrapping); |
3331 | 80 | _loopExpression->accept(*this); |
3332 | 80 | m_context.setArithmetic(previousArithmetic); |
3333 | 80 | } |
3334 | 106 | appendCode() << "}\n"; |
3335 | 106 | appendCode() << "{\n"; |
3336 | | |
3337 | 106 | if (_conditionExpression) |
3338 | 106 | { |
3339 | 106 | if (_isDoWhile) |
3340 | 2 | appendCode() << "if iszero(" << firstRun << ") {\n"; |
3341 | | |
3342 | 106 | _conditionExpression->accept(*this); |
3343 | 106 | appendCode() << |
3344 | 106 | "if iszero(" << |
3345 | 106 | expressionAsType(*_conditionExpression, *TypeProvider::boolean()) << |
3346 | 106 | ") { break }\n"; |
3347 | | |
3348 | 106 | if (_isDoWhile) |
3349 | 2 | appendCode() << "}\n" << firstRun << " := 0\n"; |
3350 | 106 | } |
3351 | | |
3352 | 106 | _body.accept(*this); |
3353 | | |
3354 | 106 | appendCode() << "}\n"; |
3355 | 106 | } |
3356 | | |
3357 | | Type const& IRGeneratorForStatements::type(Expression const& _expression) |
3358 | 22.9k | { |
3359 | 22.9k | solAssert(_expression.annotation().type, "Type of expression not set."); |
3360 | 22.9k | return *_expression.annotation().type; |
3361 | 22.9k | } |
3362 | | |
3363 | | bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement) |
3364 | 53 | { |
3365 | 53 | Expression const& externalCall = _tryStatement.externalCall(); |
3366 | 53 | externalCall.accept(*this); |
3367 | 53 | setLocation(_tryStatement); |
3368 | | |
3369 | 53 | appendCode() << "switch iszero(" << IRNames::trySuccessConditionVariable(externalCall) << ")\n"; |
3370 | | |
3371 | 53 | appendCode() << "case 0 { // success case\n"; |
3372 | 53 | TryCatchClause const& successClause = *_tryStatement.clauses().front(); |
3373 | 53 | if (successClause.parameters()) |
3374 | 8 | { |
3375 | 8 | size_t i = 0; |
3376 | 8 | for (ASTPointer<VariableDeclaration> const& varDecl: successClause.parameters()->parameters()) |
3377 | 14 | { |
3378 | 14 | solAssert(varDecl); |
3379 | 14 | define(m_context.addLocalVariable(*varDecl), |
3380 | 14 | successClause.parameters()->parameters().size() == 1 ? |
3381 | 2 | IRVariable(externalCall) : |
3382 | 14 | IRVariable(externalCall).tupleComponent(i++) |
3383 | 14 | ); |
3384 | 14 | } |
3385 | 8 | } |
3386 | | |
3387 | 53 | successClause.block().accept(*this); |
3388 | 53 | setLocation(_tryStatement); |
3389 | 53 | appendCode() << "}\n"; |
3390 | | |
3391 | 53 | appendCode() << "default { // failure case\n"; |
3392 | 53 | handleCatch(_tryStatement); |
3393 | 53 | appendCode() << "}\n"; |
3394 | | |
3395 | 53 | return false; |
3396 | 53 | } |
3397 | | |
3398 | | void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement) |
3399 | 53 | { |
3400 | 53 | setLocation(_tryStatement); |
3401 | 53 | std::string const runFallback = m_context.newYulVariable(); |
3402 | 53 | appendCode() << "let " << runFallback << " := 1\n"; |
3403 | | |
3404 | | // This function returns zero on "short returndata". We have to add a success flag |
3405 | | // once we implement custom error codes. |
3406 | 53 | if (_tryStatement.errorClause() || _tryStatement.panicClause()) |
3407 | 0 | appendCode() << "switch " << m_utils.returnDataSelectorFunction() << "()\n"; |
3408 | | |
3409 | 53 | if (TryCatchClause const* errorClause = _tryStatement.errorClause()) |
3410 | 0 | { |
3411 | 0 | appendCode() << "case " << selectorFromSignatureU32("Error(string)") << " {\n"; |
3412 | 0 | setLocation(*errorClause); |
3413 | 0 | std::string const dataVariable = m_context.newYulVariable(); |
3414 | 0 | appendCode() << "let " << dataVariable << " := " << m_utils.tryDecodeErrorMessageFunction() << "()\n"; |
3415 | 0 | appendCode() << "if " << dataVariable << " {\n"; |
3416 | 0 | appendCode() << runFallback << " := 0\n"; |
3417 | 0 | if (errorClause->parameters()) |
3418 | 0 | { |
3419 | 0 | solAssert(errorClause->parameters()->parameters().size() == 1); |
3420 | 0 | IRVariable const& var = m_context.addLocalVariable(*errorClause->parameters()->parameters().front()); |
3421 | 0 | define(var) << dataVariable << "\n"; |
3422 | 0 | } |
3423 | 0 | errorClause->accept(*this); |
3424 | 0 | setLocation(*errorClause); |
3425 | 0 | appendCode() << "}\n"; |
3426 | 0 | setLocation(_tryStatement); |
3427 | 0 | appendCode() << "}\n"; |
3428 | 0 | } |
3429 | 53 | if (TryCatchClause const* panicClause = _tryStatement.panicClause()) |
3430 | 0 | { |
3431 | 0 | appendCode() << "case " << selectorFromSignatureU32("Panic(uint256)") << " {\n"; |
3432 | 0 | setLocation(*panicClause); |
3433 | 0 | std::string const success = m_context.newYulVariable(); |
3434 | 0 | std::string const code = m_context.newYulVariable(); |
3435 | 0 | appendCode() << "let " << success << ", " << code << " := " << m_utils.tryDecodePanicDataFunction() << "()\n"; |
3436 | 0 | appendCode() << "if " << success << " {\n"; |
3437 | 0 | appendCode() << runFallback << " := 0\n"; |
3438 | 0 | if (panicClause->parameters()) |
3439 | 0 | { |
3440 | 0 | solAssert(panicClause->parameters()->parameters().size() == 1); |
3441 | 0 | IRVariable const& var = m_context.addLocalVariable(*panicClause->parameters()->parameters().front()); |
3442 | 0 | define(var) << code << "\n"; |
3443 | 0 | } |
3444 | 0 | panicClause->accept(*this); |
3445 | 0 | setLocation(*panicClause); |
3446 | 0 | appendCode() << "}\n"; |
3447 | 0 | setLocation(_tryStatement); |
3448 | 0 | appendCode() << "}\n"; |
3449 | 0 | } |
3450 | | |
3451 | 53 | setLocation(_tryStatement); |
3452 | 53 | appendCode() << "if " << runFallback << " {\n"; |
3453 | 53 | if (_tryStatement.fallbackClause()) |
3454 | 53 | handleCatchFallback(*_tryStatement.fallbackClause()); |
3455 | 0 | else |
3456 | 0 | appendCode() << m_utils.forwardingRevertFunction() << "()\n"; |
3457 | 53 | setLocation(_tryStatement); |
3458 | 53 | appendCode() << "}\n"; |
3459 | 53 | } |
3460 | | |
3461 | | void IRGeneratorForStatements::handleCatchFallback(TryCatchClause const& _fallback) |
3462 | 53 | { |
3463 | 53 | setLocation(_fallback); |
3464 | 53 | if (_fallback.parameters()) |
3465 | 0 | { |
3466 | 0 | solAssert(m_context.evmVersion().supportsReturndata()); |
3467 | 0 | solAssert( |
3468 | 0 | _fallback.parameters()->parameters().size() == 1 && |
3469 | 0 | _fallback.parameters()->parameters().front() && |
3470 | 0 | *_fallback.parameters()->parameters().front()->annotation().type == *TypeProvider::bytesMemory(), |
3471 | 0 | "" |
3472 | 0 | ); |
3473 | |
|
3474 | 0 | VariableDeclaration const& paramDecl = *_fallback.parameters()->parameters().front(); |
3475 | 0 | define(m_context.addLocalVariable(paramDecl)) << m_utils.extractReturndataFunction() << "()\n"; |
3476 | 0 | } |
3477 | 53 | _fallback.accept(*this); |
3478 | 53 | } |
3479 | | |
3480 | | void IRGeneratorForStatements::revertWithError( |
3481 | | std::string const& _signature, |
3482 | | std::vector<Type const*> const& _parameterTypes, |
3483 | | std::vector<ASTPointer<Expression const>> const& _errorArguments |
3484 | | ) |
3485 | 13 | { |
3486 | 13 | appendCode() << m_utils.revertWithError( |
3487 | 13 | _signature, |
3488 | 13 | _parameterTypes, |
3489 | 13 | _errorArguments, |
3490 | 13 | m_context.newYulVariable(), |
3491 | 13 | m_context.newYulVariable() |
3492 | 13 | ); |
3493 | 13 | } |
3494 | | |
3495 | | bool IRGeneratorForStatements::visit(TryCatchClause const& _clause) |
3496 | 53 | { |
3497 | 53 | _clause.block().accept(*this); |
3498 | 53 | return false; |
3499 | 53 | } |
3500 | | |
3501 | | std::string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const |
3502 | 47 | { |
3503 | | solAssert(_library.isLibrary()); |
3504 | 47 | return "linkersymbol(" + util::escapeAndQuoteString(_library.fullyQualifiedName()) + ")"; |
3505 | 47 | } |