/* This file is part of solidity. solidity is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. solidity is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with solidity. If not, see . */ /** * Component that translates Solidity code into Yul at statement level and below. */ #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace dev; using namespace dev::solidity; namespace { struct CopyTranslate: public yul::ASTCopier { using ExternalRefsMap = std::map; CopyTranslate(IRGenerationContext& _context, ExternalRefsMap const& _references): m_context(_context), m_references(_references) {} using ASTCopier::operator(); yul::YulString translateIdentifier(yul::YulString _name) override { return yul::YulString{"usr$" + _name.str()}; } yul::Identifier translate(yul::Identifier const& _identifier) override { if (!m_references.count(&_identifier)) return ASTCopier::translate(_identifier); auto const& reference = m_references.at(&_identifier); auto const varDecl = dynamic_cast(reference.declaration); solUnimplementedAssert(varDecl, ""); solUnimplementedAssert( reference.isOffset == false && reference.isSlot == false, "" ); return yul::Identifier{ _identifier.location, yul::YulString{m_context.localVariableName(*varDecl)} }; } private: IRGenerationContext& m_context; ExternalRefsMap const& m_references; }; } string IRGeneratorForStatements::code() const { solAssert(!m_currentLValue, "LValue not reset!"); return m_code.str(); } void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement) { for (auto const& decl: _varDeclStatement.declarations()) if (decl) m_context.addLocalVariable(*decl); if (Expression const* expression = _varDeclStatement.initialValue()) { solUnimplementedAssert(_varDeclStatement.declarations().size() == 1, ""); VariableDeclaration const& varDecl = *_varDeclStatement.declarations().front(); m_code << "let " << m_context.localVariableName(varDecl) << " := " << expressionAsType(*expression, *varDecl.type()) << "\n"; } else for (auto const& decl: _varDeclStatement.declarations()) if (decl) m_code << "let " << m_context.localVariableName(*decl) << "\n"; } bool IRGeneratorForStatements::visit(Assignment const& _assignment) { solUnimplementedAssert(_assignment.assignmentOperator() == Token::Assign, ""); _assignment.rightHandSide().accept(*this); Type const* intermediateType = type(_assignment.rightHandSide()).closestTemporaryType( &type(_assignment.leftHandSide()) ); string intermediateValue = m_context.newYulVariable(); m_code << "let " << intermediateValue << " := " << expressionAsType(_assignment.rightHandSide(), *intermediateType) << "\n"; _assignment.leftHandSide().accept(*this); solAssert(!!m_currentLValue, "LValue not retrieved."); m_currentLValue->storeValue(intermediateValue, *intermediateType); m_currentLValue.reset(); defineExpression(_assignment) << intermediateValue << "\n"; return false; } bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) { if (_tuple.isInlineArray()) solUnimplementedAssert(false, ""); else { solUnimplementedAssert(!_tuple.annotation().lValueRequested, ""); solUnimplementedAssert(_tuple.components().size() == 1, ""); solAssert(_tuple.components().front(), ""); _tuple.components().front()->accept(*this); defineExpression(_tuple) << m_context.variable(*_tuple.components().front()) << "\n"; } return false; } bool IRGeneratorForStatements::visit(ForStatement const& _for) { m_code << "for {\n"; if (_for.initializationExpression()) _for.initializationExpression()->accept(*this); m_code << "} return_flag {\n"; if (_for.loopExpression()) _for.loopExpression()->accept(*this); m_code << "}\n"; if (_for.condition()) { _for.condition()->accept(*this); m_code << "if iszero(" << expressionAsType(*_for.condition(), *TypeProvider::boolean()) << ") { break }\n"; } _for.body().accept(*this); m_code << "}\n"; // Bubble up the return condition. m_code << "if iszero(return_flag) { break }\n"; return false; } bool IRGeneratorForStatements::visit(Continue const&) { m_code << "continue\n"; return false; } bool IRGeneratorForStatements::visit(Break const&) { m_code << "break\n"; return false; } void IRGeneratorForStatements::endVisit(Return const& _return) { if (Expression const* value = _return.expression()) { solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer."); vector> const& returnParameters = _return.annotation().functionReturnParameters->parameters(); TypePointers types; for (auto const& retVariable: returnParameters) types.push_back(retVariable->annotation().type); // TODO support tuples solUnimplementedAssert(types.size() == 1, "Multi-returns not implemented."); m_code << m_context.localVariableName(*returnParameters.front()) << " := " << expressionAsType(*value, *types.front()) << "\n"; } m_code << "return_flag := 0\n" << "break\n"; } void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) { if (type(_unaryOperation).category() == Type::Category::RationalNumber) defineExpression(_unaryOperation) << formatNumber(type(_unaryOperation).literalValue(nullptr)) << "\n"; else solUnimplementedAssert(false, ""); } bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) { solAssert(!!_binOp.annotation().commonType, ""); TypePointer commonType = _binOp.annotation().commonType; langutil::Token op = _binOp.getOperator(); if (op == Token::And || op == Token::Or) { // This can short-circuit! appendAndOrOperatorCode(_binOp); return false; } _binOp.leftExpression().accept(*this); _binOp.rightExpression().accept(*this); if (commonType->category() == Type::Category::RationalNumber) defineExpression(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n"; else if (TokenTraits::isCompareOp(op)) { solUnimplementedAssert(commonType->category() != Type::Category::Function, ""); solAssert(commonType->isValueType(), ""); bool isSigned = false; if (auto type = dynamic_cast(commonType)) isSigned = type->isSigned(); string args = expressionAsType(_binOp.leftExpression(), *commonType) + ", " + expressionAsType(_binOp.rightExpression(), *commonType); string expr; if (op == Token::Equal) expr = "eq(" + move(args) + ")"; else if (op == Token::NotEqual) expr = "iszero(eq(" + move(args) + "))"; else if (op == Token::GreaterThanOrEqual) expr = "iszero(" + string(isSigned ? "slt(" : "lt(") + move(args) + "))"; else if (op == Token::LessThanOrEqual) expr = "iszero(" + string(isSigned ? "sgt(" : "gt(") + move(args) + "))"; else if (op == Token::GreaterThan) expr = (isSigned ? "sgt(" : "gt(") + move(args) + ")"; else if (op == Token::LessThan) expr = (isSigned ? "slt(" : "lt(") + move(args) + ")"; else solAssert(false, "Unknown comparison operator."); defineExpression(_binOp) << expr << "\n"; } else { solUnimplementedAssert(_binOp.getOperator() == Token::Add, ""); if (IntegerType const* type = dynamic_cast(commonType)) { solUnimplementedAssert(!type->isSigned(), ""); defineExpression(_binOp) << m_utils.overflowCheckedUIntAddFunction(type->numBits()) << "(" << expressionAsType(_binOp.leftExpression(), *commonType) << ", " << expressionAsType(_binOp.rightExpression(), *commonType) << ")\n"; } else solUnimplementedAssert(false, ""); } return false; } void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) { solUnimplementedAssert( _functionCall.annotation().kind == FunctionCallKind::FunctionCall || _functionCall.annotation().kind == FunctionCallKind::TypeConversion, "This type of function call is not yet implemented" ); Type const& funcType = type(_functionCall.expression()); if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) { solAssert(funcType.category() == Type::Category::TypeType, "Expected category to be TypeType"); solAssert(_functionCall.arguments().size() == 1, "Expected one argument for type conversion"); defineExpression(_functionCall) << expressionAsType(*_functionCall.arguments().front(), type(_functionCall)) << "\n"; return; } FunctionTypePointer functionType = dynamic_cast(&funcType); TypePointers parameterTypes = functionType->parameterTypes(); vector> const& callArguments = _functionCall.arguments(); vector> const& callArgumentNames = _functionCall.names(); if (!functionType->takesArbitraryParameters()) solAssert(callArguments.size() == parameterTypes.size(), ""); vector> arguments; if (callArgumentNames.empty()) // normal arguments arguments = callArguments; else // named arguments for (auto const& parameterName: functionType->parameterNames()) { auto const it = std::find_if(callArgumentNames.cbegin(), callArgumentNames.cend(), [&](ASTPointer const& _argName) { return *_argName == parameterName; }); solAssert(it != callArgumentNames.cend(), ""); arguments.push_back(callArguments[std::distance(callArgumentNames.begin(), it)]); } solUnimplementedAssert(!functionType->bound(), ""); switch (functionType->kind()) { case FunctionType::Kind::Internal: { vector args; for (unsigned i = 0; i < arguments.size(); ++i) if (functionType->takesArbitraryParameters()) args.emplace_back(m_context.variable(*arguments[i])); else args.emplace_back(expressionAsType(*arguments[i], *parameterTypes[i])); if (auto identifier = dynamic_cast(&_functionCall.expression())) { solAssert(!functionType->bound(), ""); if (auto functionDef = dynamic_cast(identifier->annotation().referencedDeclaration)) { // @TODO The function can very well return multiple vars. defineExpression(_functionCall) << m_context.virtualFunctionName(*functionDef) << "(" << joinHumanReadable(args) << ")\n"; return; } } // @TODO The function can very well return multiple vars. args = vector{m_context.variable(_functionCall.expression())} + args; defineExpression(_functionCall) << m_context.internalDispatch(functionType->parameterTypes().size(), functionType->returnParameterTypes().size()) << "(" << joinHumanReadable(args) << ")\n"; break; } case FunctionType::Kind::Assert: case FunctionType::Kind::Require: { solAssert(arguments.size() > 0, "Expected at least one parameter for require/assert"); solAssert(arguments.size() <= 2, "Expected no more than two parameters for require/assert"); string requireOrAssertFunction = m_utils.requireOrAssertFunction( functionType->kind() == FunctionType::Kind::Assert, arguments.size() > 1 ? arguments[1]->annotation().type : nullptr ); m_code << move(requireOrAssertFunction) << "(" << m_context.variable(*arguments[0]); if (arguments.size() > 1) m_code << ", " << m_context.variable(*arguments[1]); m_code << ")\n"; break; } default: solUnimplemented(""); } } bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) { CopyTranslate bodyCopier{m_context, _inlineAsm.annotation().externalReferences}; yul::Statement modified = bodyCopier(_inlineAsm.operations()); solAssert(modified.type() == typeid(yul::Block), ""); m_code << yul::AsmPrinter()(boost::get(std::move(modified))) << "\n"; return false; } bool IRGeneratorForStatements::visit(Identifier const& _identifier) { Declaration const* declaration = _identifier.annotation().referencedDeclaration; if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) defineExpression(_identifier) << to_string(m_context.virtualFunction(*functionDef).id()) << "\n"; else if (VariableDeclaration const* varDecl = dynamic_cast(declaration)) { // TODO for the constant case, we have to be careful: // If the value is visited twice, `defineExpression` is called twice on // the same expression. solUnimplementedAssert(!varDecl->isConstant(), ""); unique_ptr lvalue; if (m_context.isLocalVariable(*varDecl)) lvalue = make_unique(m_code, m_context, *varDecl); else if (m_context.isStateVariable(*varDecl)) lvalue = make_unique(m_code, m_context, *varDecl); else solAssert(false, "Invalid variable kind."); setLValue(_identifier, move(lvalue)); } else if (!dynamic_cast(declaration)) solUnimplemented(""); return false; } bool IRGeneratorForStatements::visit(Literal const& _literal) { Type const& literalType = type(_literal); switch (literalType.category()) { case Type::Category::RationalNumber: case Type::Category::Bool: case Type::Category::Address: defineExpression(_literal) << toCompactHexWithPrefix(literalType.literalValue(&_literal)) << "\n"; break; case Type::Category::StringLiteral: break; // will be done during conversion default: solUnimplemented("Only integer, boolean and string literals implemented for now."); } return false; } string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to) { Type const& from = type(_expression); if (from.sizeOnStack() == 0) { solAssert(from != _to, ""); return m_utils.conversionFunction(from, _to) + "()"; } else { string varName = m_context.variable(_expression); if (from == _to) return varName; else return m_utils.conversionFunction(from, _to) + "(" + std::move(varName) + ")"; } } ostream& IRGeneratorForStatements::defineExpression(Expression const& _expression) { return m_code << "let " << m_context.variable(_expression) << " := "; } void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp) { langutil::Token const op = _binOp.getOperator(); solAssert(op == Token::Or || op == Token::And, ""); _binOp.leftExpression().accept(*this); string value = m_context.variable(_binOp); m_code << "let " << value << " := " << m_context.variable(_binOp.leftExpression()) << "\n"; if (op == Token::Or) m_code << "if iszero(" << value << ") {\n"; else m_code << "if " << value << " {\n"; _binOp.rightExpression().accept(*this); m_code << value << " := " + m_context.variable(_binOp.rightExpression()) << "\n"; m_code << "}\n"; } void IRGeneratorForStatements::setLValue(Expression const& _expression, unique_ptr _lvalue) { solAssert(!m_currentLValue, ""); if (_expression.annotation().lValueRequested) // Do not define the expression, so it cannot be used as value. m_currentLValue = std::move(_lvalue); else defineExpression(_expression) << _lvalue->retrieveValue() << "\n"; } Type const& IRGeneratorForStatements::type(Expression const& _expression) { solAssert(_expression.annotation().type, "Type of expression not set."); return *_expression.annotation().type; }