mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #7914 from ethereum/solidity-upgrade-basic
[tools] Create solidity-upgrade
This commit is contained in:
		
						commit
						f44188abf5
					
				| @ -62,6 +62,11 @@ defaults: | ||||
|       path: build/solc/solc | ||||
|       destination: solc | ||||
| 
 | ||||
|   # compiled tool executable target | ||||
|   - artifacts_tools: &artifacts_tools | ||||
|       path: build/tools/solidity-upgrade | ||||
|       destination: solidity-upgrade | ||||
| 
 | ||||
|   # compiled executable targets | ||||
|   - artifacts_executables: &artifacts_executables | ||||
|       root: build | ||||
| @ -324,6 +329,7 @@ jobs: | ||||
|       - checkout | ||||
|       - run: *run_build | ||||
|       - store_artifacts: *artifacts_solc | ||||
|       - store_artifacts: *artifacts_tools | ||||
|       - persist_to_workspace: *artifacts_executables | ||||
| 
 | ||||
|   b_ubu_release: &build_ubuntu1904_release | ||||
| @ -455,6 +461,7 @@ jobs: | ||||
|             - /usr/local/Homebrew | ||||
|       - run: *run_build | ||||
|       - store_artifacts: *artifacts_solc | ||||
|       - store_artifacts: *artifacts_tools | ||||
|       - persist_to_workspace: *artifacts_build_dir | ||||
| 
 | ||||
|   t_osx_soltest: | ||||
|  | ||||
| @ -55,6 +55,7 @@ add_subdirectory(libevmasm) | ||||
| add_subdirectory(libyul) | ||||
| add_subdirectory(libsolidity) | ||||
| add_subdirectory(libsolc) | ||||
| add_subdirectory(tools) | ||||
| 
 | ||||
| if (NOT EMSCRIPTEN) | ||||
| 	add_subdirectory(solc) | ||||
|  | ||||
| @ -474,3 +474,240 @@ Error types | ||||
| 11. ``CompilerError``: Invalid use of the compiler stack - this should be reported as an issue. | ||||
| 12. ``FatalError``: Fatal error not processed correctly - this should be reported as an issue. | ||||
| 13. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible. | ||||
| 
 | ||||
| 
 | ||||
| .. _compiler-tools: | ||||
| 
 | ||||
| Compiler tools | ||||
| ************** | ||||
| 
 | ||||
| solidity-upgrade | ||||
| ---------------- | ||||
| 
 | ||||
| ``solidity-upgrade`` can help you to semi-automatically upgrade your contracts | ||||
| to breaking language changes. While it does not and cannot implement all | ||||
| required changes for every breaking release, it still supports the ones, that | ||||
| would need plenty of repetitive manual adjustments otherwise. | ||||
| 
 | ||||
| .. note:: | ||||
| 
 | ||||
|     ``solidity-upgrade`` carries out a large part of the work, but your | ||||
|     contracts will most likely need further manual adjustments. We recommend | ||||
|     using a version control system for your files. This helps reviewing and | ||||
|     eventually rolling back the changes made. | ||||
| 
 | ||||
| .. warning:: | ||||
| 
 | ||||
|     ``solidity-upgrade`` is not considered to be complete or free from bugs, so | ||||
|     please use with care. | ||||
| 
 | ||||
| How it works | ||||
| ~~~~~~~~~~~~ | ||||
| 
 | ||||
| You can pass (a) Solidity source file(s) to ``solidity-upgrade [files]``. If | ||||
| these make use of ``import`` statement which refer to files outside the | ||||
| current source file's directory, you need to specify directories that | ||||
| are allowed to read and import files from, by passing | ||||
| ``--allow-paths [directory]``. You can ignore missing files by passing | ||||
| ``--ignore-missing``. | ||||
| 
 | ||||
| ``solidity-upgrade`` is based on ``libsolidity`` and can parse, compile and | ||||
| analyse your source files, and might find applicable source upgrades in them. | ||||
| 
 | ||||
| Source upgrades are considered to be small textual changes to your source code. | ||||
| They are applied to an in-memory representation of the source files | ||||
| given. The corresponding source file is updated by default, but you can pass | ||||
| ``--dry-run`` to simulate to whole upgrade process without writing to any file. | ||||
| 
 | ||||
| The upgrade process itself has two phases. In the first phase source files are | ||||
| parsed, and since it is not possible to upgrade source code on that level, | ||||
| errors are collected and can be logged by passing ``--verbose``. No source | ||||
| upgrades available at this point. | ||||
| 
 | ||||
| In the second phase, all sources are compiled and all activated upgrade analysis | ||||
| modules are run alongside compilation. By default, all available modules are | ||||
| activated. Please read the documentation on | ||||
| :ref:`available modules <upgrade-modules>` for further details. | ||||
| 
 | ||||
| 
 | ||||
| This can result in compilation errors that may | ||||
| be fixed by source upgrades. If no errors occur, no source upgrades are being | ||||
| reported and you're done. | ||||
| If errors occur and some upgrade module reported a source upgrade, the first | ||||
| reported one gets applied and compilation is triggered again for all given | ||||
| source files. The previous step is repeated as long as source upgrades are | ||||
| reported. If errors still occur, you can log them by passing ``--verbose``. | ||||
| If no errors occur, your contracts are up to date and can be compiled with | ||||
| the latest version of the compiler. | ||||
| 
 | ||||
| .. _upgrade-modules: | ||||
| 
 | ||||
| Available upgrade modules | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| +-----------------+---------+--------------------------------------------------+ | ||||
| | Module          | Version | Description                                      | | ||||
| +=================+=========+==================================================+ | ||||
| | ``constructor`` | 0.5.0   | Constructors must now be defined using the       | | ||||
| |                 |         | ``constructor`` keyword.                         | | ||||
| +-----------------+---------+--------------------------------------------------+ | ||||
| | ``visibility``  | 0.5.0   | Explicit function visibility is now mandatory,   | | ||||
| |                 |         | defaults to ``public``.                          | | ||||
| +-----------------+---------+--------------------------------------------------+ | ||||
| | ``abstract``    | 0.6.0   | The keyword ``abstract`` has to be used if a     | | ||||
| |                 |         | contract does not implement all its functions.   | | ||||
| +-----------------+---------+--------------------------------------------------+ | ||||
| | ``virtual``     | 0.6.0   | Functions without implementation outside an      | | ||||
| |                 |         | interface have to be marked ``virtual``.         | | ||||
| +-----------------+---------+--------------------------------------------------+ | ||||
| | ``override``    | 0.6.0   | When overriding a function or modifier, the new  | | ||||
| |                 |         | keyword ``override`` must be used.               | | ||||
| +-----------------+---------+--------------------------------------------------+ | ||||
| 
 | ||||
| Please read :doc:`0.5.0 release notes <050-breaking-changes>` and | ||||
| :doc:`0.6.0 release notes <060-breaking-changes>` for further details. | ||||
| 
 | ||||
| Synopsis | ||||
| ~~~~~~~~ | ||||
| 
 | ||||
| .. code-block:: none | ||||
| 
 | ||||
|     Usage: solidity-upgrade [options] contract.sol | ||||
| 
 | ||||
|     Allowed options: | ||||
|         --help               Show help message and exit. | ||||
|         --version            Show version and exit. | ||||
|         --allow-paths path(s) | ||||
|                              Allow a given path for imports. A list of paths can be | ||||
|                              supplied by separating them with a comma. | ||||
|         --ignore-missing     Ignore missing files. | ||||
|         --modules module(s)  Only activate a specific upgrade module. A list of | ||||
|                              modules can be supplied by separating them with a comma. | ||||
|         --dry-run            Apply changes in-memory only and don't write to input | ||||
|                              file. | ||||
|         --verbose            Print logs, errors and changes. Shortens output of | ||||
|                              upgrade patches. | ||||
|         --unsafe             Accept *unsafe* changes. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| Bug Reports / Feature requests | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| If you found a bug or if you have a feature request, please | ||||
| `file an issue <https://github.com/ethereum/solidity/issues/new/choose>`_ on Github. | ||||
| 
 | ||||
| 
 | ||||
| Example | ||||
| ~~~~~~~ | ||||
| 
 | ||||
| Assume you have the following contracts you want to update declared in ``Source.sol``: | ||||
| 
 | ||||
| .. code-block:: none | ||||
| 
 | ||||
|     // This will not compile | ||||
|     pragma solidity >0.4.23; | ||||
| 
 | ||||
|     contract Updateable { | ||||
|         function run() public view returns (bool); | ||||
|         function update() public; | ||||
|     } | ||||
| 
 | ||||
|     contract Upgradable { | ||||
|         function run() public view returns (bool); | ||||
|         function upgrade(); | ||||
|     } | ||||
| 
 | ||||
