Merge pull request #1674 from ethereum/assemblyPrinter

Assembly printer.
This commit is contained in:
chriseth 2017-02-15 15:41:45 +01:00 committed by GitHub
commit ad751bd3e6
5 changed files with 271 additions and 4 deletions

View File

@ -0,0 +1,125 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2017
* Converts a parsed assembly into its textual form.
*/
#include <libsolidity/inlineasm/AsmPrinter.h>
#include <libsolidity/inlineasm/AsmData.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <memory>
#include <functional>
using namespace std;
using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::assembly;
//@TODO source locations
string AsmPrinter::operator()(assembly::Instruction const& _instruction)
{
return boost::to_lower_copy(instructionInfo(_instruction.instruction).name);
}
string AsmPrinter::operator()(assembly::Literal const& _literal)
{
if (_literal.isNumber)
return _literal.value;
string out;
for (char c: _literal.value)
if (c == '\\')
out += "\\\\";
else if (c == '"')
out += "\\\"";
else if (c == '\b')
out += "\\b";
else if (c == '\f')
out += "\\f";
else if (c == '\n')
out += "\\n";
else if (c == '\r')
out += "\\r";
else if (c == '\t')
out += "\\t";
else if (c == '\v')
out += "\\v";
else if (!isprint(c, locale::classic()))
{
ostringstream o;
o << std::hex << setfill('0') << setw(2) << (unsigned)(unsigned char)(c);
out += "\\x" + o.str();
}
else
out += c;
return "\"" + out + "\"";
}
string AsmPrinter::operator()(assembly::Identifier const& _identifier)
{
return _identifier.name;
}
string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functionalInstruction)
{
return
(*this)(_functionalInstruction.instruction) +
"(" +
boost::algorithm::join(
_functionalInstruction.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)),
", " ) +
")";
}
string AsmPrinter::operator()(assembly::Label const& _label)
{
return _label.name + ":";
}
string AsmPrinter::operator()(assembly::Assignment const& _assignment)
{
return "=: " + (*this)(_assignment.variableName);
}
string AsmPrinter::operator()(assembly::FunctionalAssignment const& _functionalAssignment)
{
return (*this)(_functionalAssignment.variableName) + " := " + boost::apply_visitor(*this, *_functionalAssignment.value);
}
string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDeclaration)
{
return "let " + _variableDeclaration.name + " := " + boost::apply_visitor(*this, *_variableDeclaration.value);
}
string AsmPrinter::operator()(Block const& _block)
{
if (_block.statements.empty())
return "{\n}";
string body = boost::algorithm::join(
_block.statements | boost::adaptors::transformed(boost::apply_visitor(*this)),
"\n"
);
boost::replace_all(body, "\n", "\n ");
return "{\n " + body + "\n}";
}

View File

@ -0,0 +1,61 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2017
* Converts a parsed assembly into its textual form.
*/
#pragma once
#include <boost/variant.hpp>
namespace dev
{
namespace solidity
{
namespace assembly
{
struct Instruction;
struct Literal;
struct Identifier;
struct FunctionalInstruction;
struct Label;
struct Assignment;
struct FunctionalAssignment;
struct VariableDeclaration;
struct FunctionDefinition;
struct FunctionCall;
struct Block;
class AsmPrinter: public boost::static_visitor<std::string>
{
public:
std::string operator()(assembly::Instruction const& _instruction);
std::string operator()(assembly::Literal const& _literal);
std::string operator()(assembly::Identifier const& _identifier);
std::string operator()(assembly::FunctionalInstruction const& _functionalInstruction);
std::string operator()(assembly::Label const& _label);
std::string operator()(assembly::Assignment const& _assignment);
std::string operator()(assembly::FunctionalAssignment const& _functionalAssignment);
std::string operator()(assembly::VariableDeclaration const& _variableDeclaration);
std::string operator()(assembly::Block const& _block);
};
}
}
}

View File

