mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Yul objects.
This commit is contained in:
parent
5e55cb1729
commit
e016cb99e6
@ -30,6 +30,7 @@
|
|||||||
#include <libyul/AsmCodeGen.h>
|
#include <libyul/AsmCodeGen.h>
|
||||||
#include <libyul/backends/evm/EVMCodeTransform.h>
|
#include <libyul/backends/evm/EVMCodeTransform.h>
|
||||||
#include <libyul/backends/evm/EVMAssembly.h>
|
#include <libyul/backends/evm/EVMAssembly.h>
|
||||||
|
#include <libyul/YulObjectParser.h>
|
||||||
|
|
||||||
#include <libevmasm/Assembly.h>
|
#include <libevmasm/Assembly.h>
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
|
|||||||
m_errors.clear();
|
m_errors.clear();
|
||||||
m_analysisSuccessful = false;
|
m_analysisSuccessful = false;
|
||||||
m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName);
|
m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName);
|
||||||
m_parserResult = yul::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false);
|
m_parserResult = yul::YulObjectParser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false);
|
||||||
if (!m_errorReporter.errors().empty())
|
if (!m_errorReporter.errors().empty())
|
||||||
return false;
|
return false;
|
||||||
solAssert(m_parserResult, "");
|
solAssert(m_parserResult, "");
|
||||||
@ -77,17 +78,6 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
|
|||||||
return analyzeParsed();
|
return analyzeParsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
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<yul::Block>(_block);
|
|
||||||
|
|
||||||
return analyzeParsed();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AssemblyStack::analyzeParsed()
|
bool AssemblyStack::analyzeParsed()
|
||||||
{
|
{
|
||||||
m_analysisInfo = make_shared<yul::AsmAnalysisInfo>();
|
m_analysisInfo = make_shared<yul::AsmAnalysisInfo>();
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
|
#include <libyul/YulObject.h>
|
||||||
|
#include <libyul/YulObjectParser.h>
|
||||||
|
|
||||||
#include <libevmasm/LinkerObject.h>
|
#include <libevmasm/LinkerObject.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -72,10 +75,6 @@ public:
|
|||||||
/// Multiple calls overwrite the previous state.
|
/// Multiple calls overwrite the previous state.
|
||||||
bool parseAndAnalyze(std::string const& _sourceName, std::string const& _source);
|
bool parseAndAnalyze(std::string const& _sourceName, std::string const& _source);
|
||||||
|
|
||||||
/// Runs analysis step on the supplied block, returns false if input cannot be assembled.
|
|
||||||
/// Multiple calls overwrite the previous state.
|
|
||||||
bool analyze(yul::Block const& _block, langutil::Scanner const* _scanner = nullptr);
|
|
||||||
|
|
||||||
/// Run the assembly step (should only be called after parseAndAnalyze).
|
/// Run the assembly step (should only be called after parseAndAnalyze).
|
||||||
MachineAssemblyObject assemble(Machine _machine) const;
|
MachineAssemblyObject assemble(Machine _machine) const;
|
||||||
|
|
||||||
@ -94,8 +93,7 @@ private:
|
|||||||
std::shared_ptr<langutil::Scanner> m_scanner;
|
std::shared_ptr<langutil::Scanner> m_scanner;
|
||||||
|
|
||||||
bool m_analysisSuccessful = false;
|
bool m_analysisSuccessful = false;
|
||||||
std::shared_ptr<yul::Block> m_parserResult;
|
std::shared_ptr<yul::YulObject> m_parserResult;
|
||||||
std::shared_ptr<yul::AsmAnalysisInfo> m_analysisInfo;
|
|
||||||
langutil::ErrorList m_errors;
|
langutil::ErrorList m_errors;
|
||||||
langutil::ErrorReporter m_errorReporter;
|
langutil::ErrorReporter m_errorReporter;
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,8 @@ add_library(yul
|
|||||||
AsmPrinter.cpp
|
AsmPrinter.cpp
|
||||||
AsmScope.cpp
|
AsmScope.cpp
|
||||||
AsmScopeFiller.cpp
|
AsmScopeFiller.cpp
|
||||||
|
Object.cpp
|
||||||
|
ObjectParser.cpp
|
||||||
backends/evm/EVMAssembly.cpp
|
backends/evm/EVMAssembly.cpp
|
||||||
backends/evm/EVMCodeTransform.cpp
|
backends/evm/EVMCodeTransform.cpp
|
||||||
optimiser/ASTCopier.cpp
|
optimiser/ASTCopier.cpp
|
||||||
|
61
libyul/Object.cpp
Normal file
61
libyul/Object.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Yul code and data object container.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libyul/Object.h>
|
||||||
|
|
||||||
|
#include <libyul/AsmPrinter.h>
|
||||||
|
#include <libyul/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libdevcore/Visitor.h>
|
||||||
|
#include <libdevcore/CommonData.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
|
||||||
|
using namespace dev;
|
||||||
|
using namespace yul;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
string indent(std::string const& _input)
|
||||||
|
{
|
||||||
|
if (_input.empty())
|
||||||
|
return _input;
|
||||||
|
return boost::replace_all_copy(" " + _input, "\n", "\n ");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
string Data::toString(bool) const
|
||||||
|
{
|
||||||
|
return "data \"" + name.str() + "\" hex\"" + dev::toHex(data) + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
string Object::toString(bool _yul) const
|
||||||
|
{
|
||||||
|
yulAssert(code, "No code");
|
||||||
|
string inner = "code " + AsmPrinter{_yul}(*code);
|
||||||
|
|
||||||
|
for (auto const& obj: subObjects)
|
||||||
|
inner += "\n" + obj->toString(_yul);
|
||||||
|
|
||||||
|
return "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}";
|
||||||
|
}
|
72
libyul/Object.h
Normal file
72
libyul/Object.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Yul code and data object container.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libyul/AsmDataForward.h>
|
||||||
|
#include <libyul/YulString.h>
|
||||||
|
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace yul
|
||||||
|
{
|
||||||
|
struct AsmAnalysisInfo;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic base class for both Yul objects and Yul data.
|
||||||
|
*/
|
||||||
|
struct ObjectNode
|
||||||
|
{
|
||||||
|
virtual ~ObjectNode() {}
|
||||||
|
virtual std::string toString(bool _yul) const = 0;
|
||||||
|
|
||||||
|
YulString name;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Named data in Yul objects.
|
||||||
|
*/
|
||||||
|
struct Data: ObjectNode
|
||||||
|
{
|
||||||
|
Data(YulString _name, dev::bytes _data): data(std::move(_data)) { name = _name; }
|
||||||
|
std::string toString(bool _yul) const override;
|
||||||
|
|
||||||
|
dev::bytes data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yul code and data object container.
|
||||||
|
*/
|
||||||
|
struct Object: ObjectNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @returns a (parseable) string representation. Includes types if @a _yul is set.
|
||||||
|
std::string toString(bool _yul) const override;
|
||||||
|
|
||||||
|
std::shared_ptr<Block> code;
|
||||||
|
std::vector<std::shared_ptr<ObjectNode>> subObjects;
|
||||||
|
std::map<YulString, size_t> subIndexByName;
|
||||||
|
std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
146
libyul/ObjectParser.cpp
Normal file
146
libyul/ObjectParser.cpp
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Parser for Yul code and data object container.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libyul/ObjectParser.h>
|
||||||
|
|
||||||
|
#include <libyul/AsmParser.h>
|
||||||
|
#include <libyul/Exceptions.h>
|
||||||
|
|
||||||
|
#include <liblangutil/Token.h>
|
||||||
|
|
||||||
|
using namespace dev;
|
||||||
|
using namespace langutil;
|
||||||
|
using namespace yul;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
shared_ptr<Object> ObjectParser::parse(shared_ptr<Scanner> const& _scanner, bool _reuseScanner)
|
||||||
|
{
|
||||||
|
m_recursionDepth = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
shared_ptr<Object> object;
|
||||||
|
m_scanner = _scanner;
|
||||||
|
if (currentToken() == Token::LBrace)
|
||||||
|
{
|
||||||
|
// Special case: Code-only form.
|
||||||
|
object = make_shared<Object>();
|
||||||
|
object->name = YulString{"object"};
|
||||||
|
object->code = parseBlock();
|
||||||
|
if (!object->code)
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
object = parseObject();
|
||||||
|
if (object && !_reuseScanner)
|
||||||
|
expectToken(Token::EOS);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
catch (FatalError const&)
|
||||||
|
{
|
||||||
|
if (m_errorReporter.errors().empty())
|
||||||
|
throw; // Something is weird here, rather throw again.
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject)
|
||||||
|
{
|
||||||
|
RecursionGuard guard(*this);
|
||||||
|
|
||||||
|
if (currentToken() != Token::Identifier || currentLiteral() != "object")
|
||||||
|
fatalParserError("Expected keyword \"object\".");
|
||||||
|
advance();
|
||||||
|
|
||||||
|
shared_ptr<Object> ret = make_shared<Object>();
|
||||||
|
ret->name = parseUniqueName(_containingObject);
|
||||||
|
|
||||||
|
expectToken(Token::LBrace);
|
||||||
|
|
||||||
|
ret->code = parseCode();
|
||||||
|
|
||||||
|
while (currentToken() != Token::RBrace)
|
||||||
|
{
|
||||||
|
if (currentToken() == Token::Identifier && currentLiteral() == "object")
|
||||||
|
parseObject(ret.get());
|
||||||
|
else if (currentToken() == Token::Identifier && currentLiteral() == "data")
|
||||||
|
parseData(*ret);
|
||||||
|
else
|
||||||
|
fatalParserError("Expected keyword \"data\" or \"object\" or \"}\".");
|
||||||
|
}
|
||||||
|
if (_containingObject)
|
||||||
|
addNamedSubObject(*_containingObject, ret->name, ret);
|
||||||
|
|
||||||
|
expectToken(Token::RBrace);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<Block> ObjectParser::parseCode()
|
||||||
|
{
|
||||||
|
if (currentToken() != Token::Identifier || currentLiteral() != "code")
|
||||||
|
fatalParserError("Expected keyword \"code\".");
|
||||||
|
advance();
|
||||||
|
|
||||||
|
return parseBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<Block> ObjectParser::parseBlock()
|
||||||
|
{
|
||||||
|
Parser parser(m_errorReporter, m_flavour);
|
||||||
|
shared_ptr<Block> block = parser.parse(m_scanner, true);
|
||||||
|
yulAssert(block || m_errorReporter.hasErrors(), "Invalid block but no error!");
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectParser::parseData(Object& _containingObject)
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
currentToken() == Token::Identifier && currentLiteral() == "data",
|
||||||
|
"parseData called on wrong input."
|
||||||
|
);
|
||||||
|
advance();
|
||||||
|
|
||||||
|
YulString name = parseUniqueName(&_containingObject);
|
||||||
|
|
||||||
|
expectToken(Token::StringLiteral, false);
|
||||||
|
addNamedSubObject(_containingObject, name, make_shared<Data>(name, asBytes(currentLiteral())));
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
YulString ObjectParser::parseUniqueName(Object const* _containingObject)
|
||||||
|
{
|
||||||
|
expectToken(Token::StringLiteral, false);
|
||||||
|
YulString name{currentLiteral()};
|
||||||
|
if (name.empty())
|
||||||
|
parserError("Object name cannot be empty.");
|
||||||
|
else if (_containingObject && _containingObject->name == name)
|
||||||
|
parserError("Object name cannot be the same as the name of the containing object.");
|
||||||
|
else if (_containingObject && _containingObject->subIndexByName.count(name))
|
||||||
|
parserError("Object name \"" + name.str() + "\" already exists inside the containing object.");
|
||||||
|
advance();
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectParser::addNamedSubObject(Object& _container, YulString _name, shared_ptr<ObjectNode> _subObject)
|
||||||
|
{
|
||||||
|
_container.subIndexByName[_name] = _container.subObjects.size();
|
||||||
|
_container.subObjects.emplace_back(std::move(_subObject));
|
||||||
|
}
|
72
libyul/ObjectParser.h
Normal file
72
libyul/ObjectParser.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Parser for Yul code and data object container.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libyul/YulString.h>
|
||||||
|
#include <libyul/Object.h>
|
||||||
|
|
||||||
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
#include <liblangutil/ParserBase.h>
|
||||||
|
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace langutil
|
||||||
|
{
|
||||||
|
class Scanner;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace yul
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yul object parser. Invokes the inline assembly parser.
|
||||||
|
*/
|
||||||
|
class ObjectParser: public langutil::ParserBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ObjectParser(
|
||||||
|
langutil::ErrorReporter& _errorReporter,
|
||||||
|
yul::AsmFlavour _flavour = yul::AsmFlavour::Loose
|
||||||
|
):
|
||||||
|
ParserBase(_errorReporter), m_flavour(_flavour) {}
|
||||||
|
|
||||||
|
/// Parses a Yul object.
|
||||||
|
/// Falls back to code-only parsing if the source starts with `{`.
|
||||||
|
/// @param _reuseScanner if true, do check for end of input after the last `}`.
|
||||||
|
/// @returns an empty shared pointer on error.
|
||||||
|
std::shared_ptr<Object> parse(std::shared_ptr<langutil::Scanner> const& _scanner, bool _reuseScanner);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Object> parseObject(Object* _containingObject = nullptr);
|
||||||
|
std::shared_ptr<Block> parseCode();
|
||||||
|
std::shared_ptr<Block> parseBlock();
|
||||||
|
void parseData(Object& _containingObject);
|
||||||
|
|
||||||
|
/// Tries to parse a name that is non-empty and unique inside the containing object.
|
||||||
|
YulString parseUniqueName(Object const* _containingObject);
|
||||||
|
void addNamedSubObject(Object& _container, YulString _name, std::shared_ptr<ObjectNode> _subObject);
|
||||||
|
|
||||||
|
yul::AsmFlavour m_flavour;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user