mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #5491 from ethereum/libyul-as-library
Libyul as static library (version 3 ;( )
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Until we have a clear separation, libyul has to be included here
|
||||
file(GLOB_RECURSE sources "*.cpp" "../libyul/*.cpp")
|
||||
file(GLOB_RECURSE headers "*.h" "../libyul/*.h")
|
||||
file(GLOB_RECURSE sources "*.cpp")
|
||||
file(GLOB_RECURSE headers "*.h")
|
||||
|
||||
find_package(Z3 QUIET)
|
||||
if (${Z3_FOUND})
|
||||
@@ -26,7 +26,7 @@ if (NOT (${Z3_FOUND} OR ${CVC4_FOUND}))
|
||||
endif()
|
||||
|
||||
add_library(solidity ${sources} ${headers})
|
||||
target_link_libraries(solidity PUBLIC evmasm langutil devcore ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY})
|
||||
target_link_libraries(solidity PUBLIC yul evmasm langutil devcore ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY})
|
||||
|
||||
if (${Z3_FOUND})
|
||||
target_link_libraries(solidity PUBLIC ${Z3_LIBRARY})
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
#include <libsolidity/analysis/ReferencesResolver.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolidity/analysis/ConstantEvaluator.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <libdevcore/StringUtils.h>
|
||||
|
||||
@@ -273,7 +273,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
||||
ErrorList errors;
|
||||
ErrorReporter errorsIgnored(errors);
|
||||
yul::ExternalIdentifierAccess::Resolver resolver =
|
||||
[&](assembly::Identifier const& _identifier, yul::IdentifierContext, bool _crossesFunctionBoundary) {
|
||||
[&](yul::Identifier const& _identifier, yul::IdentifierContext, bool _crossesFunctionBoundary) {
|
||||
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
|
||||
bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot");
|
||||
bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset");
|
||||
@@ -314,9 +314,9 @@ 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.
|
||||
assembly::AsmAnalysisInfo analysisInfo;
|
||||
yul::AsmAnalysisInfo analysisInfo;
|
||||
boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError;
|
||||
assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
|
||||
yul::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, yul::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <libdevcore/Algorithms.h>
|
||||
|
||||
@@ -954,7 +954,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
// External references have already been resolved in a prior stage and stored in the annotation.
|
||||
// We run the resolve step again regardless.
|
||||
yul::ExternalIdentifierAccess::Resolver identifierAccess = [&](
|
||||
assembly::Identifier const& _identifier,
|
||||
yul::Identifier const& _identifier,
|
||||
yul::IdentifierContext _context,
|
||||
bool
|
||||
)
|
||||
@@ -1039,13 +1039,13 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
return size_t(1);
|
||||
};
|
||||
solAssert(!_inlineAssembly.annotation().analysisInfo, "");
|
||||
_inlineAssembly.annotation().analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
|
||||
assembly::AsmAnalyzer analyzer(
|
||||
_inlineAssembly.annotation().analysisInfo = make_shared<yul::AsmAnalysisInfo>();
|
||||
yul::AsmAnalyzer analyzer(
|
||||
*_inlineAssembly.annotation().analysisInfo,
|
||||
m_errorReporter,
|
||||
m_evmVersion,
|
||||
Error::Type::SyntaxError,
|
||||
assembly::AsmFlavour::Loose,
|
||||
yul::AsmFlavour::Loose,
|
||||
identifierAccess
|
||||
);
|
||||
if (!analyzer.analyze(_inlineAssembly.operations()))
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
|
||||
#include <libevmasm/SemanticInformation.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
#include <libsolidity/ast/ExperimentalFeatures.h>
|
||||
#include <libyul/AsmData.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
@@ -40,48 +40,48 @@ public:
|
||||
explicit AssemblyViewPureChecker(std::function<void(StateMutability, SourceLocation const&)> _reportMutability):
|
||||
m_reportMutability(_reportMutability) {}
|
||||
|
||||
void operator()(assembly::Label const&) { }
|
||||
void operator()(assembly::Instruction const& _instruction)
|
||||
void operator()(yul::Label const&) { }
|
||||
void operator()(yul::Instruction const& _instruction)
|
||||
{
|
||||
checkInstruction(_instruction.location, _instruction.instruction);
|
||||
}
|
||||
void operator()(assembly::Literal const&) {}
|
||||
void operator()(assembly::Identifier const&) {}
|
||||
void operator()(assembly::FunctionalInstruction const& _instr)
|
||||
void operator()(yul::Literal const&) {}
|
||||
void operator()(yul::Identifier const&) {}
|
||||
void operator()(yul::FunctionalInstruction const& _instr)
|
||||
{
|
||||
checkInstruction(_instr.location, _instr.instruction);
|
||||
for (auto const& arg: _instr.arguments)
|
||||
boost::apply_visitor(*this, arg);
|
||||
}
|
||||
void operator()(assembly::ExpressionStatement const& _expr)
|
||||
void operator()(yul::ExpressionStatement const& _expr)
|
||||
{
|
||||
boost::apply_visitor(*this, _expr.expression);
|
||||
}
|
||||
void operator()(assembly::StackAssignment const&) {}
|
||||
void operator()(assembly::Assignment const& _assignment)
|
||||
void operator()(yul::StackAssignment const&) {}
|
||||
void operator()(yul::Assignment const& _assignment)
|
||||
{
|
||||
boost::apply_visitor(*this, *_assignment.value);
|
||||
}
|
||||
void operator()(assembly::VariableDeclaration const& _varDecl)
|
||||
void operator()(yul::VariableDeclaration const& _varDecl)
|
||||
{
|
||||
if (_varDecl.value)
|
||||
boost::apply_visitor(*this, *_varDecl.value);
|
||||
}
|
||||
void operator()(assembly::FunctionDefinition const& _funDef)
|
||||
void operator()(yul::FunctionDefinition const& _funDef)
|
||||
{
|
||||
(*this)(_funDef.body);
|
||||
}
|
||||
void operator()(assembly::FunctionCall const& _funCall)
|
||||
void operator()(yul::FunctionCall const& _funCall)
|
||||
{
|
||||
for (auto const& arg: _funCall.arguments)
|
||||
boost::apply_visitor(*this, arg);
|
||||
}
|
||||
void operator()(assembly::If const& _if)
|
||||
void operator()(yul::If const& _if)
|
||||
{
|
||||
boost::apply_visitor(*this, *_if.condition);
|
||||
(*this)(_if.body);
|
||||
}
|
||||
void operator()(assembly::Switch const& _switch)
|
||||
void operator()(yul::Switch const& _switch)
|
||||
{
|
||||
boost::apply_visitor(*this, *_switch.expression);
|
||||
for (auto const& _case: _switch.cases)
|
||||
@@ -91,14 +91,14 @@ public:
|
||||
(*this)(_case.body);
|
||||
}
|
||||
}
|
||||
void operator()(assembly::ForLoop const& _for)
|
||||
void operator()(yul::ForLoop const& _for)
|
||||
{
|
||||
(*this)(_for.pre);
|
||||
boost::apply_visitor(*this, *_for.condition);
|
||||
(*this)(_for.body);
|
||||
(*this)(_for.post);
|
||||
}
|
||||
void operator()(assembly::Block const& _block)
|
||||
void operator()(yul::Block const& _block)
|
||||
{
|
||||
for (auto const& s: _block.statements)
|
||||
boost::apply_visitor(*this, s);
|
||||
|
||||
@@ -41,6 +41,12 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace yul
|
||||
{
|
||||
// Forward-declaration to <yul/AsmData.h>
|
||||
struct Block;
|
||||
}
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
@@ -1028,12 +1034,6 @@ public:
|
||||
StatementAnnotation& annotation() const override;
|
||||
};
|
||||
|
||||
namespace assembly
|
||||
{
|
||||
// Forward-declaration to AsmData.h
|
||||
struct Block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline assembly.
|
||||
*/
|
||||
@@ -1043,18 +1043,18 @@ public:
|
||||
InlineAssembly(
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<ASTString> const& _docString,
|
||||
std::shared_ptr<assembly::Block> const& _operations
|
||||
std::shared_ptr<yul::Block> const& _operations
|
||||
):
|
||||
Statement(_location, _docString), m_operations(_operations) {}
|
||||
void accept(ASTVisitor& _visitor) override;
|
||||
void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
assembly::Block const& operations() const { return *m_operations; }
|
||||
yul::Block const& operations() const { return *m_operations; }
|
||||
|
||||
InlineAssemblyAnnotation& annotation() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<assembly::Block> m_operations;
|
||||
std::shared_ptr<yul::Block> m_operations;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
namespace yul
|
||||
{
|
||||
struct AsmAnalysisInfo;
|
||||
struct Identifier;
|
||||
}
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
@@ -120,12 +126,6 @@ struct StatementAnnotation: ASTAnnotation, DocumentedAnnotation
|
||||
{
|
||||
};
|
||||
|
||||
namespace assembly
|
||||
{
|
||||
struct AsmAnalysisInfo;
|
||||
struct Identifier;
|
||||
}
|
||||
|
||||
struct InlineAssemblyAnnotation: StatementAnnotation
|
||||
{
|
||||
struct ExternalIdentifierInfo
|
||||
@@ -137,9 +137,9 @@ struct InlineAssemblyAnnotation: StatementAnnotation
|
||||
};
|
||||
|
||||
/// Mapping containing resolved references to external identifiers and their value size
|
||||
std::map<assembly::Identifier const*, ExternalIdentifierInfo> externalReferences;
|
||||
std::map<yul::Identifier const*, ExternalIdentifierInfo> externalReferences;
|
||||
/// Information generated during analysis phase.
|
||||
std::shared_ptr<assembly::AsmAnalysisInfo> analysisInfo;
|
||||
std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo;
|
||||
};
|
||||
|
||||
struct ReturnAnnotation: StatementAnnotation
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <libdevcore/UTF8.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
#include <libsolidity/inlineasm/AsmPrinter.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/AsmPrinter.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace langutil;
|
||||
@@ -172,7 +172,7 @@ void ASTJsonConverter::appendExpressionAttributes(
|
||||
_attributes += exprAttributes;
|
||||
}
|
||||
|
||||
Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<assembly::Identifier const* ,InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const
|
||||
Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<yul::Identifier const* ,InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const
|
||||
{
|
||||
Json::Value tuple(Json::objectValue);
|
||||
tuple["src"] = sourceLocationToString(_info.first->location);
|
||||
@@ -465,7 +465,7 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node)
|
||||
}
|
||||
}
|
||||
setJsonNode(_node, "InlineAssembly", {
|
||||
make_pair("operations", Json::Value(assembly::AsmPrinter()(_node.operations()))),
|
||||
make_pair("operations", Json::Value(yul::AsmPrinter()(_node.operations()))),
|
||||
make_pair("externalReferences", std::move(externalReferences))
|
||||
});
|
||||
return false;
|
||||
|
||||
@@ -135,7 +135,7 @@ private:
|
||||
{
|
||||
return _node ? toJson(*_node) : Json::nullValue;
|
||||
}
|
||||
Json::Value inlineAssemblyIdentifierToJson(std::pair<assembly::Identifier const* , InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const;
|
||||
Json::Value inlineAssemblyIdentifierToJson(std::pair<yul::Identifier const* , InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const;
|
||||
static std::string location(VariableDeclaration::Location _location);
|
||||
static std::string contractKind(ContractDefinition::ContractKind _kind);
|
||||
static std::string functionCallKind(FunctionCallKind _kind);
|
||||
|
||||
@@ -25,15 +25,14 @@
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/codegen/Compiler.h>
|
||||
#include <libsolidity/interface/Version.h>
|
||||
#include <libsolidity/interface/SourceReferenceFormatter.h>
|
||||
#include <libyul/AsmParser.h>
|
||||
#include <libyul/AsmCodeGen.h>
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/YulString.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <libsolidity/interface/SourceReferenceFormatter.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <libsolidity/inlineasm/AsmParser.h>
|
||||
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
|
||||
#include <libyul/YulString.h>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
@@ -43,7 +42,7 @@
|
||||
// Change to "define" to output all intermediate code
|
||||
#undef SOL_OUTPUT_ASM
|
||||
#ifdef SOL_OUTPUT_ASM
|
||||
#include <libsolidity/inlineasm/AsmPrinter.h>
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -324,7 +323,7 @@ void CompilerContext::appendInlineAssembly(
|
||||
|
||||
yul::ExternalIdentifierAccess identifierAccess;
|
||||
identifierAccess.resolve = [&](
|
||||
assembly::Identifier const& _identifier,
|
||||
yul::Identifier const& _identifier,
|
||||
yul::IdentifierContext,
|
||||
bool
|
||||
)
|
||||
@@ -333,7 +332,7 @@ void CompilerContext::appendInlineAssembly(
|
||||
return it == _localVariables.end() ? size_t(-1) : 1;
|
||||
};
|
||||
identifierAccess.generateCode = [&](
|
||||
assembly::Identifier const& _identifier,
|
||||
yul::Identifier const& _identifier,
|
||||
yul::IdentifierContext _context,
|
||||
yul::AbstractAssembly& _assembly
|
||||
)
|
||||
@@ -362,19 +361,19 @@ void CompilerContext::appendInlineAssembly(
|
||||
ErrorList errors;
|
||||
ErrorReporter errorReporter(errors);
|
||||
auto scanner = make_shared<langutil::Scanner>(langutil::CharStream(_assembly), "--CODEGEN--");
|
||||
auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner, false);
|
||||
auto parserResult = yul::Parser(errorReporter, yul::AsmFlavour::Strict).parse(scanner, false);
|
||||
#ifdef SOL_OUTPUT_ASM
|
||||
cout << assembly::AsmPrinter()(*parserResult) << endl;
|
||||
cout << yul::AsmPrinter()(*parserResult) << endl;
|
||||
#endif
|
||||
assembly::AsmAnalysisInfo analysisInfo;
|
||||
yul::AsmAnalysisInfo analysisInfo;
|
||||
bool analyzerResult = false;
|
||||
if (parserResult)
|
||||
analyzerResult = assembly::AsmAnalyzer(
|
||||
analyzerResult = yul::AsmAnalyzer(
|
||||
analysisInfo,
|
||||
errorReporter,
|
||||
m_evmVersion,
|
||||
boost::none,
|
||||
assembly::AsmFlavour::Strict,
|
||||
yul::AsmFlavour::Strict,
|
||||
identifierAccess.resolve
|
||||
).analyze(*parserResult);
|
||||
if (!parserResult || !errorReporter.errors().empty() || !analyzerResult)
|
||||
@@ -396,7 +395,7 @@ void CompilerContext::appendInlineAssembly(
|
||||
}
|
||||
|
||||
solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block.");
|
||||
assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system);
|
||||
yul::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system);
|
||||
|
||||
// Reset the source location to the one of the node (instead of the CODEGEN source location)
|
||||
updateSourceLocation();
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/codegen/ContractCompiler.h>
|
||||
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <libsolidity/codegen/ExpressionCompiler.h>
|
||||
#include <libsolidity/codegen/CompilerUtils.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libyul/AsmCodeGen.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <libevmasm/Assembly.h>
|
||||
@@ -496,14 +496,14 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
||||
{
|
||||
unsigned startStackHeight = m_context.stackHeight();
|
||||
yul::ExternalIdentifierAccess identifierAccess;
|
||||
identifierAccess.resolve = [&](assembly::Identifier const& _identifier, yul::IdentifierContext, bool)
|
||||
identifierAccess.resolve = [&](yul::Identifier const& _identifier, yul::IdentifierContext, bool)
|
||||
{
|
||||
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
|
||||
if (ref == _inlineAssembly.annotation().externalReferences.end())
|
||||
return size_t(-1);
|
||||
return ref->second.valueSize;
|
||||
};
|
||||
identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, yul::IdentifierContext _context, yul::AbstractAssembly& _assembly)
|
||||
identifierAccess.generateCode = [&](yul::Identifier const& _identifier, yul::IdentifierContext _context, yul::AbstractAssembly& _assembly)
|
||||
{
|
||||
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
|
||||
solAssert(ref != _inlineAssembly.annotation().externalReferences.end(), "");
|
||||
@@ -615,7 +615,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
||||
}
|
||||
};
|
||||
solAssert(_inlineAssembly.annotation().analysisInfo, "");
|
||||
assembly::CodeGenerator::assemble(
|
||||
yul::CodeGenerator::assemble(
|
||||
_inlineAssembly.operations(),
|
||||
*_inlineAssembly.annotation().analysisInfo,
|
||||
m_context.nonConstAssembly(),
|
||||
|
||||
@@ -1,631 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Analyzer part of inline assembly.
|
||||
*/
|
||||
|
||||
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
#include <libsolidity/inlineasm/AsmScopeFiller.h>
|
||||
#include <libsolidity/inlineasm/AsmScope.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace langutil;
|
||||
using namespace dev::solidity;
|
||||
using namespace dev::solidity::assembly;
|
||||
|
||||
namespace {
|
||||
|
||||
set<string> const builtinTypes{"bool", "u8", "s8", "u32", "s32", "u64", "s64", "u128", "s128", "u256", "s256"};
|
||||
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::analyze(Block const& _block)
|
||||
{
|
||||
if (!(ScopeFiller(m_info, m_errorReporter))(_block))
|
||||
return false;
|
||||
|
||||
return (*this)(_block);
|
||||
}
|
||||
|
||||
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(solidity::Instruction::JUMPDEST, _label.location);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction)
|
||||
{
|
||||
checkLooseFeature(
|
||||
_instruction.location,
|
||||
"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;
|
||||
warnOnInstructions(_instruction.instruction, _instruction.location);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(assembly::Literal const& _literal)
|
||||
{
|
||||
expectValidType(_literal.type.str(), _literal.location);
|
||||
++m_stackHeight;
|
||||
if (_literal.kind == assembly::LiteralKind::String && _literal.value.str().size() > 32)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
_literal.location,
|
||||
"String literal too long (" + to_string(_literal.value.str().size()) + " > 32)"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
else if (_literal.kind == assembly::LiteralKind::Number && bigint(_literal.value.str()) > u256(-1))
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
_literal.location,
|
||||
"Number literal too large (> 256 bits)"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
else if (_literal.kind == assembly::LiteralKind::Boolean)
|
||||
{
|
||||
solAssert(m_flavour == AsmFlavour::Yul, "");
|
||||
solAssert(_literal.value == YulString{string("true")} || _literal.value == YulString{string("false")}, "");
|
||||
}
|
||||
m_info.stackHeightInfo[&_literal] = m_stackHeight;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier)
|
||||
{
|
||||
solAssert(!_identifier.name.empty(), "");
|
||||
size_t numErrorsBefore = m_errorReporter.errors().size();
|
||||
bool success = true;
|
||||
if (m_currentScope->lookup(_identifier.name, Scope::Visitor(
|
||||
[&](Scope::Variable const& _var)
|
||||
{
|
||||
if (!m_activeVariables.count(&_var))
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
_identifier.location,
|
||||
"Variable " + _identifier.name.str() + " used before it was declared."
|
||||
);
|
||||
success = false;
|
||||
}
|
||||
++m_stackHeight;
|
||||
},
|
||||
[&](Scope::Label const&)
|
||||
{
|
||||
++m_stackHeight;
|
||||
},
|
||||
[&](Scope::Function const&)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
_identifier.location,
|
||||
"Function " + _identifier.name.str() + " used without being called."
|
||||
);
|
||||
success = false;
|
||||
}
|
||||
)))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t stackSize(-1);
|
||||
if (m_resolver)
|
||||
{
|
||||
bool insideFunction = m_currentScope->insideFunction();
|
||||
stackSize = m_resolver(_identifier, yul::IdentifierContext::RValue, insideFunction);
|
||||
}
|
||||
if (stackSize == size_t(-1))
|
||||
{
|
||||
// Only add an error message if the callback did not do it.
|
||||
if (numErrorsBefore == m_errorReporter.errors().size())
|
||||
m_errorReporter.declarationError(_identifier.location, "Identifier not found.");
|
||||
success = false;
|
||||
}
|
||||
m_stackHeight += stackSize == size_t(-1) ? 1 : stackSize;
|
||||
}
|
||||
m_info.stackHeightInfo[&_identifier] = m_stackHeight;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
|
||||
{
|
||||
solAssert(m_flavour != AsmFlavour::Yul, "");
|
||||
bool success = true;
|
||||
for (auto const& arg: _instr.arguments | boost::adaptors::reversed)
|
||||
if (!expectExpression(arg))
|
||||
success = false;
|
||||
// Parser already checks that the number of arguments is correct.
|
||||
auto const& info = instructionInfo(_instr.instruction);
|
||||
solAssert(info.args == int(_instr.arguments.size()), "");
|
||||
m_stackHeight += info.ret - info.args;
|
||||
m_info.stackHeightInfo[&_instr] = m_stackHeight;
|
||||
warnOnInstructions(_instr.instruction, _instr.location);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement)
|
||||
{
|
||||
int initialStackHeight = m_stackHeight;
|
||||
bool success = boost::apply_visitor(*this, _statement.expression);
|
||||
if (m_stackHeight != initialStackHeight && (m_flavour != AsmFlavour::Loose || m_errorTypeForLoose))
|
||||
{
|
||||
Error::Type errorType = m_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_info.stackHeightInfo[&_statement] = m_stackHeight;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(assembly::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()(assembly::Assignment const& _assignment)
|
||||
{
|
||||
solAssert(_assignment.value, "");
|
||||
int const expectedItems = _assignment.variableNames.size();
|
||||
solAssert(expectedItems >= 1, "");
|
||||
int const stackHeight = m_stackHeight;
|
||||
bool success = boost::apply_visitor(*this, *_assignment.value);
|
||||
if ((m_stackHeight - stackHeight) != expectedItems)
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
_assignment.location,
|
||||
"Variable count does not match number of values (" +
|
||||
to_string(expectedItems) +
|
||||
" vs. " +
|
||||
to_string(m_stackHeight - stackHeight) +
|
||||
")"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
for (auto const& variableName: _assignment.variableNames)
|
||||
if (!checkAssignment(variableName, 1))
|
||||
success = false;
|
||||
m_info.stackHeightInfo[&_assignment] = m_stackHeight;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl)
|
||||
{
|
||||
bool success = true;
|
||||
int const numVariables = _varDecl.variables.size();
|
||||
if (_varDecl.value)
|
||||
{
|
||||
int const stackHeight = m_stackHeight;
|
||||
success = boost::apply_visitor(*this, *_varDecl.value);
|
||||
if ((m_stackHeight - stackHeight) != numVariables)
|
||||
{
|
||||
m_errorReporter.declarationError(_varDecl.location, "Variable count mismatch.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_stackHeight += numVariables;
|
||||
|
||||
for (auto const& variable: _varDecl.variables)
|
||||
{
|
||||
expectValidType(variable.type.str(), variable.location);
|
||||
m_activeVariables.insert(&boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name)));
|
||||
}
|
||||
m_info.stackHeightInfo[&_varDecl] = m_stackHeight;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef)
|
||||
{
|
||||
solAssert(!_funDef.name.empty(), "");
|
||||
Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get();
|
||||
solAssert(virtualBlock, "");
|
||||
Scope& varScope = scope(virtualBlock);
|
||||
for (auto const& var: _funDef.parameters + _funDef.returnVariables)
|
||||
{
|
||||
expectValidType(var.type.str(), var.location);
|
||||
m_activeVariables.insert(&boost::get<Scope::Variable>(varScope.identifiers.at(var.name)));
|
||||
}
|
||||
|
||||
int const stackHeight = m_stackHeight;
|
||||
m_stackHeight = _funDef.parameters.size() + _funDef.returnVariables.size();
|
||||
|
||||
bool success = (*this)(_funDef.body);
|
||||
|
||||
m_stackHeight = stackHeight;
|
||||
m_info.stackHeightInfo[&_funDef] = m_stackHeight;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall)
|
||||
{
|
||||
solAssert(!_funCall.functionName.name.empty(), "");
|
||||
bool success = true;
|
||||
size_t arguments = 0;
|
||||
size_t returns = 0;
|
||||
if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor(
|
||||
[&](Scope::Variable const&)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
_funCall.functionName.location,
|
||||
"Attempt to call variable instead of function."
|
||||
);
|
||||
success = false;
|
||||
},
|
||||
[&](Scope::Label const&)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
_funCall.functionName.location,
|
||||
"Attempt to call label instead of function."
|
||||
);
|
||||
success = false;
|
||||
},
|
||||
[&](Scope::Function const& _fun)
|
||||
{
|
||||
/// TODO: compare types too
|
||||
arguments = _fun.arguments.size();
|
||||
returns = _fun.returns.size();
|
||||
}
|
||||
)))
|
||||
{
|
||||
m_errorReporter.declarationError(_funCall.functionName.location, "Function not found.");
|
||||
success = false;
|
||||
}
|
||||
if (success)
|
||||
{
|
||||
if (_funCall.arguments.size() != arguments)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
_funCall.functionName.location,
|
||||
"Expected " + to_string(arguments) + " arguments but got " +
|
||||
to_string(_funCall.arguments.size()) + "."
|
||||
);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
for (auto const& arg: _funCall.arguments | boost::adaptors::reversed)
|
||||
if (!expectExpression(arg))
|
||||
success = false;
|
||||
m_stackHeight += int(returns) - int(arguments);
|
||||
m_info.stackHeightInfo[&_funCall] = m_stackHeight;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(If const& _if)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if (!expectExpression(*_if.condition))
|
||||
success = false;
|
||||
m_stackHeight--;
|
||||
|
||||
if (!(*this)(_if.body))
|
||||
success = false;
|
||||
|
||||
m_info.stackHeightInfo[&_if] = m_stackHeight;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(Switch const& _switch)
|
||||
{
|
||||
solAssert(_switch.expression, "");
|
||||
|
||||
bool success = true;
|
||||
|
||||
if (!expectExpression(*_switch.expression))
|
||||
success = false;
|
||||
|
||||
set<tuple<LiteralKind, YulString>> cases;
|
||||
for (auto const& _case: _switch.cases)
|
||||
{
|
||||
if (_case.value)
|
||||
{
|
||||
int const initialStackHeight = m_stackHeight;
|
||||
// We cannot use "expectExpression" here because *_case.value is not a
|
||||
// Statement and would be converted to a Statement otherwise.
|
||||
if (!(*this)(*_case.value))
|
||||
success = false;
|
||||
expectDeposit(1, initialStackHeight, _case.value->location);
|
||||
m_stackHeight--;
|
||||
|
||||
/// Note: the parser ensures there is only one default case
|
||||
auto val = make_tuple(_case.value->kind, _case.value->value);
|
||||
if (!cases.insert(val).second)
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
_case.location,
|
||||
"Duplicate case defined"
|
||||
);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(*this)(_case.body))
|
||||
success = false;
|
||||
}
|
||||
|
||||
m_stackHeight--;
|
||||
m_info.stackHeightInfo[&_switch] = m_stackHeight;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(assembly::ForLoop const& _for)
|
||||
{
|
||||
solAssert(_for.condition, "");
|
||||
|
||||
Scope* originalScope = m_currentScope;
|
||||
|
||||
bool success = true;
|
||||
if (!(*this)(_for.pre))
|
||||
success = false;
|
||||
// The block was closed already, but we re-open it again and stuff the
|
||||
// condition, the body and the post part inside.
|
||||
m_stackHeight += scope(&_for.pre).numberOfVariables();
|
||||
m_currentScope = &scope(&_for.pre);
|
||||
|
||||
if (!expectExpression(*_for.condition))
|
||||
success = false;
|
||||
m_stackHeight--;
|
||||
if (!(*this)(_for.body))
|
||||
success = false;
|
||||
if (!(*this)(_for.post))
|
||||
success = false;
|
||||
|
||||
m_stackHeight -= scope(&_for.pre).numberOfVariables();
|
||||
m_info.stackHeightInfo[&_for] = m_stackHeight;
|
||||
m_currentScope = originalScope;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(Block const& _block)
|
||||
{
|
||||
bool success = true;
|
||||
auto previousScope = m_currentScope;
|
||||
m_currentScope = &scope(&_block);
|
||||
|
||||
int const initialStackHeight = m_stackHeight;
|
||||
|
||||
for (auto const& s: _block.statements)
|
||||
if (!boost::apply_visitor(*this, s))
|
||||
success = false;
|
||||
|
||||
m_stackHeight -= scope(&_block).numberOfVariables();
|
||||
|
||||
int const stackDiff = m_stackHeight - initialStackHeight;
|
||||
if (stackDiff != 0)
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
_block.location,
|
||||
"Unbalanced stack at the end of a block: " +
|
||||
(
|
||||
stackDiff > 0 ?
|
||||
to_string(stackDiff) + string(" surplus item(s).") :
|
||||
to_string(-stackDiff) + string(" missing item(s).")
|
||||
)
|
||||
);
|
||||
success = false;
|
||||
}
|
||||
|
||||
m_info.stackHeightInfo[&_block] = m_stackHeight;
|
||||
m_currentScope = previousScope;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::expectExpression(Expression const& _expr)
|
||||
{
|
||||
bool success = true;
|
||||
int const initialHeight = m_stackHeight;
|
||||
if (!boost::apply_visitor(*this, _expr))
|
||||
success = false;
|
||||
if (!expectDeposit(1, initialHeight, locationOf(_expr)))
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location)
|
||||
{
|
||||
if (m_stackHeight - _oldHeight != _deposit)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
_location,
|
||||
"Expected expression to return one item to the stack, but did return " +
|
||||
to_string(m_stackHeight - _oldHeight) +
|
||||
" items."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t _valueSize)
|
||||
{
|
||||
solAssert(!_variable.name.empty(), "");
|
||||
bool success = true;
|
||||
size_t numErrorsBefore = m_errorReporter.errors().size();
|
||||
size_t variableSize(-1);
|
||||
if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name))
|
||||
{
|
||||
// Check that it is a variable
|
||||
if (var->type() != typeid(Scope::Variable))
|
||||
{
|
||||
m_errorReporter.typeError(_variable.location, "Assignment requires variable.");
|
||||
success = false;
|
||||
}
|
||||
else if (!m_activeVariables.count(&boost::get<Scope::Variable>(*var)))
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
_variable.location,
|
||||
"Variable " + _variable.name.str() + " used before it was declared."
|
||||
);
|
||||
success = false;
|
||||
}
|
||||
variableSize = 1;
|
||||
}
|
||||
else if (m_resolver)
|
||||
{
|
||||
bool insideFunction = m_currentScope->insideFunction();
|
||||
variableSize = m_resolver(_variable, yul::IdentifierContext::LValue, insideFunction);
|
||||
}
|
||||
if (variableSize == size_t(-1))
|
||||
{
|
||||
// Only add message if the callback did not.
|
||||
if (numErrorsBefore == m_errorReporter.errors().size())
|
||||
m_errorReporter.declarationError(_variable.location, "Variable not found or variable not lvalue.");
|
||||
success = false;
|
||||
}
|
||||
if (_valueSize == size_t(-1))
|
||||
_valueSize = variableSize == size_t(-1) ? 1 : variableSize;
|
||||
|
||||
m_stackHeight -= _valueSize;
|
||||
|
||||
if (_valueSize != variableSize && variableSize != size_t(-1))
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
_variable.location,
|
||||
"Variable size (" +
|
||||
to_string(variableSize) +
|
||||
") and value size (" +
|
||||
to_string(_valueSize) +
|
||||
") do not match."
|
||||
);
|
||||
success = false;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
Scope& AsmAnalyzer::scope(Block const* _block)
|
||||
{
|
||||
solAssert(m_info.scopes.count(_block) == 1, "Scope requested but not present.");
|
||||
auto scopePtr = m_info.scopes.at(_block);
|
||||
solAssert(scopePtr, "Scope requested but not present.");
|
||||
return *scopePtr;
|
||||
}
|
||||
void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location)
|
||||
{
|
||||
if (m_flavour != AsmFlavour::Yul)
|
||||
return;
|
||||
|
||||
if (!builtinTypes.count(type))
|
||||
m_errorReporter.typeError(
|
||||
_location,
|
||||
"\"" + type + "\" is not a valid type (user defined types are not yet supported)."
|
||||
);
|
||||
}
|
||||
|
||||
void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location)
|
||||
{
|
||||
// We assume that returndatacopy, returndatasize and staticcall are either all available
|
||||
// or all not available.
|
||||
solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), "");
|
||||
// Similarly we assume bitwise shifting and create2 go together.
|
||||
solAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), "");
|
||||
|
||||
if (_instr == solidity::Instruction::EXTCODEHASH)
|
||||
m_errorReporter.warning(
|
||||
_location,
|
||||
"The \"" +
|
||||
boost::to_lower_copy(instructionInfo(_instr).name)
|
||||
+ "\" instruction is not supported by the VM version \"" +
|
||||
"" + m_evmVersion.name() +
|
||||
"\" you are currently compiling for. " +
|
||||
"It will be interpreted as an invalid instruction on this VM."
|
||||
);
|
||||
else if ((
|
||||
_instr == solidity::Instruction::RETURNDATACOPY ||
|
||||
_instr == solidity::Instruction::RETURNDATASIZE ||
|
||||
_instr == solidity::Instruction::STATICCALL
|
||||
) && !m_evmVersion.supportsReturndata())
|
||||
m_errorReporter.warning(
|
||||
_location,
|
||||
"The \"" +
|
||||
boost::to_lower_copy(instructionInfo(_instr).name)
|
||||
+ "\" instruction is only available for Byzantium-compatible VMs. " +
|
||||
"You are currently compiling for \"" +
|
||||
m_evmVersion.name() +
|
||||
"\", where it will be interpreted as an invalid instruction."
|
||||
);
|
||||
else if ((
|
||||
_instr == solidity::Instruction::SHL ||
|
||||
_instr == solidity::Instruction::SHR ||
|
||||
_instr == solidity::Instruction::SAR ||
|
||||
_instr == solidity::Instruction::CREATE2
|
||||
) && !m_evmVersion.hasBitwiseShifting())
|
||||
m_errorReporter.warning(
|
||||
_location,
|
||||
"The \"" +
|
||||
boost::to_lower_copy(instructionInfo(_instr).name)
|
||||
+ "\" instruction is only available for Constantinople-compatible VMs. " +
|
||||
"You are currently compiling for \"" +
|
||||
m_evmVersion.name() +
|
||||
"\", where it will be interpreted as an invalid instruction."
|
||||
);
|
||||
|
||||
if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST)
|
||||
{
|
||||
solAssert(m_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."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
if (m_flavour != AsmFlavour::Loose)
|
||||
solAssert(false, _description);
|
||||
else if (m_errorTypeForLoose)
|
||||
m_errorReporter.error(*m_errorTypeForLoose, _location, _description);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Analysis part of inline assembly.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmScope.h>
|
||||
|
||||
#include <libyul/backends/evm/AbstractAssembly.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmDataForward.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace langutil
|
||||
{
|
||||
class ErrorReporter;
|
||||
struct SourceLocation;
|
||||
}
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
namespace assembly
|
||||
{
|
||||
|
||||
struct AsmAnalysisInfo;
|
||||
|
||||
/**
|
||||
* Performs the full analysis stage, calls the ScopeFiller internally, then resolves
|
||||
* references and performs other checks.
|
||||
* If all these checks pass, code generation should not throw errors.
|
||||
*/
|
||||
class AsmAnalyzer: public boost::static_visitor<bool>
|
||||
{
|
||||
public:
|
||||
explicit AsmAnalyzer(
|
||||
AsmAnalysisInfo& _analysisInfo,
|
||||
langutil::ErrorReporter& _errorReporter,
|
||||
EVMVersion _evmVersion,
|
||||
boost::optional<langutil::Error::Type> _errorTypeForLoose,
|
||||
AsmFlavour _flavour = AsmFlavour::Loose,
|
||||
yul::ExternalIdentifierAccess::Resolver const& _resolver = yul::ExternalIdentifierAccess::Resolver()
|
||||
):
|
||||
m_resolver(_resolver),
|
||||
m_info(_analysisInfo),
|
||||
m_errorReporter(_errorReporter),
|
||||
m_evmVersion(_evmVersion),
|
||||
m_flavour(_flavour),
|
||||
m_errorTypeForLoose(_errorTypeForLoose)
|
||||
{}
|
||||
|
||||
bool analyze(assembly::Block const& _block);
|
||||
|
||||
bool operator()(assembly::Instruction const&);
|
||||
bool operator()(assembly::Literal const& _literal);
|
||||
bool operator()(assembly::Identifier const&);
|
||||
bool operator()(assembly::FunctionalInstruction const& _functionalInstruction);
|
||||
bool operator()(assembly::Label const& _label);
|
||||
bool operator()(assembly::ExpressionStatement const&);
|
||||
bool operator()(assembly::StackAssignment const&);
|
||||
bool operator()(assembly::Assignment const& _assignment);
|
||||
bool operator()(assembly::VariableDeclaration const& _variableDeclaration);
|
||||
bool operator()(assembly::FunctionDefinition const& _functionDefinition);
|
||||
bool operator()(assembly::FunctionCall const& _functionCall);
|
||||
bool operator()(assembly::If const& _if);
|
||||
bool operator()(assembly::Switch const& _switch);
|
||||
bool operator()(assembly::ForLoop const& _forLoop);
|
||||
bool operator()(assembly::Block const& _block);
|
||||
|
||||
private:
|
||||
/// Visits the statement and expects it to deposit one item onto the stack.
|
||||
bool expectExpression(Expression const& _expr);
|
||||
bool expectDeposit(int _deposit, int _oldHeight, langutil::SourceLocation const& _location);
|
||||
|
||||
/// Verifies that a variable to be assigned to exists and has the same size
|
||||
/// as the value, @a _valueSize, unless that is equal to -1.
|
||||
bool checkAssignment(assembly::Identifier const& _assignment, size_t _valueSize = size_t(-1));
|
||||
|
||||
Scope& scope(assembly::Block const* _block);
|
||||
void expectValidType(std::string const& type, langutil::SourceLocation const& _location);
|
||||
void warnOnInstructions(solidity::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);
|
||||
|
||||
int m_stackHeight = 0;
|
||||
yul::ExternalIdentifierAccess::Resolver m_resolver;
|
||||
Scope* m_currentScope = nullptr;
|
||||
/// Variables that are active at the current point in assembly (as opposed to
|
||||
/// "part of the scope but not yet declared")
|
||||
std::set<Scope::Variable const*> m_activeVariables;
|
||||
AsmAnalysisInfo& m_info;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
EVMVersion m_evmVersion;
|
||||
AsmFlavour m_flavour = AsmFlavour::Loose;
|
||||
boost::optional<langutil::Error::Type> m_errorTypeForLoose;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Information generated during analyzer part of inline assembly.
|
||||
*/
|
||||
|
||||
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmScope.h>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Information generated during analyzer part of inline assembly.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/inlineasm/AsmDataForward.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
namespace assembly
|
||||
{
|
||||
|
||||
struct Scope;
|
||||
|
||||
struct AsmAnalysisInfo
|
||||
{
|
||||
using StackHeightInfo = std::map<void const*, int>;
|
||||
using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
|
||||
Scopes scopes;
|
||||
StackHeightInfo stackHeightInfo;
|
||||
/// Virtual blocks which will be used for scopes for function arguments and return values.
|
||||
std::map<FunctionDefinition const*, std::shared_ptr<assembly::Block const>> virtualBlocks;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2016
|
||||
* Code-generating part of inline assembly.
|
||||
*/
|
||||
|
||||
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmParser.h>
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
#include <libsolidity/inlineasm/AsmScope.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
|
||||
|
||||
#include <libevmasm/Assembly.h>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <libevmasm/Instruction.h>
|
||||
|
||||
#include <libyul/backends/evm/AbstractAssembly.h>
|
||||
#include <libyul/backends/evm/EVMCodeTransform.h>
|
||||
|
||||
#include <libdevcore/CommonIO.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
#include <boost/range/algorithm/count_if.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace langutil;
|
||||
using namespace dev::solidity;
|
||||
using namespace dev::solidity::assembly;
|
||||
|
||||
class EthAssemblyAdapter: public yul::AbstractAssembly
|
||||
{
|
||||
public:
|
||||
explicit EthAssemblyAdapter(eth::Assembly& _assembly):
|
||||
m_assembly(_assembly)
|
||||
{
|
||||
}
|
||||
virtual void setSourceLocation(SourceLocation const& _location) override
|
||||
{
|
||||
m_assembly.setSourceLocation(_location);
|
||||
}
|
||||
virtual int stackHeight() const override { return m_assembly.deposit(); }
|
||||
virtual void appendInstruction(solidity::Instruction _instruction) override
|
||||
{
|
||||
m_assembly.append(_instruction);
|
||||
}
|
||||
virtual void appendConstant(u256 const& _constant) override
|
||||
{
|
||||
m_assembly.append(_constant);
|
||||
}
|
||||
/// Append a label.
|
||||
virtual void appendLabel(LabelID _labelId) override
|
||||
{
|
||||
m_assembly.append(eth::AssemblyItem(eth::Tag, _labelId));
|
||||
}
|
||||
/// Append a label reference.
|
||||
virtual void appendLabelReference(LabelID _labelId) override
|
||||
{
|
||||
m_assembly.append(eth::AssemblyItem(eth::PushTag, _labelId));
|
||||
}
|
||||
virtual size_t newLabelId() override
|
||||
{
|
||||
return assemblyTagToIdentifier(m_assembly.newTag());
|
||||
}
|
||||
virtual size_t namedLabel(std::string const& _name) override
|
||||
{
|
||||
return assemblyTagToIdentifier(m_assembly.namedTag(_name));
|
||||
}
|
||||
virtual void appendLinkerSymbol(std::string const& _linkerSymbol) override
|
||||
{
|
||||
m_assembly.appendLibraryAddress(_linkerSymbol);
|
||||
}
|
||||
virtual void appendJump(int _stackDiffAfter) override
|
||||
{
|
||||
appendInstruction(solidity::Instruction::JUMP);
|
||||
m_assembly.adjustDeposit(_stackDiffAfter);
|
||||
}
|
||||
virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override
|
||||
{
|
||||
appendLabelReference(_labelId);
|
||||
appendJump(_stackDiffAfter);
|
||||
}
|
||||
virtual void appendJumpToIf(LabelID _labelId) override
|
||||
{
|
||||
appendLabelReference(_labelId);
|
||||
appendInstruction(solidity::Instruction::JUMPI);
|
||||
}
|
||||
virtual void appendBeginsub(LabelID, int) override
|
||||
{
|
||||
// TODO we could emulate that, though
|
||||
solAssert(false, "BEGINSUB not implemented for EVM 1.0");
|
||||
}
|
||||
/// Call a subroutine.
|
||||
virtual void appendJumpsub(LabelID, int, int) override
|
||||
{
|
||||
// TODO we could emulate that, though
|
||||
solAssert(false, "JUMPSUB not implemented for EVM 1.0");
|
||||
}
|
||||
|
||||
/// Return from a subroutine.
|
||||
virtual void appendReturnsub(int, int) override
|
||||
{
|
||||
// TODO we could emulate that, though
|
||||
solAssert(false, "RETURNSUB not implemented for EVM 1.0");
|
||||
}
|
||||
|
||||
virtual void appendAssemblySize() override
|
||||
{
|
||||
m_assembly.appendProgramSize();
|
||||
}
|
||||
|
||||
private:
|
||||
static LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag)
|
||||
{
|
||||
u256 id = _tag.data();
|
||||
solAssert(id <= std::numeric_limits<LabelID>::max(), "Tag id too large.");
|
||||
return LabelID(id);
|
||||
}
|
||||
|
||||
eth::Assembly& m_assembly;
|
||||
};
|
||||
|
||||
void assembly::CodeGenerator::assemble(
|
||||
Block const& _parsedData,
|
||||
AsmAnalysisInfo& _analysisInfo,
|
||||
eth::Assembly& _assembly,
|
||||
yul::ExternalIdentifierAccess const& _identifierAccess,
|
||||
bool _useNamedLabelsForFunctions
|
||||
)
|
||||
{
|
||||
EthAssemblyAdapter assemblyAdapter(_assembly);
|
||||
yul::CodeTransform(
|
||||
assemblyAdapter,
|
||||
_analysisInfo,
|
||||
false,
|
||||
false,
|
||||
_identifierAccess,
|
||||
_useNamedLabelsForFunctions
|
||||
)(_parsedData);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2016
|
||||
* Code-generating part of inline assembly.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace eth
|
||||
{
|
||||
class Assembly;
|
||||
}
|
||||
namespace solidity
|
||||
{
|
||||
namespace assembly
|
||||
{
|
||||
struct Block;
|
||||
|
||||
class CodeGenerator
|
||||
{
|
||||
public:
|
||||
/// Performs code generation and appends generated to _assembly.
|
||||
static void assemble(
|
||||
Block const& _parsedData,
|
||||
AsmAnalysisInfo& _analysisInfo,
|
||||
eth::Assembly& _assembly,
|
||||
yul::ExternalIdentifierAccess const& _identifierAccess = yul::ExternalIdentifierAccess(),
|
||||
bool _useNamedLabelsForFunctions = false
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2016
|
||||
* Parsed inline assembly to be used by the AST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/inlineasm/AsmDataForward.h>
|
||||
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
|
||||
#include <libyul/YulString.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
namespace assembly
|
||||
{
|
||||
|
||||
using YulString = dev::yul::YulString;
|
||||
using Type = YulString;
|
||||
|
||||
struct TypedName { langutil::SourceLocation location; YulString name; Type type; };
|
||||
using TypedNameList = std::vector<TypedName>;
|
||||
|
||||
/// Direct EVM instruction (except PUSHi and JUMPDEST)
|
||||
struct Instruction { langutil::SourceLocation location; solidity::Instruction instruction; };
|
||||
/// Literal number or string (up to 32 bytes)
|
||||
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.
|
||||
///
|
||||
/// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy
|
||||
/// a single stack slot and expects a single expression on the right hand returning
|
||||
/// the same amount of items as the number of variables.
|
||||
struct Assignment { langutil::SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Expression> value; };
|
||||
/// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))"
|
||||
struct FunctionalInstruction { langutil::SourceLocation location; solidity::Instruction instruction; std::vector<Expression> arguments; };
|
||||
struct FunctionCall { langutil::SourceLocation location; Identifier functionName; std::vector<Expression> arguments; };
|
||||
/// Statement that contains only a single expression
|
||||
struct ExpressionStatement { langutil::SourceLocation location; Expression expression; };
|
||||
/// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted
|
||||
struct VariableDeclaration { langutil::SourceLocation location; TypedNameList variables; std::shared_ptr<Expression> value; };
|
||||
/// Block that creates a scope (frees declared stack variables)
|
||||
struct Block { langutil::SourceLocation location; std::vector<Statement> statements; };
|
||||
/// Function definition ("function f(a, b) -> (d, e) { ... }")
|
||||
struct FunctionDefinition { langutil::SourceLocation location; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; };
|
||||
/// Conditional execution without "else" part.
|
||||
struct If { langutil::SourceLocation location; std::shared_ptr<Expression> condition; Block body; };
|
||||
/// Switch case or default case
|
||||
struct Case { langutil::SourceLocation location; std::shared_ptr<Literal> value; Block body; };
|
||||
/// Switch statement
|
||||
struct Switch { langutil::SourceLocation location; std::shared_ptr<Expression> expression; std::vector<Case> cases; };
|
||||
struct ForLoop { langutil::SourceLocation location; Block pre; std::shared_ptr<Expression> condition; Block post; Block body; };
|
||||
|
||||
struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
|
||||
{
|
||||
template <class T> langutil::SourceLocation operator()(T const& _node) const
|
||||
{
|
||||
return _node.location;
|
||||
}
|
||||
};
|
||||
|
||||
/// Extracts the source location from an inline assembly node.
|
||||
template <class T> inline langutil::SourceLocation locationOf(T const& _node)
|
||||
{
|
||||
return boost::apply_visitor(LocationExtractor(), _node);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2016
|
||||
* Forward declaration of classes for inline assembly / Yul AST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
namespace assembly
|
||||
{
|
||||
|
||||
struct Instruction;
|
||||
struct Literal;
|
||||
struct Label;
|
||||
struct StackAssignment;
|
||||
struct Identifier;
|
||||
struct Assignment;
|
||||
struct VariableDeclaration;
|
||||
struct FunctionalInstruction;
|
||||
struct FunctionDefinition;
|
||||
struct FunctionCall;
|
||||
struct If;
|
||||
struct Switch;
|
||||
struct Case;
|
||||
struct ForLoop;
|
||||
struct ExpressionStatement;
|
||||
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, Block>;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,617 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2016
|
||||
* Solidity inline assembly parser.
|
||||
*/
|
||||
|
||||
#include <libsolidity/inlineasm/AsmParser.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace langutil;
|
||||
using namespace dev::solidity;
|
||||
using namespace dev::solidity::assembly;
|
||||
|
||||
shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner, bool _reuseScanner)
|
||||
{
|
||||
m_recursionDepth = 0;
|
||||
try
|
||||
{
|
||||
m_scanner = _scanner;
|
||||
auto block = make_shared<Block>(parseBlock());
|
||||
if (!_reuseScanner)
|
||||
expectToken(Token::EOS);
|
||||
return block;
|
||||
}
|
||||
catch (FatalError const&)
|
||||
{
|
||||
if (m_errorReporter.errors().empty())
|
||||
throw; // Something is weird here, rather throw again.
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
assembly::Block Parser::parseBlock()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
assembly::Block block = createWithLocation<Block>();
|
||||
expectToken(Token::LBrace);
|
||||
while (currentToken() != Token::RBrace)
|
||||
block.statements.emplace_back(parseStatement());
|
||||
block.location.end = endPosition();
|
||||
advance();
|
||||
return block;
|
||||
}
|
||||
|
||||
assembly::Statement Parser::parseStatement()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
switch (currentToken())
|
||||
{
|
||||
case Token::Let:
|
||||
return parseVariableDeclaration();
|
||||
case Token::Function:
|
||||
return parseFunctionDefinition();
|
||||
case Token::LBrace:
|
||||
return parseBlock();
|
||||
case Token::If:
|
||||
{
|
||||
assembly::If _if = createWithLocation<assembly::If>();
|
||||
m_scanner->next();
|
||||
_if.condition = make_shared<Expression>(parseExpression());
|
||||
_if.body = parseBlock();
|
||||
return _if;
|
||||
}
|
||||
case Token::Switch:
|
||||
{
|
||||
assembly::Switch _switch = createWithLocation<assembly::Switch>();
|
||||
m_scanner->next();
|
||||
_switch.expression = make_shared<Expression>(parseExpression());
|
||||
while (m_scanner->currentToken() == Token::Case)
|
||||
_switch.cases.emplace_back(parseCase());
|
||||
if (m_scanner->currentToken() == Token::Default)
|
||||
_switch.cases.emplace_back(parseCase());
|
||||
if (m_scanner->currentToken() == Token::Default)
|
||||
fatalParserError("Only one default case allowed.");
|
||||
else if (m_scanner->currentToken() == Token::Case)
|
||||
fatalParserError("Case not allowed after default case.");
|
||||
if (_switch.cases.empty())
|
||||
fatalParserError("Switch statement without any cases.");
|
||||
_switch.location.end = _switch.cases.back().body.location.end;
|
||||
return _switch;
|
||||
}
|
||||
case Token::For:
|
||||
return parseForLoop();
|
||||
case Token::Assign:
|
||||
{
|
||||
if (m_flavour != AsmFlavour::Loose)
|
||||
break;
|
||||
assembly::StackAssignment assignment = createWithLocation<assembly::StackAssignment>();
|
||||
advance();
|
||||
expectToken(Token::Colon);
|
||||
assignment.variableName.location = location();
|
||||
assignment.variableName.name = YulString(currentLiteral());
|
||||
if (instructions().count(assignment.variableName.name.str()))
|
||||
fatalParserError("Identifier expected, got instruction name.");
|
||||
assignment.location.end = endPosition();
|
||||
expectToken(Token::Identifier);
|
||||
return assignment;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Options left:
|
||||
// Simple instruction (might turn into functional),
|
||||
// literal,
|
||||
// identifier (might turn into label or functional assignment)
|
||||
ElementaryOperation elementary(parseElementaryOperation());
|
||||
switch (currentToken())
|
||||
{
|
||||
case Token::LParen:
|
||||
{
|
||||
Expression expr = parseCall(std::move(elementary));
|
||||
return ExpressionStatement{locationOf(expr), expr};
|
||||
}
|
||||
case Token::Comma:
|
||||
{
|
||||
// if a comma follows, a multiple assignment is assumed
|
||||
|
||||
if (elementary.type() != typeid(assembly::Identifier))
|
||||
fatalParserError("Label name / variable name must precede \",\" (multiple assignment).");
|
||||
assembly::Identifier const& identifier = boost::get<assembly::Identifier>(elementary);
|
||||
|
||||
Assignment assignment = createWithLocation<Assignment>(identifier.location);
|
||||
assignment.variableNames.emplace_back(identifier);
|
||||
|
||||
do
|
||||
{
|
||||
expectToken(Token::Comma);
|
||||
elementary = parseElementaryOperation();
|
||||
if (elementary.type() != typeid(assembly::Identifier))
|
||||
fatalParserError("Variable name expected in multiple assignment.");
|
||||
assignment.variableNames.emplace_back(boost::get<assembly::Identifier>(elementary));
|
||||
}
|
||||
while (currentToken() == Token::Comma);
|
||||
|
||||
expectToken(Token::Colon);
|
||||
expectToken(Token::Assign);
|
||||
|
||||
assignment.value.reset(new Expression(parseExpression()));
|
||||
assignment.location.end = locationOf(*assignment.value).end;
|
||||
return assignment;
|
||||
}
|
||||
case Token::Colon:
|
||||
{
|
||||
if (elementary.type() != typeid(assembly::Identifier))
|
||||
fatalParserError("Label name / variable name must precede \":\".");
|
||||
assembly::Identifier const& identifier = boost::get<assembly::Identifier>(elementary);
|
||||
advance();
|
||||
// identifier:=: should be parsed as identifier: =: (i.e. a label),
|
||||
// while identifier:= (being followed by a non-colon) as identifier := (assignment).
|
||||
if (currentToken() == Token::Assign && peekNextToken() != Token::Colon)
|
||||
{
|
||||
assembly::Assignment assignment = createWithLocation<assembly::Assignment>(identifier.location);
|
||||
if (m_flavour != AsmFlavour::Yul && instructions().count(identifier.name.str()))
|
||||
fatalParserError("Cannot use instruction names for identifier names.");
|
||||
advance();
|
||||
assignment.variableNames.emplace_back(identifier);
|
||||
assignment.value.reset(new Expression(parseExpression()));
|
||||
assignment.location.end = locationOf(*assignment.value).end;
|
||||
return assignment;
|
||||
}
|
||||
else
|
||||
{
|
||||
// label
|
||||
if (m_flavour != AsmFlavour::Loose)
|
||||
fatalParserError("Labels are not supported.");
|
||||
Label label = createWithLocation<Label>(identifier.location);
|
||||
label.name = identifier.name;
|
||||
return label;
|
||||
}
|
||||
}
|
||||
default:
|
||||
if (m_flavour != AsmFlavour::Loose)
|
||||
fatalParserError("Call or assignment expected.");
|
||||
break;
|
||||
}
|
||||
if (elementary.type() == typeid(assembly::Identifier))
|
||||
{
|
||||
Expression expr = boost::get<assembly::Identifier>(elementary);
|
||||
return ExpressionStatement{locationOf(expr), expr};
|
||||
}
|
||||
else if (elementary.type() == typeid(assembly::Literal))
|
||||
{
|
||||
Expression expr = boost::get<assembly::Literal>(elementary);
|
||||
return ExpressionStatement{locationOf(expr), expr};
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(elementary.type() == typeid(assembly::Instruction), "Invalid elementary operation.");
|
||||
return boost::get<assembly::Instruction>(elementary);
|
||||
}
|
||||
}
|
||||
|
||||
assembly::Case Parser::parseCase()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
assembly::Case _case = createWithLocation<assembly::Case>();
|
||||
if (m_scanner->currentToken() == Token::Default)
|
||||
m_scanner->next();
|
||||
else if (m_scanner->currentToken() == Token::Case)
|
||||
{
|
||||
m_scanner->next();
|
||||
ElementaryOperation literal = parseElementaryOperation();
|
||||
if (literal.type() != typeid(assembly::Literal))
|
||||
fatalParserError("Literal expected.");
|
||||
_case.value = make_shared<Literal>(boost::get<assembly::Literal>(std::move(literal)));
|
||||
}
|
||||
else
|
||||
fatalParserError("Case or default case expected.");
|
||||
_case.body = parseBlock();
|
||||
_case.location.end = _case.body.location.end;
|
||||
return _case;
|
||||
}
|
||||
|
||||
assembly::ForLoop Parser::parseForLoop()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
ForLoop forLoop = createWithLocation<ForLoop>();
|
||||
expectToken(Token::For);
|
||||
forLoop.pre = parseBlock();
|
||||
forLoop.condition = make_shared<Expression>(parseExpression());
|
||||
forLoop.post = parseBlock();
|
||||
forLoop.body = parseBlock();
|
||||
forLoop.location.end = forLoop.body.location.end;
|
||||
return forLoop;
|
||||
}
|
||||
|
||||
assembly::Expression Parser::parseExpression()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
// In strict mode, this might parse a plain Instruction, but
|
||||
// it will be converted to a FunctionalInstruction inside
|
||||
// parseCall below.
|
||||
ElementaryOperation operation = parseElementaryOperation();
|
||||
if (operation.type() == typeid(Instruction))
|
||||
{
|
||||
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_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_flavour == AsmFlavour::Loose, "");
|
||||
Instruction& instr = boost::get<Instruction>(operation);
|
||||
return FunctionalInstruction{std::move(instr.location), instr.instruction, {}};
|
||||
}
|
||||
else if (operation.type() == typeid(assembly::Identifier))
|
||||
return boost::get<assembly::Identifier>(operation);
|
||||
else
|
||||
{
|
||||
solAssert(operation.type() == typeid(assembly::Literal), "");
|
||||
return boost::get<assembly::Literal>(operation);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<string, dev::solidity::Instruction> const& Parser::instructions()
|
||||
{
|
||||
// Allowed instructions, lowercase names.
|
||||
static map<string, dev::solidity::Instruction> s_instructions;
|
||||
if (s_instructions.empty())
|
||||
{
|
||||
for (auto const& instruction: solidity::c_instructions)
|
||||
{
|
||||
if (
|
||||
instruction.second == solidity::Instruction::JUMPDEST ||
|
||||
solidity::isPushInstruction(instruction.second)
|
||||
)
|
||||
continue;
|
||||
string name = instruction.first;
|
||||
transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); });
|
||||
s_instructions[name] = instruction.second;
|
||||
}
|
||||
}
|
||||
return s_instructions;
|
||||
}
|
||||
|
||||
std::map<dev::solidity::Instruction, string> const& Parser::instructionNames()
|
||||
{
|
||||
static map<dev::solidity::Instruction, string> s_instructionNames;
|
||||
if (s_instructionNames.empty())
|
||||
{
|
||||
for (auto const& instr: instructions())
|
||||
s_instructionNames[instr.second] = instr.first;
|
||||
// set the ambiguous instructions to a clear default
|
||||
s_instructionNames[solidity::Instruction::SELFDESTRUCT] = "selfdestruct";
|
||||
s_instructionNames[solidity::Instruction::KECCAK256] = "keccak256";
|
||||
}
|
||||
return s_instructionNames;
|
||||
}
|
||||
|
||||
Parser::ElementaryOperation Parser::parseElementaryOperation()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
ElementaryOperation ret;
|
||||
switch (currentToken())
|
||||
{
|
||||
case Token::Identifier:
|
||||
case Token::Return:
|
||||
case Token::Byte:
|
||||
case Token::Address:
|
||||
{
|
||||
string literal;
|
||||
if (currentToken() == Token::Return)
|
||||
literal = "return";
|
||||
else if (currentToken() == Token::Byte)
|
||||
literal = "byte";
|
||||
else if (currentToken() == Token::Address)
|
||||
literal = "address";
|
||||
else
|
||||
literal = currentLiteral();
|
||||
// first search the set of instructions.
|
||||
if (m_flavour != AsmFlavour::Yul && instructions().count(literal))
|
||||
{
|
||||
dev::solidity::Instruction const& instr = instructions().at(literal);
|
||||
ret = Instruction{location(), instr};
|
||||
}
|
||||
else
|
||||
ret = Identifier{location(), YulString{literal}};
|
||||
advance();
|
||||
break;
|
||||
}
|
||||
case Token::StringLiteral:
|
||||
case Token::Number:
|
||||
case Token::TrueLiteral:
|
||||
case Token::FalseLiteral:
|
||||
{
|
||||
LiteralKind kind = LiteralKind::Number;
|
||||
switch (currentToken())
|
||||
{
|
||||
case Token::StringLiteral:
|
||||
kind = LiteralKind::String;
|
||||
break;
|
||||
case Token::Number:
|
||||
if (!isValidNumberLiteral(currentLiteral()))
|
||||
fatalParserError("Invalid number literal.");
|
||||
kind = LiteralKind::Number;
|
||||
break;
|
||||
case Token::TrueLiteral:
|
||||
case Token::FalseLiteral:
|
||||
kind = LiteralKind::Boolean;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Literal literal{
|
||||
location(),
|
||||
kind,
|
||||
YulString{currentLiteral()},
|
||||
{}
|
||||
};
|
||||
advance();
|
||||
if (m_flavour == AsmFlavour::Yul)
|
||||
{
|
||||
expectToken(Token::Colon);
|
||||
literal.location.end = endPosition();
|
||||
literal.type = YulString{expectAsmIdentifier()};
|
||||
}
|
||||
else if (kind == LiteralKind::Boolean)
|
||||
fatalParserError("True and false are not valid literals.");
|
||||
ret = std::move(literal);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fatalParserError(
|
||||
m_flavour == AsmFlavour::Yul ?
|
||||
"Literal or identifier expected." :
|
||||
"Literal, identifier or instruction expected."
|
||||
);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
assembly::VariableDeclaration Parser::parseVariableDeclaration()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
|
||||
expectToken(Token::Let);
|
||||
while (true)
|
||||
{
|
||||
varDecl.variables.emplace_back(parseTypedName());
|
||||
if (currentToken() == Token::Comma)
|
||||
expectToken(Token::Comma);
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (currentToken() == Token::Colon)
|
||||
{
|
||||
expectToken(Token::Colon);
|
||||
expectToken(Token::Assign);
|
||||
varDecl.value.reset(new Expression(parseExpression()));
|
||||
varDecl.location.end = locationOf(*varDecl.value).end;
|
||||
}
|
||||
else
|
||||
varDecl.location.end = varDecl.variables.back().location.end;
|
||||
return varDecl;
|
||||
}
|
||||
|
||||
assembly::FunctionDefinition Parser::parseFunctionDefinition()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
|
||||
expectToken(Token::Function);
|
||||
funDef.name = YulString{expectAsmIdentifier()};
|
||||
expectToken(Token::LParen);
|
||||
while (currentToken() != Token::RParen)
|
||||
{
|
||||
funDef.parameters.emplace_back(parseTypedName());
|
||||
if (currentToken() == Token::RParen)
|
||||
break;
|
||||
expectToken(Token::Comma);
|
||||
}
|
||||
expectToken(Token::RParen);
|
||||
if (currentToken() == Token::Sub)
|
||||
{
|
||||
expectToken(Token::Sub);
|
||||
expectToken(Token::GreaterThan);
|
||||
while (true)
|
||||
{
|
||||
funDef.returnVariables.emplace_back(parseTypedName());
|
||||
if (currentToken() == Token::LBrace)
|
||||
break;
|
||||
expectToken(Token::Comma);
|
||||
}
|
||||
}
|
||||
funDef.body = parseBlock();
|
||||
funDef.location.end = funDef.body.location.end;
|
||||
return funDef;
|
||||
}
|
||||
|
||||
assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
if (_initialOp.type() == typeid(Instruction))
|
||||
{
|
||||
solAssert(m_flavour != AsmFlavour::Yul, "Instructions are invalid in Yul");
|
||||
Instruction& instruction = boost::get<Instruction>(_initialOp);
|
||||
FunctionalInstruction ret;
|
||||
ret.instruction = instruction.instruction;
|
||||
ret.location = std::move(instruction.location);
|
||||
solidity::Instruction instr = ret.instruction;
|
||||
InstructionInfo instrInfo = instructionInfo(instr);
|
||||
if (solidity::isDupInstruction(instr))
|
||||
fatalParserError("DUPi instructions not allowed for functional notation");
|
||||
if (solidity::isSwapInstruction(instr))
|
||||
fatalParserError("SWAPi instructions not allowed for functional notation");
|
||||
expectToken(Token::LParen);
|
||||
unsigned args = unsigned(instrInfo.args);
|
||||
for (unsigned i = 0; i < args; ++i)
|
||||
{
|
||||
/// check for premature closing parentheses
|
||||
if (currentToken() == Token::RParen)
|
||||
fatalParserError(string(
|
||||
"Expected expression (instruction \"" +
|
||||
instructionNames().at(instr) +
|
||||
"\" expects " +
|
||||
to_string(args) +
|
||||
" arguments)"
|
||||
));
|
||||
|
||||
ret.arguments.emplace_back(parseExpression());
|
||||
if (i != args - 1)
|
||||
{
|
||||
if (currentToken() != Token::Comma)
|
||||
fatalParserError(string(
|
||||
"Expected ',' (instruction \"" +
|
||||
instructionNames().at(instr) +
|
||||
"\" expects " +
|
||||
to_string(args) +
|
||||
" arguments)"
|
||||
));
|
||||
else
|
||||
advance();
|
||||
}
|
||||
}
|
||||
ret.location.end = endPosition();
|
||||
if (currentToken() == Token::Comma)
|
||||
fatalParserError(string(
|
||||
"Expected ')' (instruction \"" +
|
||||
instructionNames().at(instr) +
|
||||
"\" expects " +
|
||||
to_string(args) +
|
||||
" arguments)"
|
||||
));
|
||||
expectToken(Token::RParen);
|
||||
return ret;
|
||||
}
|
||||
else if (_initialOp.type() == typeid(Identifier))
|
||||
{
|
||||
FunctionCall ret;
|
||||
ret.functionName = std::move(boost::get<Identifier>(_initialOp));
|
||||
ret.location = ret.functionName.location;
|
||||
expectToken(Token::LParen);
|
||||
while (currentToken() != Token::RParen)
|
||||
{
|
||||
ret.arguments.emplace_back(parseExpression());
|
||||
if (currentToken() == Token::RParen)
|
||||
break;
|
||||
expectToken(Token::Comma);
|
||||
}
|
||||
ret.location.end = endPosition();
|
||||
expectToken(Token::RParen);
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
fatalParserError(
|
||||
m_flavour == AsmFlavour::Yul ?
|
||||
"Function name expected." :
|
||||
"Assembly instruction or function name required in front of \"(\")"
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
TypedName Parser::parseTypedName()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
TypedName typedName = createWithLocation<TypedName>();
|
||||
typedName.name = YulString{expectAsmIdentifier()};
|
||||
if (m_flavour == AsmFlavour::Yul)
|
||||
{
|
||||
expectToken(Token::Colon);
|
||||
typedName.location.end = endPosition();
|
||||
typedName.type = YulString{expectAsmIdentifier()};
|
||||
}
|
||||
return typedName;
|
||||
}
|
||||
|
||||
string Parser::expectAsmIdentifier()
|
||||
{
|
||||
string name = currentLiteral();
|
||||
if (m_flavour == AsmFlavour::Yul)
|
||||
{
|
||||
switch (currentToken())
|
||||
{
|
||||
case Token::Return:
|
||||
case Token::Byte:
|
||||
case Token::Address:
|
||||
case Token::Bool:
|
||||
advance();
|
||||
return name;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (instructions().count(name))
|
||||
fatalParserError("Cannot use instruction names for identifier names.");
|
||||
expectToken(Token::Identifier);
|
||||
return name;
|
||||
}
|
||||
|
||||
bool Parser::isValidNumberLiteral(string const& _literal)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to convert _literal to u256.
|
||||
auto tmp = u256(_literal);
|
||||
(void) tmp;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (boost::starts_with(_literal, "0x"))
|
||||
return true;
|
||||
else
|
||||
return _literal.find_first_not_of("0123456789") == string::npos;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2016
|
||||
* Solidity inline assembly parser.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <liblangutil/ParserBase.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
namespace assembly
|
||||
{
|
||||
|
||||
class Parser: public langutil::ParserBase
|
||||
{
|
||||
public:
|
||||
explicit Parser(langutil::ErrorReporter& _errorReporter, AsmFlavour _flavour = AsmFlavour::Loose):
|
||||
ParserBase(_errorReporter), m_flavour(_flavour) {}
|
||||
|
||||
/// Parses an inline assembly block starting with `{` and ending with `}`.
|
||||
/// @param _reuseScanner if true, do check for end of input after the `}`.
|
||||
/// @returns an empty shared pointer on error.
|
||||
std::shared_ptr<Block> parse(std::shared_ptr<langutil::Scanner> const& _scanner, bool _reuseScanner);
|
||||
|
||||
protected:
|
||||
using ElementaryOperation = boost::variant<assembly::Instruction, assembly::Literal, assembly::Identifier>;
|
||||
|
||||
/// Creates an inline assembly node with the given source location.
|
||||
template <class T> T createWithLocation(langutil::SourceLocation const& _loc = {}) const
|
||||
{
|
||||
T r;
|
||||
r.location = _loc;
|
||||
if (r.location.isEmpty())
|
||||
{
|
||||
r.location.start = position();
|
||||
r.location.end = endPosition();
|
||||
}
|
||||
if (!r.location.sourceName)
|
||||
r.location.sourceName = sourceName();
|
||||
return r;
|
||||
}
|
||||
langutil::SourceLocation location() const { return {position(), endPosition(), sourceName()}; }
|
||||
|
||||
Block parseBlock();
|
||||
Statement parseStatement();
|
||||
Case parseCase();
|
||||
ForLoop parseForLoop();
|
||||
/// Parses a functional expression that has to push exactly one stack element
|
||||
assembly::Expression parseExpression();
|
||||
static std::map<std::string, dev::solidity::Instruction> const& instructions();
|
||||
static std::map<dev::solidity::Instruction, std::string> const& instructionNames();
|
||||
/// Parses an elementary operation, i.e. a literal, identifier or instruction.
|
||||
/// This will parse instructions even in strict mode as part of the full parser
|
||||
/// for FunctionalInstruction.
|
||||
ElementaryOperation parseElementaryOperation();
|
||||
VariableDeclaration parseVariableDeclaration();
|
||||
FunctionDefinition parseFunctionDefinition();
|
||||
assembly::Expression parseCall(ElementaryOperation&& _initialOp);
|
||||
TypedName parseTypedName();
|
||||
std::string expectAsmIdentifier();
|
||||
|
||||
static bool isValidNumberLiteral(std::string const& _literal);
|
||||
|
||||
private:
|
||||
AsmFlavour m_flavour = AsmFlavour::Loose;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2017
|
||||
* Converts a parsed assembly into its textual form.
|
||||
*/
|
||||
|
||||
#include <libsolidity/inlineasm/AsmPrinter.h>
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
using namespace dev::solidity::assembly;
|
||||
|
||||
//@TODO source locations
|
||||
|
||||
string AsmPrinter::operator()(assembly::Instruction const& _instruction)
|
||||
{
|
||||
solAssert(!m_yul, "");
|
||||
solAssert(isValidInstruction(_instruction.instruction), "Invalid instruction");
|
||||
return boost::to_lower_copy(instructionInfo(_instruction.instruction).name);
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(assembly::Literal const& _literal)
|
||||
{
|
||||
switch (_literal.kind)
|
||||
{
|
||||
case LiteralKind::Number:
|
||||
solAssert(isValidDecimal(_literal.value.str()) || isValidHex(_literal.value.str()), "Invalid number literal");
|
||||
return _literal.value.str() + appendTypeName(_literal.type);
|
||||
case LiteralKind::Boolean:
|
||||
solAssert(_literal.value.str() == "true" || _literal.value.str() == "false", "Invalid bool literal.");
|
||||
return ((_literal.value.str() == "true") ? "true" : "false") + appendTypeName(_literal.type);
|
||||
case LiteralKind::String:
|
||||
break;
|
||||
}
|
||||
|
||||
string out;
|
||||
for (char c: _literal.value.str())
|
||||
if (c == '\\')
|
||||
out += "\\\\";
|
||||
else if (c == '"')
|
||||
out += "\\\"";
|
||||
else if (c == '\b')
|
||||
out += "\\b";
|
||||
else if (c == '\f')
|
||||
out += "\\f";
|
||||
else if (c == '\n')
|
||||
out += "\\n";
|
||||
else if (c == '\r')
|
||||
out += "\\r";
|
||||
else if (c == '\t')
|
||||
out += "\\t";
|
||||
else if (c == '\v')
|
||||
out += "\\v";
|
||||
else if (!isprint(c, locale::classic()))
|
||||
{
|
||||
ostringstream o;
|
||||
o << std::hex << setfill('0') << setw(2) << (unsigned)(unsigned char)(c);
|
||||
out += "\\x" + o.str();
|
||||
}
|
||||
else
|
||||
out += c;
|
||||
return "\"" + out + "\"" + appendTypeName(_literal.type);
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(assembly::Identifier const& _identifier)
|
||||
{
|
||||
solAssert(!_identifier.name.empty(), "Invalid identifier.");
|
||||
return _identifier.name.str();
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functionalInstruction)
|
||||
{
|
||||
solAssert(!m_yul, "");
|
||||
solAssert(isValidInstruction(_functionalInstruction.instruction), "Invalid instruction");
|
||||
return
|
||||
boost::to_lower_copy(instructionInfo(_functionalInstruction.instruction).name) +
|
||||
"(" +
|
||||
boost::algorithm::join(
|
||||
_functionalInstruction.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)),
|
||||
", ") +
|
||||
")";
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(ExpressionStatement const& _statement)
|
||||
{
|
||||
return boost::apply_visitor(*this, _statement.expression);
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(assembly::Label const& _label)
|
||||
{
|
||||
solAssert(!m_yul, "");
|
||||
solAssert(!_label.name.empty(), "Invalid label.");
|
||||
return _label.name.str() + ":";
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(assembly::StackAssignment const& _assignment)
|
||||
{
|
||||
solAssert(!m_yul, "");
|
||||
solAssert(!_assignment.variableName.name.empty(), "Invalid variable name.");
|
||||
return "=: " + (*this)(_assignment.variableName);
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(assembly::Assignment const& _assignment)
|
||||
{
|
||||
solAssert(_assignment.variableNames.size() >= 1, "");
|
||||
string variables = (*this)(_assignment.variableNames.front());
|
||||
for (size_t i = 1; i < _assignment.variableNames.size(); ++i)
|
||||
variables += ", " + (*this)(_assignment.variableNames[i]);
|
||||
return variables + " := " + boost::apply_visitor(*this, *_assignment.value);
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDeclaration)
|
||||
{
|
||||
string out = "let ";
|
||||
out += boost::algorithm::join(
|
||||
_variableDeclaration.variables | boost::adaptors::transformed(
|
||||
[this](TypedName argument) { return formatTypedName(argument); }
|
||||
),
|
||||
", "
|
||||
);
|
||||
if (_variableDeclaration.value)
|
||||
{
|
||||
out += " := ";
|
||||
out += boost::apply_visitor(*this, *_variableDeclaration.value);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition)
|
||||
{
|
||||
solAssert(!_functionDefinition.name.empty(), "Invalid function name.");
|
||||
string out = "function " + _functionDefinition.name.str() + "(";
|
||||
out += boost::algorithm::join(
|
||||
_functionDefinition.parameters | boost::adaptors::transformed(
|
||||
[this](TypedName argument) { return formatTypedName(argument); }
|
||||
),
|
||||
", "
|
||||
);
|
||||
out += ")";
|
||||
if (!_functionDefinition.returnVariables.empty())
|
||||
{
|
||||
out += " -> ";
|
||||
out += boost::algorithm::join(
|
||||
_functionDefinition.returnVariables | boost::adaptors::transformed(
|
||||
[this](TypedName argument) { return formatTypedName(argument); }
|
||||
),
|
||||
", "
|
||||
);
|
||||
}
|
||||
|
||||
return out + "\n" + (*this)(_functionDefinition.body);
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(assembly::FunctionCall const& _functionCall)
|
||||
{
|
||||
return
|
||||
(*this)(_functionCall.functionName) + "(" +
|
||||
boost::algorithm::join(
|
||||
_functionCall.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)),
|
||||
", " ) +
|
||||
")";
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(If const& _if)
|
||||
{
|
||||
solAssert(_if.condition, "Invalid if condition.");
|
||||
return "if " + boost::apply_visitor(*this, *_if.condition) + "\n" + (*this)(_if.body);
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(Switch const& _switch)
|
||||
{
|
||||
solAssert(_switch.expression, "Invalid expression pointer.");
|
||||
string out = "switch " + boost::apply_visitor(*this, *_switch.expression);
|
||||
for (auto const& _case: _switch.cases)
|
||||
{
|
||||
if (!_case.value)
|
||||
out += "\ndefault ";
|
||||
else
|
||||
out += "\ncase " + (*this)(*_case.value) + " ";
|
||||
out += (*this)(_case.body);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(assembly::ForLoop const& _forLoop)
|
||||
{
|
||||
solAssert(_forLoop.condition, "Invalid for loop condition.");
|
||||
string out = "for ";
|
||||
out += (*this)(_forLoop.pre);
|
||||
out += "\n";
|
||||
out += boost::apply_visitor(*this, *_forLoop.condition);
|
||||
out += "\n";
|
||||
out += (*this)(_forLoop.post);
|
||||
out += "\n";
|
||||
out += (*this)(_forLoop.body);
|
||||
return out;
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(Block const& _block)
|
||||
{
|
||||
if (_block.statements.empty())
|
||||
return "{\n}";
|
||||
string body = boost::algorithm::join(
|
||||
_block.statements | boost::adaptors::transformed(boost::apply_visitor(*this)),
|
||||
"\n"
|
||||
);
|
||||
boost::replace_all(body, "\n", "\n ");
|
||||
return "{\n " + body + "\n}";
|
||||
}
|
||||
|
||||
string AsmPrinter::formatTypedName(TypedName _variable) const
|
||||
{
|
||||
solAssert(!_variable.name.empty(), "Invalid variable name.");
|
||||
return _variable.name.str() + appendTypeName(_variable.type);
|
||||
}
|
||||
|
||||
string AsmPrinter::appendTypeName(YulString _type) const
|
||||
{
|
||||
if (m_yul)
|
||||
return ":" + _type.str();
|
||||
return "";
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2017
|
||||
* Converts a parsed assembly into its textual form.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/inlineasm/AsmDataForward.h>
|
||||
|
||||
#include <libyul/YulString.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
namespace assembly
|
||||
{
|
||||
|
||||
class AsmPrinter: public boost::static_visitor<std::string>
|
||||
{
|
||||
public:
|
||||
explicit AsmPrinter(bool _yul = false): m_yul(_yul) {}
|
||||
|
||||
std::string operator()(assembly::Instruction const& _instruction);
|
||||
std::string operator()(assembly::Literal const& _literal);
|
||||
std::string operator()(assembly::Identifier const& _identifier);
|
||||
std::string operator()(assembly::FunctionalInstruction const& _functionalInstruction);
|
||||
std::string operator()(assembly::ExpressionStatement const& _expr);
|
||||
std::string operator()(assembly::Label const& _label);
|
||||
std::string operator()(assembly::StackAssignment const& _assignment);
|
||||
std::string operator()(assembly::Assignment const& _assignment);
|
||||
std::string operator()(assembly::VariableDeclaration const& _variableDeclaration);
|
||||
std::string operator()(assembly::FunctionDefinition const& _functionDefinition);
|
||||
std::string operator()(assembly::FunctionCall const& _functionCall);
|
||||
std::string operator()(assembly::If const& _if);
|
||||
std::string operator()(assembly::Switch const& _switch);
|
||||
std::string operator()(assembly::ForLoop const& _forLoop);
|
||||
std::string operator()(assembly::Block const& _block);
|
||||
|
||||
private:
|
||||
std::string formatTypedName(TypedName _variable) const;
|
||||
std::string appendTypeName(yul::YulString _type) const;
|
||||
|
||||
bool m_yul = false;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Scopes for identifiers.
|
||||
*/
|
||||
|
||||
#include <libsolidity/inlineasm/AsmScope.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity::assembly;
|
||||
|
||||
bool Scope::registerLabel(yul::YulString _name)
|
||||
{
|
||||
if (exists(_name))
|
||||
return false;
|
||||
identifiers[_name] = Label();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Scope::registerVariable(yul::YulString _name, YulType const& _type)
|
||||
{
|
||||
if (exists(_name))
|
||||
return false;
|
||||
Variable variable;
|
||||
variable.type = _type;
|
||||
identifiers[_name] = variable;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Scope::registerFunction(yul::YulString _name, std::vector<YulType> const& _arguments, std::vector<YulType> const& _returns)
|
||||
{
|
||||
if (exists(_name))
|
||||
return false;
|
||||
identifiers[_name] = Function{_arguments, _returns};
|
||||
return true;
|
||||
}
|
||||
|
||||
Scope::Identifier* Scope::lookup(yul::YulString _name)
|
||||
{
|
||||
bool crossedFunctionBoundary = false;
|
||||
for (Scope* s = this; s; s = s->superScope)
|
||||
{
|
||||
auto id = s->identifiers.find(_name);
|
||||
if (id != s->identifiers.end())
|
||||
{
|
||||
if (crossedFunctionBoundary && id->second.type() == typeid(Scope::Variable))
|
||||
return nullptr;
|
||||
else
|
||||
return &id->second;
|
||||
}
|
||||
|
||||
if (s->functionScope)
|
||||
crossedFunctionBoundary = true;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Scope::exists(yul::YulString _name) const
|
||||
{
|
||||
if (identifiers.count(_name))
|
||||
return true;
|
||||
else if (superScope)
|
||||
return superScope->exists(_name);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t Scope::numberOfVariables() const
|
||||
{
|
||||
size_t count = 0;
|
||||
for (auto const& identifier: identifiers)
|
||||
if (identifier.second.type() == typeid(Scope::Variable))
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
bool Scope::insideFunction() const
|
||||
{
|
||||
for (Scope const* s = this; s; s = s->superScope)
|
||||
if (s->functionScope)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Scopes for identifiers.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <libyul/YulString.h>
|
||||
|
||||
#include <libdevcore/Visitor.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
namespace assembly
|
||||
{
|
||||
|
||||
struct Scope
|
||||
{
|
||||
using YulType = yul::YulString;
|
||||
using LabelID = size_t;
|
||||
|
||||
struct Variable { YulType type; };
|
||||
struct Label { };
|
||||
struct Function
|
||||
{
|
||||
std::vector<YulType> arguments;
|
||||
std::vector<YulType> returns;
|
||||
};
|
||||
|
||||
using Identifier = boost::variant<Variable, Label, Function>;
|
||||
using Visitor = GenericVisitor<Variable const, Label const, Function const>;
|
||||
using NonconstVisitor = GenericVisitor<Variable, Label, Function>;
|
||||
|
||||
bool registerVariable(yul::YulString _name, YulType const& _type);
|
||||
bool registerLabel(yul::YulString _name);
|
||||
bool registerFunction(
|
||||
yul::YulString _name,
|
||||
std::vector<YulType> const& _arguments,
|
||||
std::vector<YulType> const& _returns
|
||||
);
|
||||
|
||||
/// Looks up the identifier in this or super scopes and returns a valid pointer if found
|
||||
/// or a nullptr if not found. Variable lookups up across function boundaries will fail, as
|
||||
/// will any lookups across assembly boundaries.
|
||||
/// The pointer will be invalidated if the scope is modified.
|
||||
/// @param _crossedFunction if true, we already crossed a function boundary during recursive lookup
|
||||
Identifier* lookup(yul::YulString _name);
|
||||
/// Looks up the identifier in this and super scopes (will not find variables across function
|
||||
/// boundaries and generally stops at assembly boundaries) and calls the visitor, returns
|
||||
/// false if not found.
|
||||
template <class V>
|
||||
bool lookup(yul::YulString _name, V const& _visitor)
|
||||
{
|
||||
if (Identifier* id = lookup(_name))
|
||||
{
|
||||
boost::apply_visitor(_visitor, *id);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
/// @returns true if the name exists in this scope or in super scopes (also searches
|
||||
/// across function and assembly boundaries).
|
||||
bool exists(yul::YulString _name) const;
|
||||
|
||||
/// @returns the number of variables directly registered inside the scope.
|
||||
size_t numberOfVariables() const;
|
||||
/// @returns true if this scope is inside a function.
|
||||
bool insideFunction() const;
|
||||
|
||||
Scope* superScope = nullptr;
|
||||
/// If true, variables from the super scope are not visible here (other identifiers are),
|
||||
/// but they are still taken into account to prevent shadowing.
|
||||
bool functionScope = false;
|
||||
std::map<yul::YulString, Identifier> identifiers;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Module responsible for registering identifiers inside their scopes.
|
||||
*/
|
||||
|
||||
#include <libsolidity/inlineasm/AsmScopeFiller.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
#include <libsolidity/inlineasm/AsmScope.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace langutil;
|
||||
using namespace dev::solidity;
|
||||
using namespace dev::solidity::assembly;
|
||||
|
||||
ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter):
|
||||
m_info(_info), m_errorReporter(_errorReporter)
|
||||
{
|
||||
m_currentScope = &scope(nullptr);
|
||||
}
|
||||
|
||||
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()(assembly::VariableDeclaration const& _varDecl)
|
||||
{
|
||||
for (auto const& variable: _varDecl.variables)
|
||||
if (!registerVariable(variable, _varDecl.location, *m_currentScope))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef)
|
||||
{
|
||||
bool success = true;
|
||||
vector<Scope::YulType> arguments;
|
||||
for (auto const& _argument: _funDef.parameters)
|
||||
arguments.emplace_back(_argument.type.str());
|
||||
vector<Scope::YulType> returns;
|
||||
for (auto const& _return: _funDef.returnVariables)
|
||||
returns.emplace_back(_return.type.str());
|
||||
if (!m_currentScope->registerFunction(_funDef.name, arguments, returns))
|
||||
{
|
||||
//@TODO secondary location
|
||||
m_errorReporter.declarationError(
|
||||
_funDef.location,
|
||||
"Function name " + _funDef.name.str() + " already taken in this scope."
|
||||
);
|
||||
success = false;
|
||||
}
|
||||
|
||||
auto virtualBlock = m_info.virtualBlocks[&_funDef] = make_shared<Block>();
|
||||
Scope& varScope = scope(virtualBlock.get());
|
||||
varScope.superScope = m_currentScope;
|
||||
m_currentScope = &varScope;
|
||||
varScope.functionScope = true;
|
||||
for (auto const& var: _funDef.parameters + _funDef.returnVariables)
|
||||
if (!registerVariable(var, _funDef.location, varScope))
|
||||
success = false;
|
||||
|
||||
if (!(*this)(_funDef.body))
|
||||
success = false;
|
||||
|
||||
solAssert(m_currentScope == &varScope, "");
|
||||
m_currentScope = m_currentScope->superScope;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ScopeFiller::operator()(If const& _if)
|
||||
{
|
||||
return (*this)(_if.body);
|
||||
}
|
||||
|
||||
bool ScopeFiller::operator()(Switch const& _switch)
|
||||
{
|
||||
bool success = true;
|
||||
for (auto const& _case: _switch.cases)
|
||||
if (!(*this)(_case.body))
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ScopeFiller::operator()(ForLoop const& _forLoop)
|
||||
{
|
||||
Scope* originalScope = m_currentScope;
|
||||
|
||||
bool success = true;
|
||||
if (!(*this)(_forLoop.pre))
|
||||
success = false;
|
||||
m_currentScope = &scope(&_forLoop.pre);
|
||||
if (!boost::apply_visitor(*this, *_forLoop.condition))
|
||||
success = false;
|
||||
if (!(*this)(_forLoop.body))
|
||||
success = false;
|
||||
if (!(*this)(_forLoop.post))
|
||||
success = false;
|
||||
|
||||
m_currentScope = originalScope;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ScopeFiller::operator()(Block const& _block)
|
||||
{
|
||||
bool success = true;
|
||||
scope(&_block).superScope = m_currentScope;
|
||||
m_currentScope = &scope(&_block);
|
||||
|
||||
for (auto const& s: _block.statements)
|
||||
if (!boost::apply_visitor(*this, s))
|
||||
success = false;
|
||||
|
||||
m_currentScope = m_currentScope->superScope;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const& _location, Scope& _scope)
|
||||
{
|
||||
if (!_scope.registerVariable(_name.name, _name.type))
|
||||
{
|
||||
//@TODO secondary location
|
||||
m_errorReporter.declarationError(
|
||||
_location,
|
||||
"Variable name " + _name.name.str() + " already taken in this scope."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Scope& ScopeFiller::scope(Block const* _block)
|
||||
{
|
||||
auto& scope = m_info.scopes[_block];
|
||||
if (!scope)
|
||||
scope = make_shared<Scope>();
|
||||
return *scope;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Module responsible for registering identifiers inside their scopes.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/inlineasm/AsmDataForward.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace langutil
|
||||
{
|
||||
class ErrorReporter;
|
||||
struct SourceLocation;
|
||||
}
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
namespace assembly
|
||||
{
|
||||
|
||||
struct TypedName;
|
||||
struct Scope;
|
||||
struct AsmAnalysisInfo;
|
||||
|
||||
/**
|
||||
* Fills scopes with identifiers and checks for name clashes.
|
||||
* Does not resolve references.
|
||||
*/
|
||||
class ScopeFiller: public boost::static_visitor<bool>
|
||||
{
|
||||
public:
|
||||
ScopeFiller(AsmAnalysisInfo& _info, langutil::ErrorReporter& _errorReporter);
|
||||
|
||||
bool operator()(assembly::Instruction const&) { return true; }
|
||||
bool operator()(assembly::Literal const&) { return true; }
|
||||
bool operator()(assembly::Identifier const&) { return true; }
|
||||
bool operator()(assembly::FunctionalInstruction const&) { return true; }
|
||||
bool operator()(assembly::ExpressionStatement const& _expr);
|
||||
bool operator()(assembly::Label const& _label);
|
||||
bool operator()(assembly::StackAssignment const&) { return true; }
|
||||
bool operator()(assembly::Assignment const&) { return true; }
|
||||
bool operator()(assembly::VariableDeclaration const& _variableDeclaration);
|
||||
bool operator()(assembly::FunctionDefinition const& _functionDefinition);
|
||||
bool operator()(assembly::FunctionCall const&) { return true; }
|
||||
bool operator()(assembly::If const& _if);
|
||||
bool operator()(assembly::Switch const& _switch);
|
||||
bool operator()(assembly::ForLoop const& _forLoop);
|
||||
bool operator()(assembly::Block const& _block);
|
||||
|
||||
private:
|
||||
bool registerVariable(
|
||||
TypedName const& _name,
|
||||
langutil::SourceLocation const& _location,
|
||||
Scope& _scope
|
||||
);
|
||||
|
||||
Scope& scope(assembly::Block const* _block);
|
||||
|
||||
Scope* m_currentScope = nullptr;
|
||||
AsmAnalysisInfo& m_info;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,17 +23,16 @@
|
||||
#include <libsolidity/interface/AssemblyStack.h>
|
||||
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <libsolidity/inlineasm/AsmPrinter.h>
|
||||
#include <libsolidity/inlineasm/AsmParser.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
|
||||
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
||||
|
||||
#include <libevmasm/Assembly.h>
|
||||
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libyul/AsmParser.h>
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/AsmCodeGen.h>
|
||||
#include <libyul/backends/evm/EVMCodeTransform.h>
|
||||
#include <libyul/backends/evm/EVMAssembly.h>
|
||||
|
||||
#include <libevmasm/Assembly.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace langutil;
|
||||
@@ -41,19 +40,19 @@ using namespace dev::solidity;
|
||||
|
||||
namespace
|
||||
{
|
||||
assembly::AsmFlavour languageToAsmFlavour(AssemblyStack::Language _language)
|
||||
yul::AsmFlavour languageToAsmFlavour(AssemblyStack::Language _language)
|
||||
{
|
||||
switch (_language)
|
||||
{
|
||||
case AssemblyStack::Language::Assembly:
|
||||
return assembly::AsmFlavour::Loose;
|
||||
return yul::AsmFlavour::Loose;
|
||||
case AssemblyStack::Language::StrictAssembly:
|
||||
return assembly::AsmFlavour::Strict;
|
||||
return yul::AsmFlavour::Strict;
|
||||
case AssemblyStack::Language::Yul:
|
||||
return assembly::AsmFlavour::Yul;
|
||||
return yul::AsmFlavour::Yul;
|
||||
}
|
||||
solAssert(false, "");
|
||||
return assembly::AsmFlavour::Yul;
|
||||
return yul::AsmFlavour::Yul;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -70,7 +69,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
|
||||
m_errors.clear();
|
||||
m_analysisSuccessful = false;
|
||||
m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName);
|
||||
m_parserResult = assembly::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false);
|
||||
m_parserResult = yul::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false);
|
||||
if (!m_errorReporter.errors().empty())
|
||||
return false;
|
||||
solAssert(m_parserResult, "");
|
||||
@@ -78,21 +77,21 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
|
||||
return analyzeParsed();
|
||||
}
|
||||
|
||||
bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scanner)
|
||||
bool AssemblyStack::analyze(yul::Block const& _block, Scanner const* _scanner)
|
||||
{
|
||||
m_errors.clear();
|
||||
m_analysisSuccessful = false;
|
||||
if (_scanner)
|
||||
m_scanner = make_shared<Scanner>(*_scanner);
|
||||
m_parserResult = make_shared<assembly::Block>(_block);
|
||||
m_parserResult = make_shared<yul::Block>(_block);
|
||||
|
||||
return analyzeParsed();
|
||||
}
|
||||
|
||||
bool AssemblyStack::analyzeParsed()
|
||||
{
|
||||
m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
|
||||
assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language));
|
||||
m_analysisInfo = make_shared<yul::AsmAnalysisInfo>();
|
||||
yul::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language));
|
||||
m_analysisSuccessful = analyzer.analyze(*m_parserResult);
|
||||
return m_analysisSuccessful;
|
||||
}
|
||||
@@ -109,7 +108,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
|
||||
{
|
||||
MachineAssemblyObject object;
|
||||
eth::Assembly assembly;
|
||||
assembly::CodeGenerator::assemble(*m_parserResult, *m_analysisInfo, assembly);
|
||||
yul::CodeGenerator::assemble(*m_parserResult, *m_analysisInfo, assembly);
|
||||
object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble());
|
||||
object.assembly = assembly.assemblyString();
|
||||
return object;
|
||||
@@ -133,5 +132,5 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
|
||||
string AssemblyStack::print() const
|
||||
{
|
||||
solAssert(m_parserResult, "");
|
||||
return assembly::AsmPrinter(m_language == Language::Yul)(*m_parserResult);
|
||||
return yul::AsmPrinter(m_language == Language::Yul)(*m_parserResult);
|
||||
}
|
||||
|
||||
@@ -34,16 +34,17 @@ namespace langutil
|
||||
class Scanner;
|
||||
}
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
namespace assembly
|
||||
namespace yul
|
||||
{
|
||||
struct AsmAnalysisInfo;
|
||||
struct Block;
|
||||
}
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
struct MachineAssemblyObject
|
||||
{
|
||||
std::shared_ptr<eth::LinkerObject> bytecode;
|
||||
@@ -73,7 +74,7 @@ public:
|
||||
|
||||
/// Runs analysis step on the supplied block, returns false if input cannot be assembled.
|
||||
/// Multiple calls overwrite the previous state.
|
||||
bool analyze(assembly::Block const& _block, langutil::Scanner const* _scanner = nullptr);
|
||||
bool analyze(yul::Block const& _block, langutil::Scanner const* _scanner = nullptr);
|
||||
|
||||
/// Run the assembly step (should only be called after parseAndAnalyze).
|
||||
MachineAssemblyObject assemble(Machine _machine) const;
|
||||
@@ -93,8 +94,8 @@ private:
|
||||
std::shared_ptr<langutil::Scanner> m_scanner;
|
||||
|
||||
bool m_analysisSuccessful = false;
|
||||
std::shared_ptr<assembly::Block> m_parserResult;
|
||||
std::shared_ptr<assembly::AsmAnalysisInfo> m_analysisInfo;
|
||||
std::shared_ptr<yul::Block> m_parserResult;
|
||||
std::shared_ptr<yul::AsmAnalysisInfo> m_analysisInfo;
|
||||
langutil::ErrorList m_errors;
|
||||
langutil::ErrorReporter m_errorReporter;
|
||||
};
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <libsolidity/interface/Version.h>
|
||||
#include <libsolidity/analysis/SemVerHandler.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <libsolidity/parsing/Parser.h>
|
||||
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
|
||||
#include <libsolidity/analysis/ControlFlowGraph.h>
|
||||
@@ -45,10 +44,12 @@
|
||||
#include <libsolidity/interface/Natspec.h>
|
||||
#include <libsolidity/interface/GasEstimator.h>
|
||||
|
||||
#include <libevmasm/Exceptions.h>
|
||||
|
||||
#include <libyul/YulString.h>
|
||||
|
||||
#include <liblangutil/Scanner.h>
|
||||
|
||||
#include <libevmasm/Exceptions.h>
|
||||
|
||||
#include <libdevcore/SwarmHash.h>
|
||||
#include <libdevcore/JSON.h>
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
|
||||
#include <cctype>
|
||||
#include <vector>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <libsolidity/parsing/Parser.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <libsolidity/inlineasm/AsmParser.h>
|
||||
#include <libyul/AsmParser.h>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace langutil;
|
||||
@@ -1012,8 +1012,8 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
|
||||
m_scanner->next();
|
||||
}
|
||||
|
||||
assembly::Parser asmParser(m_errorReporter);
|
||||
shared_ptr<assembly::Block> block = asmParser.parse(m_scanner, true);
|
||||
yul::Parser asmParser(m_errorReporter);
|
||||
shared_ptr<yul::Block> block = asmParser.parse(m_scanner, true);
|
||||
nodeFactory.markEndPosition();
|
||||
return nodeFactory.createNode<InlineAssembly>(_docString, block);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user