diff options
author | chriseth <chris@ethereum.org> | 2017-12-05 21:42:20 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-05 21:42:20 +0800 |
commit | b582420b5fba1df9ce96333ff38168778ae6f4de (patch) | |
tree | ae03259a978f11e3f4771bcf36d259aa2928987b | |
parent | b47e023df166e2ab9fd085a29f74972b22c4897b (diff) | |
parent | 30d7afc2e3fd27333e463a9127011ec1f7470d3e (diff) | |
download | dexon-solidity-b582420b5fba1df9ce96333ff38168778ae6f4de.tar.gz dexon-solidity-b582420b5fba1df9ce96333ff38168778ae6f4de.tar.zst dexon-solidity-b582420b5fba1df9ce96333ff38168778ae6f4de.zip |
Merge pull request #3219 from ethereum/disambiguator
Iulia disambiguator.
-rw-r--r-- | libjulia/ASTDataForward.h (renamed from libjulia/AsmDataForward.h) | 0 | ||||
-rw-r--r-- | libjulia/backends/evm/EVMCodeTransform.h | 2 | ||||
-rw-r--r-- | libjulia/optimiser/ASTCopier.cpp | 174 | ||||
-rw-r--r-- | libjulia/optimiser/ASTCopier.h | 93 | ||||
-rw-r--r-- | libjulia/optimiser/Disambiguator.cpp | 85 | ||||
-rw-r--r-- | libjulia/optimiser/Disambiguator.h | 68 | ||||
-rw-r--r-- | test/libjulia/Common.cpp | 86 | ||||
-rw-r--r-- | test/libjulia/Common.h | 55 | ||||
-rw-r--r-- | test/libjulia/Disambiguator.cpp | 105 |
9 files changed, 667 insertions, 1 deletions
diff --git a/libjulia/AsmDataForward.h b/libjulia/ASTDataForward.h index 3806e321..3806e321 100644 --- a/libjulia/AsmDataForward.h +++ b/libjulia/ASTDataForward.h diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h index e4cb20ed..577cc8ba 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -20,7 +20,7 @@ #include <libjulia/backends/evm/EVMAssembly.h> -#include <libjulia/AsmDataForward.h> +#include <libjulia/ASTDataForward.h> #include <libsolidity/inlineasm/AsmScope.h> diff --git a/libjulia/optimiser/ASTCopier.cpp b/libjulia/optimiser/ASTCopier.cpp new file mode 100644 index 00000000..d777d731 --- /dev/null +++ b/libjulia/optimiser/ASTCopier.cpp @@ -0,0 +1,174 @@ +/* + 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; + + +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..0658fd50 --- /dev/null +++ b/libjulia/optimiser/ASTCopier.h @@ -0,0 +1,93 @@ +/* + 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> +#include <memory> + +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: + 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&) { } + virtual void leaveScope(Block const&) { } + virtual void enterFunction(FunctionDefinition const&) { } + virtual void leaveFunction(FunctionDefinition const&) { } + virtual std::string translateIdentifier(std::string const& _name) { return _name; } +}; + +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..cc9488d5 --- /dev/null +++ b/libjulia/optimiser/Disambiguator.h @@ -0,0 +1,68 @@ +/* + 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(solidity::assembly::AsmAnalysisInfo const& _analysisInfo): + 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/Common.cpp b/test/libjulia/Common.cpp new file mode 100644 index 00000000..da1538f3 --- /dev/null +++ b/test/libjulia/Common.cpp @@ -0,0 +1,86 @@ +/* + 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 + * Common functions the iulia tests. + */ + +#include <test/libjulia/Common.h> + +#include <libjulia/optimiser/Disambiguator.h> + +#include <libsolidity/parsing/Scanner.h> + +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <libsolidity/interface/ErrorReporter.h> + +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace dev::julia; +using namespace dev::solidity; + +void dev::julia::test::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<assembly::AsmAnalysisInfo>> dev::julia::test::parse(string const& _source, bool _julia) +{ + ErrorList errors; + ErrorReporter errorReporter(errors); + auto scanner = make_shared<Scanner>(CharStream(_source), ""); + auto parserResult = assembly::Parser(errorReporter, _julia).parse(scanner); + if (parserResult) + { + BOOST_REQUIRE(errorReporter.errors().empty()); + auto analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); + assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, _julia); + if (analyzer.analyze(*parserResult)) + { + BOOST_REQUIRE(errorReporter.errors().empty()); + return make_pair(parserResult, analysisInfo); + } + } + printErrors(errors, *scanner); + BOOST_FAIL("Invalid source."); + + // Unreachable. + return {}; +} + +assembly::Block dev::julia::test::disambiguate(string const& _source, bool _julia) +{ + auto result = parse(_source, _julia); + return boost::get<Block>(Disambiguator(*result.second)(*result.first)); +} + +string dev::julia::test::format(string const& _source, bool _julia) +{ + return assembly::AsmPrinter(_julia)(*parse(_source, _julia).first); +} diff --git a/test/libjulia/Common.h b/test/libjulia/Common.h new file mode 100644 index 00000000..1371101c --- /dev/null +++ b/test/libjulia/Common.h @@ -0,0 +1,55 @@ +/* + 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 + * Common functions the iulia tests. + */ + +#pragma once + +#include <libsolidity/inlineasm/AsmData.h> + +#include <string> +#include <vector> +#include <memory> + +namespace dev +{ +namespace solidity +{ +class Scanner; +class Error; +using ErrorList = std::vector<std::shared_ptr<Error const>>; +namespace assembly +{ +struct AsmAnalysisInfo; +} +} +namespace julia +{ +namespace test +{ + +void printErrors(solidity::ErrorList const& _errors, solidity::Scanner const& _scanner); +std::pair<std::shared_ptr<solidity::assembly::Block>, std::shared_ptr<solidity::assembly::AsmAnalysisInfo>> +parse(std::string const& _source, bool _julia = true); +solidity::assembly::Block disambiguate(std::string const& _source, bool _julia = true); +std::string format(std::string const& _source, bool _julia = true); + +} +} +} diff --git a/test/libjulia/Disambiguator.cpp b/test/libjulia/Disambiguator.cpp new file mode 100644 index 00000000..a6338449 --- /dev/null +++ b/test/libjulia/Disambiguator.cpp @@ -0,0 +1,105 @@ +/* + 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 <test/libjulia/Common.h> + +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace dev::julia::test; +using namespace dev::solidity; + +#define CHECK(_original, _expectation)\ +do\ +{\ + assembly::AsmPrinter p(true);\ + string result = p(disambiguate(_original));\ + BOOST_CHECK_EQUAL(result, format(_expectation));\ + BOOST_CHECK_EQUAL(result, p(disambiguate(result)));\ +}\ +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() |