|     contract Source is Updateable, Upgradable { | ||||
|         function Source() public {} | ||||
| 
 | ||||
|         function run() | ||||
|             public | ||||
|             view | ||||
|             returns (bool) {} | ||||
| 
 | ||||
|         function update() {} | ||||
|         function upgrade() {} | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| Required changes | ||||
| ^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| To bring the contracts up to date with the current Solidity version, the | ||||
| following upgrade modules have to be executed: ``constructor``, | ||||
| ``visibility``, ``abstract``, ``override`` and ``virtual``. Please read the | ||||
| documentation on :ref:`available modules <upgrade-modules>` for further details. | ||||
| 
 | ||||
| Running the upgrade | ||||
| ^^^^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| In this example, all modules needed to upgrade the contracts above, | ||||
| are available and all of them are activated by default. Therefore you | ||||
| do not need to specify the ``--modules`` option. | ||||
| 
 | ||||
| .. code-block:: none | ||||
| 
 | ||||
|     $ solidity-upgrade Source.sol --dry-run | ||||
| 
 | ||||
| .. code-block:: none | ||||
| 
 | ||||
|     Running analysis (and upgrade) on given source files. | ||||
|     .............. | ||||
| 
 | ||||
|     After upgrade: | ||||
| 
 | ||||
|     Found 0 errors. | ||||
|     Found 0 upgrades. | ||||
| 
 | ||||
| The above performs a dry-ran upgrade on the given file and logs statistics after all. | ||||
| In this case, the upgrade was successful and no further adjustments are needed. | ||||
| 
 | ||||
| Finally, you can run the upgrade and also write to the source file. | ||||
| 
 | ||||
| .. code-block:: none | ||||
| 
 | ||||
|     $ solidity-upgrade Source.sol | ||||
| 
 | ||||
| .. code-block:: none | ||||
| 
 | ||||
|     Running analysis (and upgrade) on given source files. | ||||
|     .............. | ||||
| 
 | ||||
|     After upgrade: | ||||
| 
 | ||||
|     Found 0 errors. | ||||
|     Found 0 upgrades. | ||||
| 
 | ||||
| 
 | ||||
| Review changes | ||||
| ^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| The command above applies all changes as shown below. Please review them carefully. | ||||
| 
 | ||||
| .. code-block:: none | ||||
| 
 | ||||
|     pragma solidity >0.4.23; | ||||
| 
 | ||||
|     abstract contract Updateable { | ||||
|         function run() public view virtual returns (bool); | ||||
|         function update() public virtual; | ||||
|     } | ||||
| 
 | ||||
|     abstract contract Upgradable { | ||||
|         function run() public view virtual returns (bool); | ||||
|         function upgrade() public virtual; | ||||
|     } | ||||
| 
 | ||||
|     contract Source is Updateable, Upgradable { | ||||
|         constructor() public {} | ||||
| 
 | ||||
|         function run() | ||||
|             public | ||||
|             view | ||||
|             override(Updateable,Upgradable) | ||||
|             returns (bool) {} | ||||
| 
 | ||||
|         function update() public override {} | ||||
|         function upgrade() public override {} | ||||
|     } | ||||
|  | ||||
| @ -144,8 +144,8 @@ vector<ContractDefinition const*> resolveDirectBaseContracts(ContractDefinition | ||||
| 		Declaration const* baseDecl = | ||||
| 			specifier->name().annotation().referencedDeclaration; | ||||
| 		auto contract = dynamic_cast<ContractDefinition const*>(baseDecl); | ||||
| 		solAssert(contract, "contract is null"); | ||||
| 		resolvedContracts.emplace_back(contract); | ||||
| 		if (contract) | ||||
| 			resolvedContracts.emplace_back(contract); | ||||
| 	} | ||||
| 
 | ||||
| 	return resolvedContracts; | ||||
|  | ||||
| @ -129,6 +129,7 @@ private: | ||||
| class OverrideChecker | ||||
| { | ||||
| public: | ||||
| 	using OverrideProxyBySignatureMultiSet = std::multiset<OverrideProxy, OverrideProxy::CompareBySignature>; | ||||
| 
 | ||||
| 	/// @param _errorReporter provides the error logging functionality.
 | ||||
| 	explicit OverrideChecker(langutil::ErrorReporter& _errorReporter): | ||||
| @ -137,12 +138,17 @@ public: | ||||
| 
 | ||||
| 	void check(ContractDefinition const& _contract); | ||||
| 
 | ||||
| private: | ||||
| 	struct CompareByID | ||||
| 	{ | ||||
| 		bool operator()(ContractDefinition const* _a, ContractDefinition const* _b) const; | ||||
| 	}; | ||||
| 
 | ||||
| 	/// Returns all functions of bases (including public state variables) that have not yet been overwritten.
 | ||||
| 	/// May contain the same function multiple times when used with shared bases.
 | ||||
| 	OverrideProxyBySignatureMultiSet const& inheritedFunctions(ContractDefinition const& _contract) const; | ||||
| 	OverrideProxyBySignatureMultiSet const& inheritedModifiers(ContractDefinition const& _contract) const; | ||||
| 
 | ||||
| private: | ||||
| 	void checkIllegalOverrides(ContractDefinition const& _contract); | ||||
| 	/// Performs various checks related to @a _overriding overriding @a _super like
 | ||||
| 	/// different return type, invalid visibility change, etc.
 | ||||
| @ -174,15 +180,8 @@ private: | ||||
| 	/// Resolves an override list of UserDefinedTypeNames to a list of contracts.
 | ||||
| 	std::set<ContractDefinition const*, CompareByID> resolveOverrideList(OverrideSpecifier const& _overrides) const; | ||||
| 
 | ||||
| 	using OverrideProxyBySignatureMultiSet = std::multiset<OverrideProxy, OverrideProxy::CompareBySignature>; | ||||
| 
 | ||||
| 	void checkOverrideList(OverrideProxy _item, OverrideProxyBySignatureMultiSet const& _inherited); | ||||
| 
 | ||||
| 	/// Returns all functions of bases (including public state variables) that have not yet been overwritten.
 | ||||
| 	/// May contain the same function multiple times when used with shared bases.
 | ||||
| 	OverrideProxyBySignatureMultiSet const& inheritedFunctions(ContractDefinition const& _contract) const; | ||||
| 	OverrideProxyBySignatureMultiSet const& inheritedModifiers(ContractDefinition const& _contract) const; | ||||
| 
 | ||||
| 	langutil::ErrorReporter& m_errorReporter; | ||||
| 
 | ||||
| 	/// Cache for inheritedFunctions().
 | ||||
|  | ||||
| @ -797,7 +797,7 @@ public: | ||||
| 	{ | ||||
| 		return | ||||
| 			CallableDeclaration::virtualSemantics() || | ||||
| 			annotation().contract->isInterface(); | ||||
| 			(annotation().contract && annotation().contract->isInterface()); | ||||
| 	} | ||||
| private: | ||||
| 	StateMutability m_stateMutability; | ||||
|  | ||||
							
								
								
									
										14
									
								
								tools/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tools/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| add_executable(solidity-upgrade | ||||
|     solidityUpgrade/main.cpp | ||||
|     solidityUpgrade/UpgradeChange.h | ||||
|     solidityUpgrade/UpgradeChange.cpp | ||||
|     solidityUpgrade/UpgradeSuite.h | ||||
|     solidityUpgrade/Upgrade050.cpp | ||||
|     solidityUpgrade/Upgrade060.cpp | ||||
|     solidityUpgrade/SourceTransform.h | ||||
|     solidityUpgrade/SourceUpgrade.cpp | ||||
| ) | ||||
| target_link_libraries(solidity-upgrade PRIVATE solidity Boost::boost Boost::program_options Boost::system) | ||||
| 
 | ||||
| include(GNUInstallDirs) | ||||
| install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}") | ||||
							
								
								
									
										159
									
								
								tools/solidityUpgrade/SourceTransform.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								tools/solidityUpgrade/SourceTransform.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,159 @@ | ||||
| /*
 | ||||
| 	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/>.
 | ||||
| */ | ||||
| 
 | ||||
| #include <libsolidity/analysis/OverrideChecker.h> | ||||
| 
 | ||||
| #include <libsolidity/ast/AST.h> | ||||
| 
 | ||||
| #include <regex> | ||||
| 
 | ||||
| namespace solidity::tools | ||||
| { | ||||
| 
 | ||||
| /**
 | ||||
|  * Helper that provides functions which analyze certain source locations | ||||
|  * on a textual base. They utilize regular expression to search for | ||||
|  * keywords or to determine formatting. | ||||
|  */ | ||||
| class SourceAnalysis { | ||||
| public: | ||||
| 	static bool isMultilineKeyword( | ||||
| 		langutil::SourceLocation const& _location, | ||||
| 		std::string const& _keyword | ||||
| 	) | ||||
| 	{ | ||||
| 		return regex_search( | ||||
| 			_location.text(), | ||||
| 			std::regex{"(\\b" + _keyword + "\\b\\n|\\r|\\r\\n)"} | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	static bool hasMutabilityKeyword(langutil::SourceLocation const& _location) | ||||
| 	{ | ||||
| 		return regex_search( | ||||
| 			_location.text(), | ||||
| 			std::regex{"(\\b(pure|view|nonpayable|payable)\\b)"} | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	static bool hasVirtualKeyword(langutil::SourceLocation const& _location) | ||||
| 	{ | ||||
| 		return regex_search(_location.text(), std::regex{"(\\b(virtual)\\b)"}); | ||||
| 	} | ||||
| 
 | ||||
| 	static bool hasVisibilityKeyword(langutil::SourceLocation const& _location) | ||||
| 	{ | ||||
| 		return regex_search(_location.text(), std::regex{"\\bpublic\\b"}); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Helper that provides functions which can analyse declarations and | ||||
|  * generate source snippets based on the information retrieved. | ||||
|  */ | ||||
| class SourceGeneration | ||||
| { | ||||
| public: | ||||
| 	using CompareFunction = frontend::OverrideChecker::CompareByID; | ||||
| 	using Contracts = std::set<frontend::ContractDefinition const*, CompareFunction>; | ||||
| 
 | ||||
| 	/// Generates an `override` declaration for single overrides
 | ||||
| 	/// or `override(...)` with contract list for multiple overrides.
 | ||||
| 	static std::string functionOverride(Contracts const& _contracts) | ||||
| 	{ | ||||
| 		if (_contracts.size() <= 1) | ||||
| 			return "override"; | ||||
| 
 | ||||
| 		std::string overrideList; | ||||
| 		for (auto inheritedContract: _contracts) | ||||
| 			overrideList += inheritedContract->name() + ","; | ||||
| 
 | ||||
| 		return "override(" + overrideList.substr(0, overrideList.size() - 1) + ")"; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Helper that provides functions which apply changes to Solidity source code | ||||
|  * on a textual base. In general, these utilize regular expressions applied | ||||
|  * to the given source location. | ||||
|  */ | ||||
| class SourceTransform | ||||
| { | ||||
| public: | ||||
| 	/// Searches for the keyword given and prepends the expression.
 | ||||
| 	/// E.g. `function f() view;` -> `function f() public view;`
 | ||||
| 	static std::string insertBeforeKeyword( | ||||
| 		langutil::SourceLocation const& _location, | ||||
| 		std::string const& _keyword, | ||||
| 		std::string const& _expression | ||||
| 	) | ||||
| 	{ | ||||
| 		return regex_replace( | ||||
| 			_location.text(), | ||||
| 			std::regex{"(\\b" + _keyword + "\\b)"}, | ||||
| 			_expression + " " + _keyword | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	/// Searches for the keyword given and appends the expression.
 | ||||
| 	/// E.g. `function f() public {}` -> `function f() public override {}`
 | ||||
| 	static std::string insertAfterKeyword( | ||||
| 		langutil::SourceLocation const& _location, | ||||
| 		std::string const& _keyword, | ||||
| 		std::string const& _expression | ||||
| 	) | ||||
| 	{ | ||||
| 		bool isMultiline = SourceAnalysis::isMultilineKeyword(_location, _keyword); | ||||
| 		std::string toAppend = isMultiline ? ("\n        " + _expression) : (" " + _expression); | ||||
| 		std::regex keyword{"(\\b" + _keyword + "\\b)"}; | ||||
| 
 | ||||
| 		return regex_replace(_location.text(), keyword, _keyword + toAppend); | ||||
| 	} | ||||
| 
 | ||||
| 	/// Searches for the first right parenthesis and appends the expression
 | ||||
| 	/// given.
 | ||||
| 	/// E.g. `function f() {}` -> `function f() public {}`
 | ||||
| 	static std::string insertAfterRightParenthesis( | ||||
| 		langutil::SourceLocation const& _location, | ||||
| 		std::string const& _expression | ||||
| 	) | ||||
| 	{ | ||||
| 		return regex_replace( | ||||
| 			_location.text(), | ||||
| 			std::regex{"(\\))"}, | ||||
| 			") " + _expression | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	/// Searches for the `function` keyword and its identifier and replaces
 | ||||
| 	/// both by the expression given.
 | ||||
| 	/// E.g. `function Storage() {}` -> `constructor() {}`
 | ||||
| 	static std::string replaceFunctionName( | ||||
| 		langutil::SourceLocation const& _location, | ||||
| 		std::string const& _name, | ||||
| 		std::string const& _expression | ||||
| 	) | ||||
| 	{ | ||||
| 		return regex_replace( | ||||
| 			_location.text(), | ||||
| 			std::regex{"(\\bfunction\\s*" + _name + "\\b)"}, | ||||
| 			_expression | ||||
| 		); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										525
									
								
								tools/solidityUpgrade/SourceUpgrade.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										525
									
								
								tools/solidityUpgrade/SourceUpgrade.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,525 @@ | ||||
| /*
 | ||||
| 	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/>.
 | ||||
| */ | ||||
| #include <tools/solidityUpgrade/SourceUpgrade.h> | ||||
| 
 | ||||
| #include <liblangutil/Exceptions.h> | ||||
| #include <liblangutil/SourceReferenceFormatterHuman.h> | ||||
| 
 | ||||
| #include <libsolidity/ast/AST.h> | ||||
| 
 | ||||
| #include <boost/filesystem.hpp> | ||||
| #include <boost/filesystem/operations.hpp> | ||||
| #include <boost/algorithm/string.hpp> | ||||
| 
 | ||||
| namespace po = boost::program_options; | ||||
| namespace fs = boost::filesystem; | ||||
| 
 | ||||
| using namespace solidity; | ||||
| using namespace solidity::langutil; | ||||
| using namespace solidity::tools; | ||||
| using namespace solidity::util; | ||||
| using namespace solidity::frontend; | ||||
| using namespace std; | ||||
| 
 | ||||
| 
 | ||||
| static string const g_argHelp = "help"; | ||||
| static string const g_argVersion = "version"; | ||||
| static string const g_argInputFile = "input-file"; | ||||
| static string const g_argModules = "modules"; | ||||
| static string const g_argDryRun = "dry-run"; | ||||
| static string const g_argUnsafe = "unsafe"; | ||||
| static string const g_argVerbose = "verbose"; | ||||
| static string const g_argIgnoreMissingFiles = "ignore-missing"; | ||||
| static string const g_argAllowPaths = "allow-paths"; | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
| 
 | ||||
| ostream& out() | ||||
| { | ||||
| 	return cout; | ||||
| } | ||||
| 
 | ||||
| AnsiColorized log() | ||||
| { | ||||
| 	return AnsiColorized(cout, true, {}); | ||||
| } | ||||
| 
 | ||||
| AnsiColorized success() | ||||
| { | ||||
| 	return AnsiColorized(cout, true, {formatting::CYAN}); | ||||
| } | ||||
| 
 | ||||
| AnsiColorized warning() | ||||
| { | ||||
| 	return AnsiColorized(cout, true, {formatting::YELLOW}); | ||||
| } | ||||
| 
 | ||||
| AnsiColorized error() | ||||
| { | ||||
| 	return AnsiColorized(cout, true, {formatting::MAGENTA}); | ||||
| } | ||||
| 
 | ||||
| void logVersion() | ||||
| { | ||||
| 	/// TODO Replace by variable that can be set during build.
 | ||||
| 	out() << "0.1.0" << endl; | ||||
| } | ||||
| 
 | ||||
| void logProgress() | ||||
| { | ||||
| 	out() << "."; | ||||
| 	out().flush(); | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| bool SourceUpgrade::parseArguments(int _argc, char** _argv) | ||||
| { | ||||
| 	po::options_description desc(R"(solidity-upgrade, the Solidity upgrade assistant. | ||||
| 
 | ||||
| The solidity-upgrade tool can help upgrade smart contracts to breaking language features. | ||||
| 
 | ||||
| It does not support all breaking changes for each version, but will hopefully assist | ||||
| upgrading your contracts to the desired Solidity version. | ||||
| 
 | ||||
| solidity-upgrade is distributed in the hope that it will be useful, | ||||
| but WITHOUT ANY WARRANTY. Please be careful when running upgrades on | ||||
| your contracts. | ||||
| 
 | ||||
| Usage: solidity-upgrade [options] contract.sol | ||||
| 
 | ||||
| Allowed options)", | ||||
| 		po::options_description::m_default_line_length, | ||||
| 		po::options_description::m_default_line_length - 23 | ||||
| 	); | ||||
| 	desc.add_options() | ||||
| 		(g_argHelp.c_str(), "Show help message and exit.") | ||||
| 		(g_argVersion.c_str(), "Show version and exit.") | ||||
| 		( | ||||
| 			g_argAllowPaths.c_str(), | ||||
| 			po::value<string>()->value_name("path(s)"), | ||||
| 			"Allow a given path for imports. A list of paths can be supplied by separating them " | ||||
| 			"with a comma." | ||||
| 		) | ||||
| 		(g_argIgnoreMissingFiles.c_str(), "Ignore missing files.") | ||||
| 		( | ||||
| 			g_argModules.c_str(), | ||||
| 			po::value<string>()->value_name("module(s)"), | ||||
| 			"Only activate a specific upgrade module. A list of " | ||||
| 			"modules can be supplied by separating them with a comma." | ||||
| 		) | ||||
| 		(g_argDryRun.c_str(), "Apply changes in-memory only and don't write to input file.") | ||||
| 		(g_argVerbose.c_str(), "Print logs, errors and changes. Shortens output of upgrade patches.") | ||||
| 		(g_argUnsafe.c_str(), "Accept *unsafe* changes."); | ||||
| 
 | ||||
| 
 | ||||
| 	po::options_description allOptions = desc; | ||||
| 	allOptions.add_options()("input-file", po::value<vector<string>>(), "input file"); | ||||
| 
 | ||||
| 	po::positional_options_description filesPositions; | ||||
| 	filesPositions.add("input-file", -1); | ||||
| 
 | ||||
| 	// parse the compiler arguments
 | ||||
| 	try | ||||
| 	{ | ||||
| 		po::command_line_parser cmdLineParser(_argc, _argv); | ||||
| 		cmdLineParser.style( | ||||
| 			po::command_line_style::default_style & (~po::command_line_style::allow_guessing) | ||||
| 		); | ||||
| 		cmdLineParser.options(allOptions).positional(filesPositions); | ||||
| 		po::store(cmdLineParser.run(), m_args); | ||||
| 	} | ||||
| 	catch (po::error const& _exception) | ||||
| 	{ | ||||
| 		error() << _exception.what() << endl; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1)) | ||||
| 	{ | ||||
| 		out() << endl; | ||||
| 		log() << desc; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_args.count(g_argVersion)) | ||||
| 	{ | ||||
| 		logVersion(); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_args.count(g_argModules)) | ||||
| 	{ | ||||
| 		vector<string> moduleArgs; | ||||
| 		auto modules = boost::split( | ||||
| 			moduleArgs, m_args[g_argModules].as<string>(), boost::is_any_of(",") | ||||
| 		); | ||||
| 
 | ||||
| 		/// All modules are activated by default. Clear them before activating single ones.
 | ||||
| 		m_suite.deactivateModules(); | ||||
| 
 | ||||
| 		for (string const& module: modules) | ||||
| 		{ | ||||
| 			if (module == "constructor") | ||||
| 				m_suite.activateModule(Module::ConstructorKeyword); | ||||
| 			else if (module == "visibility") | ||||
| 				m_suite.activateModule(Module::VisibilitySpecifier); | ||||
| 			else if (module == "abstract") | ||||
| 				m_suite.activateModule(Module::AbstractContract); | ||||
| 			else if (module == "override") | ||||
| 				m_suite.activateModule(Module::OverridingFunction); | ||||
| 			else if (module == "virtual") | ||||
| 				m_suite.activateModule(Module::VirtualFunction); | ||||
| 			else | ||||
| 			{ | ||||
| 				error() << "Unknown upgrade module \"" + module + "\"" << endl; | ||||
| 				return false; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/// TODO Share with solc commandline interface.
 | ||||
| 	if (m_args.count(g_argAllowPaths)) | ||||
| 	{ | ||||
| 		vector<string> paths; | ||||
| 		auto allowedPaths = boost::split( | ||||
| 			paths, m_args[g_argAllowPaths].as<string>(), boost::is_any_of(",") | ||||
| 		); | ||||
| 		for (string const& path: allowedPaths) | ||||
| 		{ | ||||
| 			auto filesystem_path = boost::filesystem::path(path); | ||||
| 			/// If the given path had a trailing slash, the Boost filesystem
 | ||||
| 			/// path will have it's last component set to '.'. This breaks
 | ||||
| 			/// path comparison in later parts of the code, so we need to strip
 | ||||
| 			/// it.
 | ||||
| 			if (filesystem_path.filename() == ".") | ||||
| 				filesystem_path.remove_filename(); | ||||
| 			m_allowedDirectories.push_back(filesystem_path); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| void SourceUpgrade::printPrologue() | ||||
| { | ||||
| 	out() << endl; | ||||
| 	out() << endl; | ||||
| 
 | ||||
| 	log() << | ||||
| 		"solidity-upgrade does not support all breaking changes for each version." << | ||||
| 		endl << | ||||
| 		"Please run `solidity-upgrade --help` and get a list of implemented upgrades." << | ||||
| 		endl << | ||||
| 		endl; | ||||
| 
 | ||||
| 	log() << | ||||
| 		"Running analysis (and upgrade) on given source files." << | ||||
| 		endl; | ||||
| } | ||||
| 
 | ||||
| bool SourceUpgrade::processInput() | ||||
| { | ||||
| 	if (!readInputFiles()) | ||||
| 		return false; | ||||
| 
 | ||||
| 	resetCompiler(fileReader()); | ||||
| 
 | ||||
| 	tryCompile(); | ||||
| 	runUpgrade(); | ||||
| 
 | ||||
| 	printStatistics(); | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| void SourceUpgrade::tryCompile() const | ||||
| { | ||||
| 	bool verbose = m_args.count(g_argVerbose); | ||||
| 
 | ||||
| 	if (verbose) | ||||
| 		log() << "Running compilation phases." << endl << endl; | ||||
| 	else | ||||
| 		logProgress(); | ||||
| 
 | ||||
| 	try | ||||
| 	{ | ||||
| 		if (m_compiler->parse()) | ||||
| 		{ | ||||
| 			if (m_compiler->analyze()) | ||||
| 				m_compiler->compile(); | ||||
| 			else | ||||
| 				if (verbose) | ||||
| 				{ | ||||
| 					error() << | ||||
| 						"Compilation errors that solidity-upgrade may resolve occurred." << | ||||
| 						endl << | ||||
| 						endl; | ||||
| 
 | ||||
| 					printErrors(); | ||||
| 				} | ||||
| 		} | ||||
| 		else | ||||
| 			if (verbose) | ||||
| 			{ | ||||
| 				error() << | ||||
| 					"Compilation errors that solidity-upgrade cannot resolve occurred." << | ||||
| 					endl << | ||||
| 					endl; | ||||
| 
 | ||||
| 				printErrors(); | ||||
| 			} | ||||
| 	} | ||||
| 	catch (Exception const& _exception) | ||||
| 	{ | ||||
| 		error() << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl; | ||||
| 	} | ||||
| 	catch (std::exception const& _e) | ||||
| 	{ | ||||
| 		error() << (_e.what() ? ": " + string(_e.what()) : ".") << endl; | ||||
| 	} | ||||
| 	catch (...) | ||||
| 	{ | ||||
| 		error() << "Unknown exception during compilation." << endl; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void SourceUpgrade::runUpgrade() | ||||
| { | ||||
| 	bool recompile = true; | ||||
| 
 | ||||
| 	while (recompile && !m_compiler->errors().empty()) | ||||
| 	{ | ||||
| 		for (auto& sourceCode: m_sourceCodes) | ||||
| 		{ | ||||
| 			recompile = analyzeAndUpgrade(sourceCode); | ||||
| 			if (recompile) | ||||
| 				break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (recompile) | ||||
| 		{ | ||||
| 			m_suite.reset(); | ||||
| 			resetCompiler(); | ||||
| 			tryCompile(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| bool SourceUpgrade::analyzeAndUpgrade(pair<string, string> const& _sourceCode) | ||||
| { | ||||
| 	bool applyUnsafe = m_args.count(g_argUnsafe); | ||||
| 	bool verbose = m_args.count(g_argVerbose); | ||||
| 
 | ||||
| 	if (verbose) | ||||
| 		log() << "Analyzing and upgrading " << _sourceCode.first << "." << endl; | ||||
| 
 | ||||
| 	if (m_compiler->state() >= CompilerStack::State::AnalysisPerformed) | ||||
| 		m_suite.analyze(m_compiler->ast(_sourceCode.first)); | ||||
| 
 | ||||
| 	if (!m_suite.changes().empty()) | ||||
| 	{ | ||||
| 		auto& change = m_suite.changes().front(); | ||||
| 
 | ||||
| 		if (verbose) | ||||
| 			change.log(true); | ||||
| 
 | ||||
| 		if (change.level() == UpgradeChange::Level::Safe) | ||||
| 		{ | ||||
| 			applyChange(_sourceCode, change); | ||||
| 			return true; | ||||
| 		} | ||||
| 		else if (change.level() == UpgradeChange::Level::Unsafe) | ||||
| 		{ | ||||
| 			if (applyUnsafe) | ||||
| 			{ | ||||
| 				applyChange(_sourceCode, change); | ||||
| 				return true; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| void SourceUpgrade::applyChange( | ||||
| 	pair<string, string> const& _sourceCode, | ||||
| 	UpgradeChange& _change | ||||
| ) | ||||
| { | ||||
| 	bool dryRun = m_args.count(g_argDryRun); | ||||
| 	bool verbose = m_args.count(g_argVerbose); | ||||
| 
 | ||||
| 	if (verbose) | ||||
| 	{ | ||||
| 		log() << "Applying change to " << _sourceCode.first << endl << endl; | ||||
| 		log() << _change.patch(); | ||||
| 	} | ||||
| 
 | ||||
| 	_change.apply(); | ||||
| 	m_sourceCodes[_sourceCode.first] = _change.source(); | ||||
| 
 | ||||
| 	if (!dryRun) | ||||
| 		writeInputFile(_sourceCode.first, _change.source()); | ||||
| } | ||||
| 
 | ||||
| void SourceUpgrade::printErrors() const | ||||
| { | ||||
| 	auto formatter = make_unique<langutil::SourceReferenceFormatterHuman>(cout, true); | ||||
| 
 | ||||
| 	for (auto const& error: m_compiler->errors()) | ||||
| 		if (error->type() != langutil::Error::Type::Warning) | ||||
| 			formatter->printErrorInformation(*error); | ||||
| } | ||||
| 
 | ||||
| void SourceUpgrade::printStatistics() const | ||||
| { | ||||
| 	out() << endl; | ||||
| 	out() << endl; | ||||
| 	out() << "After upgrade:" << endl; | ||||
| 	out() << endl; | ||||
| 	error() << "Found " << m_compiler->errors().size() << " errors." << endl; | ||||
| 	success() << "Found " << m_suite.changes().size() << " upgrades." << endl; | ||||
| } | ||||
| 
 | ||||
| bool SourceUpgrade::readInputFiles() | ||||
| { | ||||
| 	bool ignoreMissing = m_args.count(g_argIgnoreMissingFiles); | ||||
| 
 | ||||
| 	/// TODO Share with solc commandline interface.
 | ||||
| 	if (m_args.count(g_argInputFile)) | ||||
| 		for (string path: m_args[g_argInputFile].as<vector<string>>()) | ||||
| 		{ | ||||
| 			auto infile = boost::filesystem::path(path); | ||||
| 			if (!boost::filesystem::exists(infile)) | ||||
| 			{ | ||||
| 				if (!ignoreMissing) | ||||
| 				{ | ||||
| 					error() << infile << " is not found." << endl; | ||||
| 					return false; | ||||
| 				} | ||||
| 				else | ||||
| 					error() << infile << " is not found. Skipping." << endl; | ||||
| 
 | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			if (!boost::filesystem::is_regular_file(infile)) | ||||
| 			{ | ||||
| 				if (!ignoreMissing) | ||||
| 				{ | ||||
| 					error() << infile << " is not a valid file." << endl; | ||||
| 					return false; | ||||
| 				} | ||||
| 				else | ||||
| 					error() << infile << " is not a valid file. Skipping." << endl; | ||||
| 
 | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			m_sourceCodes[infile.generic_string()] = readFileAsString(infile.string()); | ||||
| 			path = boost::filesystem::canonical(infile).string(); | ||||
| 		} | ||||
| 
 | ||||
| 	if (m_sourceCodes.size() == 0) | ||||
| 	{ | ||||
| 		warning() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| bool SourceUpgrade::writeInputFile(string const& _path, string const& _source) | ||||
| { | ||||
| 	bool verbose = m_args.count(g_argVerbose); | ||||
| 
 | ||||
| 	if (verbose) | ||||
| 	{ | ||||
| 		out() << endl; | ||||
| 		log() << "Writing to input file " << _path << "." << endl; | ||||
| 	} | ||||
| 
 | ||||
| 	ofstream file(_path, ios::trunc); | ||||
| 	file << _source; | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| ReadCallback::Callback SourceUpgrade::fileReader() | ||||
| { | ||||
| 	/// TODO Share with solc commandline interface.
 | ||||
| 	ReadCallback::Callback fileReader = [this](string const&, string const& _path) | ||||
| 	{ | ||||
| 		try | ||||
| 		{ | ||||
| 			auto path = boost::filesystem::path(_path); | ||||
| 			auto canonicalPath = boost::filesystem::weakly_canonical(path); | ||||
| 			bool isAllowed = false; | ||||
| 			for (auto const& allowedDir: m_allowedDirectories) | ||||
| 			{ | ||||
| 				// If dir is a prefix of boostPath, we are fine.
 | ||||
| 				if ( | ||||
| 					std::distance(allowedDir.begin(), allowedDir.end()) <= std::distance(canonicalPath.begin(), canonicalPath.end()) && | ||||
| 					std::equal(allowedDir.begin(), allowedDir.end(), canonicalPath.begin()) | ||||
| 				) | ||||
| 				{ | ||||
| 					isAllowed = true; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 			if (!isAllowed) | ||||
| 				return ReadCallback::Result{false, "File outside of allowed directories."}; | ||||
| 
 | ||||
| 			if (!boost::filesystem::exists(canonicalPath)) | ||||
| 				return ReadCallback::Result{false, "File not found."}; | ||||
| 
 | ||||
| 			if (!boost::filesystem::is_regular_file(canonicalPath)) | ||||
| 				return ReadCallback::Result{false, "Not a valid file."}; | ||||
| 
 | ||||
| 			auto contents = readFileAsString(canonicalPath.string()); | ||||
| 			m_sourceCodes[path.generic_string()] = contents; | ||||
| 			return ReadCallback::Result{true, contents}; | ||||
| 		} | ||||
| 		catch (Exception const& _exception) | ||||
| 		{ | ||||
| 			return ReadCallback::Result{false, "Exception in read callback: " + boost::diagnostic_information(_exception)}; | ||||
| 		} | ||||
| 		catch (...) | ||||
| 		{ | ||||
| 			return ReadCallback::Result{false, "Unknown exception in read callback."}; | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	return fileReader; | ||||
| } | ||||
| 
 | ||||
| void SourceUpgrade::resetCompiler() | ||||
| { | ||||
| 	m_compiler->reset(); | ||||
| 	m_compiler->setSources(m_sourceCodes); | ||||
| 	m_compiler->setParserErrorRecovery(true); | ||||
| } | ||||
| 
 | ||||
| void SourceUpgrade::resetCompiler(ReadCallback::Callback const& _callback) | ||||
| { | ||||
| 	m_compiler.reset(new CompilerStack(_callback)); | ||||
| 	m_compiler->setSources(m_sourceCodes); | ||||
| 	m_compiler->setParserErrorRecovery(true); | ||||
| } | ||||
							
								
								
									
										158
									
								
								tools/solidityUpgrade/SourceUpgrade.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								tools/solidityUpgrade/SourceUpgrade.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,158 @@ | ||||
| /*
 | ||||
| 	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/>.
 | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <tools/solidityUpgrade/UpgradeChange.h> | ||||
| #include <tools/solidityUpgrade/Upgrade050.h> | ||||
| #include <tools/solidityUpgrade/Upgrade060.h> | ||||
| 
 | ||||
| #include <libsolidity/interface/CompilerStack.h> | ||||
| #include <libsolidity/interface/DebugSettings.h> | ||||
| #include <libyul/AssemblyStack.h> | ||||
| #include <liblangutil/EVMVersion.h> | ||||
| 
 | ||||
| #include <boost/program_options.hpp> | ||||
| #include <boost/filesystem/path.hpp> | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| namespace solidity::tools | ||||
| { | ||||
| 
 | ||||
| /**
 | ||||
|  * The Solidity source upgrade tool. It supplies a command line interface | ||||
|  * and connects this to a compiler stack that the upgrade logic is facilitated | ||||
|  * with. | ||||
|  */ | ||||
| class SourceUpgrade | ||||
| { | ||||
| public: | ||||
| 	/// Parse command line arguments and return false in case of a failure.
 | ||||
| 	bool parseArguments(int _argc, char** _argv); | ||||
| 	/// Prints additional information on the upgrade tool.
 | ||||
| 	void printPrologue(); | ||||
| 	/// Parse / compile files and runs upgrade analysis on them.
 | ||||
| 	bool processInput(); | ||||
| 
 | ||||
| private: | ||||
| 	/// All available upgrade modules
 | ||||
| 	enum class Module | ||||
| 	{ | ||||
| 		ConstructorKeyword, | ||||
| 		VisibilitySpecifier, | ||||
| 		AbstractContract, | ||||
| 		OverridingFunction, | ||||
| 		VirtualFunction | ||||
| 	}; | ||||
| 
 | ||||
| 	/// Upgrade suite that hosts all available modules.
 | ||||
| 	class Suite: public UpgradeSuite | ||||
| 	{ | ||||
| 	public: | ||||
| 		void analyze(frontend::SourceUnit const& _sourceUnit) | ||||
| 		{ | ||||
| 			/// Solidity 0.5.0
 | ||||
| 			if (isActivated(Module::ConstructorKeyword)) | ||||
| 				ConstructorKeyword{m_changes}.analyze(_sourceUnit); | ||||
| 			if (isActivated(Module::VisibilitySpecifier)) | ||||
| 				VisibilitySpecifier{m_changes}.analyze(_sourceUnit); | ||||
| 
 | ||||
| 			/// Solidity 0.6.0
 | ||||
| 			if (isActivated(Module::AbstractContract)) | ||||
| 				AbstractContract{m_changes}.analyze(_sourceUnit); | ||||
| 			if (isActivated(Module::OverridingFunction)) | ||||
| 				OverridingFunction{m_changes}.analyze(_sourceUnit); | ||||
| 			if (isActivated(Module::VirtualFunction)) | ||||
| 				VirtualFunction{m_changes}.analyze(_sourceUnit); | ||||
| 		} | ||||
| 
 | ||||
| 		void activateModule(Module _module) { m_modules.insert(_module); } | ||||
| 		void deactivateModules() { m_modules.clear(); } | ||||
| 
 | ||||
| 	private: | ||||
| 		bool isActivated(Module _module) const | ||||
| 		{ | ||||
| 			return m_modules.find(_module) != m_modules.end(); | ||||
| 		} | ||||
| 
 | ||||
| 		/// All modules are activated by default. Clear them before activating
 | ||||
| 		/// single ones.
 | ||||
| 		std::set<Module> m_modules = { | ||||
| 			Module::ConstructorKeyword, | ||||
| 			Module::VisibilitySpecifier, | ||||
| 			Module::AbstractContract, | ||||
| 			Module::OverridingFunction, | ||||
| 			Module::VirtualFunction | ||||
| 		}; | ||||
| 	}; | ||||
| 
 | ||||
| 	/// Parses the current sources and runs analyses as well as compilation on
 | ||||
| 	/// them if parsing was successful.
 | ||||
| 	void tryCompile() const; | ||||
| 	/// Analyses and upgrades the sources given. The upgrade happens in a loop,
 | ||||
| 	/// applying one change at a time, which is run until no applicable changes
 | ||||
| 	/// are found any more. Only one change is done at a time and all sources
 | ||||
| 	/// are being compiled again after each change.
 | ||||
| 	void runUpgrade(); | ||||
| 	/// Runs upgrade analysis on source and applies upgrades changes to it.
 | ||||
| 	/// Returns `true` if there're still changes that can be applied,
 | ||||
| 	/// `false` otherwise.
 | ||||
| 	bool analyzeAndUpgrade( | ||||
| 		std::pair<std::string, std::string> const& _sourceCode | ||||
| 	); | ||||
| 
 | ||||
| 	/// Applies the change given to its source code. If no `--dry-run` was
 | ||||
| 	/// passed via the commandline, the upgraded source code is written back
 | ||||
| 	/// to its file.
 | ||||
| 	void applyChange( | ||||
| 		std::pair<std::string, std::string> const& _sourceCode, | ||||
| 		UpgradeChange& _change | ||||
| 	); | ||||
| 
 | ||||
| 	/// Prints all errors (excluding warnings) the compiler currently reported.
 | ||||
| 	void printErrors() const; | ||||
| 	/// Prints error and upgrade overview at the end of each full run.
 | ||||
| 	void printStatistics() const; | ||||
| 
 | ||||
| 	/// Reads all input files given and stores sources in the internal data
 | ||||
| 	/// structure. Reports errors if files cannot be found.
 | ||||
| 	bool readInputFiles(); | ||||
| 	/// Writes source to file given.
 | ||||
| 	bool writeInputFile(std::string const& _path, std::string const& _source); | ||||
| 	/// Returns a file reader function that fills `m_sources`.
 | ||||
| 	frontend::ReadCallback::Callback fileReader(); | ||||
| 
 | ||||
| 	/// Resets the compiler stack and configures sources to compile.
 | ||||
| 	/// Also enables error recovery.
 | ||||
| 	void resetCompiler(); | ||||
| 	/// Resets the compiler stack and configures sources to compile.
 | ||||
| 	/// Also enables error recovery. Passes read callback to the compiler stack.
 | ||||
| 	void resetCompiler(frontend::ReadCallback::Callback const& _callback); | ||||
| 
 | ||||
| 	/// Compiler arguments variable map
 | ||||
| 	boost::program_options::variables_map m_args; | ||||
| 	/// Map of input files to source code strings
 | ||||
| 	std::map<std::string, std::string> m_sourceCodes; | ||||
| 	/// Solidity compiler stack
 | ||||
| 	std::unique_ptr<frontend::CompilerStack> m_compiler; | ||||
| 	/// List of allowed directories to read files from
 | ||||
| 	std::vector<boost::filesystem::path> m_allowedDirectories; | ||||
| 	/// Holds all upgrade modules and source upgrades.
 | ||||
| 	Suite m_suite; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										58
									
								
								tools/solidityUpgrade/Upgrade050.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								tools/solidityUpgrade/Upgrade050.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,58 @@ | ||||
| /*
 | ||||
| 	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/>.
 | ||||
| */ | ||||
| #include <tools/solidityUpgrade/Upgrade050.h> | ||||
| #include <tools/solidityUpgrade/SourceTransform.h> | ||||
| 
 | ||||
| #include <libsolidity/analysis/OverrideChecker.h> | ||||
| 
 | ||||
| #include <libyul/AsmData.h> | ||||
| 
 | ||||
| #include <regex> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace solidity; | ||||
| using namespace solidity::frontend; | ||||
| using namespace solidity::tools; | ||||
| 
 | ||||
| void ConstructorKeyword::endVisit(ContractDefinition const& _contract) | ||||
| { | ||||
| 	for (auto const* function: _contract.definedFunctions()) | ||||
| 		if (function->name() == _contract.name()) | ||||
| 			m_changes.push_back( | ||||
| 				UpgradeChange{ | ||||
| 					UpgradeChange::Level::Safe, | ||||
| 					function->location(), | ||||
| 					SourceTransform::replaceFunctionName( | ||||
| 						function->location(), | ||||
| 						function->name(), | ||||
| 						"constructor" | ||||
| 					) | ||||
| 				} | ||||
| 			); | ||||
| } | ||||
| 
 | ||||
| void VisibilitySpecifier::endVisit(FunctionDefinition const& _function) | ||||
| { | ||||
| 	if (_function.noVisibilitySpecified()) | ||||
| 		m_changes.push_back( | ||||
| 			UpgradeChange{ | ||||
| 				UpgradeChange::Level::Safe, | ||||
| 				_function.location(), | ||||
| 				SourceTransform::insertAfterRightParenthesis(_function.location(), "public") | ||||
| 			} | ||||
| 		); | ||||
| } | ||||
							
								
								
									
										58
									
								
								tools/solidityUpgrade/Upgrade050.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								tools/solidityUpgrade/Upgrade050.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,58 @@ | ||||
| /*
 | ||||
| 	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/>.
 | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <tools/solidityUpgrade/UpgradeChange.h> | ||||
| #include <tools/solidityUpgrade/UpgradeSuite.h> | ||||
| 
 | ||||
| #include <libsolidity/ast/ASTVisitor.h> | ||||
| 
 | ||||
| namespace solidity::tools | ||||
| { | ||||
| 
 | ||||
| /**
 | ||||
|  * Module that performs analysis on the AST. It visits all contract | ||||
|  * definitions and its defined functions and reports a source upgrade, | ||||
|  * if one of the declared functions is the constructor but does not | ||||
|  * use the `constructor` keyword. | ||||
|  */ | ||||
| class ConstructorKeyword: public AnalysisUpgrade | ||||
| { | ||||
| public: | ||||
| 	using AnalysisUpgrade::AnalysisUpgrade; | ||||
| 
 | ||||
| 	void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } | ||||
| private: | ||||
| 	void endVisit(frontend::ContractDefinition const& _contract); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Module that performs analysis on the AST. It visits function definitions | ||||
|  * and reports a source upgrade, if this function's visibility is `public`, | ||||
|  * but not marked explicitly as such. | ||||
|  */ | ||||
| class VisibilitySpecifier: public AnalysisUpgrade | ||||
| { | ||||
| public: | ||||
| 	using AnalysisUpgrade::AnalysisUpgrade; | ||||
| 
 | ||||
| 	void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } | ||||
| private: | ||||
| 	void endVisit(frontend::FunctionDefinition const& _function); | ||||
| }; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										212
									
								
								tools/solidityUpgrade/Upgrade060.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								tools/solidityUpgrade/Upgrade060.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,212 @@ | ||||
| /*
 | ||||
| 	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/>.
 | ||||
| */ | ||||
| #include <tools/solidityUpgrade/Upgrade060.h> | ||||
| #include <tools/solidityUpgrade/SourceTransform.h> | ||||
| 
 | ||||
| #include <libsolidity/analysis/OverrideChecker.h> | ||||
| 
 | ||||
| #include <libyul/AsmData.h> | ||||
| 
 | ||||
| #include <regex> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace solidity; | ||||
| using namespace solidity::frontend; | ||||
| using namespace solidity::tools; | ||||
| 
 | ||||
| using Contracts = set<ContractDefinition const*, OverrideChecker::CompareByID>; | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
| 
 | ||||
| inline string appendOverride( | ||||
| 	FunctionDefinition const& _function, | ||||
| 	Contracts const& _expectedContracts | ||||
| ) | ||||
| { | ||||
| 	auto location = _function.location(); | ||||
| 	string upgradedCode; | ||||
| 	string overrideExpression = SourceGeneration::functionOverride(_expectedContracts); | ||||
| 
 | ||||
| 	if (SourceAnalysis::hasVirtualKeyword(location)) | ||||
| 		upgradedCode = SourceTransform::insertAfterKeyword( | ||||
| 			location, | ||||
| 			"virtual", | ||||
| 			overrideExpression | ||||
| 		); | ||||
| 	else if (SourceAnalysis::hasMutabilityKeyword(location)) | ||||
| 		upgradedCode = SourceTransform::insertAfterKeyword( | ||||
| 			location, | ||||
| 			stateMutabilityToString(_function.stateMutability()), | ||||
| 			overrideExpression | ||||
| 		); | ||||
| 	else if (SourceAnalysis::hasVisibilityKeyword(location)) | ||||
| 		upgradedCode = SourceTransform::insertAfterKeyword( | ||||
| 			location, | ||||
| 			Declaration::visibilityToString(_function.visibility()), | ||||
| 			overrideExpression | ||||
| 		); | ||||
| 	else | ||||
| 		upgradedCode = SourceTransform::insertAfterRightParenthesis( | ||||
| 			location, | ||||
| 			overrideExpression | ||||
| 		); | ||||
| 
 | ||||
| 	return upgradedCode; | ||||
| } | ||||
| 
 | ||||
| inline string appendVirtual(FunctionDefinition const& _function) | ||||
| { | ||||
| 	auto location = _function.location(); | ||||
| 	string upgradedCode; | ||||
| 
 | ||||
| 	if (SourceAnalysis::hasMutabilityKeyword(location)) | ||||
| 		upgradedCode = SourceTransform::insertAfterKeyword( | ||||
| 			location, | ||||
| 			stateMutabilityToString(_function.stateMutability()), | ||||
| 			"virtual" | ||||
| 		); | ||||
| 	else if (SourceAnalysis::hasVisibilityKeyword(location)) | ||||
| 		upgradedCode = SourceTransform::insertAfterKeyword( | ||||
| 			location, | ||||
| 			Declaration::visibilityToString(_function.visibility()), | ||||
| 			"virtual" | ||||
| 		); | ||||
| 	else | ||||
| 		upgradedCode = SourceTransform::insertAfterRightParenthesis( | ||||
| 			_function.location(), | ||||
| 			"virtual" | ||||
| 		); | ||||
| 
 | ||||
| 	return upgradedCode; | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void AbstractContract::endVisit(ContractDefinition const& _contract) | ||||
| { | ||||
| 	bool isFullyImplemented = _contract.annotation().unimplementedFunctions.empty(); | ||||
| 
 | ||||
| 	if ( | ||||
| 		!isFullyImplemented && | ||||
| 		!_contract.abstract() && | ||||
| 		!_contract.isInterface() | ||||
| 	) | ||||
| 		m_changes.push_back( | ||||
| 			UpgradeChange{ | ||||
| 				UpgradeChange::Level::Safe, | ||||
| 				_contract.location(), | ||||
| 				SourceTransform::insertBeforeKeyword(_contract.location(), "contract", "abstract") | ||||
| 			} | ||||
| 		); | ||||
| } | ||||
| 
 | ||||
| void OverridingFunction::endVisit(ContractDefinition const& _contract) | ||||
| { | ||||
| 	auto const& inheritedFunctions = m_overrideChecker.inheritedFunctions(_contract); | ||||
| 
 | ||||
| 	for (auto const* function: _contract.definedFunctions()) | ||||
| 	{ | ||||
| 		Contracts expectedContracts; | ||||
| 		OverrideProxy proxy{function}; | ||||
| 
 | ||||
| 		if (!function->isConstructor()) | ||||
| 		{ | ||||
| 			/// Build list of contracts expected to be mentioned in the override list (if any).
 | ||||
| 			for (auto [begin, end] = inheritedFunctions.equal_range(proxy); begin != end; begin++) | ||||
| 				expectedContracts.insert(&begin->contract()); | ||||
| 
 | ||||
| 			/// Add override with contract list, if needed.
 | ||||
| 			if (!function->overrides() && expectedContracts.size() > 1) | ||||
| 				m_changes.push_back( | ||||
| 					UpgradeChange{ | ||||
| 						UpgradeChange::Level::Safe, | ||||
| 						function->location(), | ||||
| 						appendOverride(*function, expectedContracts) | ||||
| 					} | ||||
| 				); | ||||
| 
 | ||||
| 			for (auto [begin, end] = inheritedFunctions.equal_range(proxy); begin != end; begin++) | ||||
| 			{ | ||||
| 				auto& super = (*begin); | ||||
| 				auto functionType = FunctionType(*function).asCallableFunction(false); | ||||
| 				auto superType = super.functionType()->asCallableFunction(false); | ||||
| 
 | ||||
| 				if (functionType && functionType->hasEqualParameterTypes(*superType)) | ||||
| 				{ | ||||
| 					/// If function does not specify override and no override with
 | ||||
| 					/// contract list was added before.
 | ||||
| 					if (!function->overrides() && expectedContracts.size() <= 1) | ||||
| 						m_changes.push_back( | ||||
| 							UpgradeChange{ | ||||
| 								UpgradeChange::Level::Safe, | ||||
| 								function->location(), | ||||
| 								appendOverride(*function, expectedContracts) | ||||
| 							} | ||||
| 						); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void VirtualFunction::endVisit(ContractDefinition const& _contract) | ||||
| { | ||||
| 	auto const& inheritedFunctions = m_overrideChecker.inheritedFunctions(_contract); | ||||
| 
 | ||||
| 	for (FunctionDefinition const* function: _contract.definedFunctions()) | ||||
| 	{ | ||||
| 		OverrideProxy proxy{function}; | ||||
| 
 | ||||
| 		if (!function->isConstructor()) | ||||
| 		{ | ||||
| 			if ( | ||||
| 				!function->markedVirtual() && | ||||
| 				!function->isImplemented() && | ||||
| 				!function->virtualSemantics() && | ||||
| 				function->visibility() > Visibility::Private | ||||
| 			) | ||||
| 			{ | ||||
| 				m_changes.push_back( | ||||
| 					UpgradeChange{ | ||||
| 						UpgradeChange::Level::Safe, | ||||
| 						function->location(), | ||||
| 						appendVirtual(*function) | ||||
| 					} | ||||
| 				); | ||||
| 			} | ||||
| 
 | ||||
| 			for (auto [begin, end] = inheritedFunctions.equal_range(proxy); begin != end; begin++) | ||||
| 			{ | ||||
| 				auto& super = (*begin); | ||||
| 				if ( | ||||
| 					!function->markedVirtual() && | ||||
| 					!super.virtualSemantics() | ||||
| 				) | ||||
| 				{ | ||||
| 					m_changes.push_back( | ||||
| 						UpgradeChange{ | ||||
| 							UpgradeChange::Level::Safe, | ||||
| 							function->location(), | ||||
| 							appendVirtual(*function) | ||||
| 						} | ||||
| 					); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										69
									
								
								tools/solidityUpgrade/Upgrade060.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								tools/solidityUpgrade/Upgrade060.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| /*
 | ||||
| 	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/>.
 | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <tools/solidityUpgrade/UpgradeChange.h> | ||||
| #include <tools/solidityUpgrade/UpgradeSuite.h> | ||||
| 
 | ||||
| #include <libsolidity/ast/ASTVisitor.h> | ||||
| 
 | ||||
| namespace solidity::tools | ||||
| { | ||||
| 
 | ||||
| /**
 | ||||
|  * Module that performs analysis on the AST. Finds abstract contracts that are | ||||
|  * not marked as such and adds the `abstract` keyword. | ||||
|  */ | ||||
| class AbstractContract: public AnalysisUpgrade | ||||
| { | ||||
| public: | ||||
| 	using AnalysisUpgrade::AnalysisUpgrade; | ||||
| 
 | ||||
| 	void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } | ||||
| private: | ||||
| 	void endVisit(frontend::ContractDefinition const& _contract); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Module that performs analysis on the AST. Finds functions that need to be | ||||
|  * marked `override` and adds the keyword to the function header. | ||||
|  */ | ||||
| class OverridingFunction: public AnalysisUpgrade | ||||
| { | ||||
| public: | ||||
| 	using AnalysisUpgrade::AnalysisUpgrade; | ||||
| 
 | ||||
| 	void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } | ||||
| private: | ||||
| 	void endVisit(frontend::ContractDefinition const& _contract); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Module that performs analysis on the AST. Finds functions that need to be | ||||
|  * marked `virtual` and adds the keyword to the function header. | ||||
|  */ | ||||
| class VirtualFunction: public AnalysisUpgrade | ||||
| { | ||||
| public: | ||||
| 	using AnalysisUpgrade::AnalysisUpgrade; | ||||
| 
 | ||||
| 	void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } | ||||
| private: | ||||
| 	void endVisit(frontend::ContractDefinition const& _function); | ||||
| }; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										73
									
								
								tools/solidityUpgrade/UpgradeChange.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								tools/solidityUpgrade/UpgradeChange.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| /*
 | ||||
| 	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/>.
 | ||||
| */ | ||||
| #include <tools/solidityUpgrade/UpgradeChange.h> | ||||
| 
 | ||||
| #include <liblangutil/SourceReferenceExtractor.h> | ||||
| #include <liblangutil/SourceReferenceFormatterHuman.h> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace solidity; | ||||
| using namespace solidity::langutil; | ||||
| using namespace solidity::util; | ||||
| using namespace solidity::tools; | ||||
| 
 | ||||
| void UpgradeChange::apply() | ||||
| { | ||||
| 	m_source.replace(m_location.start, m_location.end - m_location.start, m_patch); | ||||
| } | ||||
| 
 | ||||
| void UpgradeChange::log(bool const _shorten) const | ||||
| { | ||||
| 	stringstream os; | ||||
| 	SourceReferenceFormatterHuman formatter{os, true}; | ||||
| 
 | ||||
| 	string start = to_string(m_location.start); | ||||
| 	string end = to_string(m_location.end); | ||||
| 
 | ||||
| 	auto color = m_level == Level::Unsafe ? formatting::MAGENTA : formatting::CYAN; | ||||
| 	auto level = m_level == Level::Unsafe ? "unsafe" : "safe"; | ||||
| 
 | ||||
| 	os << endl; | ||||
| 	AnsiColorized(os, true, {formatting::BOLD, color}) << "Upgrade change (" << level << ")" << endl; | ||||
| 	os << "=======================" << endl; | ||||
| 	formatter.printSourceLocation(SourceReferenceExtractor::extract(&m_location)); | ||||
| 	os << endl; | ||||
| 
 | ||||
| 	LineColumn lineEnd = m_location.source->translatePositionToLineColumn(m_location.end); | ||||
| 	int const leftpad = static_cast<int>(log10(max(lineEnd.line, 1))) + 2; | ||||
| 
 | ||||
| 	stringstream output; | ||||
| 	output << (_shorten ? shortenSource(m_patch) : m_patch); | ||||
| 
 | ||||
| 	string line; | ||||
| 	while (getline(output, line)) | ||||
| 	{ | ||||
| 		os << string(leftpad, ' '); | ||||
| 		AnsiColorized(os, true, {formatting::BOLD, formatting::BLUE}) << "| "; | ||||
| 		AnsiColorized(os, true, {}) << line << endl; | ||||
| 	} | ||||
| 	cout << os.str(); | ||||
| 	cout << endl; | ||||
| } | ||||
| 
 | ||||
| string UpgradeChange::shortenSource(string const& _source) | ||||
| { | ||||
| 	size_t constexpr maxSourceLength = 1000; | ||||
| 	if (_source.size() > maxSourceLength) | ||||
| 		return _source.substr(0, min(_source.size(), maxSourceLength)) + "..."; | ||||
| 	return _source; | ||||
| } | ||||
							
								
								
									
										82
									
								
								tools/solidityUpgrade/UpgradeChange.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								tools/solidityUpgrade/UpgradeChange.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | ||||
| /*
 | ||||
| 	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/>.
 | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <libsolutil/AnsiColorized.h> | ||||
| 
 | ||||
| #include <liblangutil/SourceLocation.h> | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| namespace solidity::tools | ||||
| { | ||||
| 
 | ||||
| /**
 | ||||
|  * Models a single source code change, based on the initial source location | ||||
|  * and a patch, which needs to be applied. | ||||
|  * It implements the concept of level of confidence in the change and distiguishes | ||||
|  * safe from unsafe changes. A "safe" change is considered to not break | ||||
|  * compilation or change semantics. An "unsafe" change is considered to potentially | ||||
|  * change semantics or require further manual management. | ||||
|  */ | ||||
| class UpgradeChange | ||||
| { | ||||
| public: | ||||
| 	enum class Level | ||||
| 	{ | ||||
| 		Safe, | ||||
| 		Unsafe | ||||
| 	}; | ||||
| 
 | ||||
| 	UpgradeChange( | ||||
| 		Level _level, | ||||
| 		langutil::SourceLocation _location, | ||||
| 		std::string _patch | ||||
| 	) | ||||
| 	: | ||||
| 		m_location(_location), | ||||
| 		m_source(_location.source->source()), | ||||
| 		m_patch(_patch), | ||||
| 		m_level(_level) {} | ||||
| 
 | ||||
| 	~UpgradeChange() {} | ||||
| 
 | ||||
| 	langutil::SourceLocation const& location() { return m_location; } | ||||
| 	std::string source() const { return m_source; } | ||||
| 	std::string patch() { return m_patch; } | ||||
| 	Level level() const { return m_level; } | ||||
| 
 | ||||
| 	/// Does the actual replacement of code under at current source location.
 | ||||
| 	/// The change is applied on the upgrade-specific copy of source code.
 | ||||
| 	/// The altered code is then requested by the upgrade routine later on.
 | ||||
| 	void apply(); | ||||
| 	/// Does a pretty-print of this upgrade change. It uses a source formatter
 | ||||
| 	/// provided by the compiler in order to print affected code. Since the patch
 | ||||
| 	/// can contain a lot of code lines, it can be shortened, which is signaled
 | ||||
| 	/// by setting the flag.
 | ||||
| 	void log(bool const _shorten = true) const; | ||||
| private: | ||||
| 	langutil::SourceLocation m_location; | ||||
| 	std::string m_source; | ||||
| 	std::string m_patch; | ||||
| 	Level m_level; | ||||
| 
 | ||||
| 	/// Shortens the given source to a constant limit.
 | ||||
| 	static std::string shortenSource(std::string const& _source); | ||||
| }; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										90
									
								
								tools/solidityUpgrade/UpgradeSuite.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								tools/solidityUpgrade/UpgradeSuite.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,90 @@ | ||||
| /*
 | ||||
| 	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/>.
 | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <tools/solidityUpgrade/UpgradeChange.h> | ||||
| 
 | ||||
| #include <liblangutil/ErrorReporter.h> | ||||
| 
 | ||||
| #include <libsolidity/ast/ASTVisitor.h> | ||||
| #include <libsolidity/analysis/OverrideChecker.h> | ||||
| 
 | ||||
| #include <regex> | ||||
| 
 | ||||
| namespace solidity::tools | ||||
| { | ||||
| 
 | ||||
| /**
 | ||||
|  * The base upgrade module that can be inherited from. Doing so | ||||
|  * creates a basic upgrade module that facilitates access to | ||||
|  * change reporting. | ||||
|  */ | ||||
| class Upgrade | ||||
| { | ||||
| public: | ||||
| 	Upgrade(std::vector<UpgradeChange>& _changes): m_changes(_changes) {} | ||||
| 
 | ||||
| protected: | ||||
| 	/// A reference to a suite-specific set of changes.
 | ||||
| 	/// It is passed to all upgrade modules and meant to collect
 | ||||
| 	/// reported changes.
 | ||||
| 	std::vector<UpgradeChange>& m_changes; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * A specific upgrade module meant to be run after the analysis phase | ||||
|  * of the compiler. | ||||
|  */ | ||||
| class AnalysisUpgrade: public Upgrade, public frontend::ASTConstVisitor | ||||
| { | ||||
| public: | ||||
| 	AnalysisUpgrade(std::vector<UpgradeChange>& _changes): | ||||
| 		Upgrade(_changes), | ||||
| 		m_errorReporter(m_errors), | ||||
| 		m_overrideChecker(m_errorReporter) | ||||
| 	{} | ||||
| 	/// Interface function for all upgrade modules that are meant
 | ||||
| 	/// be run after the analysis phase of the compiler.
 | ||||
| 	void analyze(frontend::SourceUnit const&) {} | ||||
| protected: | ||||
| 	langutil::ErrorList m_errors; | ||||
| 	langutil::ErrorReporter m_errorReporter; | ||||
| 	frontend::OverrideChecker m_overrideChecker; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * The generic upgrade suite. Should be inherited from for each set of | ||||
|  * desired upgrade modules. | ||||
|  */ | ||||
| class UpgradeSuite | ||||
| { | ||||
| public: | ||||
| 	/// The base interface function that needs to be implemented for each
 | ||||
| 	/// suite. It should create suite-specific upgrade modules and trigger
 | ||||
| 	/// their analysis.
 | ||||
| 	void analyze(frontend::SourceUnit const& _sourceUnit); | ||||
| 	/// Resets all changes collected so far.
 | ||||
| 	void reset() { m_changes.clear(); } | ||||
| 
 | ||||
| 	std::vector<UpgradeChange>& changes() { return m_changes; } | ||||
| 	std::vector<UpgradeChange> const& changes() const { return m_changes; } | ||||
| 
 | ||||
| protected: | ||||
| 	std::vector<UpgradeChange> m_changes; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										23
									
								
								tools/solidityUpgrade/contracts/DocsExamplePass.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								tools/solidityUpgrade/contracts/DocsExamplePass.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| pragma solidity >0.4.23; | ||||
| 
 | ||||
| contract Updateable { | ||||
|     function run() public view returns (bool); | ||||
|     function update() public; | ||||
| } | ||||
| 
 | ||||
| contract Upgradable { | ||||
|     function run() public view returns (bool); | ||||
|     function upgrade(); | ||||
| } | ||||
| 
 | ||||
| contract Source is Updateable, Upgradable { | ||||
|     function Source() public {} | ||||
| 
 | ||||
|     function run() | ||||
|         public | ||||
|         view | ||||
|         returns (bool) {} | ||||
| 
 | ||||
|     function update() {} | ||||
|     function upgrade() {} | ||||
| } | ||||
							
								
								
									
										18
									
								
								tools/solidityUpgrade/contracts/Test.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								tools/solidityUpgrade/contracts/Test.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| pragma solidity >0.4.23; | ||||
| 
 | ||||
| contract Storage { | ||||
|     function Storage() public {} | ||||
|     function start(); | ||||
|     function state() public view returns (bool); | ||||
|     function stop() public; | ||||
| } | ||||
| 
 | ||||
| contract Observable { | ||||
|     function state() public view returns (bool); | ||||
| } | ||||
| 
 | ||||
| contract VolatileStorage is Storage, Observable { | ||||
|     function start() {} | ||||
|     function state() public view returns (bool) {} | ||||
|     function stop() {} | ||||
| } | ||||
							
								
								
									
										21
									
								
								tools/solidityUpgrade/contracts/TestMultiline.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tools/solidityUpgrade/contracts/TestMultiline.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| pragma solidity >0.4.23; | ||||
| 
 | ||||
| contract Storage { | ||||
|     function Storage() {} | ||||
|     function init() public; | ||||
|     function idle(); | ||||
|     function destroy() public view; | ||||
| } | ||||
| 
 | ||||
| contract VolatileStorage is Storage { | ||||
|     function init() | ||||
|         public | ||||
|     {} | ||||
| 
 | ||||
|     function idle() {} | ||||
| 
 | ||||
|     function destroy() | ||||
|         public | ||||
|         view | ||||
|     {} | ||||
| } | ||||
							
								
								
									
										15
									
								
								tools/solidityUpgrade/contracts/TestNonFixable.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								tools/solidityUpgrade/contracts/TestNonFixable.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| pragma solidity >0.4.23; | ||||
| 
 | ||||
| contract Storage { | ||||
|     function Storage() {} | ||||
|     function init() public; | ||||
|     function idle(); | ||||
|     function destroy() public view; | ||||
| } | ||||
| 
 | ||||
| contract VolatileStorage is Storage { | ||||
|     uint[] array; | ||||
|     function init() public { array.length = 3; } | ||||
|     function idle() {} | ||||
|     function destroy() public view {} | ||||
| } | ||||
							
								
								
									
										81
									
								
								tools/solidityUpgrade/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								tools/solidityUpgrade/main.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | ||||
| /*
 | ||||
| 	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/>.
 | ||||
| */ | ||||
| #include <tools/solidityUpgrade/SourceUpgrade.h> | ||||
| 
 | ||||
| #include <libsolutil/CommonIO.h> | ||||
| #include <libsolutil/AnsiColorized.h> | ||||
| 
 | ||||
| //#include <test/Common.h>
 | ||||
| 
 | ||||
| #include <boost/algorithm/string.hpp> | ||||
| #include <boost/algorithm/string/replace.hpp> | ||||
| #include <boost/filesystem.hpp> | ||||
| #include <boost/program_options.hpp> | ||||
| 
 | ||||
| #include <cstdlib> | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
| #include <queue> | ||||
| #include <regex> | ||||
| 
 | ||||
| #if defined(_WIN32) | ||||
| #include <windows.h> | ||||
| #endif | ||||
| 
 | ||||
| using namespace solidity; | ||||
| using namespace std; | ||||
| namespace po = boost::program_options; | ||||
| namespace fs = boost::filesystem; | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
| 
 | ||||
| void setupTerminal() | ||||
| { | ||||
| #if defined(_WIN32) && defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) | ||||
| 	// Set output mode to handle virtual terminal (ANSI escape sequences)
 | ||||
| 	// ignore any error, as this is just a "nice-to-have"
 | ||||
| 	// only windows needs to be taken care of, as other platforms (Linux/OSX) support them natively.
 | ||||
| 	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
| 	if (hOut == INVALID_HANDLE_VALUE) | ||||
| 		return; | ||||
| 
 | ||||
| 	DWORD dwMode = 0; | ||||
| 	if (!GetConsoleMode(hOut, &dwMode)) | ||||
| 		return; | ||||
| 
 | ||||
| 	dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; | ||||
| 	if (!SetConsoleMode(hOut, dwMode)) | ||||
| 		return; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char** argv) | ||||
| { | ||||
| 	setupTerminal(); | ||||
| 
 | ||||
| 	tools::SourceUpgrade upgrade; | ||||
| 	if (!upgrade.parseArguments(argc, argv)) | ||||
| 		return 1; | ||||
| 	upgrade.printPrologue(); | ||||
| 	if (!upgrade.processInput()) | ||||
| 		return 1; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user