/* 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 . */ #include #include #include #include using namespace solidity::util; using namespace solidity::langutil; using namespace solidity::yul; using namespace solidity::yul::test; using namespace std; bool StackShufflingTest::parse(string const& _source) { CharStream stream(_source, ""); Scanner scanner(stream); auto expectToken = [&](Token _token) { soltestAssert( scanner.next() == _token, "Invalid token. Expected: \"" + TokenTraits::friendlyName(_token) + "\"." ); }; auto parseStack = [&](Stack& stack) -> bool { if (scanner.currentToken() != Token::LBrack) return false; scanner.next(); while (scanner.currentToken() != Token::RBrack && scanner.currentToken() != Token::EOS) { string literal = scanner.currentLiteral(); if (literal == "RET") { scanner.next(); if (scanner.currentToken() == Token::LBrack) { scanner.next(); string functionName = scanner.currentLiteral(); auto call = yul::FunctionCall{ {}, yul::Identifier{{}, YulString(functionName)}, {} }; stack.emplace_back(FunctionCallReturnLabelSlot{ m_functions.insert( make_pair(functionName, call) ).first->second }); expectToken(Token::RBrack); } else { static Scope::Function function; stack.emplace_back(FunctionReturnLabelSlot{function}); continue; } } else if (literal == "TMP") { expectToken(Token::LBrack); scanner.next(); string functionName = scanner.currentLiteral(); auto call = yul::FunctionCall{ {}, yul::Identifier{{}, YulString(functionName)}, {} }; expectToken(Token::Comma); scanner.next(); size_t index = size_t(atoi(scanner.currentLiteral().c_str())); stack.emplace_back(TemporarySlot{ m_functions.insert(make_pair(functionName, call)).first->second, index }); expectToken(Token::RBrack); } else if (literal.find("0x") != string::npos || scanner.currentToken() == Token::Number) { stack.emplace_back(LiteralSlot{u256(literal)}); } else if (literal == "JUNK") { stack.emplace_back(JunkSlot()); } else if (literal == "GHOST") { expectToken(Token::LBrack); scanner.next(); // read number of ghost variables as ghostVariableId string ghostVariableId = scanner.currentLiteral(); Scope::Variable ghostVar = Scope::Variable{""_yulstring, YulString(literal + "[" + ghostVariableId + "]")}; stack.emplace_back(VariableSlot{ m_variables.insert(make_pair(ghostVar.name, ghostVar)).first->second }); expectToken(Token::RBrack); } else { Scope::Variable var = Scope::Variable{""_yulstring, YulString(literal)}; stack.emplace_back(VariableSlot{ m_variables.insert( make_pair(literal, var) ).first->second }); } scanner.next(); } return scanner.currentToken() == Token::RBrack; }; if (!parseStack(m_sourceStack)) return false; scanner.next(); return parseStack(m_targetStack); } StackShufflingTest::StackShufflingTest(string const& _filename): TestCase(_filename) { m_source = m_reader.source(); m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult StackShufflingTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { if (!parse(m_source)) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; return TestResult::FatalError; } ostringstream output; createStackLayout( m_sourceStack, m_targetStack, [&](unsigned _swapDepth) // swap { output << stackToString(m_sourceStack) << endl; output << "SWAP" << _swapDepth << endl; }, [&](StackSlot const& _slot) // dupOrPush { output << stackToString(m_sourceStack) << endl; if (canBeFreelyGenerated(_slot)) output << "PUSH " << stackSlotToString(_slot) << endl; else { if (auto depth = util::findOffset(m_sourceStack | ranges::views::reverse, _slot)) output << "DUP" << *depth + 1 << endl; else BOOST_THROW_EXCEPTION(runtime_error("Invalid DUP operation.")); } }, [&](){ // pop output << stackToString(m_sourceStack) << endl; output << "POP" << endl; } ); output << stackToString(m_sourceStack) << endl; m_obtainedResult = output.str(); return checkResult(_stream, _linePrefix, _formatted); }