mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Parsing for inline assembly.
This commit is contained in:
parent
8236732e9a
commit
949b00ed59
@ -6,6 +6,7 @@ aux_source_directory(codegen SRC_LIST)
|
||||
aux_source_directory(formal SRC_LIST)
|
||||
aux_source_directory(interface SRC_LIST)
|
||||
aux_source_directory(parsing SRC_LIST)
|
||||
aux_source_directory(inlineasm SRC_LIST)
|
||||
|
||||
set(EXECUTABLE solidity)
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <memory>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <libevmasm/SourceLocation.h>
|
||||
#include <libevmcore/Instruction.h>
|
||||
#include <libsolidity/interface/Utils.h>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <libsolidity/parsing/Token.h>
|
||||
@ -854,6 +855,30 @@ public:
|
||||
virtual StatementAnnotation& annotation() const override;
|
||||
};
|
||||
|
||||
// Forward-declaration to InlineAssembly.h
|
||||
class AsmData;
|
||||
|
||||
/**
|
||||
* Inline assembly.
|
||||
*/
|
||||
class InlineAssembly: public Statement
|
||||
{
|
||||
public:
|
||||
InlineAssembly(
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<ASTString> const& _docString,
|
||||
std::shared_ptr<AsmData> const& _operations
|
||||
):
|
||||
Statement(_location, _docString), m_operations(_operations) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
AsmData const& operations() const { return *m_operations; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<AsmData> m_operations;
|
||||
};
|
||||
|
||||
/**
|
||||
* Brace-enclosed block containing zero or more statements.
|
||||
*/
|
||||
|
@ -157,6 +157,12 @@ bool ASTJsonConverter::visit(Mapping const&)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ASTJsonConverter::visit(InlineAssembly const&)
|
||||
{
|
||||
addJsonNode("InlineAssembly", {}, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ASTJsonConverter::visit(Block const&)
|
||||
{
|
||||
addJsonNode("Block", {}, true);
|
||||
@ -355,6 +361,10 @@ void ASTJsonConverter::endVisit(Mapping const&)
|
||||
{
|
||||
}
|
||||
|
||||
void ASTJsonConverter::endVisit(InlineAssembly const&)
|
||||
{
|
||||
}
|
||||
|
||||
void ASTJsonConverter::endVisit(Block const&)
|
||||
{
|
||||
goUp();
|
||||
|
@ -57,6 +57,7 @@ public:
|
||||
bool visit(ElementaryTypeName const& _node) override;
|
||||
bool visit(UserDefinedTypeName const& _node) override;
|
||||
bool visit(Mapping const& _node) override;
|
||||
bool visit(InlineAssembly const& _node) override;
|
||||
bool visit(Block const& _node) override;
|
||||
bool visit(IfStatement const& _node) override;
|
||||
bool visit(WhileStatement const& _node) override;
|
||||
@ -90,6 +91,7 @@ public:
|
||||
void endVisit(ElementaryTypeName const&) override;
|
||||
void endVisit(UserDefinedTypeName const&) override;
|
||||
void endVisit(Mapping const&) override;
|
||||
void endVisit(InlineAssembly const&) override;
|
||||
void endVisit(Block const&) override;
|
||||
void endVisit(IfStatement const&) override;
|
||||
void endVisit(WhileStatement const&) override;
|
||||
|
@ -171,6 +171,13 @@ bool ASTPrinter::visit(ArrayTypeName const& _node)
|
||||
return goDeeper();
|
||||
}
|
||||
|
||||
bool ASTPrinter::visit(InlineAssembly const& _node)
|
||||
{
|
||||
writeLine("InlineAssembly");
|
||||
printSourcePart(_node);
|
||||
return goDeeper();
|
||||
}
|
||||
|
||||
bool ASTPrinter::visit(Block const& _node)
|
||||
{
|
||||
writeLine("Block");
|
||||
@ -433,6 +440,11 @@ void ASTPrinter::endVisit(ArrayTypeName const&)
|
||||
m_indentation--;
|
||||
}
|
||||
|
||||
void ASTPrinter::endVisit(InlineAssembly const&)
|
||||
{
|
||||
m_indentation--;
|
||||
}
|
||||
|
||||
void ASTPrinter::endVisit(Block const&)
|
||||
{
|
||||
m_indentation--;
|
||||
|
@ -64,6 +64,7 @@ public:
|
||||
bool visit(UserDefinedTypeName const& _node) override;
|
||||
bool visit(Mapping const& _node) override;
|
||||
bool visit(ArrayTypeName const& _node) override;
|
||||
bool visit(InlineAssembly const& _node) override;
|
||||
bool visit(Block const& _node) override;
|
||||
bool visit(PlaceholderStatement const& _node) override;
|
||||
bool visit(IfStatement const& _node) override;
|
||||
@ -105,6 +106,7 @@ public:
|
||||
void endVisit(UserDefinedTypeName const&) override;
|
||||
void endVisit(Mapping const&) override;
|
||||
void endVisit(ArrayTypeName const&) override;
|
||||
void endVisit(InlineAssembly const&) override;
|
||||
void endVisit(Block const&) override;
|
||||
void endVisit(PlaceholderStatement const&) override;
|
||||
void endVisit(IfStatement const&) override;
|
||||
|
@ -62,6 +62,7 @@ public:
|
||||
virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); }
|
||||
virtual bool visit(Mapping& _node) { return visitNode(_node); }
|
||||
virtual bool visit(ArrayTypeName& _node) { return visitNode(_node); }
|
||||
virtual bool visit(InlineAssembly& _node) { return visitNode(_node); }
|
||||
virtual bool visit(Block& _node) { return visitNode(_node); }
|
||||
virtual bool visit(PlaceholderStatement& _node) { return visitNode(_node); }
|
||||
virtual bool visit(IfStatement& _node) { return visitNode(_node); }
|
||||
@ -105,6 +106,7 @@ public:
|
||||
virtual void endVisit(UserDefinedTypeName& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(Mapping& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(ArrayTypeName& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(InlineAssembly& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(Block& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(PlaceholderStatement& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(IfStatement& _node) { endVisitNode(_node); }
|
||||
@ -166,6 +168,7 @@ public:
|
||||
virtual bool visit(WhileStatement const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(ForStatement const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(Continue const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(InlineAssembly const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(Break const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(Return const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(Throw const& _node) { return visitNode(_node); }
|
||||
@ -209,6 +212,7 @@ public:
|
||||
virtual void endVisit(WhileStatement const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(ForStatement const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(Continue const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(InlineAssembly const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(Break const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(Return const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(Throw const& _node) { endVisitNode(_node); }
|
||||
|
@ -357,6 +357,18 @@ void ArrayTypeName::accept(ASTConstVisitor& _visitor) const
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void InlineAssembly::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
_visitor.visit(*this);
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void InlineAssembly::accept(ASTConstVisitor& _visitor) const
|
||||
{
|
||||
_visitor.visit(*this);
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void Block::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
|
70
libsolidity/inlineasm/AsmData.h
Normal file
70
libsolidity/inlineasm/AsmData.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
This file is part of cpp-ethereum.
|
||||
|
||||
cpp-ethereum 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.
|
||||
|
||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2016
|
||||
* Parsed inline assembly to be used by the AST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <libevmcore/Instruction.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
class AsmData
|
||||
{
|
||||
public:
|
||||
/// Direct EVM instruction (except PUSHi and JUMPDEST)
|
||||
struct Instruction { eth::Instruction instruction; };
|
||||
/// Literal number or string (up to 32 bytes)
|
||||
struct Literal { bool isNumber; std::string value; };
|
||||
/// External / internal identifier or label reference
|
||||
struct Identifier { std::string name; };
|
||||
struct FunctionalInstruction;
|
||||
/// Jump label ("name:")
|
||||
struct Label { std::string name; };
|
||||
/// Assignemnt (":= x", moves stack top into x, potentially multiple slots)
|
||||
struct Assignment { Identifier variableName; };
|
||||
struct FunctionalAssignment;
|
||||
struct VariableDeclaration;
|
||||
struct Block;
|
||||
using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionalInstruction, VariableDeclaration, Block>;
|
||||
/// Functional assignment ("x := mload(20)", expects push-1-expression on the right hand
|
||||
/// side and requires x to occupy exactly one stack slot.
|
||||
struct FunctionalAssignment { Identifier variableName; std::shared_ptr<Statement> value; };
|
||||
/// Functional instruction, e.g. "mul(mload(20), add(2, x))"
|
||||
struct FunctionalInstruction { Instruction instruction; std::vector<Statement> arguments; };
|
||||
/// Block-scope variable declaration ("let x := mload(20)"), non-hoisted
|
||||
struct VariableDeclaration { std::string name; std::shared_ptr<Statement> value; };
|
||||
/// Block that creates a scope (frees declared stack variables)
|
||||
struct Block { std::vector<Statement> statements; };
|
||||
|
||||
AsmData(Block&& _statements): m_statements(_statements) {}
|
||||
|
||||
Block const& statements() const { return m_statements; }
|
||||
|
||||
private:
|
||||
Block m_statements;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
212
libsolidity/inlineasm/AsmParser.cpp
Normal file
212
libsolidity/inlineasm/AsmParser.cpp
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
This file is part of cpp-ethereum.
|
||||
|
||||
cpp-ethereum 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.
|
||||
|
||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2016
|
||||
* Solidity inline assembly parser.
|
||||
*/
|
||||
|
||||
#include <libsolidity/inlineasm/AsmParser.h>
|
||||
#include <ctype.h>
|
||||
#include <algorithm>
|
||||
#include <libevmcore/Instruction.h>
|
||||
#include <libsolidity/parsing/Scanner.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
|
||||
shared_ptr<AsmData> InlineAssemblyParser::parse(std::shared_ptr<Scanner> const& _scanner)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_scanner = _scanner;
|
||||
return make_shared<AsmData>(parseBlock());
|
||||
}
|
||||
catch (FatalError const&)
|
||||
{
|
||||
if (m_errors.empty())
|
||||
throw; // Something is weird here, rather throw again.
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AsmData::Block InlineAssemblyParser::parseBlock()
|
||||
{
|
||||
expectToken(Token::LBrace);
|
||||
AsmData::Block block;
|
||||
while (m_scanner->currentToken() != Token::RBrace)
|
||||
block.statements.emplace_back(parseStatement());
|
||||
m_scanner->next();
|
||||
return block;
|
||||
}
|
||||
|
||||
AsmData::Statement InlineAssemblyParser::parseStatement()
|
||||
{
|
||||
switch (m_scanner->currentToken())
|
||||
{
|
||||
case Token::Let:
|
||||
return parseVariableDeclaration();
|
||||
case Token::LBrace:
|
||||
return parseBlock();
|
||||
case Token::Assign:
|
||||
{
|
||||
m_scanner->next();
|
||||
expectToken(Token::Colon);
|
||||
string name = m_scanner->currentLiteral();
|
||||
expectToken(Token::Identifier);
|
||||
return AsmData::Assignment{AsmData::Identifier{name}};
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Options left:
|
||||
// Simple instruction (might turn into functional),
|
||||
// literal,
|
||||
// identifier (might turn into label or functional assignment)
|
||||
AsmData::Statement statement(parseElementaryOperation());
|
||||
switch (m_scanner->currentToken())
|
||||
{
|
||||
case Token::LParen:
|
||||
return parseFunctionalInstruction(statement);
|
||||
case Token::Colon:
|
||||
{
|
||||
if (statement.type() != typeid(AsmData::Identifier))
|
||||
fatalParserError("Label name / variable name must precede \":\".");
|
||||
string const& name = boost::get<AsmData::Identifier>(statement).name;
|
||||
m_scanner->next();
|
||||
if (m_scanner->currentToken() == Token::Assign)
|
||||
{
|
||||
// functional assignment
|
||||
m_scanner->next();
|
||||
unique_ptr<AsmData::Statement> value;
|
||||
value.reset(new AsmData::Statement(parseExpression()));
|
||||
return AsmData::FunctionalAssignment{{move(name)}, move(value)};
|
||||
}
|
||||
else
|
||||
// label
|
||||
return AsmData::Label{name};
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return statement;
|
||||
}
|
||||
|
||||
AsmData::Statement InlineAssemblyParser::parseExpression()
|
||||
{
|
||||
AsmData::Statement operation = parseElementaryOperation(true);
|
||||
if (m_scanner->currentToken() == Token::LParen)
|
||||
return parseFunctionalInstruction(operation);
|
||||
else
|
||||
return operation;
|
||||
}
|
||||
|
||||
AsmData::Statement InlineAssemblyParser::parseElementaryOperation(bool _onlySinglePusher)
|
||||
{
|
||||
// Allowed instructions, lowercase names.
|
||||
static map<string, eth::Instruction> s_instructions;
|
||||
if (s_instructions.empty())
|
||||
for (auto const& instruction: eth::c_instructions)
|
||||
{
|
||||
if (
|
||||
instruction.second == eth::Instruction::JUMPDEST ||
|
||||
(eth::Instruction::PUSH1 <= instruction.second && instruction.second <= eth::Instruction::PUSH32)
|
||||
)
|
||||
continue;
|
||||
string name = instruction.first;
|
||||
transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); });
|
||||
s_instructions[name] = instruction.second;
|
||||
}
|
||||
|
||||
//@TODO track location
|
||||
|
||||
switch (m_scanner->currentToken())
|
||||
{
|
||||
case Token::Identifier:
|
||||
{
|
||||
string literal = m_scanner->currentLiteral();
|
||||
// first search the set of instructions.
|
||||
if (s_instructions.count(literal))
|
||||
{
|
||||
eth::Instruction const& instr = s_instructions[literal];
|
||||
if (_onlySinglePusher)
|
||||
{
|
||||
eth::InstructionInfo info = eth::instructionInfo(instr);
|
||||
if (info.ret != 1)
|
||||
fatalParserError("Instruction " + info.name + " not allowed in this context.");
|
||||
}
|
||||
m_scanner->next();
|
||||
return AsmData::Instruction{instr};
|
||||
}
|
||||
else
|
||||
m_scanner->next();
|
||||
return AsmData::Identifier{literal};
|
||||
break;
|
||||
}
|
||||
case Token::StringLiteral:
|
||||
case Token::Number:
|
||||
{
|
||||
AsmData::Literal literal{
|
||||
m_scanner->currentToken() == Token::Number,
|
||||
m_scanner->currentLiteral()
|
||||
};
|
||||
m_scanner->next();
|
||||
return literal;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fatalParserError("Expected elementary inline assembly operation.");
|
||||
return {};
|
||||
}
|
||||
|
||||
AsmData::VariableDeclaration InlineAssemblyParser::parseVariableDeclaration()
|
||||
{
|
||||
expectToken(Token::Let);
|
||||
string name = m_scanner->currentLiteral();
|
||||
expectToken(Token::Identifier);
|
||||
expectToken(Token::Colon);
|
||||
expectToken(Token::Assign);
|
||||
unique_ptr<AsmData::Statement> value;
|
||||
value.reset(new AsmData::Statement(parseExpression()));
|
||||
return AsmData::VariableDeclaration{name, move(value)};
|
||||
}
|
||||
|
||||
AsmData::FunctionalInstruction InlineAssemblyParser::parseFunctionalInstruction(AsmData::Statement const& _instruction)
|
||||
{
|
||||
if (_instruction.type() != typeid(AsmData::Instruction))
|
||||
fatalParserError("Assembly instruction required in front of \"(\")");
|
||||
eth::Instruction instr = boost::get<AsmData::Instruction>(_instruction).instruction;
|
||||
eth::InstructionInfo instrInfo = eth::instructionInfo(instr);
|
||||
if (eth::Instruction::DUP1 <= instr && instr <= eth::Instruction::DUP16)
|
||||
fatalParserError("DUPi instructions not allowed for functional notation");
|
||||
if (eth::Instruction::SWAP1 <= instr && instr <= eth::Instruction::SWAP16)
|
||||
fatalParserError("SWAPi instructions not allowed for functional notation");
|
||||
|
||||
expectToken(Token::LParen);
|
||||
vector<AsmData::Statement> arguments;
|
||||
unsigned args = unsigned(instrInfo.args);
|
||||
for (unsigned i = 0; i < args; ++i)
|
||||
{
|
||||
arguments.push_back(parseExpression());
|
||||
if (i != args - 1)
|
||||
expectToken(Token::Comma);
|
||||
}
|
||||
expectToken(Token::RParen);
|
||||
return AsmData::FunctionalInstruction{{instr}, move(arguments)};
|
||||
}
|
55
libsolidity/inlineasm/AsmParser.h
Normal file
55
libsolidity/inlineasm/AsmParser.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
This file is part of cpp-ethereum.
|
||||
|
||||
cpp-ethereum 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.
|
||||
|
||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2016
|
||||
* Solidity inline assembly parser.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
#include <libsolidity/parsing/ParserBase.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
class InlineAssemblyParser: public ParserBase
|
||||
{
|
||||
public:
|
||||
InlineAssemblyParser(ErrorList& _errors): ParserBase(_errors) {}
|
||||
|
||||
/// Parses an inline assembly block starting with `{` and ending with `}`.
|
||||
/// @returns an empty shared pointer on error.
|
||||
std::shared_ptr<AsmData> parse(std::shared_ptr<Scanner> const& _scanner);
|
||||
|
||||
protected:
|
||||
AsmData::Block parseBlock();
|
||||
AsmData::Statement parseStatement();
|
||||
/// Parses a functional expression that has to push exactly one stack element
|
||||
AsmData::Statement parseExpression();
|
||||
AsmData::Statement parseElementaryOperation(bool _onlySinglePusher = false);
|
||||
AsmData::VariableDeclaration parseVariableDeclaration();
|
||||
AsmData::FunctionalInstruction parseFunctionalInstruction(AsmData::Statement const& _instruction);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/interface/SourceReferenceFormatter.h>
|
||||
#include <libsolidity/interface/CompilerStack.h>
|
||||
#include <libsolidity/parsing/Scanner.h>
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
@ -85,7 +84,7 @@ void SourceReferenceFormatter::printExceptionInformation(
|
||||
ostream& _stream,
|
||||
Exception const& _exception,
|
||||
string const& _name,
|
||||
CompilerStack const& _compiler
|
||||
function<Scanner const&(string const&)> const& _scannerFromSourceName
|
||||
)
|
||||
{
|
||||
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
|
||||
@ -94,7 +93,7 @@ void SourceReferenceFormatter::printExceptionInformation(
|
||||
|
||||
if (location)
|
||||
{
|
||||
scannerPtr = &_compiler.scanner(*location->sourceName);
|
||||
scannerPtr = &_scannerFromSourceName(*location->sourceName);
|
||||
printSourceName(_stream, *location, *scannerPtr);
|
||||
}
|
||||
|
||||
@ -104,7 +103,7 @@ void SourceReferenceFormatter::printExceptionInformation(
|
||||
|
||||
if (location)
|
||||
{
|
||||
scannerPtr = &_compiler.scanner(*location->sourceName);
|
||||
scannerPtr = &_scannerFromSourceName(*location->sourceName);
|
||||
printSourceLocation(_stream, *location, *scannerPtr);
|
||||
}
|
||||
|
||||
@ -112,7 +111,7 @@ void SourceReferenceFormatter::printExceptionInformation(
|
||||
{
|
||||
for (auto info: secondarylocation->infos)
|
||||
{
|
||||
scannerPtr = &_compiler.scanner(*info.second.sourceName);
|
||||
scannerPtr = &_scannerFromSourceName(*info.second.sourceName);
|
||||
_stream << info.first << " ";
|
||||
printSourceName(_stream, info.second, *scannerPtr);
|
||||
_stream << endl;
|
||||
|
@ -23,6 +23,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <functional>
|
||||
#include <libevmasm/SourceLocation.h>
|
||||
|
||||
namespace dev
|
||||
@ -44,7 +45,7 @@ public:
|
||||
std::ostream& _stream,
|
||||
Exception const& _exception,
|
||||
std::string const& _name,
|
||||
CompilerStack const& _compiler
|
||||
std::function<Scanner const&(std::string const&)> const& _scannerFromSourceName
|
||||
);
|
||||
private:
|
||||
static void printSourceName(std::ostream& _stream, SourceLocation const& _location, Scanner const& _scanner);
|
||||
|
@ -20,11 +20,13 @@
|
||||
* Solidity parser.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <vector>
|
||||
#include <libdevcore/Log.h>
|
||||
#include <libevmasm/SourceLocation.h>
|
||||
#include <libsolidity/parsing/Parser.h>
|
||||
#include <libsolidity/parsing/Scanner.h>
|
||||
#include <libsolidity/inlineasm/AsmParser.h>
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
#include <libsolidity/interface/InterfaceHandler.h>
|
||||
|
||||
@ -712,6 +714,8 @@ ASTPointer<Statement> Parser::parseStatement()
|
||||
m_scanner->next();
|
||||
break;
|
||||
}
|
||||
case Token::Assembly:
|
||||
return parseInlineAssembly(docString);
|
||||
case Token::Identifier:
|
||||
if (m_insideModifier && m_scanner->currentLiteral() == "_")
|
||||
{
|
||||
@ -727,6 +731,22 @@ ASTPointer<Statement> Parser::parseStatement()
|
||||
return statement;
|
||||
}
|
||||
|
||||
ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> const& _docString)
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
expectToken(Token::Assembly);
|
||||
if (m_scanner->currentToken() != Token::StringLiteral)
|
||||
fatalParserError("Expected assembly name.");
|
||||
if (m_scanner->currentLiteral() != "evmasm")
|
||||
fatalParserError("Only \"evmasm\" supported.");
|
||||
m_scanner->next();
|
||||
|
||||
InlineAssemblyParser parser(m_errors);
|
||||
shared_ptr<InlineAssemblyBlock> operations = parser.parse(m_scanner);
|
||||
nodeFactory.markEndPosition();
|
||||
return nodeFactory.createNode<InlineAssembly>(_docString, operations);
|
||||
}
|
||||
|
||||
ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _docString)
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
|
@ -81,6 +81,7 @@ private:
|
||||
);
|
||||
ASTPointer<Block> parseBlock(ASTPointer<ASTString> const& _docString = {});
|
||||
ASTPointer<Statement> parseStatement();
|
||||
ASTPointer<InlineAssembly> parseInlineAssembly(ASTPointer<ASTString> const& _docString = {});
|
||||
ASTPointer<IfStatement> parseIfStatement(ASTPointer<ASTString> const& _docString);
|
||||
ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString);
|
||||
ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString);
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include <libsolidity/interface/CompilerStack.h>
|
||||
#include <libsolidity/interface/SourceReferenceFormatter.h>
|
||||
#include <libsolidity/interface/GasEstimator.h>
|
||||
#include <libsolidity/inlineasm/AsmParser.h>
|
||||
#include <libsolidity/formal/Why3Translator.h>
|
||||
|
||||
using namespace std;
|
||||
@ -430,6 +431,10 @@ Allowed options)",
|
||||
"Output a single json document containing the specified information."
|
||||
)
|
||||
(g_argGas.c_str(), "Print an estimate of the maximal gas usage for each function.")
|
||||
(
|
||||
"assemble",
|
||||
"Switch to assembly mode, ignoring all options and assumes input is assembly."
|
||||
)
|
||||
(
|
||||
"link",
|
||||
"Switch to linker mode, ignoring all options apart from --libraries "
|
||||
@ -509,6 +514,12 @@ bool CommandLineInterface::processInput()
|
||||
if (!parseLibraryOption(library))
|
||||
return false;
|
||||
|
||||
if (m_args.count("assemble"))
|
||||
{
|
||||
// switch to assembly mode
|
||||
m_onlyAssemble = true;
|
||||
return assemble();
|
||||
}
|
||||
if (m_args.count("link"))
|
||||
{
|
||||
// switch to linker mode
|
||||
@ -574,6 +585,7 @@ bool CommandLineInterface::processInput()
|
||||
};
|
||||
|
||||
m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0, fileReader));
|
||||
auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); };
|
||||
try
|
||||
{
|
||||
for (auto const& sourceCode: m_sourceCodes)
|
||||
@ -593,7 +605,8 @@ bool CommandLineInterface::processInput()
|
||||
SourceReferenceFormatter::printExceptionInformation(
|
||||
cerr,
|
||||
*error,
|
||||
(error->type() == Error::Type::Warning) ? "Warning" : "Error", *m_compiler
|
||||
(error->type() == Error::Type::Warning) ? "Warning" : "Error",
|
||||
scannerFromSourceName
|
||||
);
|
||||
|
||||
if (!successful)
|
||||
@ -601,7 +614,7 @@ bool CommandLineInterface::processInput()
|
||||
}
|
||||
catch (CompilerError const& _exception)
|
||||
{
|
||||
SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Compiler error", *m_compiler);
|
||||
SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Compiler error", scannerFromSourceName);
|
||||
return false;
|
||||
}
|
||||
catch (InternalCompilerError const& _exception)
|
||||
@ -615,7 +628,7 @@ bool CommandLineInterface::processInput()
|
||||
if (_error.type() == Error::Type::DocstringParsingError)
|
||||
cerr << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_error) << endl;
|
||||
else
|
||||
SourceReferenceFormatter::printExceptionInformation(cerr, _error, _error.typeName(), *m_compiler);
|
||||
SourceReferenceFormatter::printExceptionInformation(cerr, _error, _error.typeName(), scannerFromSourceName);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -759,7 +772,9 @@ void CommandLineInterface::handleAst(string const& _argStr)
|
||||
|
||||
void CommandLineInterface::actOnInput()
|
||||
{
|
||||
if (m_onlyLink)
|
||||
if (m_onlyAssemble)
|
||||
return; //@TODO
|
||||
else if (m_onlyLink)
|
||||
writeLinkedFiles();
|
||||
else
|
||||
outputCompilationResults();
|
||||
@ -812,6 +827,31 @@ void CommandLineInterface::writeLinkedFiles()
|
||||
writeFile(src.first, src.second);
|
||||
}
|
||||
|
||||
bool CommandLineInterface::assemble()
|
||||
{
|
||||
//@TODO later, we will use the convenience interface and should also remove the include above
|
||||
bool successful = true;
|
||||
ErrorList errors;
|
||||
map<string, shared_ptr<Scanner>> scanners;
|
||||
for (auto const& src: m_sourceCodes)
|
||||
{
|
||||
auto scanner = make_shared<Scanner>(CharStream(src.second), src.first);
|
||||
scanners[src.first] = scanner;
|
||||
InlineAssemblyParser parser(errors);
|
||||
if (!parser.parse(scanner))
|
||||
successful = false;
|
||||
}
|
||||
for (auto const& error: errors)
|
||||
SourceReferenceFormatter::printExceptionInformation(
|
||||
cerr,
|
||||
*error,
|
||||
(error->type() == Error::Type::Warning) ? "Warning" : "Error",
|
||||
[&](string const& _source) -> Scanner const& { return *scanners.at(_source); }
|
||||
);
|
||||
|
||||
return successful;
|
||||
}
|
||||
|
||||
void CommandLineInterface::outputCompilationResults()
|
||||
{
|
||||
handleCombinedJSON();
|
||||
|
@ -50,6 +50,9 @@ private:
|
||||
bool link();
|
||||
void writeLinkedFiles();
|
||||
|
||||
/// Parse assembly input.
|
||||
bool assemble();
|
||||
|
||||
void outputCompilationResults();
|
||||
|
||||
void handleCombinedJSON();
|
||||
@ -73,6 +76,7 @@ private:
|
||||
/// @arg _data to be written
|
||||
void createFile(std::string const& _fileName, std::string const& _data);
|
||||
|
||||
bool m_onlyAssemble = false;
|
||||
bool m_onlyLink = false;
|
||||
|
||||
/// Compiler arguments variable map
|
||||
|
@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <json/json.h>
|
||||
#include <libdevcore/Common.h>
|
||||
@ -47,10 +48,14 @@ extern "C" {
|
||||
typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, char** o_error);
|
||||
}
|
||||
|
||||
string formatError(Exception const& _exception, string const& _name, CompilerStack const& _compiler)
|
||||
string formatError(
|
||||
Exception const& _exception,
|
||||
string const& _name,
|
||||
function<Scanner const&(string const&)> const& _scannerFromSourceName
|
||||
)
|
||||
{
|
||||
ostringstream errorOutput;
|
||||
SourceReferenceFormatter::printExceptionInformation(errorOutput, _exception, _name, _compiler);
|
||||
SourceReferenceFormatter::printExceptionInformation(errorOutput, _exception, _name, _scannerFromSourceName);
|
||||
return errorOutput.str();
|
||||
}
|
||||
|
||||
@ -150,6 +155,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
|
||||
};
|
||||
}
|
||||
CompilerStack compiler(true, readCallback);
|
||||
auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return compiler.scanner(_sourceName); };
|
||||
bool success = false;
|
||||
try
|
||||
{
|
||||
@ -161,22 +167,22 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
|
||||
errors.append(formatError(
|
||||
*error,
|
||||
(err->type() == Error::Type::Warning) ? "Warning" : "Error",
|
||||
compiler
|
||||
scannerFromSourceName
|
||||
));
|
||||
}
|
||||
success = succ; // keep success false on exception
|
||||
}
|
||||
catch (Error const& error)
|
||||
{
|
||||
errors.append(formatError(error, error.typeName(), compiler));
|
||||
errors.append(formatError(error, error.typeName(), scannerFromSourceName));
|
||||
}
|
||||
catch (CompilerError const& exception)
|
||||
{
|
||||
errors.append(formatError(exception, "Compiler error", compiler));
|
||||
errors.append(formatError(exception, "Compiler error", scannerFromSourceName));
|
||||
}
|
||||
catch (InternalCompilerError const& exception)
|
||||
{
|
||||
errors.append(formatError(exception, "Internal compiler error", compiler));
|
||||
errors.append(formatError(exception, "Internal compiler error", scannerFromSourceName));
|
||||
}
|
||||
catch (Exception const& exception)
|
||||
{
|
||||
|
138
test/libsolidity/InlineAssembly.cpp
Normal file
138
test/libsolidity/InlineAssembly.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
This file is part of cpp-ethereum.
|
||||
|
||||
cpp-ethereum 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.
|
||||
|
||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2016
|
||||
* Unit tests for inline assembly.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <libdevcore/Log.h>
|
||||
#include <libsolidity/parsing/Scanner.h>
|
||||
#include <libsolidity/inlineasm/AsmParser.h>
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include "../TestHelper.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
namespace test
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
shared_ptr<InlineAssemblyBlock> parse(std::string const& _source, ErrorList& _errors)
|
||||
{
|
||||
return InlineAssemblyParser(_errors).parse(std::make_shared<Scanner>(CharStream(_source)));
|
||||
}
|
||||
|
||||
bool successParse(std::string const& _source)
|
||||
{
|
||||
ErrorList errors;
|
||||
try
|
||||
{
|
||||
auto assembly = parse(_source, errors);
|
||||
if (!assembly)
|
||||
return false;
|
||||
}
|
||||
catch (FatalError const&)
|
||||
{
|
||||
if (Error::containsErrorOfType(errors, Error::Type::ParserError))
|
||||
return false;
|
||||
}
|
||||
if (Error::containsErrorOfType(errors, Error::Type::ParserError))
|
||||
return false;
|
||||
|
||||
BOOST_CHECK(Error::containsOnlyWarnings(errors));
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(smoke_test)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(simple_instructions)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ dup1 dup1 mul dup1 sub }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(constants)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ 7 8 mul }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(vardecl)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ let x := 7 }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(assignment)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ 7 8 add =: x }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(label)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ 7 abc: 8 eq abc jump }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(label_complex)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ 7 abc: 8 eq jump(abc) jumpi(eq(7, 8), abc) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(functional)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ add(7, mul(6, x)) add mul(7, 8) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(functional_assignment)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ x := 7 }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(functional_assignment_complex)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ x := add(7, mul(6, x)) add mul(7, 8) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(vardecl_complex)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ let x := add(7, mul(6, x)) add mul(7, 8) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(blocks)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
}
|
||||
}
|
||||
} // end namespaces
|
Loading…
Reference in New Issue
Block a user