mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6818 from ethereum/alwaysUseBuiltins
Provide builtin functions even in loose assembly mode.
This commit is contained in:
commit
0291abf10f
@ -27,7 +27,6 @@
|
|||||||
#include <libyul/AsmAnalysis.h>
|
#include <libyul/AsmAnalysis.h>
|
||||||
#include <libyul/AsmAnalysisInfo.h>
|
#include <libyul/AsmAnalysisInfo.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
#include <libyul/backends/evm/EVMDialect.h>
|
|
||||||
|
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
|
||||||
@ -707,7 +706,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
*_inlineAssembly.annotation().analysisInfo,
|
*_inlineAssembly.annotation().analysisInfo,
|
||||||
m_errorReporter,
|
m_errorReporter,
|
||||||
Error::Type::SyntaxError,
|
Error::Type::SyntaxError,
|
||||||
yul::EVMDialect::looseAssemblyForEVM(m_evmVersion),
|
_inlineAssembly.dialect(),
|
||||||
identifierAccess
|
identifierAccess
|
||||||
);
|
);
|
||||||
if (!analyzer.analyze(_inlineAssembly.operations()))
|
if (!analyzer.analyze(_inlineAssembly.operations()))
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <libsolidity/analysis/ViewPureChecker.h>
|
#include <libsolidity/analysis/ViewPureChecker.h>
|
||||||
#include <libsolidity/ast/ExperimentalFeatures.h>
|
#include <libsolidity/ast/ExperimentalFeatures.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
|
#include <libyul/backends/evm/EVMDialect.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <libevmasm/SemanticInformation.h>
|
#include <libevmasm/SemanticInformation.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -33,7 +34,11 @@ namespace
|
|||||||
class AssemblyViewPureChecker: public boost::static_visitor<void>
|
class AssemblyViewPureChecker: public boost::static_visitor<void>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit AssemblyViewPureChecker(std::function<void(StateMutability, SourceLocation const&)> _reportMutability):
|
explicit AssemblyViewPureChecker(
|
||||||
|
yul::Dialect const& _dialect,
|
||||||
|
std::function<void(StateMutability, SourceLocation const&)> _reportMutability
|
||||||
|
):
|
||||||
|
m_dialect(_dialect),
|
||||||
m_reportMutability(_reportMutability) {}
|
m_reportMutability(_reportMutability) {}
|
||||||
|
|
||||||
void operator()(yul::Label const&) { }
|
void operator()(yul::Label const&) { }
|
||||||
@ -69,6 +74,11 @@ public:
|
|||||||
}
|
}
|
||||||
void operator()(yul::FunctionCall const& _funCall)
|
void operator()(yul::FunctionCall const& _funCall)
|
||||||
{
|
{
|
||||||
|
if (yul::EVMDialect const* dialect = dynamic_cast<decltype(dialect)>(&m_dialect))
|
||||||
|
if (yul::BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name))
|
||||||
|
if (fun->instruction)
|
||||||
|
checkInstruction(_funCall.location, *fun->instruction);
|
||||||
|
|
||||||
for (auto const& arg: _funCall.arguments)
|
for (auto const& arg: _funCall.arguments)
|
||||||
boost::apply_visitor(*this, arg);
|
boost::apply_visitor(*this, arg);
|
||||||
}
|
}
|
||||||
@ -107,7 +117,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<void(StateMutability, SourceLocation const&)> m_reportMutability;
|
|
||||||
void checkInstruction(SourceLocation _location, dev::eth::Instruction _instruction)
|
void checkInstruction(SourceLocation _location, dev::eth::Instruction _instruction)
|
||||||
{
|
{
|
||||||
if (eth::SemanticInformation::invalidInViewFunctions(_instruction))
|
if (eth::SemanticInformation::invalidInViewFunctions(_instruction))
|
||||||
@ -115,6 +124,9 @@ private:
|
|||||||
else if (eth::SemanticInformation::invalidInPureFunctions(_instruction))
|
else if (eth::SemanticInformation::invalidInPureFunctions(_instruction))
|
||||||
m_reportMutability(StateMutability::View, _location);
|
m_reportMutability(StateMutability::View, _location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yul::Dialect const& m_dialect;
|
||||||
|
std::function<void(StateMutability, SourceLocation const&)> m_reportMutability;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -221,6 +233,7 @@ void ViewPureChecker::endVisit(Identifier const& _identifier)
|
|||||||
void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly)
|
void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly)
|
||||||
{
|
{
|
||||||
AssemblyViewPureChecker{
|
AssemblyViewPureChecker{
|
||||||
|
_inlineAssembly.dialect(),
|
||||||
[=](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); }
|
[=](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); }
|
||||||
}(_inlineAssembly.operations());
|
}(_inlineAssembly.operations());
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ namespace yul
|
|||||||
{
|
{
|
||||||
// Forward-declaration to <yul/AsmData.h>
|
// Forward-declaration to <yul/AsmData.h>
|
||||||
struct Block;
|
struct Block;
|
||||||
|
struct Dialect;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
@ -1049,17 +1050,20 @@ public:
|
|||||||
InlineAssembly(
|
InlineAssembly(
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
ASTPointer<ASTString> const& _docString,
|
ASTPointer<ASTString> const& _docString,
|
||||||
|
yul::Dialect const& _dialect,
|
||||||
std::shared_ptr<yul::Block> const& _operations
|
std::shared_ptr<yul::Block> const& _operations
|
||||||
):
|
):
|
||||||
Statement(_location, _docString), m_operations(_operations) {}
|
Statement(_location, _docString), m_dialect(_dialect), m_operations(_operations) {}
|
||||||
void accept(ASTVisitor& _visitor) override;
|
void accept(ASTVisitor& _visitor) override;
|
||||||
void accept(ASTConstVisitor& _visitor) const override;
|
void accept(ASTConstVisitor& _visitor) const override;
|
||||||
|
|
||||||
|
yul::Dialect const& dialect() const { return m_dialect; }
|
||||||
yul::Block const& operations() const { return *m_operations; }
|
yul::Block const& operations() const { return *m_operations; }
|
||||||
|
|
||||||
InlineAssemblyAnnotation& annotation() const override;
|
InlineAssemblyAnnotation& annotation() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
yul::Dialect const& m_dialect;
|
||||||
std::shared_ptr<yul::Block> m_operations;
|
std::shared_ptr<yul::Block> m_operations;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,8 +35,9 @@
|
|||||||
|
|
||||||
namespace yul
|
namespace yul
|
||||||
{
|
{
|
||||||
struct AsmAnalysisInfo;
|
struct AsmAnalysisInfo;
|
||||||
struct Identifier;
|
struct Identifier;
|
||||||
|
struct Dialect;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include <libyul/AsmPrinter.h>
|
#include <libyul/AsmPrinter.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
|
#include <libyul/Dialect.h>
|
||||||
#include <libyul/optimiser/ASTCopier.h>
|
#include <libyul/optimiser/ASTCopier.h>
|
||||||
|
|
||||||
#include <libdevcore/Whiskers.h>
|
#include <libdevcore/Whiskers.h>
|
||||||
@ -50,14 +51,21 @@ struct CopyTranslate: public yul::ASTCopier
|
|||||||
{
|
{
|
||||||
using ExternalRefsMap = std::map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo>;
|
using ExternalRefsMap = std::map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo>;
|
||||||
|
|
||||||
CopyTranslate(IRGenerationContext& _context, ExternalRefsMap const& _references):
|
CopyTranslate(yul::Dialect const& _dialect, IRGenerationContext& _context, ExternalRefsMap const& _references):
|
||||||
m_context(_context), m_references(_references) {}
|
m_dialect(_dialect), m_context(_context), m_references(_references) {}
|
||||||
|
|
||||||
using ASTCopier::operator();
|
using ASTCopier::operator();
|
||||||
|
|
||||||
yul::YulString translateIdentifier(yul::YulString _name) override
|
yul::YulString translateIdentifier(yul::YulString _name) override
|
||||||
{
|
{
|
||||||
return yul::YulString{"usr$" + _name.str()};
|
// Strictly, the dialect used by inline assembly (m_dialect) could be different
|
||||||
|
// from the Yul dialect we are compiling to. So we are assuming here that the builtin
|
||||||
|
// functions are identical. This should not be a problem for now since everything
|
||||||
|
// is EVM anyway.
|
||||||
|
if (m_dialect.builtin(_name))
|
||||||
|
return _name;
|
||||||
|
else
|
||||||
|
return yul::YulString{"usr$" + _name.str()};
|
||||||
}
|
}
|
||||||
|
|
||||||
yul::Identifier translate(yul::Identifier const& _identifier) override
|
yul::Identifier translate(yul::Identifier const& _identifier) override
|
||||||
@ -80,6 +88,7 @@ struct CopyTranslate: public yul::ASTCopier
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
yul::Dialect const& m_dialect;
|
||||||
IRGenerationContext& m_context;
|
IRGenerationContext& m_context;
|
||||||
ExternalRefsMap const& m_references;
|
ExternalRefsMap const& m_references;
|
||||||
};
|
};
|
||||||
@ -723,7 +732,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
|
|
||||||
bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
|
bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
|
||||||
{
|
{
|
||||||
CopyTranslate bodyCopier{m_context, _inlineAsm.annotation().externalReferences};
|
CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences};
|
||||||
|
|
||||||
yul::Statement modified = bodyCopier(_inlineAsm.operations());
|
yul::Statement modified = bodyCopier(_inlineAsm.operations());
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ bool CompilerStack::parse()
|
|||||||
string const& path = sourcesToParse[i];
|
string const& path = sourcesToParse[i];
|
||||||
Source& source = m_sources[path];
|
Source& source = m_sources[path];
|
||||||
source.scanner->reset();
|
source.scanner->reset();
|
||||||
source.ast = Parser(m_errorReporter).parse(source.scanner);
|
source.ast = Parser(m_errorReporter, m_evmVersion).parse(source.scanner);
|
||||||
if (!source.ast)
|
if (!source.ast)
|
||||||
solAssert(!Error::containsOnlyWarnings(m_errorReporter.errors()), "Parser returned null but did not report error.");
|
solAssert(!Error::containsOnlyWarnings(m_errorReporter.errors()), "Parser returned null but did not report error.");
|
||||||
else
|
else
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <liblangutil/Scanner.h>
|
#include <liblangutil/Scanner.h>
|
||||||
#include <liblangutil/SemVerHandler.h>
|
#include <liblangutil/SemVerHandler.h>
|
||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
|
#include <libyul/backends/evm/EVMDialect.h>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -1039,21 +1040,22 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
|
|||||||
SourceLocation location{position(), -1, source()};
|
SourceLocation location{position(), -1, source()};
|
||||||
|
|
||||||
expectToken(Token::Assembly);
|
expectToken(Token::Assembly);
|
||||||
|
yul::Dialect const& dialect = yul::EVMDialect::looseAssemblyForEVM(m_evmVersion);
|
||||||
if (m_scanner->currentToken() == Token::StringLiteral)
|
if (m_scanner->currentToken() == Token::StringLiteral)
|
||||||
{
|
{
|
||||||
if (m_scanner->currentLiteral() != "evmasm")
|
if (m_scanner->currentLiteral() != "evmasm")
|
||||||
fatalParserError("Only \"evmasm\" supported.");
|
fatalParserError("Only \"evmasm\" supported.");
|
||||||
|
// This can be used in the future to set the dialect.
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Using latest EVM Version for now, it will be run again later.
|
yul::Parser asmParser(m_errorReporter, dialect);
|
||||||
yul::Parser asmParser(m_errorReporter, yul::EVMDialect::looseAssemblyForEVM(EVMVersion{}));
|
|
||||||
shared_ptr<yul::Block> block = asmParser.parse(m_scanner, true);
|
shared_ptr<yul::Block> block = asmParser.parse(m_scanner, true);
|
||||||
if (block == nullptr)
|
if (block == nullptr)
|
||||||
BOOST_THROW_EXCEPTION(FatalError());
|
BOOST_THROW_EXCEPTION(FatalError());
|
||||||
|
|
||||||
location.end = block->location.end;
|
location.end = block->location.end;
|
||||||
return make_shared<InlineAssembly>(location, _docString, block);
|
return make_shared<InlineAssembly>(location, _docString, dialect, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _docString)
|
ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _docString)
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <liblangutil/ParserBase.h>
|
#include <liblangutil/ParserBase.h>
|
||||||
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
{
|
{
|
||||||
@ -38,7 +39,13 @@ namespace solidity
|
|||||||
class Parser: public langutil::ParserBase
|
class Parser: public langutil::ParserBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Parser(langutil::ErrorReporter& _errorReporter): ParserBase(_errorReporter) {}
|
explicit Parser(
|
||||||
|
langutil::ErrorReporter& _errorReporter,
|
||||||
|
langutil::EVMVersion _evmVersion
|
||||||
|
):
|
||||||
|
ParserBase(_errorReporter),
|
||||||
|
m_evmVersion(_evmVersion)
|
||||||
|
{}
|
||||||
|
|
||||||
ASTPointer<SourceUnit> parse(std::shared_ptr<langutil::Scanner> const& _scanner);
|
ASTPointer<SourceUnit> parse(std::shared_ptr<langutil::Scanner> const& _scanner);
|
||||||
|
|
||||||
@ -181,6 +188,7 @@ private:
|
|||||||
|
|
||||||
/// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier.
|
/// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier.
|
||||||
bool m_insideModifier = false;
|
bool m_insideModifier = false;
|
||||||
|
langutil::EVMVersion m_evmVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -399,9 +399,11 @@ bool AsmAnalyzer::operator()(If const& _if)
|
|||||||
{
|
{
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
|
int const initialHeight = m_stackHeight;
|
||||||
if (!expectExpression(*_if.condition))
|
if (!expectExpression(*_if.condition))
|
||||||
success = false;
|
success = false;
|
||||||
m_stackHeight--;
|
|
||||||
|
m_stackHeight = initialHeight;
|
||||||
|
|
||||||
if (!(*this)(_if.body))
|
if (!(*this)(_if.body))
|
||||||
success = false;
|
success = false;
|
||||||
@ -417,6 +419,7 @@ bool AsmAnalyzer::operator()(Switch const& _switch)
|
|||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
|
int const initialHeight = m_stackHeight;
|
||||||
if (!expectExpression(*_switch.expression))
|
if (!expectExpression(*_switch.expression))
|
||||||
success = false;
|
success = false;
|
||||||
|
|
||||||
@ -476,7 +479,7 @@ bool AsmAnalyzer::operator()(Switch const& _switch)
|
|||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_stackHeight--;
|
m_stackHeight = initialHeight;
|
||||||
m_info.stackHeightInfo[&_switch] = m_stackHeight;
|
m_info.stackHeightInfo[&_switch] = m_stackHeight;
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
@ -488,6 +491,8 @@ bool AsmAnalyzer::operator()(ForLoop const& _for)
|
|||||||
|
|
||||||
Scope* outerScope = m_currentScope;
|
Scope* outerScope = m_currentScope;
|
||||||
|
|
||||||
|
int const initialHeight = m_stackHeight;
|
||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
if (!(*this)(_for.pre))
|
if (!(*this)(_for.pre))
|
||||||
success = false;
|
success = false;
|
||||||
@ -498,6 +503,7 @@ bool AsmAnalyzer::operator()(ForLoop const& _for)
|
|||||||
|
|
||||||
if (!expectExpression(*_for.condition))
|
if (!expectExpression(*_for.condition))
|
||||||
success = false;
|
success = false;
|
||||||
|
|
||||||
m_stackHeight--;
|
m_stackHeight--;
|
||||||
|
|
||||||
// backup outer for-loop & create new state
|
// backup outer for-loop & create new state
|
||||||
@ -510,7 +516,7 @@ bool AsmAnalyzer::operator()(ForLoop const& _for)
|
|||||||
if (!(*this)(_for.post))
|
if (!(*this)(_for.post))
|
||||||
success = false;
|
success = false;
|
||||||
|
|
||||||
m_stackHeight -= scope(&_for.pre).numberOfVariables();
|
m_stackHeight = initialHeight;
|
||||||
m_info.stackHeightInfo[&_for] = m_stackHeight;
|
m_info.stackHeightInfo[&_for] = m_stackHeight;
|
||||||
m_currentScope = outerScope;
|
m_currentScope = outerScope;
|
||||||
m_currentForLoop = outerForLoop;
|
m_currentForLoop = outerForLoop;
|
||||||
|
@ -248,14 +248,7 @@ Statement Parser::parseStatement()
|
|||||||
if (elementary.type() == typeid(Identifier))
|
if (elementary.type() == typeid(Identifier))
|
||||||
{
|
{
|
||||||
Identifier& identifier = boost::get<Identifier>(elementary);
|
Identifier& identifier = boost::get<Identifier>(elementary);
|
||||||
// Fallback from builtin function to Instruction for loose assembly.
|
return ExpressionStatement{identifier.location, { move(identifier) }};
|
||||||
if (
|
|
||||||
m_dialect.flavour == AsmFlavour::Loose &&
|
|
||||||
instructions().count(identifier.name.str())
|
|
||||||
)
|
|
||||||
return Instruction{identifier.location, instructions().at(identifier.name.str())};
|
|
||||||
else
|
|
||||||
return ExpressionStatement{identifier.location, { move(identifier) }};
|
|
||||||
}
|
}
|
||||||
else if (elementary.type() == typeid(Literal))
|
else if (elementary.type() == typeid(Literal))
|
||||||
{
|
{
|
||||||
@ -405,11 +398,18 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
|
|||||||
// first search the set of builtins, then the instructions.
|
// first search the set of builtins, then the instructions.
|
||||||
if (m_dialect.builtin(literal))
|
if (m_dialect.builtin(literal))
|
||||||
{
|
{
|
||||||
// For builtins we already check here that they are followed by `(`.
|
Identifier identifier{location(), literal};
|
||||||
ret = FunctionCall{location(), Identifier{location(), literal}, {}};
|
|
||||||
advance();
|
advance();
|
||||||
expectToken(Token::LParen, false);
|
// If the builtin is not followed by `(` and we are in loose mode,
|
||||||
return ret;
|
// fall back to instruction.
|
||||||
|
if (
|
||||||
|
m_dialect.flavour == AsmFlavour::Loose &&
|
||||||
|
instructions().count(identifier.name.str()) &&
|
||||||
|
m_scanner->currentToken() != Token::LParen
|
||||||
|
)
|
||||||
|
return Instruction{identifier.location, instructions().at(identifier.name.str())};
|
||||||
|
else
|
||||||
|
return FunctionCall{identifier.location, identifier, {}};
|
||||||
}
|
}
|
||||||
else if (m_dialect.flavour == AsmFlavour::Loose && instructions().count(literal.str()))
|
else if (m_dialect.flavour == AsmFlavour::Loose && instructions().count(literal.str()))
|
||||||
{
|
{
|
||||||
|
@ -90,19 +90,18 @@ pair<YulString, BuiltinFunctionForEVM> createFunction(
|
|||||||
return {name, f};
|
return {name, f};
|
||||||
}
|
}
|
||||||
|
|
||||||
map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVersion, bool _objectAccess, bool _evmOpcodes)
|
map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVersion, bool _objectAccess)
|
||||||
{
|
{
|
||||||
map<YulString, BuiltinFunctionForEVM> builtins;
|
map<YulString, BuiltinFunctionForEVM> builtins;
|
||||||
if (_evmOpcodes)
|
for (auto const& instr: Parser::instructions())
|
||||||
for (auto const& instr: Parser::instructions())
|
if (
|
||||||
if (
|
!dev::eth::isDupInstruction(instr.second) &&
|
||||||
!dev::eth::isDupInstruction(instr.second) &&
|
!dev::eth::isSwapInstruction(instr.second) &&
|
||||||
!dev::eth::isSwapInstruction(instr.second) &&
|
instr.second != eth::Instruction::JUMP &&
|
||||||
instr.second != eth::Instruction::JUMP &&
|
instr.second != eth::Instruction::JUMPI &&
|
||||||
instr.second != eth::Instruction::JUMPI &&
|
_evmVersion.hasOpcode(instr.second)
|
||||||
_evmVersion.hasOpcode(instr.second)
|
)
|
||||||
)
|
builtins.emplace(createEVMFunction(instr.first, instr.second));
|
||||||
builtins.emplace(createEVMFunction(instr.first, instr.second));
|
|
||||||
|
|
||||||
if (_objectAccess)
|
if (_objectAccess)
|
||||||
{
|
{
|
||||||
@ -167,7 +166,7 @@ EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVer
|
|||||||
Dialect{_flavour},
|
Dialect{_flavour},
|
||||||
m_objectAccess(_objectAccess),
|
m_objectAccess(_objectAccess),
|
||||||
m_evmVersion(_evmVersion),
|
m_evmVersion(_evmVersion),
|
||||||
m_functions(createBuiltins(_evmVersion, _objectAccess, _flavour != AsmFlavour::Loose))
|
m_functions(createBuiltins(_evmVersion, _objectAccess))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ eth::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
|
|||||||
{
|
{
|
||||||
ErrorList errors;
|
ErrorList errors;
|
||||||
ErrorReporter errorReporter(errors);
|
ErrorReporter errorReporter(errors);
|
||||||
Parser parser(errorReporter);
|
Parser parser(errorReporter, dev::test::Options::get().evmVersion());
|
||||||
ASTPointer<SourceUnit> sourceUnit;
|
ASTPointer<SourceUnit> sourceUnit;
|
||||||
BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(_sourceCode)));
|
BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(_sourceCode)));
|
||||||
BOOST_CHECK(!!sourceUnit);
|
BOOST_CHECK(!!sourceUnit);
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
#include <liblangutil/Scanner.h>
|
#include <liblangutil/Scanner.h>
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
#include <liblangutil/SourceReferenceFormatter.h>
|
||||||
|
|
||||||
#include <libevmasm/Assembly.h>
|
#include <libevmasm/Assembly.h>
|
||||||
|
|
||||||
@ -80,7 +81,12 @@ boost::optional<Error> parseAndReturnFirstError(
|
|||||||
if (_allowWarnings && e->type() == Error::Type::Warning)
|
if (_allowWarnings && e->type() == Error::Type::Warning)
|
||||||
continue;
|
continue;
|
||||||
if (error)
|
if (error)
|
||||||
BOOST_FAIL("Found more than one error.");
|
{
|
||||||
|
string errors;
|
||||||
|
for (auto const& err: stack.errors())
|
||||||
|
errors += SourceReferenceFormatter::formatErrorInformation(*err);
|
||||||
|
BOOST_FAIL("Found more than one error:\n" + errors);
|
||||||
|
}
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
if (!success)
|
if (!success)
|
||||||
@ -299,7 +305,7 @@ BOOST_AUTO_TEST_CASE(if_statement_invalid)
|
|||||||
{
|
{
|
||||||
CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
|
CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
|
||||||
BOOST_CHECK("{ if calldatasize() {}");
|
BOOST_CHECK("{ if calldatasize() {}");
|
||||||
CHECK_PARSE_ERROR("{ if mstore(1, 1) {} }", ParserError, "Instruction \"mstore\" not allowed in this context");
|
CHECK_PARSE_ERROR("{ if mstore(1, 1) {} }", TypeError, "Expected expression to return one item to the stack, but did return 0 items");
|
||||||
CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected '{' but got reserved keyword 'let'");
|
CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected '{' but got reserved keyword 'let'");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,7 +334,7 @@ BOOST_AUTO_TEST_CASE(switch_invalid_expression)
|
|||||||
{
|
{
|
||||||
CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal, identifier or instruction expected.");
|
CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal, identifier or instruction expected.");
|
||||||
CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
|
CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
|
||||||
CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", ParserError, "Instruction \"mstore\" not allowed in this context");
|
CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", TypeError, "Expected expression to return one item to the stack, but did return 0 items");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(switch_default_before_case)
|
BOOST_AUTO_TEST_CASE(switch_default_before_case)
|
||||||
@ -364,7 +370,7 @@ BOOST_AUTO_TEST_CASE(for_invalid_expression)
|
|||||||
CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected '{' but got 'Number'");
|
CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected '{' but got 'Number'");
|
||||||
CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected '{' but got 'Number'");
|
CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected '{' but got 'Number'");
|
||||||
CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
|
CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
|
||||||
CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", ParserError, "Instruction \"mstore\" not allowed in this context");
|
CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", TypeError, "Expected expression to return one item to the stack, but did return 0 items");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(for_visibility)
|
BOOST_AUTO_TEST_CASE(for_visibility)
|
||||||
@ -417,13 +423,13 @@ BOOST_AUTO_TEST_CASE(function_calls)
|
|||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(opcode_for_functions)
|
BOOST_AUTO_TEST_CASE(opcode_for_functions)
|
||||||
{
|
{
|
||||||
CHECK_PARSE_ERROR("{ function gas() { } }", ParserError, "Cannot use instruction names for identifier names.");
|
CHECK_PARSE_ERROR("{ function gas() { } }", ParserError, "Cannot use builtin");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(opcode_for_function_args)
|
BOOST_AUTO_TEST_CASE(opcode_for_function_args)
|
||||||
{
|
{
|
||||||
CHECK_PARSE_ERROR("{ function f(gas) { } }", ParserError, "Cannot use instruction names for identifier names.");
|
CHECK_PARSE_ERROR("{ function f(gas) { } }", ParserError, "Cannot use builtin");
|
||||||
CHECK_PARSE_ERROR("{ function f() -> gas { } }", ParserError, "Cannot use instruction names for identifier names.");
|
CHECK_PARSE_ERROR("{ function f() -> gas { } }", ParserError, "Cannot use builtin");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(name_clashes)
|
BOOST_AUTO_TEST_CASE(name_clashes)
|
||||||
@ -467,13 +473,13 @@ BOOST_AUTO_TEST_CASE(invalid_tuple_assignment)
|
|||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(instruction_too_few_arguments)
|
BOOST_AUTO_TEST_CASE(instruction_too_few_arguments)
|
||||||
{
|
{
|
||||||
CHECK_PARSE_ERROR("{ mul() }", ParserError, "Expected expression (instruction \"mul\" expects 2 arguments)");
|
CHECK_PARSE_ERROR("{ pop(mul()) }", TypeError, "Function expects 2 arguments but got 0.");
|
||||||
CHECK_PARSE_ERROR("{ mul(1) }", ParserError, "Expected ',' (instruction \"mul\" expects 2 arguments)");
|
CHECK_PARSE_ERROR("{ pop(mul(1)) }", TypeError, "Function expects 2 arguments but got 1.");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(instruction_too_many_arguments)
|
BOOST_AUTO_TEST_CASE(instruction_too_many_arguments)
|
||||||
{
|
{
|
||||||
CHECK_PARSE_ERROR("{ mul(1, 2, 3) }", ParserError, "Expected ')' (instruction \"mul\" expects 2 arguments)");
|
CHECK_PARSE_ERROR("{ pop(mul(1, 2, 3)) }", TypeError, "Function expects 2 arguments but got 3");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(recursion_depth)
|
BOOST_AUTO_TEST_CASE(recursion_depth)
|
||||||
@ -517,7 +523,7 @@ BOOST_AUTO_TEST_CASE(no_opcodes_in_strict)
|
|||||||
BOOST_CHECK(successParse("{ pop(callvalue) }"));
|
BOOST_CHECK(successParse("{ pop(callvalue) }"));
|
||||||
BOOST_CHECK(successParse("{ callvalue pop }"));
|
BOOST_CHECK(successParse("{ callvalue pop }"));
|
||||||
CHECK_STRICT_ERROR("{ pop(callvalue) }", ParserError, "Expected '(' but got ')'");
|
CHECK_STRICT_ERROR("{ pop(callvalue) }", ParserError, "Expected '(' but got ')'");
|
||||||
CHECK_STRICT_ERROR("{ callvalue pop }", ParserError, "Expected '(' but got identifier");
|
CHECK_STRICT_ERROR("{ callvalue pop }", ParserError, "Call or assignment expected");
|
||||||
SUCCESS_STRICT("{ pop(callvalue()) }");
|
SUCCESS_STRICT("{ pop(callvalue()) }");
|
||||||
BOOST_CHECK(successParse("{ switch callvalue case 0 {} }"));
|
BOOST_CHECK(successParse("{ switch callvalue case 0 {} }"));
|
||||||
CHECK_STRICT_ERROR("{ switch callvalue case 0 {} }", ParserError, "Expected '(' but got reserved keyword 'case'");
|
CHECK_STRICT_ERROR("{ switch callvalue case 0 {} }", ParserError, "Expected '(' but got reserved keyword 'case'");
|
||||||
@ -692,12 +698,12 @@ BOOST_AUTO_TEST_CASE(designated_invalid_instruction)
|
|||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration)
|
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration)
|
||||||
{
|
{
|
||||||
CHECK_ASSEMBLE_ERROR("{ let gas := 1 }", ParserError, "Cannot use instruction names for identifier names.");
|
CHECK_ASSEMBLE_ERROR("{ let gas := 1 }", ParserError, "Cannot use builtin");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment)
|
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment)
|
||||||
{
|
{
|
||||||
CHECK_ASSEMBLE_ERROR("{ 2 =: gas }", ParserError, "Identifier expected, got instruction name.");
|
CHECK_ASSEMBLE_ERROR("{ 2 =: gas }", ParserError, "Identifier expected, got builtin symbol");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_functional_assignment)
|
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_functional_assignment)
|
||||||
|
@ -102,7 +102,9 @@ bytes compileFirstExpression(
|
|||||||
{
|
{
|
||||||
ErrorList errors;
|
ErrorList errors;
|
||||||
ErrorReporter errorReporter(errors);
|
ErrorReporter errorReporter(errors);
|
||||||
sourceUnit = Parser(errorReporter).parse(make_shared<Scanner>(CharStream(_sourceCode, "")));
|
sourceUnit = Parser(errorReporter, dev::test::Options::get().evmVersion()).parse(
|
||||||
|
make_shared<Scanner>(CharStream(_sourceCode, ""))
|
||||||
|
);
|
||||||
if (!sourceUnit)
|
if (!sourceUnit)
|
||||||
return bytes();
|
return bytes();
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,10 @@ namespace
|
|||||||
ASTPointer<ContractDefinition> parseText(std::string const& _source, ErrorList& _errors)
|
ASTPointer<ContractDefinition> parseText(std::string const& _source, ErrorList& _errors)
|
||||||
{
|
{
|
||||||
ErrorReporter errorReporter(_errors);
|
ErrorReporter errorReporter(_errors);
|
||||||
ASTPointer<SourceUnit> sourceUnit = Parser(errorReporter).parse(std::make_shared<Scanner>(CharStream(_source, "")));
|
ASTPointer<SourceUnit> sourceUnit = Parser(
|
||||||
|
errorReporter,
|
||||||
|
dev::test::Options::get().evmVersion()
|
||||||
|
).parse(std::make_shared<Scanner>(CharStream(_source, "")));
|
||||||
if (!sourceUnit)
|
if (!sourceUnit)
|
||||||
return ASTPointer<ContractDefinition>();
|
return ASTPointer<ContractDefinition>();
|
||||||
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
||||||
|
@ -6,4 +6,4 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// ParserError: (67-70): Cannot use instruction names for identifier names.
|
// ParserError: (67-70): Cannot use builtin function name "mod" as identifier name.
|
||||||
|
@ -7,4 +7,4 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// ParserError: (102-105): Cannot use instruction names for identifier names.
|
// ParserError: (102-105): Cannot use builtin function name "sub" as identifier name.
|
||||||
|
@ -549,11 +549,11 @@ BOOST_AUTO_TEST_CASE(builtins_parser)
|
|||||||
SimpleDialect dialect;
|
SimpleDialect dialect;
|
||||||
CHECK_ERROR_DIALECT("{ let builtin := 6 }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
|
CHECK_ERROR_DIALECT("{ let builtin := 6 }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
|
||||||
CHECK_ERROR_DIALECT("{ function builtin() {} }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
|
CHECK_ERROR_DIALECT("{ function builtin() {} }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
|
||||||
CHECK_ERROR_DIALECT("{ builtin := 6 }", ParserError, "Expected '(' but got ':='", dialect);
|
CHECK_ERROR_DIALECT("{ builtin := 6 }", ParserError, "Variable name must precede \":=\" in assignment.", dialect);
|
||||||
CHECK_ERROR_DIALECT("{ function f(x) { f(builtin) } }", ParserError, "Expected '(' but got ')'", dialect);
|
CHECK_ERROR_DIALECT("{ function f(x) { f(builtin) } }", ParserError, "Expected '(' but got ')'", dialect);
|
||||||
CHECK_ERROR_DIALECT("{ function f(builtin) {}", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
|
CHECK_ERROR_DIALECT("{ function f(builtin) {}", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
|
||||||
CHECK_ERROR_DIALECT("{ function f() -> builtin {}", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
|
CHECK_ERROR_DIALECT("{ function f() -> builtin {}", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
|
||||||
CHECK_ERROR_DIALECT("{ function g() -> a,b {} builtin, builtin2 := g() }", ParserError, "Expected '(' but got ','", dialect);
|
CHECK_ERROR_DIALECT("{ function g() -> a,b {} builtin, builtin2 := g() }", ParserError, "Variable name must precede \",\" in multiple assignment.", dialect);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(builtins_analysis)
|
BOOST_AUTO_TEST_CASE(builtins_analysis)
|
||||||
|
Loading…
Reference in New Issue
Block a user