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 <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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user