mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #1674 from ethereum/assemblyPrinter
Assembly printer.
This commit is contained in:
commit
ad751bd3e6
125
libsolidity/inlineasm/AsmPrinter.cpp
Normal file
125
libsolidity/inlineasm/AsmPrinter.cpp
Normal 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}";
|
||||||
|
}
|
61
libsolidity/inlineasm/AsmPrinter.h
Normal file
61
libsolidity/inlineasm/AsmPrinter.h
Normal 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,12 +21,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/inlineasm/AsmStack.h>
|
#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/AsmParser.h>
|
||||||
#include <libsolidity/inlineasm/AsmCodeGen.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 std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
@ -44,6 +49,11 @@ bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string InlineAssemblyStack::toString()
|
||||||
|
{
|
||||||
|
return AsmPrinter()(*m_parserResult);
|
||||||
|
}
|
||||||
|
|
||||||
eth::Assembly InlineAssemblyStack::assemble()
|
eth::Assembly InlineAssemblyStack::assemble()
|
||||||
{
|
{
|
||||||
CodeGenerator codeGen(*m_parserResult, m_errors);
|
CodeGenerator codeGen(*m_parserResult, m_errors);
|
||||||
|
@ -46,6 +46,10 @@ public:
|
|||||||
/// Parse the given inline assembly chunk starting with `{` and ending with the corresponding `}`.
|
/// Parse the given inline assembly chunk starting with `{` and ending with the corresponding `}`.
|
||||||
/// @return false or error.
|
/// @return false or error.
|
||||||
bool parse(std::shared_ptr<Scanner> const& _scanner);
|
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();
|
eth::Assembly assemble();
|
||||||
|
|
||||||
/// Parse and assemble a string in one run - for use in Solidity code generation itself.
|
/// Parse and assemble a string in one run - for use in Solidity code generation itself.
|
||||||
|
@ -73,11 +73,22 @@ bool successAssemble(string const& _source, bool _allowWarnings = true)
|
|||||||
return successParse(_source, true, _allowWarnings);
|
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(SolidityInlineAssembly)
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(Parsing)
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(smoke_test)
|
BOOST_AUTO_TEST_CASE(smoke_test)
|
||||||
{
|
{
|
||||||
BOOST_CHECK(successParse("{ }"));
|
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_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_AUTO_TEST_CASE(string_literals)
|
||||||
{
|
{
|
||||||
BOOST_CHECK(successAssemble("{ let x := \"12345678901234567890123456789012\" }"));
|
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()
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // end namespaces
|
} // end namespaces
|
||||||
|
Loading…
Reference in New Issue
Block a user