mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7386 from ethereum/060-strict-inline-assembly
Defaulting to strict inline assembly (instead of loose)
This commit is contained in:
commit
607bf24afe
@ -10,9 +10,9 @@ Breaking changes:
|
||||
* General: New reserved keywords: ``virtual``.
|
||||
* Standard JSON Interface: Add option to disable or choose hash method between IPFS and Swarm for the bytecode metadata.
|
||||
* Syntax: ``push(element)`` for dynamic storage arrays do not return the new length anymore.
|
||||
* Inline Assembly: Only strict inline assembly is allowed.
|
||||
* Type checker: Resulting type of exponentiation is equal to the type of the base. Also allow signed types for the base.
|
||||
|
||||
|
||||
Language Features:
|
||||
* Allow global enums and structs.
|
||||
* Allow underscores as delimiters in hex strings.
|
||||
|
@ -763,8 +763,6 @@ Grammar::
|
||||
AssemblyExpression |
|
||||
AssemblyLocalDefinition |
|
||||
AssemblyAssignment |
|
||||
AssemblyStackAssignment |
|
||||
LabelDefinition |
|
||||
AssemblyIf |
|
||||
AssemblySwitch |
|
||||
AssemblyFunctionDefinition |
|
||||
@ -780,8 +778,6 @@ Grammar::
|
||||
AssemblyAssignment = IdentifierOrList ':=' AssemblyExpression
|
||||
IdentifierOrList = Identifier | '(' IdentifierList ')'
|
||||
IdentifierList = Identifier ( ',' Identifier)*
|
||||
AssemblyStackAssignment = '=:' Identifier
|
||||
LabelDefinition = Identifier ':'
|
||||
AssemblyIf = 'if' AssemblyExpression AssemblyBlock
|
||||
AssemblySwitch = 'switch' AssemblyExpression AssemblyCase*
|
||||
( 'default' AssemblyBlock )?
|
||||
|
@ -323,12 +323,10 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
||||
// Will be re-generated later with correct information
|
||||
// We use the latest EVM version because we will re-run it anyway.
|
||||
yul::AsmAnalysisInfo analysisInfo;
|
||||
boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError;
|
||||
yul::AsmAnalyzer(
|
||||
analysisInfo,
|
||||
errorsIgnored,
|
||||
errorTypeForLoose,
|
||||
yul::EVMDialect::looseAssemblyForEVM(EVMVersion{}),
|
||||
yul::EVMDialect::strictAssemblyForEVM(EVMVersion{}),
|
||||
resolver
|
||||
).analyze(_inlineAssembly.operations());
|
||||
return false;
|
||||
|
@ -735,7 +735,6 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
yul::AsmAnalyzer analyzer(
|
||||
*_inlineAssembly.annotation().analysisInfo,
|
||||
m_errorReporter,
|
||||
Error::Type::SyntaxError,
|
||||
_inlineAssembly.dialect(),
|
||||
identifierAccess
|
||||
);
|
||||
|
@ -41,7 +41,6 @@ public:
|
||||
m_dialect(_dialect),
|
||||
m_reportMutability(_reportMutability) {}
|
||||
|
||||
void operator()(yul::Label const&) { }
|
||||
void operator()(yul::Instruction const& _instruction)
|
||||
{
|
||||
checkInstruction(_instruction.location, _instruction.instruction);
|
||||
@ -58,7 +57,6 @@ public:
|
||||
{
|
||||
boost::apply_visitor(*this, _expr.expression);
|
||||
}
|
||||
void operator()(yul::StackAssignment const&) {}
|
||||
void operator()(yul::Assignment const& _assignment)
|
||||
{
|
||||
boost::apply_visitor(*this, *_assignment.value);
|
||||
|
@ -411,7 +411,6 @@ void CompilerContext::appendInlineAssembly(
|
||||
analyzerResult = yul::AsmAnalyzer(
|
||||
analysisInfo,
|
||||
errorReporter,
|
||||
boost::none,
|
||||
dialect,
|
||||
identifierAccess.resolve
|
||||
).analyze(*parserResult);
|
||||
|
@ -1152,7 +1152,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
|
||||
SourceLocation location{position(), -1, source()};
|
||||
|
||||
expectToken(Token::Assembly);
|
||||
yul::Dialect const& dialect = yul::EVMDialect::looseAssemblyForEVM(m_evmVersion);
|
||||
yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion);
|
||||
if (m_scanner->currentToken() == Token::StringLiteral)
|
||||
{
|
||||
if (m_scanner->currentLiteral() != "evmasm")
|
||||
|
@ -78,7 +78,6 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect,
|
||||
bool success = yul::AsmAnalyzer(
|
||||
analysisInfo,
|
||||
errors,
|
||||
Error::Type::SyntaxError,
|
||||
_dialect,
|
||||
{},
|
||||
_object.dataNames()
|
||||
@ -87,24 +86,9 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect,
|
||||
return analysisInfo;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(Label const& _label)
|
||||
{
|
||||
solAssert(!_label.name.empty(), "");
|
||||
checkLooseFeature(
|
||||
_label.location,
|
||||
"The use of labels is disallowed. Please use \"if\", \"switch\", \"for\" or function calls instead."
|
||||
);
|
||||
m_info.stackHeightInfo[&_label] = m_stackHeight;
|
||||
warnOnInstructions(dev::eth::Instruction::JUMPDEST, _label.location);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(yul::Instruction const& _instruction)
|
||||
{
|
||||
checkLooseFeature(
|
||||
_instruction.location,
|
||||
"The use of non-functional instructions is disallowed. Please use functional notation instead."
|
||||
);
|
||||
solAssert(false, "The use of non-functional instructions is disallowed. Please use functional notation instead.");
|
||||
auto const& info = instructionInfo(_instruction.instruction);
|
||||
m_stackHeight += info.ret - info.args;
|
||||
m_info.stackHeightInfo[&_instruction] = m_stackHeight;
|
||||
@ -215,34 +199,21 @@ bool AsmAnalyzer::operator()(ExpressionStatement const& _statement)
|
||||
{
|
||||
int initialStackHeight = m_stackHeight;
|
||||
bool success = boost::apply_visitor(*this, _statement.expression);
|
||||
if (m_stackHeight != initialStackHeight && (m_dialect.flavour != AsmFlavour::Loose || m_errorTypeForLoose))
|
||||
if (success && m_stackHeight != initialStackHeight)
|
||||
{
|
||||
Error::Type errorType = m_dialect.flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError;
|
||||
string msg =
|
||||
"Top-level expressions are not supposed to return values (this expression returns " +
|
||||
to_string(m_stackHeight - initialStackHeight) +
|
||||
" value" +
|
||||
(m_stackHeight - initialStackHeight == 1 ? "" : "s") +
|
||||
"). Use ``pop()`` or assign them.";
|
||||
m_errorReporter.error(errorType, _statement.location, msg);
|
||||
if (errorType != Error::Type::Warning)
|
||||
success = false;
|
||||
m_errorReporter.error(Error::Type::TypeError, _statement.location, msg);
|
||||
success = false;
|
||||
}
|
||||
m_info.stackHeightInfo[&_statement] = m_stackHeight;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(StackAssignment const& _assignment)
|
||||
{
|
||||
checkLooseFeature(
|
||||
_assignment.location,
|
||||
"The use of stack assignment is disallowed. Please use assignment in functional notation instead."
|
||||
);
|
||||
bool success = checkAssignment(_assignment.variableName, size_t(-1));
|
||||
m_info.stackHeightInfo[&_assignment] = m_stackHeight;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(Assignment const& _assignment)
|
||||
{
|
||||
solAssert(_assignment.value, "");
|
||||
@ -366,7 +337,8 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
||||
}
|
||||
)))
|
||||
{
|
||||
m_errorReporter.declarationError(_funCall.functionName.location, "Function not found.");
|
||||
if (!warnOnInstructions(_funCall.functionName.name.str(), _funCall.functionName.location))
|
||||
m_errorReporter.declarationError(_funCall.functionName.location, "Function not found.");
|
||||
success = false;
|
||||
}
|
||||
if (success)
|
||||
@ -562,7 +534,7 @@ bool AsmAnalyzer::operator()(Block const& _block)
|
||||
m_stackHeight -= scope(&_block).numberOfVariables();
|
||||
|
||||
int const stackDiff = m_stackHeight - initialStackHeight;
|
||||
if (stackDiff != 0)
|
||||
if (success && stackDiff != 0)
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
_block.location,
|
||||
@ -587,7 +559,7 @@ bool AsmAnalyzer::expectExpression(Expression const& _expr)
|
||||
int const initialHeight = m_stackHeight;
|
||||
if (!boost::apply_visitor(*this, _expr))
|
||||
success = false;
|
||||
if (!expectDeposit(1, initialHeight, locationOf(_expr)))
|
||||
if (success && !expectDeposit(1, initialHeight, locationOf(_expr)))
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
@ -682,7 +654,16 @@ void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _loc
|
||||
);
|
||||
}
|
||||
|
||||
void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocation const& _location)
|
||||
bool AsmAnalyzer::warnOnInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location)
|
||||
{
|
||||
auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}).builtin(YulString(_instructionIdentifier));
|
||||
if (builtin)
|
||||
return warnOnInstructions(builtin->instruction.get(), _location);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocation const& _location)
|
||||
{
|
||||
// We assume that returndatacopy, returndatasize and staticcall are either all available
|
||||
// or all not available.
|
||||
@ -746,29 +727,16 @@ void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocatio
|
||||
_instr == dev::eth::Instruction::JUMPDEST
|
||||
)
|
||||
{
|
||||
if (m_dialect.flavour == AsmFlavour::Loose)
|
||||
m_errorReporter.error(
|
||||
m_errorTypeForLoose ? *m_errorTypeForLoose : Error::Type::Warning,
|
||||
_location,
|
||||
"Jump instructions and labels are low-level EVM features that can lead to "
|
||||
"incorrect stack access. Because of that they are discouraged. "
|
||||
"Please consider using \"switch\", \"if\" or \"for\" statements instead."
|
||||
);
|
||||
else
|
||||
m_errorReporter.error(
|
||||
Error::Type::SyntaxError,
|
||||
_location,
|
||||
"Jump instructions and labels are low-level EVM features that can lead to "
|
||||
"incorrect stack access. Because of that they are disallowed in strict assembly. "
|
||||
"Use functions, \"switch\", \"if\" or \"for\" statements instead."
|
||||
);
|
||||
m_errorReporter.error(
|
||||
Error::Type::SyntaxError,
|
||||
_location,
|
||||
"Jump instructions and labels are low-level EVM features that can lead to "
|
||||
"incorrect stack access. Because of that they are disallowed in strict assembly. "
|
||||
"Use functions, \"switch\", \"if\" or \"for\" statements instead."
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
if (m_dialect.flavour != AsmFlavour::Loose)
|
||||
solAssert(false, _description);
|
||||
else if (m_errorTypeForLoose)
|
||||
m_errorReporter.error(*m_errorTypeForLoose, _location, _description);
|
||||
return true;
|
||||
}
|
||||
|
@ -59,7 +59,6 @@ public:
|
||||
explicit AsmAnalyzer(
|
||||
AsmAnalysisInfo& _analysisInfo,
|
||||
langutil::ErrorReporter& _errorReporter,
|
||||
boost::optional<langutil::Error::Type> _errorTypeForLoose,
|
||||
Dialect const& _dialect,
|
||||
ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver(),
|
||||
std::set<YulString> const& _dataNames = {}
|
||||
@ -68,7 +67,6 @@ public:
|
||||
m_info(_analysisInfo),
|
||||
m_errorReporter(_errorReporter),
|
||||
m_dialect(_dialect),
|
||||
m_errorTypeForLoose(_errorTypeForLoose),
|
||||
m_dataNames(_dataNames)
|
||||
{
|
||||
if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&m_dialect))
|
||||
@ -85,9 +83,7 @@ public:
|
||||
bool operator()(Literal const& _literal);
|
||||
bool operator()(Identifier const&);
|
||||
bool operator()(FunctionalInstruction const& _functionalInstruction);
|
||||
bool operator()(Label const& _label);
|
||||
bool operator()(ExpressionStatement const&);
|
||||
bool operator()(StackAssignment const&);
|
||||
bool operator()(Assignment const& _assignment);
|
||||
bool operator()(VariableDeclaration const& _variableDeclaration);
|
||||
bool operator()(FunctionDefinition const& _functionDefinition);
|
||||
@ -110,12 +106,8 @@ private:
|
||||
|
||||
Scope& scope(Block const* _block);
|
||||
void expectValidType(std::string const& type, langutil::SourceLocation const& _location);
|
||||
void warnOnInstructions(dev::eth::Instruction _instr, langutil::SourceLocation const& _location);
|
||||
|
||||
/// Depending on @a m_flavour and @a m_errorTypeForLoose, throws an internal compiler
|
||||
/// exception (if the flavour is not Loose), reports an error/warning
|
||||
/// (if m_errorTypeForLoose is set) or does nothing.
|
||||
void checkLooseFeature(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
bool warnOnInstructions(dev::eth::Instruction _instr, langutil::SourceLocation const& _location);
|
||||
bool warnOnInstructions(std::string const& _instrIdentifier, langutil::SourceLocation const& _location);
|
||||
|
||||
int m_stackHeight = 0;
|
||||
yul::ExternalIdentifierAccess::Resolver m_resolver;
|
||||
@ -127,7 +119,6 @@ private:
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
Dialect const& m_dialect;
|
||||
boost::optional<langutil::Error::Type> m_errorTypeForLoose;
|
||||
/// Names of data objects to be referenced by builtin functions with literal arguments.
|
||||
std::set<YulString> m_dataNames;
|
||||
ForLoop const* m_currentForLoop = nullptr;
|
||||
|
@ -49,10 +49,6 @@ enum class LiteralKind { Number, Boolean, String };
|
||||
struct Literal { langutil::SourceLocation location; LiteralKind kind; YulString value; Type type; };
|
||||
/// External / internal identifier or label reference
|
||||
struct Identifier { langutil::SourceLocation location; YulString name; };
|
||||
/// Jump label ("name:")
|
||||
struct Label { langutil::SourceLocation location; YulString name; };
|
||||
/// Assignment from stack (":= x", moves stack top into x, potentially multiple slots)
|
||||
struct StackAssignment { langutil::SourceLocation location; Identifier variableName; };
|
||||
/// Assignment ("x := mload(20:u256)", expects push-1-expression on the right hand
|
||||
/// side and requires x to occupy exactly one stack slot.
|
||||
///
|
||||
|
@ -30,7 +30,6 @@ namespace yul
|
||||
struct Instruction;
|
||||
struct Literal;
|
||||
struct Label;
|
||||
struct StackAssignment;
|
||||
struct Identifier;
|
||||
struct Assignment;
|
||||
struct VariableDeclaration;
|
||||
@ -49,6 +48,6 @@ struct Block;
|
||||
struct TypedName;
|
||||
|
||||
using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
|
||||
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>;
|
||||
using Statement = boost::variant<ExpressionStatement, Instruction, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>;
|
||||
|
||||
}
|
||||
|
@ -144,23 +144,6 @@ Statement Parser::parseStatement()
|
||||
m_scanner->next();
|
||||
return stmt;
|
||||
}
|
||||
case Token::Assign:
|
||||
{
|
||||
if (m_dialect.flavour != AsmFlavour::Loose)
|
||||
break;
|
||||
StackAssignment assignment = createWithLocation<StackAssignment>();
|
||||
advance();
|
||||
expectToken(Token::Colon);
|
||||
assignment.variableName.location = location();
|
||||
assignment.variableName.name = YulString(currentLiteral());
|
||||
if (m_dialect.builtin(assignment.variableName.name))
|
||||
fatalParserError("Identifier expected, got builtin symbol.");
|
||||
else if (instructions().count(assignment.variableName.name.str()))
|
||||
fatalParserError("Identifier expected, got instruction name.");
|
||||
assignment.location.end = endPosition();
|
||||
expectToken(Token::Identifier);
|
||||
return Statement{move(assignment)};
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -222,26 +205,8 @@ Statement Parser::parseStatement()
|
||||
|
||||
return Statement{std::move(assignment)};
|
||||
}
|
||||
case Token::Colon:
|
||||
{
|
||||
if (elementary.type() != typeid(Identifier))
|
||||
fatalParserError("Label name must precede \":\".");
|
||||
|
||||
Identifier const& identifier = boost::get<Identifier>(elementary);
|
||||
|
||||
advance();
|
||||
|
||||
// label
|
||||
if (m_dialect.flavour != AsmFlavour::Loose)
|
||||
fatalParserError("Labels are not supported.");
|
||||
|
||||
Label label = createWithLocation<Label>(identifier.location);
|
||||
label.name = identifier.name;
|
||||
return label;
|
||||
}
|
||||
default:
|
||||
if (m_dialect.flavour != AsmFlavour::Loose)
|
||||
fatalParserError("Call or assignment expected.");
|
||||
fatalParserError("Call or assignment expected.");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -311,47 +276,8 @@ Expression Parser::parseExpression()
|
||||
RecursionGuard recursionGuard(*this);
|
||||
|
||||
ElementaryOperation operation = parseElementaryOperation();
|
||||
if (operation.type() == typeid(FunctionCall))
|
||||
if (operation.type() == typeid(FunctionCall) || currentToken() == Token::LParen)
|
||||
return parseCall(std::move(operation));
|
||||
else if (operation.type() == typeid(Instruction))
|
||||
{
|
||||
solAssert(m_dialect.flavour == AsmFlavour::Loose, "");
|
||||
Instruction const& instr = boost::get<Instruction>(operation);
|
||||
// Disallow instructions returning multiple values (and DUP/SWAP) as expression.
|
||||
if (
|
||||
instructionInfo(instr.instruction).ret != 1 ||
|
||||
isDupInstruction(instr.instruction) ||
|
||||
isSwapInstruction(instr.instruction)
|
||||
)
|
||||
fatalParserError(
|
||||
"Instruction \"" +
|
||||
instructionNames().at(instr.instruction) +
|
||||
"\" not allowed in this context."
|
||||
);
|
||||
if (m_dialect.flavour != AsmFlavour::Loose && currentToken() != Token::LParen)
|
||||
fatalParserError(
|
||||
"Non-functional instructions are not allowed in this context."
|
||||
);
|
||||
// Enforce functional notation for instructions requiring multiple arguments.
|
||||
int args = instructionInfo(instr.instruction).args;
|
||||
if (args > 0 && currentToken() != Token::LParen)
|
||||
fatalParserError(string(
|
||||
"Expected '(' (instruction \"" +
|
||||
instructionNames().at(instr.instruction) +
|
||||
"\" expects " +
|
||||
to_string(args) +
|
||||
" arguments)"
|
||||
));
|
||||
}
|
||||
if (currentToken() == Token::LParen)
|
||||
return parseCall(std::move(operation));
|
||||
else if (operation.type() == typeid(Instruction))
|
||||
{
|
||||
// Instructions not taking arguments are allowed as expressions.
|
||||
solAssert(m_dialect.flavour == AsmFlavour::Loose, "");
|
||||
Instruction& instr = boost::get<Instruction>(operation);
|
||||
return FunctionalInstruction{std::move(instr.location), instr.instruction, {}};
|
||||
}
|
||||
else if (operation.type() == typeid(Identifier))
|
||||
return boost::get<Identifier>(operation);
|
||||
else
|
||||
@ -388,26 +314,12 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
|
||||
case Token::Address:
|
||||
{
|
||||
YulString literal{currentLiteral()};
|
||||
// first search the set of builtins, then the instructions.
|
||||
if (m_dialect.builtin(literal))
|
||||
{
|
||||
Identifier identifier{location(), literal};
|
||||
advance();
|
||||
// If the builtin is not followed by `(` and we are in loose mode,
|
||||
// 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()))
|
||||
{
|
||||
dev::eth::Instruction const& instr = instructions().at(literal.str());
|
||||
ret = Instruction{location(), instr};
|
||||
expectToken(Token::LParen, false);
|
||||
return FunctionCall{identifier.location, identifier, {}};
|
||||
}
|
||||
else
|
||||
ret = Identifier{location(), literal};
|
||||
@ -657,8 +569,6 @@ YulString Parser::expectAsmIdentifier()
|
||||
|
||||
if (m_dialect.builtin(name))
|
||||
fatalParserError("Cannot use builtin function name \"" + name.str() + "\" as identifier name.");
|
||||
else if (m_dialect.flavour == AsmFlavour::Loose && instructions().count(name.str()))
|
||||
fatalParserError("Cannot use instruction name \"" + name.str() + "\" as identifier name.");
|
||||
advance();
|
||||
return name;
|
||||
}
|
||||
|
@ -113,20 +113,6 @@ string AsmPrinter::operator()(ExpressionStatement const& _statement) const
|
||||
return boost::apply_visitor(*this, _statement.expression);
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(Label const& _label) const
|
||||
{
|
||||
solAssert(!m_yul, "");
|
||||
solAssert(!_label.name.empty(), "Invalid label.");
|
||||
return _label.name.str() + ":";
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(StackAssignment const& _assignment) const
|
||||
{
|
||||
solAssert(!m_yul, "");
|
||||
solAssert(!_assignment.variableName.name.empty(), "Invalid variable name.");
|
||||
return "=: " + (*this)(_assignment.variableName);
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(Assignment const& _assignment) const
|
||||
{
|
||||
solAssert(_assignment.variableNames.size() >= 1, "");
|
||||
|
@ -41,8 +41,6 @@ public:
|
||||
std::string operator()(Identifier const& _identifier) const;
|
||||
std::string operator()(FunctionalInstruction const& _functionalInstruction) const;
|
||||
std::string operator()(ExpressionStatement const& _expr) const;
|
||||
std::string operator()(Label const& _label) const;
|
||||
std::string operator()(StackAssignment const& _assignment) const;
|
||||
std::string operator()(Assignment const& _assignment) const;
|
||||
std::string operator()(VariableDeclaration const& _variableDeclaration) const;
|
||||
std::string operator()(FunctionDefinition const& _functionDefinition) const;
|
||||
|
@ -50,20 +50,6 @@ bool ScopeFiller::operator()(ExpressionStatement const& _expr)
|
||||
return boost::apply_visitor(*this, _expr.expression);
|
||||
}
|
||||
|
||||
bool ScopeFiller::operator()(Label const& _item)
|
||||
{
|
||||
if (!m_currentScope->registerLabel(_item.name))
|
||||
{
|
||||
//@TODO secondary location
|
||||
m_errorReporter.declarationError(
|
||||
_item.location,
|
||||
"Label name " + _item.name.str() + " already taken in this scope."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopeFiller::operator()(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
for (auto const& variable: _varDecl.variables)
|
||||
|
@ -54,8 +54,6 @@ public:
|
||||
bool operator()(Identifier const&) { return true; }
|
||||
bool operator()(FunctionalInstruction const&) { return true; }
|
||||
bool operator()(ExpressionStatement const& _expr);
|
||||
bool operator()(Label const& _label);
|
||||
bool operator()(StackAssignment const&) { return true; }
|
||||
bool operator()(Assignment const&) { return true; }
|
||||
bool operator()(VariableDeclaration const& _variableDeclaration);
|
||||
bool operator()(FunctionDefinition const& _functionDefinition);
|
||||
|
@ -54,7 +54,6 @@ Dialect const& languageToDialect(AssemblyStack::Language _language, EVMVersion _
|
||||
switch (_language)
|
||||
{
|
||||
case AssemblyStack::Language::Assembly:
|
||||
return EVMDialect::looseAssemblyForEVM(_version);
|
||||
case AssemblyStack::Language::StrictAssembly:
|
||||
return EVMDialect::strictAssemblyForEVMObjects(_version);
|
||||
case AssemblyStack::Language::Yul:
|
||||
@ -117,7 +116,6 @@ bool AssemblyStack::analyzeParsed(Object& _object)
|
||||
AsmAnalyzer analyzer(
|
||||
*_object.analysisInfo,
|
||||
m_errorReporter,
|
||||
boost::none,
|
||||
languageToDialect(m_language, m_evmVersion),
|
||||
{},
|
||||
_object.dataNames()
|
||||
@ -133,15 +131,19 @@ bool AssemblyStack::analyzeParsed(Object& _object)
|
||||
void AssemblyStack::compileEVM(AbstractAssembly& _assembly, bool _evm15, bool _optimize) const
|
||||
{
|
||||
EVMDialect const* dialect = nullptr;
|
||||
|
||||
if (m_language == Language::Assembly)
|
||||
dialect = &EVMDialect::looseAssemblyForEVM(m_evmVersion);
|
||||
else if (m_language == AssemblyStack::Language::StrictAssembly)
|
||||
dialect = &EVMDialect::strictAssemblyForEVMObjects(m_evmVersion);
|
||||
else if (m_language == AssemblyStack::Language::Yul)
|
||||
dialect = &EVMDialect::yulForEVM(m_evmVersion);
|
||||
else
|
||||
solAssert(false, "Invalid language.");
|
||||
switch (m_language)
|
||||
{
|
||||
case Language::Assembly:
|
||||
case Language::StrictAssembly:
|
||||
dialect = &EVMDialect::strictAssemblyForEVMObjects(m_evmVersion);
|
||||
break;
|
||||
case Language::Yul:
|
||||
dialect = &EVMDialect::yulForEVM(m_evmVersion);
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "Invalid language.");
|
||||
break;
|
||||
}
|
||||
|
||||
EVMObjectCompiler::compile(*m_parserResult, _assembly, *dialect, _evm15, _optimize);
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ using Type = YulString;
|
||||
|
||||
enum class AsmFlavour
|
||||
{
|
||||
Loose, // no types, EVM instructions as function, jumps and direct stack manipulations
|
||||
Strict, // no types, EVM instructions as functions, but no jumps and no direct stack manipulations
|
||||
Yul // same as Strict mode with types
|
||||
};
|
||||
@ -55,7 +54,7 @@ struct BuiltinFunction
|
||||
|
||||
struct Dialect: boost::noncopyable
|
||||
{
|
||||
AsmFlavour const flavour = AsmFlavour::Loose;
|
||||
AsmFlavour const flavour = AsmFlavour::Strict;
|
||||
/// @returns the builtin function of the given name or a nullptr if it is not a builtin function.
|
||||
virtual BuiltinFunction const* builtin(YulString /*_name*/) const { return nullptr; }
|
||||
|
||||
|
@ -250,14 +250,6 @@ void CodeTransform::operator()(Assignment const& _assignment)
|
||||
checkStackHeight(&_assignment);
|
||||
}
|
||||
|
||||
void CodeTransform::operator()(StackAssignment const& _assignment)
|
||||
{
|
||||
solAssert(!m_allowStackOpt, "");
|
||||
m_assembly.setSourceLocation(_assignment.location);
|
||||
generateAssignment(_assignment.variableName);
|
||||
checkStackHeight(&_assignment);
|
||||
}
|
||||
|
||||
void CodeTransform::operator()(ExpressionStatement const& _statement)
|
||||
{
|
||||
m_assembly.setSourceLocation(_statement.location);
|
||||
@ -265,17 +257,6 @@ void CodeTransform::operator()(ExpressionStatement const& _statement)
|
||||
checkStackHeight(&_statement);
|
||||
}
|
||||
|
||||
void CodeTransform::operator()(Label const& _label)
|
||||
{
|
||||
solAssert(!m_allowStackOpt, "");
|
||||
m_assembly.setSourceLocation(_label.location);
|
||||
solAssert(m_scope, "");
|
||||
solAssert(m_scope->identifiers.count(_label.name), "");
|
||||
Scope::Label& label = boost::get<Scope::Label>(m_scope->identifiers.at(_label.name));
|
||||
m_assembly.appendLabel(labelID(label));
|
||||
checkStackHeight(&_label);
|
||||
}
|
||||
|
||||
void CodeTransform::operator()(FunctionCall const& _call)
|
||||
{
|
||||
solAssert(m_scope, "");
|
||||
|
@ -177,8 +177,6 @@ public:
|
||||
void operator()(FunctionalInstruction const& _instr);
|
||||
void operator()(FunctionCall const&);
|
||||
void operator()(ExpressionStatement const& _statement);
|
||||
void operator()(Label const& _label);
|
||||
void operator()(StackAssignment const& _assignment);
|
||||
void operator()(Assignment const& _assignment);
|
||||
void operator()(VariableDeclaration const& _varDecl);
|
||||
void operator()(If const& _if);
|
||||
|
@ -185,15 +185,6 @@ BuiltinFunctionForEVM const* EVMDialect::builtin(YulString _name) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EVMDialect const& EVMDialect::looseAssemblyForEVM(langutil::EVMVersion _version)
|
||||
{
|
||||
static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects;
|
||||
static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }};
|
||||
if (!dialects[_version])
|
||||
dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Loose, false, _version);
|
||||
return *dialects[_version];
|
||||
}
|
||||
|
||||
EVMDialect const& EVMDialect::strictAssemblyForEVM(langutil::EVMVersion _version)
|
||||
{
|
||||
static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects;
|
||||
|
@ -72,7 +72,6 @@ struct EVMDialect: public Dialect
|
||||
BuiltinFunctionForEVM const* equalityFunction() const override { return builtin("eq"_yulstring); }
|
||||
BuiltinFunctionForEVM const* booleanNegationFunction() const override { return builtin("iszero"_yulstring); }
|
||||
|
||||
static EVMDialect const& looseAssemblyForEVM(langutil::EVMVersion _version);
|
||||
static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version);
|
||||
static EVMDialect const& strictAssemblyForEVMObjects(langutil::EVMVersion _version);
|
||||
static EVMDialect const& yulForEVM(langutil::EVMVersion _version);
|
||||
|
@ -658,7 +658,7 @@ Object EVMToEWasmTranslator::run(Object const& _object)
|
||||
|
||||
ErrorList errors;
|
||||
ErrorReporter errorReporter(errors);
|
||||
AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, boost::none, WasmDialect::instance(), {}, _object.dataNames());
|
||||
AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, WasmDialect::instance(), {}, _object.dataNames());
|
||||
if (!analyzer.analyze(*ret.code))
|
||||
{
|
||||
// TODO the errors here are "wrong" because they have invalid source references!
|
||||
|
@ -108,23 +108,11 @@ wasm::Expression EWasmCodeTransform::operator()(Assignment const& _assignment)
|
||||
return generateMultiAssignment(move(variableNames), visit(*_assignment.value));
|
||||
}
|
||||
|
||||
wasm::Expression EWasmCodeTransform::operator()(StackAssignment const&)
|
||||
{
|
||||
yulAssert(false, "");
|
||||
return {};
|
||||
}
|
||||
|
||||
wasm::Expression EWasmCodeTransform::operator()(ExpressionStatement const& _statement)
|
||||
{
|
||||
return visitReturnByValue(_statement.expression);
|
||||
}
|
||||
|
||||
wasm::Expression EWasmCodeTransform::operator()(Label const&)
|
||||
{
|
||||
yulAssert(false, "");
|
||||
return {};
|
||||
}
|
||||
|
||||
wasm::Expression EWasmCodeTransform::operator()(FunctionalInstruction const& _f)
|
||||
{
|
||||
yulAssert(false, "EVM instruction in ewasm code: " + eth::instructionInfo(_f.instruction).name);
|
||||
|
@ -44,8 +44,6 @@ public:
|
||||
wasm::Expression operator()(yul::FunctionalInstruction const& _instr);
|
||||
wasm::Expression operator()(yul::FunctionCall const&);
|
||||
wasm::Expression operator()(yul::ExpressionStatement const& _statement);
|
||||
wasm::Expression operator()(yul::Label const& _label);
|
||||
wasm::Expression operator()(yul::StackAssignment const& _assignment);
|
||||
wasm::Expression operator()(yul::Assignment const& _assignment);
|
||||
wasm::Expression operator()(yul::VariableDeclaration const& _varDecl);
|
||||
wasm::Expression operator()(yul::If const& _if);
|
||||
|
@ -59,18 +59,6 @@ Statement ASTCopier::operator()(Assignment const& _assignment)
|
||||
};
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(StackAssignment const&)
|
||||
{
|
||||
assertThrow(false, OptimizerException, "Invalid operation.");
|
||||
return {};
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(Label const&)
|
||||
{
|
||||
assertThrow(false, OptimizerException, "Invalid operation.");
|
||||
return {};
|
||||
}
|
||||
|
||||
Expression ASTCopier::operator()(FunctionCall const& _call)
|
||||
{
|
||||
return FunctionCall{
|
||||
|
@ -50,8 +50,6 @@ public:
|
||||
virtual ~StatementCopier() = default;
|
||||
virtual Statement operator()(ExpressionStatement const& _statement) = 0;
|
||||
virtual Statement operator()(Instruction const& _instruction) = 0;
|
||||
virtual Statement operator()(Label const& _label) = 0;
|
||||
virtual Statement operator()(StackAssignment const& _assignment) = 0;
|
||||
virtual Statement operator()(Assignment const& _assignment) = 0;
|
||||
virtual Statement operator()(VariableDeclaration const& _varDecl) = 0;
|
||||
virtual Statement operator()(If const& _if) = 0;
|
||||
@ -77,8 +75,6 @@ public:
|
||||
Expression operator()(FunctionalInstruction const& _instr) override;
|
||||
Expression operator()(FunctionCall const&) override;
|
||||
Statement operator()(ExpressionStatement const& _statement) override;
|
||||
Statement operator()(Label const& _label) override;
|
||||
Statement operator()(StackAssignment const& _assignment) override;
|
||||
Statement operator()(Assignment const& _assignment) override;
|
||||
Statement operator()(VariableDeclaration const& _varDecl) override;
|
||||
Statement operator()(If const& _if) override;
|
||||
|
@ -48,8 +48,6 @@ public:
|
||||
virtual void operator()(FunctionalInstruction const& _instr);
|
||||
virtual void operator()(FunctionCall const& _funCall);
|
||||
virtual void operator()(ExpressionStatement const& _statement);
|
||||
virtual void operator()(Label const&) { assertThrow(false, OptimizerException, ""); }
|
||||
virtual void operator()(StackAssignment const&) { assertThrow(false, OptimizerException, ""); }
|
||||
virtual void operator()(Assignment const& _assignment);
|
||||
virtual void operator()(VariableDeclaration const& _varDecl);
|
||||
virtual void operator()(If const& _if);
|
||||
@ -85,8 +83,6 @@ public:
|
||||
virtual void operator()(FunctionalInstruction& _instr);
|
||||
virtual void operator()(FunctionCall& _funCall);
|
||||
virtual void operator()(ExpressionStatement& _statement);
|
||||
virtual void operator()(Label&) { assertThrow(false, OptimizerException, ""); }
|
||||
virtual void operator()(StackAssignment&) { assertThrow(false, OptimizerException, ""); }
|
||||
virtual void operator()(Assignment& _assignment);
|
||||
virtual void operator()(VariableDeclaration& _varDecl);
|
||||
virtual void operator()(If& _if);
|
||||
|
@ -164,16 +164,6 @@ bool SyntacticallyEqual::statementEqual(Instruction const&, Instruction const&)
|
||||
assertThrow(false, OptimizerException, "");
|
||||
}
|
||||
|
||||
bool SyntacticallyEqual::statementEqual(Label const&, Label const&)
|
||||
{
|
||||
assertThrow(false, OptimizerException, "");
|
||||
}
|
||||
|
||||
bool SyntacticallyEqual::statementEqual(StackAssignment const&, StackAssignment const&)
|
||||
{
|
||||
assertThrow(false, OptimizerException, "");
|
||||
}
|
||||
|
||||
bool SyntacticallyEqual::statementEqual(Block const& _lhs, Block const& _rhs)
|
||||
{
|
||||
return containerEqual(_lhs.statements, _rhs.statements, [this](Statement const& _lhsStmt, Statement const& _rhsStmt) -> bool {
|
||||
|
@ -60,8 +60,6 @@ public:
|
||||
bool statementEqual(Block const& _lhs, Block const& _rhs);
|
||||
private:
|
||||
bool statementEqual(Instruction const& _lhs, Instruction const& _rhs);
|
||||
bool statementEqual(Label const& _lhs, Label const& _rhs);
|
||||
bool statementEqual(StackAssignment const& _lhs, StackAssignment const& _rhs);
|
||||
|
||||
bool visitDeclaration(TypedName const& _lhs, TypedName const& _rhs);
|
||||
|
||||
|
@ -188,22 +188,22 @@ BOOST_AUTO_TEST_CASE(surplus_input)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(simple_instructions)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ dup1 dup1 mul dup1 sub pop }"));
|
||||
BOOST_CHECK(successParse("{ let y := mul(0x10, mul(0x20, mload(0x40)))}"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(selfdestruct)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ 0x02 selfdestruct }"));
|
||||
BOOST_CHECK(successParse("{ selfdestruct(0x02) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(keywords)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ 1 2 byte 2 return address pop }"));
|
||||
BOOST_CHECK(successParse("{ return (byte(1, 2), 2) pop(address()) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(constants)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ 7 8 mul pop }"));
|
||||
BOOST_CHECK(successParse("{ pop(mul(7, 8)) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(vardecl)
|
||||
@ -237,29 +237,14 @@ BOOST_AUTO_TEST_CASE(vardecl_empty)
|
||||
BOOST_CHECK(successParse("{ let x }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(assignment)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ let x := 2 7 8 add =: x }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(label)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ 7 abc: 8 eq abc jump pop }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(label_complex)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ 7 abc: 8 eq jump(abc) jumpi(eq(7, 8), abc) pop }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(functional)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ let x := 2 add(7, mul(6, x)) mul(7, 8) add =: x }"));
|
||||
BOOST_CHECK(successParse("{ let x := 2 x := add(add(7, mul(6, x)), mul(7, 8)) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(functional_partial)
|
||||
{
|
||||
CHECK_PARSE_ERROR("{ let x := byte }", ParserError, "Expected '(' (instruction \"byte\" expects 2 arguments)");
|
||||
CHECK_PARSE_ERROR("{ let x := byte }", ParserError, "Expected '(' but got '}'");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(functional_partial_success)
|
||||
@ -274,12 +259,12 @@ BOOST_AUTO_TEST_CASE(functional_assignment)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(functional_assignment_complex)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ let x := 2 x := add(7, mul(6, x)) mul(7, 8) add }"));
|
||||
BOOST_CHECK(successParse("{ let x := 2 x := add(add(7, mul(6, x)), mul(7, 8)) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(vardecl_complex)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ let y := 2 let x := add(7, mul(6, y)) add mul(7, 8) }"));
|
||||
BOOST_CHECK(successParse("{ let y := 2 let x := add(add(7, mul(6, y)), mul(7, 8)) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(variable_use_before_decl)
|
||||
@ -303,7 +288,7 @@ BOOST_AUTO_TEST_CASE(if_statement_scope)
|
||||
|
||||
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 '(' but got '{'");
|
||||
BOOST_CHECK("{ if calldatasize() {}");
|
||||
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'");
|
||||
@ -333,7 +318,7 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case)
|
||||
BOOST_AUTO_TEST_CASE(switch_invalid_expression)
|
||||
{
|
||||
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 '(' but got reserved keyword 'default'");
|
||||
CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", TypeError, "Expected expression to return one item to the stack, but did return 0 items");
|
||||
}
|
||||
|
||||
@ -369,7 +354,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 {} mload {} {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
|
||||
CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected '(' but got '{'");
|
||||
CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", TypeError, "Expected expression to return one item to the stack, but did return 0 items");
|
||||
}
|
||||
|
||||
@ -463,7 +448,7 @@ BOOST_AUTO_TEST_CASE(functions_in_parallel_scopes)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(variable_access_cross_functions)
|
||||
{
|
||||
CHECK_PARSE_ERROR("{ let x := 2 function g() { x pop } }", DeclarationError, "Identifier not found.");
|
||||
CHECK_PARSE_ERROR("{ let x := 2 function g() { pop(x) } }", DeclarationError, "Identifier not found.");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(invalid_tuple_assignment)
|
||||
@ -516,46 +501,6 @@ BOOST_AUTO_TEST_CASE(multiple_assignment)
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(LooseStrictMode)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(no_opcodes_in_strict)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ pop(callvalue) }"));
|
||||
BOOST_CHECK(successParse("{ callvalue pop }"));
|
||||
CHECK_STRICT_ERROR("{ pop(callvalue) }", ParserError, "Expected '(' but got ')'");
|
||||
CHECK_STRICT_ERROR("{ callvalue pop }", ParserError, "Call or assignment expected");
|
||||
SUCCESS_STRICT("{ pop(callvalue()) }");
|
||||
BOOST_CHECK(successParse("{ switch callvalue case 0 {} }"));
|
||||
CHECK_STRICT_ERROR("{ switch callvalue case 0 {} }", ParserError, "Expected '(' but got reserved keyword 'case'");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(no_labels_in_strict)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ a: }"));
|
||||
CHECK_STRICT_ERROR("{ a: }", ParserError, "Labels are not supported");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(no_stack_assign_in_strict)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ let x 4 =: x }"));
|
||||
CHECK_STRICT_ERROR("{ let x 4 =: x }", ParserError, "Call or assignment expected.");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(no_dup_swap_in_strict)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ swap1 }"));
|
||||
CHECK_STRICT_ERROR("{ swap1 }", ParserError, "Call or assignment expected.");
|
||||
BOOST_CHECK(successParse("{ dup1 pop }"));
|
||||
CHECK_STRICT_ERROR("{ dup1 pop }", ParserError, "Call or assignment expected.");
|
||||
BOOST_CHECK(successParse("{ swap2 }"));
|
||||
CHECK_STRICT_ERROR("{ swap2 }", ParserError, "Call or assignment expected.");
|
||||
BOOST_CHECK(successParse("{ dup2 pop }"));
|
||||
CHECK_STRICT_ERROR("{ dup2 pop }", ParserError, "Call or assignment expected.");
|
||||
CHECK_PARSE_ERROR("{ switch dup1 case 0 {} }", ParserError, "Instruction \"dup1\" not allowed in this context");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(Printing)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_smoke)
|
||||
@ -565,12 +510,12 @@ BOOST_AUTO_TEST_CASE(print_smoke)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_instructions)
|
||||
{
|
||||
parsePrintCompare("{\n 7\n 8\n mul\n dup10\n add\n pop\n}");
|
||||
parsePrintCompare("{ pop(7) }");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_subblock)
|
||||
{
|
||||
parsePrintCompare("{\n {\n dup4\n add\n }\n}");
|
||||
parsePrintCompare("{ { pop(7) } }");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_functional)
|
||||
@ -578,14 +523,9 @@ BOOST_AUTO_TEST_CASE(print_functional)
|
||||
parsePrintCompare("{ let x := mul(sload(0x12), 7) }");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_label)
|
||||
{
|
||||
parsePrintCompare("{\n loop:\n jump(loop)\n}", true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_assignments)
|
||||
{
|
||||
parsePrintCompare("{\n let x := mul(2, 3)\n 7\n =: x\n x := add(1, 2)\n}");
|
||||
parsePrintCompare("{\n let x := mul(2, 3)\n pop(7)\n x := add(1, 2)\n}");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_multi_assignments)
|
||||
@ -595,7 +535,7 @@ BOOST_AUTO_TEST_CASE(print_multi_assignments)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_string_literals)
|
||||
{
|
||||
parsePrintCompare("{\n \"\\n'\\xab\\x95\\\"\"\n pop\n}");
|
||||
parsePrintCompare("{ let x := \"\\n'\\xab\\x95\\\"\" }");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_string_literal_unicode)
|
||||
@ -661,39 +601,21 @@ BOOST_AUTO_TEST_CASE(oversize_string_literals)
|
||||
CHECK_ASSEMBLE_ERROR("{ let x := \"123456789012345678901234567890123\" }", TypeError, "String literal too long");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(assignment_after_tag)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ let x := 1 { 7 tag: =: x } }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(magic_variables)
|
||||
{
|
||||
CHECK_ASSEMBLE_ERROR("{ this pop }", DeclarationError, "Identifier not found");
|
||||
CHECK_ASSEMBLE_ERROR("{ ecrecover pop }", DeclarationError, "Identifier not found");
|
||||
BOOST_CHECK(successAssemble("{ let ecrecover := 1 ecrecover pop }"));
|
||||
CHECK_ASSEMBLE_ERROR("{ pop(this) }", DeclarationError, "Identifier not found");
|
||||
CHECK_ASSEMBLE_ERROR("{ pop(ecrecover) }", DeclarationError, "Identifier not found");
|
||||
BOOST_CHECK(successAssemble("{ let ecrecover := 1 pop(ecrecover) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(stack_variables)
|
||||
{
|
||||
BOOST_CHECK(successAssemble("{ let y := 3 { 2 { let x := y } pop} }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(imbalanced_stack)
|
||||
{
|
||||
BOOST_CHECK(successAssemble("{ 1 2 mul pop }", false));
|
||||
CHECK_ASSEMBLE_ERROR("{ 1 }", DeclarationError, "Unbalanced stack at the end of a block: 1 surplus item(s).");
|
||||
CHECK_ASSEMBLE_ERROR("{ pop }", DeclarationError, "Unbalanced stack at the end of a block: 1 missing item(s).");
|
||||
BOOST_CHECK(successAssemble("{ let x := 4 7 add }", false));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(error_tag)
|
||||
{
|
||||
CHECK_ERROR("{ jump(invalidJumpLabel) }", true, DeclarationError, "Identifier not found", true);
|
||||
BOOST_CHECK(successAssemble("{ let y := 3 { let z := 2 { let x := y } } }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(designated_invalid_instruction)
|
||||
{
|
||||
BOOST_CHECK(successAssemble("{ invalid }"));
|
||||
BOOST_CHECK(successAssemble("{ invalid() }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration)
|
||||
@ -701,16 +623,6 @@ BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration)
|
||||
CHECK_ASSEMBLE_ERROR("{ let gas := 1 }", ParserError, "Cannot use builtin");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment)
|
||||
{
|
||||
CHECK_ASSEMBLE_ERROR("{ 2 =: gas }", ParserError, "Identifier expected, got builtin symbol");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_functional_assignment)
|
||||
{
|
||||
CHECK_ASSEMBLE_ERROR("{ gas := 2 }", ParserError, "Variable name must precede \":=\"");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(revert)
|
||||
{
|
||||
BOOST_CHECK(successAssemble("{ revert(0, 0) }"));
|
||||
@ -772,18 +684,10 @@ BOOST_AUTO_TEST_CASE(large_constant)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(keccak256)
|
||||
{
|
||||
BOOST_CHECK(successAssemble("{ 0 0 keccak256 pop }"));
|
||||
BOOST_CHECK(successAssemble("{ pop(keccak256(0, 0)) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(returndatasize)
|
||||
{
|
||||
if (!dev::test::Options::get().evmVersion().supportsReturndata())
|
||||
return;
|
||||
BOOST_CHECK(successAssemble("{ let r := returndatasize }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(returndatasize_functional)
|
||||
{
|
||||
if (!dev::test::Options::get().evmVersion().supportsReturndata())
|
||||
return;
|
||||
@ -794,7 +698,7 @@ BOOST_AUTO_TEST_CASE(returndatacopy)
|
||||
{
|
||||
if (!dev::test::Options::get().evmVersion().supportsReturndata())
|
||||
return;
|
||||
BOOST_CHECK(successAssemble("{ 64 32 0 returndatacopy }"));
|
||||
BOOST_CHECK(successAssemble("{ returndatacopy(0, 32, 64) }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(returndatacopy_functional)
|
||||
@ -836,27 +740,10 @@ BOOST_AUTO_TEST_CASE(shift_constantinople_warning)
|
||||
CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", TypeError, "The \"sar\" instruction is only available for Constantinople-compatible VMs");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(chainid_instanbul_warning)
|
||||
BOOST_AUTO_TEST_CASE(jump_error)
|
||||
{
|
||||
if (dev::test::Options::get().evmVersion().hasChainID())
|
||||
return;
|
||||
CHECK_PARSE_WARNING("{ pop(chainid()) }", TypeError, "The \"chainid\" instruction is only available for Istanbul-compatible VMs");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(selfbalance_instanbul_warning)
|
||||
{
|
||||
if (dev::test::Options::get().evmVersion().hasSelfBalance())
|
||||
return;
|
||||
CHECK_PARSE_WARNING("{ pop(selfbalance()) }", TypeError, "The \"selfbalance\" instruction is only available for Istanbul-compatible VMs");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(jump_warning)
|
||||
{
|
||||
CHECK_PARSE_WARNING("{ 1 jump }", Warning, "Jump instructions");
|
||||
CHECK_PARSE_WARNING("{ 1 2 jumpi }", Warning, "Jump instructions");
|
||||
CHECK_PARSE_WARNING("{ jump(44) }", Warning, "Jump instructions");
|
||||
CHECK_PARSE_WARNING("{ jumpi(44, 2) }", Warning, "Jump instructions");
|
||||
CHECK_PARSE_WARNING("{ a: }", Warning, "Jump instructions");
|
||||
CHECK_PARSE_WARNING("{ jump(44) }", DeclarationError, "Function not found.");
|
||||
CHECK_PARSE_WARNING("{ jumpi(44, 2) }", DeclarationError, "Function not found.");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -10274,7 +10274,7 @@ BOOST_AUTO_TEST_CASE(correctly_initialize_memory_array_in_constructor)
|
||||
// Make memory dirty.
|
||||
assembly {
|
||||
for { let i := 0 } lt(i, 64) { i := add(i, 1) } {
|
||||
mstore(msize, not(0))
|
||||
mstore(msize(), not(0))
|
||||
}
|
||||
}
|
||||
uint16[3] memory c;
|
||||
@ -12920,7 +12920,7 @@ BOOST_AUTO_TEST_CASE(snark)
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
assembly {
|
||||
success := call(sub(gas, 2000), 6, 0, input, 0xc0, r, 0x60)
|
||||
success := call(sub(gas(), 2000), 6, 0, input, 0xc0, r, 0x60)
|
||||
// Use "invalid" to make gas estimation work
|
||||
switch success case 0 { invalid() }
|
||||
}
|
||||
@ -12936,7 +12936,7 @@ BOOST_AUTO_TEST_CASE(snark)
|
||||
input[2] = s;
|
||||
bool success;
|
||||
assembly {
|
||||
success := call(sub(gas, 2000), 7, 0, input, 0x80, r, 0x60)
|
||||
success := call(sub(gas(), 2000), 7, 0, input, 0x80, r, 0x60)
|
||||
// Use "invalid" to make gas estimation work
|
||||
switch success case 0 { invalid() }
|
||||
}
|
||||
@ -12964,7 +12964,7 @@ BOOST_AUTO_TEST_CASE(snark)
|
||||
uint[1] memory out;
|
||||
bool success;
|
||||
assembly {
|
||||
success := call(sub(gas, 2000), 8, 0, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
success := call(sub(gas(), 2000), 8, 0, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
// Use "invalid" to make gas estimation work
|
||||
switch success case 0 { invalid() }
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(assembly_staticcall)
|
||||
string text = R"(
|
||||
contract C {
|
||||
function i() view public {
|
||||
assembly { pop(staticcall(gas, 1, 2, 3, 4, 5)) }
|
||||
assembly { pop(staticcall(gas(), 1, 2, 3, 4, 5)) }
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
@ -1,13 +0,0 @@
|
||||
contract C {
|
||||
function f() public pure {
|
||||
assembly {
|
||||
l:
|
||||
|
||||
l()
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// SyntaxError: (63-64): The use of labels is disallowed. Please use "if", "switch", "for" or function calls instead.
|
||||
// SyntaxError: (63-64): Jump instructions and labels are low-level EVM features that can lead to incorrect stack access. Because of that they are discouraged. Please consider using "switch", "if" or "for" statements instead.
|
||||
// TypeError: (73-74): Attempt to call label instead of function.
|
@ -8,4 +8,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (86-87): Function k used without being called.
|
||||
// ParserError: (92-93): Call or assignment expected.
|
||||
|
@ -7,5 +7,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// SyntaxError: (75-82): The use of non-functional instructions is disallowed. Please use functional notation instead.
|
||||
// SyntaxError: (95-98): The use of non-functional instructions is disallowed. Please use functional notation instead.
|
||||
// ParserError: (95-98): Expected '(' but got identifier
|
||||
|
@ -6,4 +6,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// SyntaxError: (75-82): Jump instructions and labels are low-level EVM features that can lead to incorrect stack access. Because of that they are discouraged. Please consider using "switch", "if" or "for" statements instead.
|
||||
// DeclarationError: (75-79): Function not found.
|
||||
|
@ -1,10 +0,0 @@
|
||||
contract C {
|
||||
function f() public pure {
|
||||
assembly {
|
||||
jump(xy)
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// DeclarationError: (68-70): Identifier not found.
|
||||
// SyntaxError: (63-71): Jump instructions and labels are low-level EVM features that can lead to incorrect stack access. Because of that they are discouraged. Please consider using "switch", "if" or "for" statements instead.
|
@ -6,5 +6,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// SyntaxError: (75-80): The use of labels is disallowed. Please use "if", "switch", "for" or function calls instead.
|
||||
// SyntaxError: (75-80): Jump instructions and labels are low-level EVM features that can lead to incorrect stack access. Because of that they are discouraged. Please consider using "switch", "if" or "for" statements instead.
|
||||
// ParserError: (80-81): Call or assignment expected.
|
||||
|
@ -6,5 +6,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// SyntaxError: (75-83): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them.
|
||||
// DeclarationError: (61-93): Unbalanced stack at the end of a block: 1 surplus item(s).
|
||||
// TypeError: (75-83): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them.
|
||||
|
@ -6,5 +6,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// SyntaxError: (75-76): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them.
|
||||
// DeclarationError: (61-86): Unbalanced stack at the end of a block: 1 surplus item(s).
|
||||
// ParserError: (85-86): Call or assignment expected.
|
||||
|
@ -1,9 +0,0 @@
|
||||
contract C {
|
||||
function f() public pure {
|
||||
assembly {
|
||||
return := 1
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// ParserError: (70-72): Variable name must precede ":=" in assignment.
|
@ -1,9 +0,0 @@
|
||||
contract C {
|
||||
function f() public pure {
|
||||
assembly {
|
||||
return : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// ParserError: (70-71): Label name must precede ":".
|
@ -6,5 +6,4 @@ contract test {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// SyntaxError: (73-76): The use of non-functional instructions is disallowed. Please use functional notation instead.
|
||||
// DeclarationError: (59-86): Unbalanced stack at the end of a block: 1 missing item(s).
|
||||
// ParserError: (85-86): Expected '(' but got '}'
|
||||
|
@ -6,5 +6,4 @@ contract test {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// SyntaxError: (73-74): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them.
|
||||
// DeclarationError: (59-84): Unbalanced stack at the end of a block: 1 surplus item(s).
|
||||
// ParserError: (83-84): Call or assignment expected.
|
||||
|
@ -1,9 +1,9 @@
|
||||
contract C {
|
||||
function f() public pure {
|
||||
assembly {
|
||||
_offset
|
||||
let x := _offset
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// DeclarationError: (75-82): In variable names _slot and _offset can only be used as a suffix.
|
||||
// DeclarationError: (84-91): In variable names _slot and _offset can only be used as a suffix.
|
||||
|
@ -1,9 +1,9 @@
|
||||
contract C {
|
||||
function f() public pure {
|
||||
assembly {
|
||||
_slot
|
||||
let x := _slot
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// DeclarationError: (75-80): In variable names _slot and _offset can only be used as a suffix.
|
||||
// DeclarationError: (84-89): In variable names _slot and _offset can only be used as a suffix.
|
||||
|
@ -9,7 +9,7 @@ contract C {
|
||||
assembly { x := 7 }
|
||||
}
|
||||
function g() view public {
|
||||
assembly { for {} 1 { pop(sload(0)) } { } pop(gas) }
|
||||
assembly { for {} 1 { pop(sload(0)) } { } pop(gas()) }
|
||||
}
|
||||
function h() view public {
|
||||
assembly { function g() { pop(blockhash(20)) } }
|
||||
@ -18,6 +18,6 @@ contract C {
|
||||
assembly { pop(call(0, 1, 2, 3, 4, 5, 6)) }
|
||||
}
|
||||
function k() public {
|
||||
assembly { pop(call(gas, 1, 2, 3, 4, 5, 6)) }
|
||||
assembly { pop(call(gas(), 1, 2, 3, 4, 5, 6)) }
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,6 @@ bool parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorR
|
||||
return (yul::AsmAnalyzer(
|
||||
analysisInfo,
|
||||
errorReporter,
|
||||
boost::none,
|
||||
_dialect
|
||||
)).analyze(*parserResult);
|
||||
}
|
||||
@ -208,11 +207,6 @@ BOOST_AUTO_TEST_CASE(tuple_assignment)
|
||||
BOOST_CHECK(successParse("{ function f() -> a:u256, b:u256, c:u256 {} let x:u256, y:u256, z:u256 := f() }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(label)
|
||||
{
|
||||
CHECK_ERROR("{ label: }", ParserError, "Labels are not supported.");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(instructions)
|
||||
{
|
||||
CHECK_ERROR("{ pop }", ParserError, "Call or assignment expected.");
|
||||
@ -549,11 +543,9 @@ BOOST_AUTO_TEST_CASE(builtins_parser)
|
||||
SimpleDialect 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("{ 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(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, "Variable name must precede \",\" in multiple assignment.", dialect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(builtins_analysis)
|
||||
|
@ -106,7 +106,6 @@ public:
|
||||
AsmAnalyzer analyzer(
|
||||
*m_analysisInfo,
|
||||
errorReporter,
|
||||
langutil::Error::Type::SyntaxError,
|
||||
m_dialect
|
||||
);
|
||||
if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty())
|
||||
|
Loading…
Reference in New Issue
Block a user