@ -21,12 +21,17 @@
*/
#include <libsolidity/inlineasm/AsmStack.h>
#include <memory>
#include <libevmasm/Assembly.h>
#include <libevmasm/SourceLocation.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/inlineasm/AsmParser.h>
#include <libsolidity/inlineasm/AsmCodeGen.h>
#include <libsolidity/inlineasm/AsmPrinter.h>
#include <libsolidity/parsing/Scanner.h>
#include <libevmasm/Assembly.h>
#include <libevmasm/SourceLocation.h>
#include <memory>
using namespace std;
using namespace dev;
@ -44,6 +49,11 @@ bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner)
return true;
}
string InlineAssemblyStack::toString()
{
return AsmPrinter()(*m_parserResult);
}
eth::Assembly InlineAssemblyStack::assemble()
{
CodeGenerator codeGen(*m_parserResult, m_errors);

View File

@ -46,6 +46,10 @@ public:
/// Parse the given inline assembly chunk starting with `{` and ending with the corresponding `}`.
/// @return false or error.
bool parse(std::shared_ptr<Scanner> const& _scanner);
/// Converts the parser result back into a string form (not necessarily the same form
/// as the source form, but it should parse into the same parsed form again).
std::string toString();
eth::Assembly assemble();
/// Parse and assemble a string in one run - for use in Solidity code generation itself.

View File

@ -73,11 +73,22 @@ bool successAssemble(string const& _source, bool _allowWarnings = true)
return successParse(_source, true, _allowWarnings);
}
void parsePrintCompare(string const& _source)
{
assembly::InlineAssemblyStack stack;
BOOST_REQUIRE(stack.parse(std::make_shared<Scanner>(CharStream(_source))));
BOOST_REQUIRE(stack.errors().empty());
BOOST_CHECK_EQUAL(stack.toString(), _source);
}
}
BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly)
BOOST_AUTO_TEST_SUITE(Parsing)
BOOST_AUTO_TEST_CASE(smoke_test)
{
BOOST_CHECK(successParse("{ }"));
@ -148,6 +159,60 @@ BOOST_AUTO_TEST_CASE(blocks)
BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }"));
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(Printing)
BOOST_AUTO_TEST_CASE(print_smoke)
{
parsePrintCompare("{\n}");
}
BOOST_AUTO_TEST_CASE(print_instructions)
{
parsePrintCompare("{\n 7\n 8\n mul\n dup10\n add\n}");
}
BOOST_AUTO_TEST_CASE(print_subblock)
{
parsePrintCompare("{\n {\n dup4\n add\n }\n}");
}
BOOST_AUTO_TEST_CASE(print_functional)
{
parsePrintCompare("{\n mul(sload(0x12), 7)\n}");
}
BOOST_AUTO_TEST_CASE(print_label)
{
parsePrintCompare("{\n loop:\n jump(loop)\n}");
}
BOOST_AUTO_TEST_CASE(print_assignments)
{
parsePrintCompare("{\n let x := mul(2, 3)\n 7\n =: x\n x := add(1, 2)\n}");
}
BOOST_AUTO_TEST_CASE(print_string_literals)
{
parsePrintCompare("{\n \"\\n'\\xab\\x95\\\"\"\n}");
}
BOOST_AUTO_TEST_CASE(print_string_literal_unicode)
{
string source = "{ \"\\u1bac\" }";
string parsed = "{\n \"\\xe1\\xae\\xac\"\n}";
assembly::InlineAssemblyStack stack;
BOOST_REQUIRE(stack.parse(std::make_shared<Scanner>(CharStream(source))));
BOOST_REQUIRE(stack.errors().empty());
BOOST_CHECK_EQUAL(stack.toString(), parsed);
parsePrintCompare(parsed);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(Analysis)
BOOST_AUTO_TEST_CASE(string_literals)
{
BOOST_CHECK(successAssemble("{ let x := \"12345678901234567890123456789012\" }"));
@ -212,6 +277,8 @@ BOOST_AUTO_TEST_CASE(revert)
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
}
}
} // end namespaces