diff options
-rw-r--r-- | libjulia/optimiser/ASTCopier.cpp | 179 | ||||
-rw-r--r-- | libjulia/optimiser/ASTCopier.h | 101 | ||||
-rw-r--r-- | libjulia/optimiser/Disambiguator.cpp | 85 | ||||
-rw-r--r-- | libjulia/optimiser/Disambiguator.h | 70 | ||||
-rw-r--r-- | test/libjulia/Disambiguator.cpp | 177 |
5 files changed, 612 insertions, 0 deletions
diff --git a/libjulia/optimiser/ASTCopier.cpp b/libjulia/optimiser/ASTCopier.cpp new file mode 100644 index 00000000..2f9eade3 --- /dev/null +++ b/libjulia/optimiser/ASTCopier.cpp @@ -0,0 +1,179 @@ +/* + 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/>. +*/ +/** + * Creates an independent copy of an AST, renaming identifiers to be unique. + */ + +#include <libjulia/optimiser/ASTCopier.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libsolidity/interface/Exceptions.h> + +#include <libdevcore/Common.h> + +using namespace std; +using namespace dev; +using namespace dev::julia; + + +shared_ptr<Block> ASTCopier::run() +{ + return make_shared<Block>(translate(m_block)); +} + +Statement ASTCopier::operator()(Instruction const& _instruction) +{ + return _instruction; +} + +Statement ASTCopier::operator()(VariableDeclaration const& _varDecl) +{ + return VariableDeclaration{ + _varDecl.location, + translateVector(_varDecl.variables), + translate(_varDecl.value) + }; +} + +Statement ASTCopier::operator()(Assignment const& _assignment) +{ + return Assignment{ + _assignment.location, + translateVector(_assignment.variableNames), + translate(_assignment.value) + }; +} + +Statement ASTCopier::operator()(StackAssignment const&) +{ + solAssert(false, "Invalid operation."); + return {}; +} + +Statement ASTCopier::operator()(Label const&) +{ + solAssert(false, "Invalid operation."); + return {}; +} + +Statement ASTCopier::operator()(FunctionCall const& _call) +{ + return FunctionCall{ + _call.location, + translate(_call.functionName), + translateVector(_call.arguments) + }; +} + +Statement ASTCopier::operator()(FunctionalInstruction const& _instruction) +{ + return FunctionalInstruction{ + _instruction.location, + _instruction.instruction, + translateVector(_instruction.arguments) + }; +} + +Statement ASTCopier::operator()(Identifier const& _identifier) +{ + return Identifier{_identifier.location, translateIdentifier(_identifier.name)}; +} + +Statement ASTCopier::operator()(Literal const& _literal) +{ + return translate(_literal); +} + +Statement ASTCopier::operator()(If const& _if) +{ + return If{_if.location, translate(_if.condition), translate(_if.body)}; +} + +Statement ASTCopier::operator()(Switch const& _switch) +{ + return Switch{_switch.location, translate(_switch.expression), translateVector(_switch.cases)}; +} + +Statement ASTCopier::operator()(FunctionDefinition const& _function) +{ + string translatedName = translateIdentifier(_function.name); + + enterFunction(_function); + ScopeGuard g([&]() { this->leaveFunction(_function); }); + + return FunctionDefinition{ + _function.location, + move(translatedName), + translateVector(_function.parameters), + translateVector(_function.returnVariables), + translate(_function.body) + }; +} + +Statement ASTCopier::operator()(ForLoop const& _forLoop) +{ + enterScope(_forLoop.pre); + ScopeGuard g([&]() { this->leaveScope(_forLoop.pre); }); + + return ForLoop{ + _forLoop.location, + translate(_forLoop.pre), + translate(_forLoop.condition), + translate(_forLoop.post), + translate(_forLoop.body) + }; +} + +Statement ASTCopier::operator ()(Block const& _block) +{ + return translate(_block); +} + +Statement ASTCopier::translate(Statement const& _statement) +{ + return boost::apply_visitor(*this, _statement); +} + +Block ASTCopier::translate(Block const& _block) +{ + enterScope(_block); + ScopeGuard g([&]() { this->leaveScope(_block); }); + + return Block{_block.location, translateVector(_block.statements)}; +} + +Case ASTCopier::translate(Case const& _case) +{ + return Case{_case.location, translate(_case.value), translate(_case.body)}; +} + +Identifier ASTCopier::translate(Identifier const& _identifier) +{ + return Identifier{_identifier.location, translateIdentifier(_identifier.name)}; +} + +Literal ASTCopier::translate(Literal const& _literal) +{ + return _literal; +} + +TypedName ASTCopier::translate(TypedName const& _typedName) +{ + return TypedName{_typedName.location, translateIdentifier(_typedName.name), _typedName.type}; +} + diff --git a/libjulia/optimiser/ASTCopier.h b/libjulia/optimiser/ASTCopier.h new file mode 100644 index 00000000..759dce31 --- /dev/null +++ b/libjulia/optimiser/ASTCopier.h @@ -0,0 +1,101 @@ +/* + 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/>. +*/ +/** + * Creates an independent copy of an AST, renaming identifiers to be unique. + */ + +#pragma once + +#include <libjulia/ASTDataForward.h> + +#include <boost/variant.hpp> +#include <boost/optional.hpp> + +#include <vector> +#include <set> + +namespace dev +{ +namespace julia +{ + +/** + * Creates a copy of a iulia AST potentially replacing identifier names. + * Base class to be extended. + */ +class ASTCopier: public boost::static_visitor<Statement> +{ +public: + ASTCopier(Block const& _block): + m_block(_block) + {} + + std::shared_ptr<Block> run(); + +public: + Statement operator()(Literal const& _literal); + Statement operator()(Instruction const& _instruction); + Statement operator()(Identifier const& _identifier); + Statement operator()(FunctionalInstruction const& _instr); + Statement operator()(FunctionCall const&); + Statement operator()(Label const& _label); + Statement operator()(StackAssignment const& _assignment); + Statement operator()(Assignment const& _assignment); + Statement operator()(VariableDeclaration const& _varDecl); + Statement operator()(If const& _if); + Statement operator()(Switch const& _switch); + Statement operator()(FunctionDefinition const&); + Statement operator()(ForLoop const&); + Statement operator()(Block const& _block); + +protected: + template <typename T> + std::vector<T> translateVector(std::vector<T> const& _values); + + template <typename T> + std::shared_ptr<T> translate(std::shared_ptr<T> const& _v) + { + return _v ? std::make_shared<T>(translate(*_v)) : nullptr; + } + Statement translate(Statement const& _statement); + Block translate(Block const& _block); + Case translate(Case const& _case); + Identifier translate(Identifier const& _identifier); + Literal translate(Literal const& _literal); + TypedName translate(TypedName const& _typedName); + + virtual void enterScope(Block const& _block) = 0; + virtual void leaveScope(Block const& _block) = 0; + virtual void enterFunction(FunctionDefinition const& _function) = 0; + virtual void leaveFunction(FunctionDefinition const& _function) = 0; + virtual std::string translateIdentifier(std::string const& _name) { return _name; } + + Block const& m_block; +}; + +template <typename T> +std::vector<T> ASTCopier::translateVector(std::vector<T> const& _values) +{ + std::vector<T> translated; + for (auto const& v: _values) + translated.emplace_back(translate(v)); + return translated; +} + + +} +} diff --git a/libjulia/optimiser/Disambiguator.cpp b/libjulia/optimiser/Disambiguator.cpp new file mode 100644 index 00000000..df2984e5 --- /dev/null +++ b/libjulia/optimiser/Disambiguator.cpp @@ -0,0 +1,85 @@ +/* + 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/>. +*/ +/** + * Optimiser component that makes all identifiers unique. + */ + +#include <libjulia/optimiser/Disambiguator.h> + +#include <libsolidity/inlineasm/AsmData.h> +#include <libsolidity/inlineasm/AsmScope.h> + +#include <libsolidity/interface/Exceptions.h> + +using namespace std; +using namespace dev; +using namespace dev::julia; +using namespace dev::solidity; + +using Scope = dev::solidity::assembly::Scope; + +string Disambiguator::translateIdentifier(string const& _originalName) +{ + solAssert(!m_scopes.empty() && m_scopes.back(), ""); + Scope::Identifier const* id = m_scopes.back()->lookup(_originalName); + solAssert(id, ""); + if (!m_translations.count(id)) + { + string translated = _originalName; + size_t suffix = 0; + while (m_usedNames.count(translated)) + { + suffix++; + translated = _originalName + "_" + std::to_string(suffix); + } + m_usedNames.insert(translated); + m_translations[id] = translated; + } + return m_translations.at(id); +} + +void Disambiguator::enterScope(Block const& _block) +{ + enterScopeInternal(*m_info.scopes.at(&_block)); +} + +void Disambiguator::leaveScope(Block const& _block) +{ + leaveScopeInternal(*m_info.scopes.at(&_block)); +} + +void Disambiguator::enterFunction(FunctionDefinition const& _function) +{ + enterScopeInternal(*m_info.scopes.at(m_info.virtualBlocks.at(&_function).get())); +} + +void Disambiguator::leaveFunction(FunctionDefinition const& _function) +{ + leaveScopeInternal(*m_info.scopes.at(m_info.virtualBlocks.at(&_function).get())); +} + +void Disambiguator::enterScopeInternal(Scope& _scope) +{ + m_scopes.push_back(&_scope); +} + +void Disambiguator::leaveScopeInternal(Scope& _scope) +{ + solAssert(!m_scopes.empty(), ""); + solAssert(m_scopes.back() == &_scope, ""); + m_scopes.pop_back(); +} diff --git a/libjulia/optimiser/Disambiguator.h b/libjulia/optimiser/Disambiguator.h new file mode 100644 index 00000000..39615cf7 --- /dev/null +++ b/libjulia/optimiser/Disambiguator.h @@ -0,0 +1,70 @@ +/* + 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/>. +*/ +/** + * Optimiser component that makes all identifiers unique. + */ + +#pragma once + +#include <libjulia/ASTDataForward.h> + +#include <libjulia/optimiser/ASTCopier.h> + +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> + +#include <boost/variant.hpp> +#include <boost/optional.hpp> + +#include <set> + +namespace dev +{ +namespace julia +{ +class EVMAssembly; + +/** + * Creates a copy of a iulia AST replacing all identifiers by unique names. + */ +class Disambiguator: public ASTCopier +{ +public: + Disambiguator( + Block const& _block, + solidity::assembly::AsmAnalysisInfo const& _analysisInfo + ): ASTCopier(_block), m_info(_analysisInfo) + {} + +protected: + virtual void enterScope(Block const& _block) override; + virtual void leaveScope(Block const& _block) override; + virtual void enterFunction(FunctionDefinition const& _function) override; + virtual void leaveFunction(FunctionDefinition const& _function) override; + virtual std::string translateIdentifier(std::string const& _name) override; + + void enterScopeInternal(solidity::assembly::Scope& _scope); + void leaveScopeInternal(solidity::assembly::Scope& _scope); + + solidity::assembly::AsmAnalysisInfo const& m_info; + + std::vector<solidity::assembly::Scope*> m_scopes; + std::map<void const*, std::string> m_translations; + std::set<std::string> m_usedNames; +}; + +} +} diff --git a/test/libjulia/Disambiguator.cpp b/test/libjulia/Disambiguator.cpp new file mode 100644 index 00000000..baee4641 --- /dev/null +++ b/test/libjulia/Disambiguator.cpp @@ -0,0 +1,177 @@ +/* + 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/>. +*/ +/** + * @date 2017 + * Unit tests for the iulia name disambiguator. + */ + +#include <libsolidity/parsing/Scanner.h> + +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <libsolidity/interface/ErrorReporter.h> +#include <libsolidity/interface/SourceReferenceFormatter.h> + +#include <libjulia/optimiser/Disambiguator.h> + +#include <boost/test/unit_test.hpp> + +#include <string> +#include <memory> + +using namespace std; +using namespace dev::solidity::assembly; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +namespace +{ + +void printErrors(ErrorList const& _errors, Scanner const& _scanner) +{ + for (auto const& error: _errors) + SourceReferenceFormatter::printExceptionInformation( + cout, + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error", + [&](std::string const&) -> Scanner const& { return _scanner; } + ); +} + + +pair<shared_ptr<Block>, shared_ptr<AsmAnalysisInfo>> parse(string const& _source) +{ + ErrorList errors; + ErrorReporter errorReporter(errors); + auto scanner = make_shared<Scanner>(CharStream(_source), ""); + auto parserResult = assembly::Parser(errorReporter, true).parse(scanner); + if (parserResult) + { + BOOST_REQUIRE(errorReporter.errors().empty()); + auto analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); + assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, true); + if (analyzer.analyze(*parserResult)) + { + BOOST_REQUIRE(errorReporter.errors().empty()); + return make_pair(parserResult, analysisInfo); + } + } + printErrors(errors, *scanner); + BOOST_FAIL("Invalid source."); + + // Unreachable. + return {}; +} + +string disambiguate(string const& _source) +{ + auto result = parse(_source); + julia::Disambiguator disambiguator(*result.first, *result.second); + return assembly::AsmPrinter(true)(*disambiguator.run()); +} + +string format(string const& _source) +{ + return assembly::AsmPrinter(true)(*parse(_source).first); +} + +} + +#define CHECK(_original, _expectation)\ +do\ +{\ + BOOST_CHECK_EQUAL(disambiguate(_original), format(_expectation));\ + BOOST_CHECK_EQUAL(disambiguate(_original), disambiguate(disambiguate(_original)));\ +}\ +while(false) + +BOOST_AUTO_TEST_SUITE(IuliaDisambiguator) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CHECK("{ }", "{ }"); +} + +BOOST_AUTO_TEST_CASE(variables) +{ + CHECK( + "{ { let a:u256 } { let a:u256 } }", + "{ { let a:u256 } { let a_1:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(variables_clash) +{ + CHECK( + "{ { let a:u256 let a_1:u256 } { let a:u256 } }", + "{ { let a:u256 let a_1:u256 } { let a_2:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(variables_inside_functions) +{ + CHECK( + "{ { let c:u256 let b:u256 } function f(a:u256, c:u256) -> b:u256 { let x:u256 } { let a:u256 let x:u256 } }", + "{ { let c:u256 let b:u256 } function f(a:u256, c_1:u256) -> b_1:u256 { let x:u256 } { let a_1:u256 let x_1:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(function_call) +{ + CHECK( + "{ { let a:u256, b:u256, c:u256, d:u256, f:u256 } { function f(a:u256) -> c:u256, d:u256 { let b:u256, c_1:u256 := f(a) } } }", + "{ { let a:u256, b:u256, c:u256, d:u256, f:u256 } { function f_1(a_1:u256) -> c_1:u256, d_1:u256 { let b_1:u256, c_1_1:u256 := f_1(a_1) } } }" + ); +} + +BOOST_AUTO_TEST_CASE(for_statement) +{ + CHECK( + "{ { let a:u256, b:u256 } { for { let a:u256 } a { a := a } { let b:u256 := a } } }", + "{ { let a:u256, b:u256 } { for { let a_1:u256 } a_1 { a_1 := a_1 } { let b_1:u256 := a_1 } } }" + ); +} + +BOOST_AUTO_TEST_CASE(switch_statement) +{ + CHECK( + "{ { let a:u256, b:u256, c:u256 } { let a:u256 switch a case 0:u256 { let b:u256 := a } default { let c:u256 := a } } }", + "{ { let a:u256, b:u256, c:u256 } { let a_1:u256 switch a_1 case 0:u256 { let b_1:u256 := a_1 } default { let c_1:u256 := a_1 } } }" + ); +} + +BOOST_AUTO_TEST_CASE(if_statement) +{ + CHECK( + "{ { let a:u256, b:u256, c:u256 } { let a:bool if a { let b:bool := a } } }", + "{ { let a:u256, b:u256, c:u256 } { let a_1:bool if a_1 { let b_1:bool := a_1 } } }" + ); +} + +BOOST_AUTO_TEST_SUITE_END() + + +} +} +} // end namespaces |