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